I want to exec() a QDialog without GUI for my test - there's no need to show GUI.
In the main window, when I exec() a QDialog, it shows its GUI window. I connect a signal from thread in QDialog to a slot in QDialog, so QDialog.exec() is needed. QDialog.hide() is not working.
Is there any solution?
You should never exec() a dialog. This can lead to subtle bugs, because the exec() call can reenter code you don't plan on being reentered.
If you really insist on using exec(), then the simple solution is to post a CloseEvent to the dialog. The event will be processed once the dialog's event loop starts running.
MyDialog dialog;
QCoreApplication::postEvent(&dialog, new QCloseEvent());
dialog.exec();
This code is functionally equivalent to the following:
MyDialog dialog;
QMetaObject::invokeMethod(&dialog, "close", Qt::QueuedConnection);
dialog.exec();
An alternative, much safer way to accomplish this is not to use exec. Simply connect the dialog's accepted() and rejected() signals to slots in your code - after all, the dialog's acceptance or rejection happens asynchronously.
It's then easy enough simply not to show() the dialog in the test code path. You can also easily simulate the dialog being accepted or rejected by invoking either accept or reject slots.
I really don't understand exactly how you "connected" signals from a thread to dialog's slots, and so on. Feel free to paste the example below into your question, modify it to do the threaded "stuff", and show how it doesn't work. It'd also help to see how you implement the test harness.
The example below is all a single file, it'd help if you would keep it that way when producing a test case that illustrates your problem.
// main.cpp
#include <QApplication>
#include <QDialog>
#include <QLabel>
#include <QGridLayout>
#include <QPushButton>
#include <QDialogButtonBox>
class MyDialog : public QDialog {
QGridLayout m_layout;
QDialogButtonBox m_box;
public:
MyDialog(QWidget * parent = 0) : QDialog(parent), m_layout(this),
m_box(QDialogButtonBox::Ok | QDialogButtonBox::Cancel)
{
m_layout.addWidget(&m_box);
connect(&m_box, SIGNAL(accepted()), SLOT(accept()));
connect(&m_box, SIGNAL(rejected()), SLOT(reject()));
}
};
class MyGui : public QWidget {
Q_OBJECT
QGridLayout m_layout;
QLabel m_label;
QPushButton m_button;
MyDialog m_dialog;
Q_SLOT void on_button_clicked() {
m_dialog.show();
}
Q_SLOT void on_dialog_accepted() {
m_label.setText("The dialog was accepted");
}
Q_SLOT void on_dialog_rejected() {
m_label.setText("The dialog was rejected");
}
public:
MyGui() : m_layout(this), m_button("Show Dialog"), m_dialog(this) {
m_button.setObjectName("button");
m_dialog.setObjectName("dialog");
m_layout.addWidget(&m_label);
m_layout.addWidget(&m_button);
QMetaObject::connectSlotsByName(this);
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MyGui gui;
gui.show();
return app.exec();
}
#include "main.moc"
Related
I'm trying to understand what is and isn't allowed when it comes to QWidget and Qt concurrency. I've created a Widget which has a slow_function and I'm considering three cases:
Run the slow_function on the GUI thread. This results in the expected behaviour; the GUI becomes unresponsive while waiting for the function to return.
Use QtConcurrent::run(this, &Widget::slow_function). I was surprised to see that this didn't block the GUI. I've confirmed that the thread affinity of my instance is still the GUI thread, nevertheless, the function seems to be executing on a separate thread. Is such an approach allowed and is this the expected behaviour (documentation link would be really helpful)? Is such an approach safe if I can guarantee that slow_function is thread-safe?
Create a subclass of QThread which holds a pointer to my widget. Override the run method to call slow_function. The behaviour is the same as Case 2. This is also surprising as the thread affinity is still the GUI thread (besides, we are not even allowed to use moveToThread on a QWidget). Why is this running on a separate thread? Is moveToThread meant to be useful only when we are interested in calling slots via signals sent from another thread?
Thank you for reading. Here is the relevant code starting with my the header file:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QDebug>
#include <QPushButton>
#include <QLayout>
#include <windows.h>
#include <QtConcurrent/QtConcurrent>
#include <QThread>
#include <QApplication>
class Widget;
class Thread: public QThread
{
public:
Thread(Widget* widget)
: m_widget(widget){}
protected:
void run() override;
private:
Widget* m_widget;
};
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget* parent = nullptr)
: QWidget(parent)
, m_thread(this){
auto layout = new QVBoxLayout(this);
auto button = new QPushButton("Case 1: Run on gui thread");
auto button2 = new QPushButton("Case 2: Run with qtconcurrent");
auto button3 = new QPushButton("Case 3: Run with qthread");
connect(button, &QPushButton::clicked, this, &Widget::slow_function);
connect(button2, &QPushButton::clicked, this, &Widget::use_concurrent);
connect(button3, &QPushButton::clicked, this, &Widget::use_qthread);
layout->addWidget(button);
layout->addWidget(button2);
layout->addWidget(button3);
}
~Widget()
{
m_thread.quit();
m_thread.wait();
}
public slots:
void slow_function()
{
qDebug() << "Starting";
auto gui_thread = QApplication::instance()->thread();
auto this_thread = thread();
qDebug() << "Thread affinity is" << (gui_thread == this_thread ? "gui_thread" : "non_gui_thread");
Sleep(5000);
qDebug() << "Finished";
}
void use_concurrent()
{
QtConcurrent::run(this, &Widget::slow_function);
}
void use_qthread()
{
m_thread.start();
}
private:
Thread m_thread;
};
#endif // WIDGET_H
and the main.cpp file:
#include "widget.h"
#include <QApplication>
void Thread::run()
{
m_widget->slow_function();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
You should not do any UI things in nonmain threads. The UI means
widget interaction
model
Run the slow_function on the GUI thread
That's not allowed, nothing should block the GUI thread.
Is such an approach safe if I can guarantee that slow_function is thread-safe?
It's okay to run slow function in a different thread. But...
what happens when the user closes the application, but the function still executes?
I have done this, but I prefer to encapsulate it in a separate class.
Create a subclass of QThread which holds a pointer to my widget.
You should only subclass QThread if the subclass is a thread. Like, subclassing an animal class will give you an animal, not a chair with four legs.
Is moveToThread meant to be useful only when we are interested in calling slots via signals sent from another thread?
moveToThread changes the affinity of the object. So, slots are always executed in the correct thread, when you call invokeMethod, the method is executed in the correct thread. When an event is delivered, the event handler is always called in the appropriate thread.
The whole purpose of QtConcurrent::run() is to make running heavy workloads in a thread easier. If your use-case allows for it, great!
Pair it with a QFutureWatcher to retrieve the result after your slow_function is finished.
Using QThread is another option, but it makes more sense when you have a long-lived object. Instead of subclassing, I find it easier to use the worker model: create a worker class with signals/slots, call moveToThread on it with a vanilla QThread object, connect/subscribe, start the thread
QWidget is a way to create GUI functionality in Qt. While it may be possible to use such objects in non-gui threads, it's best you separate your compute workloads from GUI (don't put your slow_function into widget classes).
I am a newbie in Qt-programming. I have read a book about GUI-programming with Qt. I have an trouble in creating a dialog. Here is sample code:
// gotocell.h
#ifndef GOTOCELL_H
#define GOTOCELL_H
#include <QDialog>
#include <QtWidgets>
#include "ui_gotocell.h"
class GoToCellDialog : public QDialog, public Ui::GoToCellDialog
{
Q_OBJECT
public:
GoToCellDialog (QWidget *parent = 0);
private slots:
void on_lineEdit_textChanged();
};
#endif // GOTOCELL_H
// gotocell.cpp
#include <QtWidgets>
#include "gotocell.h"
#include <QtWidgets>
GoToCellDialog::GoToCellDialog (QWidget *parent):
QDialog (parent)
{
setupUi(this);
QRegExp regExp ("[A-Za-z][1-9][0-9]{0,2}");
lineEdit->setValidator(new QRegExpValidator(regExp, this));
connect (okButton, SIGNAL(clicked()), this, SLOT(accept()));
connect (cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
}
void GoToCellDialog::on_lineEdit_textChanged()
{
okButton->setEnabled(lineEdit->hasAcceptableInput());
}
// main.cpp
#include "gotocell.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GoToCellDialog *dialog = new GoToCellDialog;
dialog->show();
return a.exec();
}
but when I compiled, there is an error: no known conversion for argument 1 from 'GoToCellDialog* const' to 'QMainWindow*'at setupUi() function. I think because the designer in Qt Creator created a QMainWindow, not a QDialog. So I changed GoToCellDialog class to QMainWindow. But there is no slots whose name is "accepted", "rejected" in QMainWindow. Can anyone help me?
If you want to display a Dialog as main window you have two choices:
1. make the whole main window QDialog based
2. design the Dialog separately and set it as the main windows central Widget (QMainWindow->setCentralWidget()).
In both cases you still have the problem what semantics you give to the OK and Cancel buttons. Generally it may be a better idea to consider what the main window of the application should contain, and design the dialogs later.
A experienced the following bug in Qt 4.8.5, under Ubuntu 13.04 (and I'm nem to Qt)
I have have an application with the following structure:
Mainwondow
-CentralWidget
--VerticalLayout
---TabWidget
---QLabel (created with code, and added to the layout)
---StatusBar
In fullscreen mode I hide the TabWidget, and the Statusbar, then the QLabel stops refreshing. (i have a thread to do the refresh) The strange thing is, when i restore the TabWidget or the StatusBar it works fine. It also works good, if i add a 1x1 pixel label to the VerticalLayout.
The slot responsible for the gui change;
void Mainview::onToggleFullScreen()
{
if (this->isFullScreen())
{
this->showNormal();
this->statusbar->show();
this->tabWidget->show();
}
else
{
this->showFullScreen();
this->statusbar->hide();
this->tabWidget->hide();
}
}
But the thing I cant understand if I put a QLabel near the image, it works, and if I add this single line to the MainWindow constructor, it stops refreshing:
label_10->hide(); //this is the label
Any idea what is the problem?
(Thanks in advance)
You're probably doing it in some wrong way, but you don't show the code, so how can we know?
Below is a safe SSCCE of how one might do it. Works under both Qt 4.8 and 5.1.
Nitpick: The status bar should not be a part of the centralWidget()! QMainWindow provides a statusBar() for you.
The only safe way of passing images between threads is via QImage. You can not use QPixmap anywhere but in the GUI thread. End of story right there.
In the example below, all of the important stuff happens behind the scenes. The DrawThing QObject lives in another thread. This QThread's default implementation of the run() method spins a message loop. That's why the timer can fire, you need a spinning message loop for that.
Every time the new image is generated, it is transmitted to the GUI thread by implicitly posting a message to MainWindow. The message is received by Qt event loop code and re-synthesized into a slot call. This is done since the two ends of a connection (DrawThing and MainWindow instances) live in different threads.
That the beauty of Qt's "code less, create more" approach to design :) The more you leverage what Qt does for you, the less you need to worry about the boilerplate.
//main.cpp
#include <QMainWindow>
#include <QVBoxLayout>
#include <QStatusBar>
#include <QLabel>
#include <QThread>
#include <QPainter>
#include <QImage>
#include <QApplication>
#include <QBasicTimer>
#include <QPushButton>
class DrawThing : public QObject {
Q_OBJECT
int m_ctr;
QBasicTimer t;
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() != t.timerId()) return;
QImage img(128, 128, QImage::Format_RGB32);
QPainter p(&img);
p.translate(img.size().width()/2, img.size().height()/2);
p.scale(img.size().width()/2, img.size().height()/2);
p.eraseRect(-1, -1, 2, 2);
p.setBrush(Qt::NoBrush);
p.setPen(QPen(Qt::black, 0.05));
p.drawEllipse(QPointF(), 0.9, 0.9);
p.rotate(m_ctr*360/12);
p.setPen(QPen(Qt::red, 0.1));
p.drawLine(0, 0, 0, 1);
m_ctr = (m_ctr + 1) % 12;
emit newImage(img);
}
public:
explicit DrawThing(QObject *parent = 0) : QObject(parent), m_ctr(0) { t.start(1000, this); }
Q_SIGNAL void newImage(const QImage &);
};
class MainWindow : public QMainWindow {
Q_OBJECT
QLabel *m_label;
public:
explicit MainWindow(QWidget *parent = 0, Qt::WindowFlags flags = 0) : QMainWindow(parent, flags) {
QWidget * cw = new QWidget;
QTabWidget * tw = new QTabWidget();
QVBoxLayout * l = new QVBoxLayout(cw);
l->addWidget(tw);
l->addWidget(m_label = new QLabel("Label"));
setCentralWidget(cw);
QPushButton * pb = new QPushButton("Toggle Status Bar");
tw->addTab(pb, "Tab 1");
connect(pb, SIGNAL(clicked()), SLOT(toggleStatusBar()));
statusBar()->showMessage("The Status Bar");
}
Q_SLOT void setImage(const QImage & img) {
m_label->setPixmap(QPixmap::fromImage(img));
}
Q_SLOT void toggleStatusBar() {
statusBar()->setHidden(!statusBar()->isHidden());
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QThread t;
DrawThing thing;
MainWindow w;
thing.moveToThread(&t);
t.start();
w.connect(&thing, SIGNAL(newImage(QImage)), SLOT(setImage(QImage)));
w.show();
t.connect(&a, SIGNAL(aboutToQuit()), SLOT(quit()));
int rc = a.exec();
t.wait();
return rc;
}
#include "main.moc"
I can't seem to figure out what went wrong so I'm here to ask you. I have made a simple class called BOBSNetworkSessionManager defined below. It is a simple class that inherits the QOBject so that I can use signals and slots but it does not have a dialog or any kind of window associated with it. It will eventually call a log in dialog and use the credentials to connect to a tcp server that I have created. This class serves as a layer to manage the connection state of the program because it will only run properly when connected to the server and when being used within 15 minutes without break due to p.c.i. compliance. If these conditions are not true this class will lock the window and force a new login. As of right now I just try to arbitrarily open the main window as though credentials had passed and i wasbconnected to the server. The problem is when I open the mainwindow it disapears right away. I cannot seem to figure out why it is diappearing. I have included all of my files.
BOBSDCNetworkSessionManager .h header file
#ifndef BOBSDCNETWORKSESSIONMANAGER_H
#define BOBSDCNETWORKSESSIONMANAGER_H
#include <QObject>
#include <QSettings>
class BOBSDCNetworkSessionManager : public QObject
{
Q_OBJECT
public:
explicit BOBSDCNetworkSessionManager(QObject *parent = 0);
protected:
void destroyed(QObject *);
signals:
public slots:
private:
void readSettings();
void writeSettings();
QSettings networkSettings;
};
#endif // BOBSDCNETWORKSESSIONMANAGER_H
BOBSDCNetworkSessionManager Implementation .cpp file
#include "bobsdcnetworksessionmanager.h"
#include "bobsmainwindow.h"
BOBSDCNetworkSessionManager::BOBSDCNetworkSessionManager(QObject *parent) :
QObject(parent)
{
BOBSMainWindow w;
w.show();
}
Main.cpp file
#include "bobsdcnetworksessionmanager.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setApplicationName("Enterprise Management Suite");
a.setApplicationVersion("Beta Version: 0.0.0.01");
a.setOrganizationName("Enigma Web Consulting");
a.setOrganizationDomain("http://www.EnigmaWebCo.com");
BOBSDCNetworkSessionManager netMgr;
return a.exec();
}
The problem is here:
{
BOBSMainWindow w;
w.show();
}
w.show() is not a blocking call. So you're creating a window, showing it, and then it immediately is destructed when it goes out of scope. You should either declare w as a member variable or construct it on the heap:
BOBSMainWindow *w = new BOBSMainWindow(this);
I am trying to implement a input method with Qt Embedded.
There is a lookup table for choosing the candidate words for typing. "text input area" to the "lookup table" and the selected word cannot be sent to the "text input area".
Dose anyone have any idea to solve this problem? Thanks~
Here I give a simple example:
main.cpp
#include "InputWidget.h"
#include "ButtonWidget.h"
#include <QApplication>
int main(int argc,char *argv[])
{
QApplication app(argc,argv);
InputWidget *inputWidget=new InputWidget();
ButtonWidget *buttonWidget=new ButtonWidget();
inputWidget->show();
buttonWidget->show();
int ref=app.exec();
inputWidget->deleteLater();
buttonWidget->deleteLater();
return ref;
}
InputWidget.h
#include <QWidget>
#include <QPlainTextEdit>
#ifndef _InputWidget_H_
#define _InputWidget_H_
class InputWidget:public QWidget
{
Q_OBJECT
public:
InputWidget(QWidget *parent=0);
private:
QPlainTextEdit *inputArea;
};
#endif
InputWidget.cpp
#include "InputWidget.h"
#include <QPushButton>
#include <QVBoxLayout>
InputWidget::InputWidget(QWidget *parent):QWidget(parent)
{
//input area setup
inputArea=new QPlainTextEdit(this);
//main layout
QVBoxLayout *mainLayout=new QVBoxLayout(this);
mainLayout->setContentsMargins(1,4,1,1);
mainLayout->addWidget(inputArea);
setLayout(mainLayout);
}
ButtonWidget.h
#include <QWidget>
#include <QPushButton>
#ifndef _ButtonWidget_H_
#define _ButtonWidget_H_
class ButtonWidget:public QWidget
{
Q_OBJECT
public:
ButtonWidget(QWidget *parent=0);
private:
QPushButton *selectedBtn;
public slots:
void changeBtnText();
};
#endif
ButtonWidget.cpp
#include "ButtonWidget.h"
#include <QPushButton>
#include <QVBoxLayout>
ButtonWidget::ButtonWidget(QWidget *parent):QWidget(parent)
{
//selectedBtn setup
selectedBtn=new QPushButton(tr("Click Me!!"),this);
connect(selectedBtn,SIGNAL(clicked()),this,SLOT(changeBtnText()));
//main layout
QVBoxLayout *mainLayout=new QVBoxLayout(this);
mainLayout->setContentsMargins(1,4,1,1);
mainLayout->addWidget(selectedBtn);
setLayout(mainLayout);
}
void
ButtonWidget::changeBtnText()
{
selectedBtn->setText("I am clicked :)");
}
Those codes would generate a widget which has a PlainTextEdit "inputArea" and a widget which has a PushButton "selectedBtn".
First, I input some words in the "inputArea". The current foucs is on "inputArea" in the InputWidget.
But when I move mouse to ButtonWidget and click the "selectedBtn", the foucs is changed to "selectedBtn" in the ButtonWidget.
How do I click the "selectedBtn" but still keep the foucs on "inputArea"? Thanks~
Just like my comment described in laura's answer, InputWidget and ButtonWidget may have no identical parent and I cannot use QWidget's "setFocus" slot to change the current focus between them.
First of all, you will need to make the two widgets know about each other. Once you have done that (by setting the text widget into the button widget or by adding them both to the same parent widget), you can try to see if QWidget's setFocus slot can help you (look at the other slots too, some might be useful for this).
Perhaps implement something like this, in the ButtonWidget:
(in the header) declare a signal foo()
(in the constructor) connect button widget's foo signal to InputWidget's setFocus slot
(in the changeBtnText) after you've done everything you wanted, emit foo()
Note though that setFocus works if the window is active.
You might be able to get what you want by playing with the focusPolicy for your widget. Pay attention to the Qt::NoFocus option. I don't think it prevents mouse clicks on your widget, but you'll want to test to be sure.
Thank laura and cjhuitt, your responses give me a big hint to solve my question.
Because InputWidget and ButtonWidget are two independent widgets, if we want to deal with the focus issue between them, we need a "global" control, i.e., use QApplication to do the focus job.
The key point is using QApplication's "focusChanged" slot and QWidget's "activateWindow". The following is my modification.
main.cpp
#include "InputWidget.h"
#include "ButtonWidget.h"
#include <QApplication>
int main(int argc,char *argv[])
{
QApplication app(argc,argv);
InputWidget *inputWidget=new InputWidget();
ButtonWidget *buttonWidget=new ButtonWidget(0,&app);
inputWidget->show();
buttonWidget->show();
int ref=app.exec();
inputWidget->deleteLater();
buttonWidget->deleteLater();
return ref;
}
ButtonWidget.h
#include <QWidget>
#include <QPushButton>
#include <QApplication>
#ifndef _ButtonWidget_H_
#define _ButtonWidget_H_
class ButtonWidget:public QWidget
{
Q_OBJECT
public:
ButtonWidget(QWidget *parent=0,QApplication *app=0);
private:
QPushButton *selectedBtn;
public slots:
void changeBtnText();
void changeFocus(QWidget *oldWidget,QWidget *curWidget);
};
#endif
ButtonWidget.cpp
#include "ButtonWidget.h"
#include <QPushButton>
#include <QVBoxLayout>
ButtonWidget::ButtonWidget(QWidget *parent,QApplication *app):QWidget(parent)
{
//selectedBtn setup
selectedBtn=new QPushButton(tr("Click Me!!"),this);
connect(selectedBtn,SIGNAL(clicked()),this,SLOT(changeBtnText()));
//main layout
QVBoxLayout *mainLayout=new QVBoxLayout(this);
mainLayout->setContentsMargins(1,4,1,1);
mainLayout->addWidget(selectedBtn);
setLayout(mainLayout);
//deal with focus
connect(app,SIGNAL(focusChanged(QWidget*,QWidget*)),this,SLOT(changeFocus(QWidget*,QWidget*)));
}
void
ButtonWidget::changeBtnText()
{
selectedBtn->setText("I am clicked :)");
}
void
ButtonWidget::changeFocus(QWidget *oldWidget,QWidget *curWidget)
{
if(oldWidget && curWidget==this)
oldWidget->activateWindow();
}
InputWidget.cpp and InputWidget are not modified.
This solution can work in this example, but I am not sure that it can work in two independent Qt programs(they have their own QApplication), especially in Qt Embedded environment. I would continue doing some tests on it. If there are any results, I would also post them here.
Thank for this discussion, I have learned a lot and thank you all again!!