Run QDialog object from a static function after QMainWindow is opened - c++

I would like to open a modal dialog box (to log in) from a static function after the QMainWindow object is opened.
class DialogLog : public QDialog {
DialogLog(QWidget * parent) : QDialog(parent) {
//some code
exec();
}
};
class LogHandler {
static bool log(QWidget * parent) {
DialogLog dl(parent);
//some code
}
};
class WinMain : public QMainWindow {}
main(..) {
QApplication app(..);
WinMain fen;
fen.show;
app.exec();
};
EDIT : How can i run LogHandler::log() after/at the same time of WinMain ?

There could be a better solution, but here is what you could do : use a singleshot timer that will shot immediatly (i.e. as soon as the event pump will be looping).
The timer will call a slot of, for instance, your WinMain class :
void main(..) {
QApplication app(..);
WinMain fen;
fen.show;
QTimer::singleShot(0, &fen, SLOT(login()));
app.exec();
};
In the login() slot of WinMainyou would have your call to LogHandler::log() :
void WinMain::login() {
if (!LogHandler::log(this))
qApp->quit();
}

Related

Linking a QWidget to another QWidget new created

I am try to, by pressing a button in the main QWidget, to create a new QWidget. In that new created QWidget I want to have a button connected to a slot of the main QWidget.
class UI : public QWidget
{
public:
UI(){connection();};
private:
QPushButton* all = new QPushButton{ "ALL" };
void connection(){
QObject::connect(all,QPushButton::clicked,[](){
SmallGUI* s=new SmallGUI{};
s->show();
});
}
void something(){
//something
}
and the second class
class SmallGUI1 :
public QWidget
{
public:
SmallGUI(){connection();};
private:
QPushButton* N =new QPushButton;
void connection(){
//to connect N to something()
}
I want to connect N to something() .
Before we start, there are some other problems with you code.
Note that in your second class, the constructor is not named the same as the class, which will cause some... Problems.
You also forgot to put a parent for your buttons (which may thus cause some unexpected results) AND for your Widgets (which is again not a good idea).
So, that being said, let us get to the main topic.
I tend to only put prototypes and declare the attributes in the .h file to make the code clearer, but you may of course adapt it to your needs or to your own programming convention.
There are several ways to do something like this, but the simplest one should look like this :
SmallGUI1.h :
#include "UI.h" //The file in which the class UI is declared
//OR :
//class UI; //If you need to include this file in UI.h
class SmallGUI1 : public QWidget{
Q_OBJECT //Q_OBJECT macro, check the doc for more explainations about it
public:
explicit SmallGUI1(UI *parent = nullptr); //Explicit means that this constructor cannot be used for implicit casts
~SmallGUI1();//Destructor needed because I only put the constructor above
private:
QPushButton* N; //Not very good looking to initialize attributes in the .h in my opinion, but works fine.
}
SmallGUI1.cpp :
SmallGUI1::SmallGUI1(UI *parent) : QWidget(parent){
N = new QPushButton(tr("Some text on the button") , this); //tr to enable translation on this string
//************* If I understood your question correctly, this is what you are looking for *************
connect(N , &QPushButton::clicked , parent , &UI::doSomething); //Select the signal you want
/*
Some code here
*/
show();
}
SmallGUI1::~SmallGUI1(){qDeleteAll(children());}
UI.h :
class UI : public QWidget{
Q_OBJECT
public:
explicit UI(QWidget *parent = nullptr);
~UI();
private:
QPushButton* all;
private slots :
void createSmallGUI1();
void doSomething();
}
UI.cpp :
#include "SmallGUI1.h"
UI::UI(QWidget *parent) : QWidget(parent){
all = new QPushButton(tr("ALL") , this);
connect(all , &QPushButton::clicked , this , &UI::createSmallGUI1);
/*
Some code here
*/
}
UI::~UI(){qDeleteAll(children());}
void UI::createSmallGUI1(){SmallGUI1 *gui = new SmallGUI1(this);}
void UI::doSomething(){
/*
Clever code here
*/
}
You can define the second widget as a child of the main widget to make things easier:
class UI : public QWidget {
...
private:
SmallGUI* s;
...
and then initialize it in the UI constructor, along with your all button. You can initially hide the child widget or disable it:
UI() {
all = new QPushButton{"ALL", this};
setWindowTitle("UI"); // just for clarification
s = new SmallGUI(this);
s->hide();
connection();
};
and 'show' it with button clicked signal
connect(all, &QPushButton::clicked, s, &SmallGUI::show);
Doing so gives you the option to connect the clicked signal of your N button to the something function in the parent class
connect(s->N, &QPushButton::clicked, this, &UI::something);
The complete program would be as follows,
#include <QApplication>
#include <QMessageBox>
#include <QPushButton>
#include <QWidget>
class SmallGUI : public QWidget {
public:
SmallGUI(QWidget* parent) : QWidget(parent) {
N = new QPushButton{"btn2", this};
connection();
};
QPushButton* N;
private:
void connection(){};
};
class UI : public QWidget {
public:
UI() {
all = new QPushButton{"ALL", this};
setWindowTitle("UI"); // just for clarification
s = new SmallGUI(this);
s->hide();
connection();
};
private:
SmallGUI* s;
QPushButton* all;
void connection() {
connect(all, &QPushButton::clicked, s, &SmallGUI::show);
connect(s->N, &QPushButton::clicked, this, &UI::something);
}
void something() { QMessageBox::information(this, "Hello", "Hello"); }
};
int main(int argc, char* argv[]) {
QApplication a(argc, argv);
UI w;
w.show();
return a.exec();
}
It is not good idea to connect to parent's slots from "nested" class, since SmallGUI1 will be tied to class UI.
Here is better solution, I think:
class UI : public QWidget
{
public:
UI(){connection();};
private:
QPushButton* all = new QPushButton{ "ALL" };
void connection(){
QObject::connect(all,QPushButton::clicked,[](){
SmallGUI1* s=new SmallGUI1;
connect(s,&USmallGUI1::button_clicked,this,&UI::something);
s->show();
});
}
void something(){
//something
}
And SmallGUI1 class:
class SmallGUI1 :
public QWidget
{
public:
SmallGUI1(){connection();};
signals:
void button_clicked();
private:
QPushButton* N;
void connection(){
//to connect N to something()
N = new QPushButton;
connect(N,&QPushButton::clicked,this,&SmallGUI1::button_clicked)
}
This way, you are connecting QPusButton::clicked signal from SmallGUI1 to the signal SmallGUI1::button_clicked(). Dont need to implement additional slot, just connect signal to signal.
And in UI you are connecting button_clicked() signal to the slot dosomething()
DONT FORGET THE CONSTRUCTOR OF SmallGUI1! In your code, SmallGUI() will be just a method, which will not be called when SmallGUI1 is instantiated, and you have to call it by yourself.

Access MainWindow class members from another class

I googled a lot before writing here. I found a couple of posts but I couldn't quite get them. So I'm starting a new post.
I am developing a simple UI. There is this main class (see below), which is a main dialogue box which is shown when the app is started.
class MainWindow : public QMainWindow
There are few buttons on the MainWindow dialogue box, when I click a button, it hides the MainWindow and opens another QDialog box. This dialogue box is of the class as shown below
class libinsert : public QDialog
I create the libinsert object this way:
void MainWindow::on_pushButton_clicked()
{
this->hide();
libinsert lib; // I create libinsert object this way
lib.setModal(true);
lib.exec();
}
This is libinsert.cpp:
libinsert::libinsert(QWidget *parent) :
QDialog(parent),
ui(new Ui::libinsert)
{
ui->setupUi(this);
}
When the libinsert dialogue box is open, I need to access a socket variable which is a member of class MainWindow. How do I achieve this ?
My main app looks like this:
{
QApplication a(argc, argv);
MainWindow w(sock);
w.show();
return a.exec();
};
Alternatively: in general how are multiple dialogue boxes built, as parent-child relation?
libinsert will not be a child of MainWindow. QDialog-based windows are always top-level windows, regardless of what you pass as their parent when you construct them.
If there will only ever exist a single instance (per process) of MainWindow at any given time, you can give MainWindow a static function which returns a pointer to the window:
class MainWindow : public QMainWindow
{
public:
MainWindow()
{
theWindow = this;
}
virtual ~MainWindow()
{
theWindow = nullptr;
}
static get()
{
return theWindow;
}
static MainWindow *theWindow;
}
MainWindow *MainWindow::theWindow = nullptr;
Your dialogs can then access the main window's members thusly:
MainWindow::get()->someMemberFunction();
A more robust approach is to have libinsert store a pointer to the main window when it is created:
class MainWindow;
class libinsert : public QDialog
{
public:
libinsert(MainWindow *mw)
: mainWindow(mw)
{ ... }
MainWindow *getMainWindow()
{
return mainWindow;
}
MainWindow *mainWindow;
}
When MainWindow creates the dialog, it passes a pointer to itself to the constructor:
void MainWindow::createDialog()
{
libinsert *dialog = new libinsert(this):
}
This approach will work even if there are multiple instances of MainWindow active at the same time.

QWidget loses its parent

In my application I have a QDialog which itself contains a complex, QWidget-derived GUI element. The QDialog is modal and opened with exec() and the embedded GUI element handles all user interactions.
So only this child QWidget knows when the QDialog can be closed, which is done this way:
QDialog* parent=qobject_cast<QDialog*>(parentWidget());
if (parent) parent->close();
This is necessary because the QDialog has to be closed and not only the QWidget.
Now a user reported a situation where QDialog::exec() has returned but where the dialog (or only the GUI element?) was still visible. From the log files I can see QDialog::exec() really has returned and the code right after this call was executed.
So my current assumption: the GUI element has lost its parent so that the close() call shown above was not called because "parent" was null.
Any idea how this can happen? Is there a regular way where the parent of a QWidget can disappear?
Generally speaking, using QDialog::exec to reenter the event loop will cause trouble, because suddenly all the code that runs in the main thread must be reentrant. Most likely you're facing fallout from that. Don't reenter the event loop, and you'll be fine or the problem will become reproducible.
If you need to react to the dialog being accepted or rejected, connect code to the relevant slots. I.e. change this:
void do() {
MyDialog dialog{this};
auto rc = dialog.exec();
qDebug() << "dialog returned" << rc;
}
to that:
class Foo : public QWidget {
MyDialog dialog{this};
...
Foo() {
connect(&dialog, &QDialog::done, this, &Foo::dialogDone);
}
void do() {
dialog.show();
}
void dialogDone(int rc) {
qDebug() << "dialog returned" << rc;
}
};
or, if you want to lazily initialize the dialog:
class Foo : public QWidget {
MyDialog * m_dialog = nullptr;
MyDialog * dialog() {
if (! m_dialog) {
m_dialog = new MyDialog{this};
connect(m_dialog, &QDialog::done, this, &Foo::dialogDone);
}
return m_dialog;
}
...
void do() {
dialog()->show();
}
void dialogDone(int rc) {
qDebug() << "dialog returned" << rc;
}
};
It is a horrible antipattern for the child widget to attempt to meddle with the parent. The knowledge that the widget has a parent should not leak into the widget, it should be localized to the parent. Thus, the child widget should emit a signal that indicates that e.g. the data was accepted. When you create the dialog, connect this signal to the dialog's accept() or close() slots:
class MyWidget : public QWidget {
Q_OBJECT
public:
Q_SIGNAL void isDone();
...
};
class MyDialog : public QDialog {
QGridLayout layout{this};
MyWidget widget;
public:
MyDialog() {
layout.addWidget(&widget, 0, 0);
connect(&widget, &MyWidget::isDone, this, &QDialog::accepted);
}
};

start QTimer from another class

I have the following classes:
class MainWindow : public QMainWindow
{
public:
void StartTimer()
{
timer = new QTimer(this);
timer.start(100);
}
private:
QTimer *timer;
};
class AnotherClass
{
public:
MainWindow *window;
void runTimer()
{
window->StartTimer();
}
};
Assuming the window pointer is correctly pointing to the mainwindow, if I try to call runTimer() , I receive this error:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is MainWindow(0x7fff51ffe9f0), parent's thread is QThread(0x7fd1c8d001d0), current thread is QThread(0x7fd1c8f870c0)
QObject::startTimer: Timers can only be used with threads started with QThread
My guess for this error was that since runTimer was being called from a different thread it was also trying to initialize in that same thread? instead of the mainwindow thread?
If I initialize the timer in the default constructor of the main window I receive
QObject::startTimer: Timers cannot be started from another thread
How can I tell a QTimer to start from another class?
You can use signals and slots.
class AnotherClass : public QObject
{
Q_OBJECT
public:
MainWindow * window;
AnotherClass() : window( new MainWindow )
{
// Connect signal to slot (or just normal function, in this case )
connect( this, &AnotherClass::signalStartTimer,
window, &MainWindow::StartTimer,
// This ensures thread safety, usually the default behavior, but it doesn't hurt to be explicit
Qt::QueuedConnection );
runTimer();
}
void runTimer()
{
emit signalStartTimer();
}
signals:
void signalStartTimer();
};

How to close main window from another class?

I have a class which inherits from QDialog (dialog_game_over). I show an object of that class in a slot of another class. I want to close mainwindow and QDialog when user clicked on ok button. First I built an object of mainwindow in dialog_game_over and close it. But this way it wasn't correct. What do I do for closing program in class other than main class?
dilog_game_over::dilog_game_over(QWidget *parent) :
QDialog(parent),x_size(400),y_size(400)
{
ok=new QPushButton(this);
ok->setText("OK");
ok->move(200,200);
connect(ok,SIGNAL(clicked()),this,SLOT(on_ok_clicked()));
}
void dilog_game_over::on_ok_clicked()
{
accept();
this->close();
}
class Myenemy1 : public QGraphicsObject
{
Q_OBJECT
public slots:
void loss();
private:
dilog_game_over dlg;
}
void Myenemy1::loss()
{
....
dlg.exec();
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),size_of_plane_y(600),size_of_plane_x(2500)
{
set_scene();
put_cloud();
put_point();
}
All over your application you can exit by :
qApp->quit();
Or
qApp->exit(0);