Application with multiple windows in Qt - c++

What is a good way to create application with multiple windows in Qt? Something like GIMP. I have already read this answer and it is not what I need.
I have two windows, one with controls and one with OpenGL inside. Right now I have something like this:
int main()
{
// some code
MainWindow mw;
mw.show();
GLWindow gw;
gw.show();
// ...
}
I don't like this for two reasons. First reason is when I start my application, window with OpenGL will be on top (because it is last to call show()) but MainWindow will be buried somewhere under all opened windows. What I need is both windows in front of everything (like GIMP), preferably with focus on MainWindow (I guess I can bring them to front, so that is minor issue). Second reason I don't like this is that my application will be closed completely only when I close both windows.
So I was thinking of having a reference to GLWindow inside MainWindow, and creating it from MainWindow.
Would that be a good way to create application with several windows?
EDIT: GLWindow inherits from QOpenGLWindow.

You are doing right, but with the following simple tricks, you can resolve both issues that cause you do not like your right method:
To activate both windows, just do as follows:
MainWindow mw;
mw.show();
mw.activateWindow();
GLWindow gw;
gw.show();
gw.activateWindow();
To resolve the quit problem, you have to override the closeEvent in both windows. To do that, add the following code into the header file of your both windows:
protected:
void closeEvent(QCloseEvent *event) override;
and in the implementation of the closeEvent, just write qApp->quit();. This way once you close either window, your application will terminate completely.
MainWindow.cpp
void MainWindow::closeEvent(QCloseEvent *event)
{
qApp->quit();
}
and
GLWindow.cpp
void GLWindow::closeEvent(QCloseEvent *event)
{
qApp->quit();
}

I'm not sure I completely understand the situation but, typically, you can make the 'secondary' widgets child dialogs of the QMainWindow. Given your example code that would be something like...
int main ()
{
// some code
MainWindow mw;
mw.show();
/*
* Secondary windows should be created with the QMainWindow mw
* as their parent.
*/
GLWindow gw(&mw);
/*
* Set the Qt::Dlaog window flag to `encourage' the QMainWindow
* and its children to behave as a group.
*/
gw.setWindowFlags(gw.windowFlags() | Qt::Dialog);
gw.show();
// ...
}
Now all widgets behave as a group (possibly subject to the window manager being used) and closing the QMainWindow will, by default, close the application.

Related

how do I call primary window using pushbutton c++

I'm trying to create a C++ Widget Application in QT with multiple windows where you can return to the mainwindow by clicking a Pushbutton.
I'm a complete noobie so I try to follow YouTube tutorial and for opening windows by pushbuttons I watched this one (minute 8:00): https://youtu.be/tP70B-pdTH0
It works when opening secondary windows from the main one but if I try to do the same from a secondary windows to the mainwindow it doesn't. It appears an error "cannot initialize object parameter of type 'Widget' with an expression of type 'MainWindow'"
in the source file I wrote:
void Crediti::on_pushButton_clicked()
{
close();
mainwindow = new MainWindow(this);
mainwindow->show();
}
mainwindow->show(); is the incriminated part
I also included mainwindow in the header of the secondary window and specified the class
MainWindow *mainwindow
in the header so It recognizes mainwindow initially in the source.
I'm doubting if doing this thing is possible at all, and if not so how can I make a pushbutton that, when clicked, can redirect me to the mainwindow?
Please I need this for a school application, thanks
So here you're creating a new main window each time you click on the button. From your description that's not the behaviour you want. I understand you have an application with a main window and other secondary windows and want to bring up the main window when clicking on the button, assuming the main window still exists somewhere and hasn't been deleted.
What I would try is to find the main window when hitting the push button and show / raise it, something along the line of:
#include <QApplication>
#include "MainWindow.h" // Adapt that one to you main window header
// ... some code of your secondary window
void SecondaryWindow::on_pushButton_clicked()
{
for(auto widget : QApplication::topLevelWidgets())
{
// This will return a nullptr if the widget is not the main window
auto mainWindow = dynamic_cast<MainWindow*>(widget);
// skip if not the main window
if(!mainWindow)
continue;
// Show it if hidden
if(mainWindow->isHidden())
mainWindow->show();
// raise it, as in bring it forward, over all other windows
mainWindow->raise();
}
// eventually close the current window if that's what you want
close();
// if you close it and don't need it any more you might also want to delete it
deleteLater();
}
Note that this function won't do anything if the main window has been deleted in the meantime, which might be the case if you closed it and the Qt::WA_DeleteOnClose attribute is set.
Hope that helps.

Non-modal QWidget dialog that stays on top of the window

I want a dialog which stays on top of my main window and not other windows. I derived a class and added some flags. If I call the dialog now with show() the dialog appears and is staying on top as long as I don't press a button or whatever. Then the dialog goes to background again.
Dial::Dial(QWidget *parent) : QWidget(parent)
{
this->setWindowFlags(Qt::Tool | Qt::Dialog);
// ...
Consequently, I looked into the docu and found this:
Indicates that the widget is a tool window. A tool window is often a
small window with a smaller than usual title bar and decoration,
typically used for collections of tool buttons. If there is a parent,
the tool window will always be kept on top of it.
Happily, I added this line into my singleton creating the dialog.
d->mainWindow = new Foo();
d->dial->setParent(d->mainWindow);
Now the dialog is just embedded into my central widget (QOpenGlWidget) and is not a dialog anymore. Somehow, I seem to lack understanding what the docu is telling me? How can I get the dialog stay on top of my application and what does the docu mean?
I'm not able to reproduce your problem. The following code will generate a QWidget that will allways stay on top of the QMainWindow:
#include "QApplication"
#include "QMainWindow"
#include "QLineEdit"
int main(int argc, char * argv[])
{
QApplication a(argc, argv);
QMainWindow w;
w.show ();
QWidget *pLineEdit = new QWidget(&w);
pLineEdit->setWindowFlags(Qt::Tool | Qt::Dialog);
pLineEdit->show ();
a.exec ();
}
Tested with Qt 5.9.
Not sure if you've already solved this by now but you can try the WindowStaysOnTopHint flag when you construct the dialog:
Qt::WindowFlags flags = this->windowFlags();
flags |= Qt::WindowStaysOnTopHint;
this->setWindowFlags(flags);
Then use show() instead of exec() to make it non-modal:
dlg->show();
You need to set the modality (documentation) of the widget, like this:
QWidget *dialog = new QWidget(window, Qt::Dialog);
dialog->setWindowModality(Qt::ApplicationModal);
dialog->show();
However, I'd recommend to use the pre-configured QDialog class, which handles all that stuff for you:
QDialog *dialog = new QDialog(window);
dialog->exec();
Use QDialog instead of QWidget, and pass the parent widget in its constructor function.
QDialog* pDlg = new QDialog(this);
pDlg->show();

hide QWidget from a different QWidget

I have a MainWindow with two widgets, buttonsWidget and infoWidget.
I'm trying to to hide infoWidget after clicking a button within buttonsWidget (and ultimately show a different widget).
I've tried:
mainwindow.h
public:
void hideInfo();
mainwindow.cpp
void MainWindow::hideInfo()
{
ui->info->hide();
}
buttonsWidget.cpp
void buttonsWidget::on_timingButton_clicked()
{
MainWindow::hideInfo();
//Then will do something to show 'timingWidget'..
}
Many thanks
You should use Signals and Slots for this.
Add a signal in the buttonsWidget.h.
signals:
void hideInfoSignal();
In the main function, connect the button signal with the mainwindow method hideInfo().
QObject::connect(this->info, SIGNAL(hideInfoSignal),this, SLOT(hideInfo));
I haven't tested this, because I dont have Qt on this machine, but that should work, with possible minor modifications. If any errors appear, let me know and I will help. Also, read the signals and slots documentation.

Restoring or bringing to front Qt desktop application

I have made my app into a single instance app using the RunGuard code found on this SO question:
Qt: Best practice for a single instance app protection
What I'd like to do is when the user tries to start the application while there is one running is to bring the existing running application to the front, and if minimised, restore it.
In my Delphi Windows programming days I used to broadcast a Windows message from the new application before closing it. The existing app would receive this and restore itself and come to the front.
Is something like this possible with Qt on Windows and Linux platforms?
Did you have any specific trouble with QtSingleApplication? It should be sufficient for what you want and will enable you to send a message to the running application. You just need a slot to get that message and if it matches what you expect then you restore it.
http://doc.qt.digia.com/solutions/4/qtsingleapplication/qtsingleapplication-example-trivial.html
The logview object is also set as the application's activation window. Every time a message is received, the window will be raised and activated automatically.
For some reason setActivationWindow() and activateWindow() don't work for me. This is my workaround:
#include <QWidget>
#include <qtsingleapplication.h>
class Window : public QWidget
{
Q_OBJECT
public:
Window(QWidget *parent = 0) : QWidget(parent) {}
public slots:
void readMessage(const QString &str) { showNormal(); }
};
int main(int argc, char *argv[])
{
QtSingleApplication instance(argc, argv);
Window *window = new Window;
window->show();
QObject::connect(&instance, SIGNAL(messageReceived(const QString &)), window, SLOT(readMessage(const QString &)));
if (instance.sendMessage(""))
return 0;
return instance.exec();
}
#include "main.moc"
In common, it is not possible without IPC. QtSingleApplication provide such IPC, but you will get extra dependency from QtNetwork module. (As #svlasov answered)
First problem that you will have: you can't raise any window of application if this application is not foreground. There are solutions for Windows and OS X, how to force raising of windows.

Qt Architecture Advice Needed

iI have a Qt application using QGLWidget for drawing (simply a Viewport for 3D drawing etc...)
There is two main classes in the application.
MainWindow
Inherits from QWidget which holds many GUI widgets (menubar, toolbars, viewport, treeview...etc)
System
Does every other operation from GUI (math, geometry, IO, data processing, etc and holds "Scene" object which has drawable components.) Also it has Singleton pattern to create one global Instance for itself.
I am using Qt signal-slot mechanism to communucate between MainWindow and System, actually MainWindow has the signals and System has the slots. My problem starts here, how can I signal from System to MainWindow slots? When I define MainWindow in System object it gives me lots of error. Normaly System references in MainWindow don't give error. But when I include MainWindow's header file in System.h, System references give error in MainWindow side "'System': the symbol to the left of a '::' must be a type".
Basically my structure is look like this.
// MainWindow.h
#include "System.h"
class MainWindow : public QWidget
{
Q_OBJECT
public:
QToolBar* MyToolBar; // etc...
MainWindow()
{
ConnectSignals();
}
void ConnectSignals() { connect(my_action, SIGNAL(triggered()), System::GetInstance()->Actions, SLOT(action())); }
}
// System.h
#include "MainWindow.h" // if I wrote this, it gives me error in compile time.
class System
{
static bool m_instance;
static System* m_system;
// private constructor
System()
{
Actions = new MyActionList();
}
public:
MyActionList* Actions;
System* GetInstance()
{
if (!m_instance)
{
m_system = new System();
m_instance = true;
return m_system;
}
else { return m_system; }
}
}
// System.cpp
bool System::m_instance = false;
System* System::m_system = NULL;
Of course Actions has slot action()
So how can I access MainWindow from System?
The problem in your approach is the cyclic dependency between MainWindow and System - MainWindow includes System, System includes MainWindow.
In order to pass signals from System to MainWindow you need to make MyActionList of Sytem to emit signals that any receiver (MainWindow in your case) can handle. You absolutely do not need to include MainWindow stuff into the System - keep your backend (System) independent of any GUI component. Just incapsulate System into your MainWindow class and connect MyActionList signals to MainWindow slots. You need to have something like this in your MainWindow:
connect(my_action, SIGNAL(triggered()), System::GetInstance()->Actions, SLOT(action()));
connect(System::GetInstance()->Actions, SIGNAL(systemSignal()), this, SLOT(handleSystemSignal()));
where systemSignal() is a signal emitted from System or its MyActionList component.
As #vahancho states, you should keep a separation between the GUI and other systems. Another method to do this would be to introduce a delegate object to handle the communication between the two.
In addition, if you're inlining code as you've shown in your question, then that will increase the possibility of cyclic dependencies. Move the implementation into the .cpp file and use forward declarations instead of including headers in other header files where possible. This also has the benefit of speeding up compilation, which you'll notice with large projects.