I have the following Qt code that loads a local html file and prints it using the installed PDFCreator. When printing to PDFCreator the text is cut off at the right and only printed on the first page. Subsequent pages are without text just filled with the background color.
When using QPrinter::PdfFormat as output Format and setting a file path a PDF file is successfully generated and the content is showing correctly. I'm using Qt 5.4.
Do I need to set special parameters for the printer or is this still a Qt bug (there was one reported for Qt 5.0).
#include <QApplication>
#include <QWebPage>
#include <QWebFrame>
#include <QTimer>
#include <QFileInfo>
#include <QPrinter>
class Task : public QObject {
Q_OBJECT
public:
Task(QObject *parent = 0) : QObject(parent) {}
public slots:
void run()
{
QFileInfo fileInfo("test.html");
QUrl url = QUrl::fromLocalFile(fileInfo.absoluteFilePath());
connect(&page, SIGNAL(loadFinished(bool)), this, SLOT(print()));
page.mainFrame()->load(url);
}
void print()
{
QPrinter printer;
printer.setPrinterName("PDFCreator");
printer.setPaperSize(QPrinter::A4);
#ifdef PRINT_TO_PDF
printer.setOutputFormat(QPrinter::PdfFormat);
printer.setOutputFileName("output.pdf");
#endif
page.mainFrame()->print(&printer);
emit finished();
}
signals:
void finished();
private:
QWebPage page;
};
#include "main.moc"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// Task parented to the application so that it
// will be deleted by the application.
Task *task = new Task(&app);
// This will cause the application to exit when
// the task signals finished.
QObject::connect(task, SIGNAL(finished()), &app, SLOT(quit()));
// This will run the task from the application event loop.
QTimer::singleShot(0, task, SLOT(run()));
return app.exec();
}
Related
I'm developing C++ application in QT with GUI. To make the GUI always respond, I create a thread for other blocking process. However, the application is waiting for the blocking process and therefore the GUI didn't respond.
Is creating a thread for blocking process is a wrong way?
Or it doesn't work in QT? If so, how to make the GUI respond? Please give me example.
This is a simple example of multithreaded application with responsive GUI:
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
#include <QLineEdit>
#include <QThread>
class Worker : public QThread
{
protected:
/// Wait 3s which simulates time demanding job.
void run() { sleep(3); }
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
public slots:
void doJob(bool);
void jobFinished();
private:
Worker worker;
QLineEdit *line;
QPushButton *button;
int counter;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include <QVBoxLayout>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent),
line(new QLineEdit()),
button(new QPushButton()),
counter(0)
{
line->setDisabled(true);
line->setAlignment(Qt::AlignRight);
line->setText(QString::number(counter));
button->setText("Push");
connect(button, SIGNAL(clicked(bool)), this, SLOT(doJob(bool)));
connect(&worker, SIGNAL(finished()), this, SLOT(jobFinished()));
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(line);
layout->addWidget(button);
QWidget *window = new QWidget();
window->setLayout(layout);
setCentralWidget(window);
}
void MainWindow::doJob(bool)
{
// Only one thread is running at a time.
// If you want a thread pool, the implementation is up to you :)
worker.start();
// Uncomment to wait. If waiting, GUI is not responsive.
//worker.wait();
}
void MainWindow::jobFinished()
{
++counter;
line->setText(QString::number(counter));
}
Qt has a very good multithreading support. You probably do something wrong and we can't help you if you don't provide any code. There are a lot of ways of implementing "responsive" GUI! (Including a lot of ways how to implement another thread!)
I am having difficulty in my Qt program with connecting button signals to my slots. My code is:
Main.cpp
#include <QtGui/QApplication>
#include "MainWidget.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWidget mainWidget;
mainWidget.show();
return app.exec();
}
MainWidget.h
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include <QWidget>
class MainWidget : public QWidget
{
public:
MainWidget();
public slots:
void bAdvice_clicked();
void bWeather_clicked();
void bNextMeeting_clicked();
void bQuit_clicked();
};
#endif // MAINWIDGET_H
MainWidget.cpp
#include "MainWidget.h"
#include <QMessageBox>
#include <QPushButton>
#include <QTextEdit>
#include <QVBoxLayout>
MainWidget::MainWidget()
{
QLayout *layout = new QVBoxLayout();
this->setLayout(layout);
QTextEdit *message = new QTextEdit();
layout->addWidget(message);
QPushButton *bAdvice = new QPushButton("Advice");
connect(bAdvice, SIGNAL(clicked()), this, SLOT(bAdvice_clicked()));
layout->addWidget(bAdvice);
QPushButton *bWeather = new QPushButton("Weather");
connect(bWeather, SIGNAL(clicked()), this, SLOT(bWeather_clicked()));
layout->addWidget(bWeather);
QPushButton *bNextMeeting = new QPushButton("Next Meeting");
connect(bNextMeeting, SIGNAL(clicked()), this, SLOT(bNextMeeting_clicked()));
layout->addWidget(bNextMeeting);
QPushButton *bQuit = new QPushButton("Quit");
connect(bQuit, SIGNAL(clicked()), this, SLOT(bQuit_clicked()));
layout->addWidget(bQuit);
}
void MainWidget::bAdvice_clicked()
{
}
void MainWidget::bWeather_clicked()
{
}
void MainWidget::bNextMeeting_clicked()
{
QMessageBox::information(this, "Next Meeting", "Today", QMessageBox::Ok);
}
void MainWidget::bQuit_clicked()
{
this->close();
}
The program outputs the following:
Starting C:\Users\Sameer\Documents\PartAQuestion2\debug\PartAQuestion2.exe...
Object::connect: No such slot QWidget::bAdvice_clicked() in MainWidget.cpp:16
Object::connect: No such slot QWidget::bWeather_clicked() in MainWidget.cpp:20
Object::connect: No such slot QWidget::bNextMeeting_clicked() in MainWidget.cpp:24
Object::connect: No such slot QWidget::bQuit_clicked() in MainWidget.cpp:28
C:\Users\Sameer\Documents\PartAQuestion2\debug\PartAQuestion2.exe exited with code 0
The code seems right, no compiler warnings. Just this output at runtime. But it looks like I hooked the signals and slots up correctly.
Add Q_OBJECT to your class, like this:
class MainWidget : public QWidget
{
Q_OBJECT
You also have to run moc to generate some helper code. qmake does that automatically for your, but if you compile this yourself, you need to run moc.
When I started with Qt, I had this problem a lot. As I see it your slots are defined wrong. If you look at the signature for the signal (Qt Clicked Signal Docs), you will see that the argument list is (bool clicked = false).
The way Qt's signal & slots connect work at run time, is that it will only connect the signal and slot if they have the exact same signatures. If they don't match exactly, no connection.
so in MainWidget.h
public slots:
void bAdvice_clicked(bool);
In MainWidget.cpp
connect(bAdvice, SIGNAL(clicked(bool)), this, SLOT(bAdvice_clicked(bool)));
Things will start working for you.
Edited:
Compiled your code and all the slots were correctly called.
It was just the Q_OBJECT macro that was missing.
i have flash player running on QWebkit , and on the flash player there are some web links
that needs to be open in external browser , what i did is :
m_webView->page()->setLinkDelegationPolicy(QWebPage::LinkDelegationPolicy::DelegateAllLinks);
connect(m_webView->page(),SIGNAL(linkClicked(const QUrl&)),
this,
SLOT(linkClickedHandler(const QUrl&)),Qt::DirectConnection);
void WebBroswerDeleget::linkClickedHandler(const QUrl& url)
{
QDesktopServices::openUrl(QUrl(url.toString(), QUrl::TolerantMode));
}
but its never triggered even of i change in the connect from m_webView->page() to m_webView
i overrided the QWebview::createWindow like this:
QWebView* MyAdWebview::createWindow (QWebPage::WebWindowType type)
{
QWebView* p = new QWebView(0);
connect(p->page()->networkAccessManager(), SIGNAL(finished(QNetworkReply*)), this, SLOT(newWindowLoadFinished(QNetworkReply*)), Qt::UniqueConnection);
return p;
}
void MyAdWebview::newWindowLoadFinished(QNetworkReply* reply) {
QDesktopServices::openUrl(reply->url().toString());
}
QDesktopServices::openUrl is a cutom function which opens the default system browser with this url
This is working for me on both 4.7.2 QtEmbedded and 4.8.1 on mac. What I don't understand is this:
m_webView->page()->setLinkDelegationPolicy(QWebPage::LinkDelegationPolicy::DelegateAllLinks);
Just do:
m_webView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
Something like this works for me:
#include <QWebPage>
#include <QWebView>
#include <QApplication>
#include "sigrec.h"
int main(int argc, char** argv)
{
QApplication a(argc, argv);
SigRec rec;
QWebView view;
view.page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
QObject::connect(view.page(), SIGNAL(linkClicked(const QUrl&)), &rec, SLOT(onLinkClicked(const QUrl&)),
Qt::DirectConnection);
view.show();
view.setUrl(QUrl("http://www.google.com"));
return a.exec();
}
Where SigRec is something like this:
#ifndef SIGREC_H
#define SIGREC_H
#include <QObject>
#include <QUrl>
class SigRec : public QObject
{
Q_OBJECT
public:
explicit SigRec(QObject *parent = 0);
public slots:
void onLinkClicked(const QUrl &url);
};
#endif // SIGREC_H
I am trying to program an App that fetches files from a server.
I have a 'Window' class(mainwindow.cpp, which is a widget class that would be the UI) and then I have a 'Backend' class(Backend.cpp).
The GUI has a push button and two radio buttons. If the radio button "remote" is seleted, then upon clicking the push button will lead to fetching files from server.
However, there is some problem in the 'connect' call in Backend.cpp which I can't figure out. The error I get is: no matching function call to 'QObject::connect(QNetworkReply*&), const char[13], Backend* const, const char[20])'
Here are the codes:
ANSWER: Avoid circular inclusions!!!!
Here are the updated codes:
mainwindow.h
#ifndef WINDOW_H
#define WINDOW_H
#include <QWidget>
#include <QRadioButton>
#include <QtNetwork/QTcpSocket>
#include <QtNetwork/QHostAddress>
#include <QFile>
#include <QUrl>
#include "Backend.h"
class QGroupBox;
class Window : public QWidget
{
Q_OBJECT
public:
Window(QWidget *parent = 0);
QTcpSocket *conn;
QFile *file;
QUrl url;
Backend backend_inst;
private:
QRadioButton *button_local;
QRadioButton *button_remote;
QGroupBox *createPushButtonGroup();
private slots:
void onClick_button1();
void onCheck_local();
void onCheck_remote();
};
#endif
mainwindow.c
#include <QtGui>
#include "mainwindow.h"
Window::Window(QWidget *parent)
: QWidget(parent)
{
QGridLayout *grid = new QGridLayout;
grid->addWidget(createPushButtonGroup(), 1, 1);
setLayout(grid);
setWindowTitle(tr("File-Fetch App"));
resize(480, 420);
}
QGroupBox *Window::createPushButtonGroup()
{
QGroupBox *groupBox = new QGroupBox();
QPushButton *pushButton1 = new QPushButton(tr("Fetch Files!!"));
button_local = new QRadioButton(tr("&Download Files from Local Storage"));
button_remote = new QRadioButton(tr("&Download Files from a Web-Server"));
button_local->setChecked(1);
connect(pushButton1,SIGNAL(clicked()), this, SLOT(onClick_button1()));
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(pushButton1);
vbox->addSpacing(50);
vbox->addWidget(button_local);
vbox->addWidget(button_remote);
vbox->addStretch(1);
groupBox->setLayout(vbox);
return groupBox;
}
void Window::onClick_button1()
{
QTextStream out(stdout);
out << "fetch button clicked\n";
if (button_local->isChecked()){
onCheck_local();
}
else if (button_remote->isChecked()){
onCheck_remote();
}
}
void Window::onCheck_local()
{
QTextStream out(stdout);
out << "local update button checked\n";
}
void Window::onCheck_remote()
{
QTextStream out(stdout);
out << "remote update button checked\n";
QString pathname= "http://192.168.1.1:8000/example.txt";
QUrl webaddr = pathname;
backend_inst.FetchFile(webaddr);
}
Backend.h
#ifndef BACKEND_H
#define BACKEND_H
#include <QObject>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QUrl>
#include <QTextStream>
class Backend : public QObject
{
Q_OBJECT
public:
Backend(QObject* parent=0);
void FetchFile(QUrl fpath);
public slots:
void getBytesFromFile();
private:
QNetworkReply *reply;
QNetworkAccessManager qnam;
};
#endif // BACKEND_H
Backend.cpp
#include "Backend.h"
Backend::Backend(QObject* parent)
: QObject(parent)
{
}
void Backend::FetchFile(QUrl fpath)
{
reply = qnam.get(QNetworkRequest(fpath));
QObject::connect(reply, SIGNAL(readyRead()),this, SLOT(getBytesFromFile()));
//qnam = new QNetworkAccessManager;
//QObject::connect(&qnam, SIGNAL(finished(QNetworkReply*)), this, SLOT(getBytesFromFile()));
}
void Backend::getBytesFromFile(){
QByteArray downloadedData;
QTextStream out(stdout);
out << "we are loading data from URL\n";
downloadedData =reply->readAll();
out << downloadedData;
delete reply;
}
main.cpp
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Window window;
window.show();
return app.exec();
}
To use signals and slots, your classes (both signaling and slotting) must derive from QObject, i.e.
#include "mainwindow.h"
#include <QObject>
class Backend : public QObject
{
Q_OBJECT
public:
Backend(QObject* parent=0);
[...]
Backend::Backend(QObject* parent)
: QObject(parent)
{
}
You posted this:
class Backend
{
// Q_OBJECT
public:
Backend();
void FetchFile(QUrl fpath);
public slots:
void getBytesFromFile();
private:
QNetworkReply *reply;
QNetworkAccessManager qnam;
};
Q_OBJECT is still commented if yes remove it.. you are using signal and slots..
EDIT :
try to avoid circular inclusion:
you included Backend in mainwindow and viceversa..
The commented out lines:
qnam = new QNetworkAccessManager;
QObject::connect(&qnam, SIGNAL(finished(QNetworkReply*)), this, SLOT(getBytesFromFile()));
Were what's causing your issue. Connect excepts a pointer, not a pointer-to-pointer. qnam is a pointer was a pointer in the previous version of the code and using the address-of operator on it would turn it into a pointer-to-pointer. Second mistake is that you need to have the same signature for your signal and slot in order to get it called (otherwise you get a runtime error). So, correctly:
connect(qnam, SIGNAL(finished(QNetworkReply*)), this, SLOT(getBytesFromFile(QNetworkReply*)));
(and obviously, change the actual signature of the getBytesFromFile method).
As to why your error persists despite commenting the code out: I think you are running an old build and the new one is failing to build due to the vtable issue (as you described in the comment thread). My guess is that qmake is glitching out, which I have experienced when adding the Q_OBJECT macro to already existing classes. Go to your build folders and delete Makefile* everywhere. Then go back to Qt creator and rebuild the project, that should force qmake to generate the Makefiles again.
It seems you have some problems with build mechanism. With posted code and un-commented
QObject::connect(&qnam, SIGNAL(finished(QNetworkReply*)), this, SLOT(getBytesFromFile())); in Backend::FetchFile function, all your code works. Running and with checked "Download Files from Web-Server" it prints the "we are loading data from URL" from getBytesFromFile - isn't this the slot you want to be called ?
I am having difficulty in my Qt program with connecting button signals to my slots. My code is:
Main.cpp
#include <QtGui/QApplication>
#include "MainWidget.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWidget mainWidget;
mainWidget.show();
return app.exec();
}
MainWidget.h
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include <QWidget>
class MainWidget : public QWidget
{
public:
MainWidget();
public slots:
void bAdvice_clicked();
void bWeather_clicked();
void bNextMeeting_clicked();
void bQuit_clicked();
};
#endif // MAINWIDGET_H
MainWidget.cpp
#include "MainWidget.h"
#include <QMessageBox>
#include <QPushButton>
#include <QTextEdit>
#include <QVBoxLayout>
MainWidget::MainWidget()
{
QLayout *layout = new QVBoxLayout();
this->setLayout(layout);
QTextEdit *message = new QTextEdit();
layout->addWidget(message);
QPushButton *bAdvice = new QPushButton("Advice");
connect(bAdvice, SIGNAL(clicked()), this, SLOT(bAdvice_clicked()));
layout->addWidget(bAdvice);
QPushButton *bWeather = new QPushButton("Weather");
connect(bWeather, SIGNAL(clicked()), this, SLOT(bWeather_clicked()));
layout->addWidget(bWeather);
QPushButton *bNextMeeting = new QPushButton("Next Meeting");
connect(bNextMeeting, SIGNAL(clicked()), this, SLOT(bNextMeeting_clicked()));
layout->addWidget(bNextMeeting);
QPushButton *bQuit = new QPushButton("Quit");
connect(bQuit, SIGNAL(clicked()), this, SLOT(bQuit_clicked()));
layout->addWidget(bQuit);
}
void MainWidget::bAdvice_clicked()
{
}
void MainWidget::bWeather_clicked()
{
}
void MainWidget::bNextMeeting_clicked()
{
QMessageBox::information(this, "Next Meeting", "Today", QMessageBox::Ok);
}
void MainWidget::bQuit_clicked()
{
this->close();
}
The program outputs the following:
Starting C:\Users\Sameer\Documents\PartAQuestion2\debug\PartAQuestion2.exe...
Object::connect: No such slot QWidget::bAdvice_clicked() in MainWidget.cpp:16
Object::connect: No such slot QWidget::bWeather_clicked() in MainWidget.cpp:20
Object::connect: No such slot QWidget::bNextMeeting_clicked() in MainWidget.cpp:24
Object::connect: No such slot QWidget::bQuit_clicked() in MainWidget.cpp:28
C:\Users\Sameer\Documents\PartAQuestion2\debug\PartAQuestion2.exe exited with code 0
The code seems right, no compiler warnings. Just this output at runtime. But it looks like I hooked the signals and slots up correctly.
Add Q_OBJECT to your class, like this:
class MainWidget : public QWidget
{
Q_OBJECT
You also have to run moc to generate some helper code. qmake does that automatically for your, but if you compile this yourself, you need to run moc.
When I started with Qt, I had this problem a lot. As I see it your slots are defined wrong. If you look at the signature for the signal (Qt Clicked Signal Docs), you will see that the argument list is (bool clicked = false).
The way Qt's signal & slots connect work at run time, is that it will only connect the signal and slot if they have the exact same signatures. If they don't match exactly, no connection.
so in MainWidget.h
public slots:
void bAdvice_clicked(bool);
In MainWidget.cpp
connect(bAdvice, SIGNAL(clicked(bool)), this, SLOT(bAdvice_clicked(bool)));
Things will start working for you.
Edited:
Compiled your code and all the slots were correctly called.
It was just the Q_OBJECT macro that was missing.