Why is the recommended QDialog instantiation the way it is? - c++

I have a Qt Widgets application, created and edited in Qt-Creator.
The main window (MainWindow class) has a menubar, with a button to open a small dialog (with text or widgets for settings).
To create a new "window" I open the "create new file" dialog in Qt-Creator and select Qt Designer Form Class, which creates the needed header, source, and ui files (dialogabout.h, dialogabout.cpp, dialogabout.ui).
If I follow along with the docs, I then open the QDialog like so:
QDialog * widget = new QDialog;
Ui::DialogAbout about_ui;
about_ui.setupUi(widget);
widget->exec();
This works, but if I modify the new dialog's instantiator to connect a pushbutton to the close signal, the connect statement (along with any other code there) is never reached.
DialogAbout::DialogAbout(QWidget *parent) :
QDialog(parent),
ui(new Ui::DialogAbout)
{
ui->setupUi(this);
qDebug() << "I'm alive!"; // No output happens
connect(ui->pushButton_close, SIGNAL(clicked(bool)), this, SIGNAL(please_close())); // No signal created on pushbutton click.
}
I suspect that this is because I haven't explicitly done widget = new DialogAbout(this). If I instead instantiate the new dialog this different way:
DialogAbout * newwindow;
newwindow = new DialogAbout(this);
newwindow->exec();
Then the connect statement and qDebug work.
My question is: what are the pitfalls of deviating from the documentation's recommended way to create dialogs? Is there a way to get this functionality with the prior instantiation method?

Note that DialogAbout is not the same as Ui::DialogAbout. Ui::DialogAbout is a class of build placed in the UI namespace, created automatically by uic. In your project, the name of this file should be "ui_dialogabout h".
class Ui_DialogAbout
{
public:
QPushButton *pushButton_close;
void setupUi(QDialog *DialogAbout)
{
...
} // setupUi
void retranslateUi(QDialog *DialogAbout)
{
...
} // retranslateUi
};
namespace Ui {
class DialogAbout: public Ui_DialogAbout {};
} // namespace Ui
Here you use a class QDialog and uses the Ui::DialogAbout to build a layout in it. Note that Ui::DialogAbout has the function to create the components in QDialog.
QDialog * widget = new QDialog;
Ui::DialogAbout about_ui;
about_ui.setupUi(widget);
widget->exec();
If you specialize QDialog for DialogAbout your code should look like this:
DialogAbout * widget = new DialogAbout();
Ui::DialogAbout about_ui;
about_ui.setupUi(widget);
widget->exec();
But as setupUi() is already within DialogAbout, you cannot call again, resulting in:
DialogAbout * widget = new DialogAbout();
widget->exec();

Related

QDockWidgets merging incorrectly

I have a QDockWidget class and a QMainWindow:
// docker.hpp
class Docker : public QDockWidget
{
Q_OBJECT
public:
Docker(QString title, QWidget* parent = 0);
}
// docker.cpp
Docker::Docker(QString title, QWidget* parent): QDockWidget(title, parent)
{
QWidget* widget = new QWidget(this);
widget.setMinimumSize(200, 200);
setWidget(widget);
widget->setStyleSheet("border:5px solid gray;");
setAllowedAreas(Qt::AllDockWidgetAreas);
}
// mainwindow.hpp
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget* parent);
private slots:
void createDockers();
};
// mainwindow.cpp
MainWindow::MainWindow(QWidget* parent): QMainWindow(parent)
{
setDockOptions(dockOptions() |
QMainWindow::AllowTabbedDocks |
QMainWindow::GroupedDragging);
// The following line of code does not change the situation.
// setTabPosition(Qt::RightDockWidgetArea, QTabWidget::East);
// There are some other codes which connect a button to the void createDockers() method
}
void createDockers()
{
Docker* dock = new Docker("Docker", this);
dock->setFloating(true);
dock->show();
}
I am able to create two Dockers with clicks of the button mentioned above.
However, when I drag one QDockWidget onto the other, the border disappears and no tabs show up:
I am expecting the following to happen: (Achieved by spawning several QDockWidgets)
I am also noticing that one of the QDockWidgets did not vanish. Instead, it merged back to the MainWindow. This only happens if they are the "first two" QDockWidgets.
What caused this problem and how to solve it? I am trying to mimic this project.
I guess it's linked to the QMainWindow::GroupedDragging option. I'm pretty sure it should work well without it (I mean for the not showing tab issue). Do you have restrictions on dock position somewhere else? The documentation implies it could create issues: http://doc.qt.io/qt-5/qmainwindow.html#DockOption-enum
For the style issue, you may need to redefine it on tab event, because once tabbed, the widget may inherit the tab style instead of the dock widget style you defined (not certified at all ^^)
Last guess/thing you can try, is to start with the dock tabbed and not floating to see if you have any new bahaviour, it was what I was doing in a previous project and it was working pretty well.
Sorry but no other ideas for the moment.

Qt creating MDI document window

I am trying to create a MDI document program. I have a question on creating the subwindow.
This is my mainwindow constructor:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setWindowTitle(tr("MDI"));
workspace = new QMdiArea;
setCentralWidget(workspace);
//fileNew();
createActions();
createMenus();
createToolbars();
statusBar()->showMessage(tr("Done"));
enableActions();
}
The interesting point is the fileNew(); function. It is a private slot function actually which I want to invoke when "New File" button is triggered. Here is the private slot fileNew() function:
void MainWindow::fileNew()
{
DocumentWindows* document = new DocumentWindows;
workspace->addSubWindow(document);
}
This function works perfectly when I call from the mainwindow constructor. However, there is a problem when I call it from the createActions(); function which uses a signal-slot mechanism.
Here is my createActions()
void MainWindow::createActions()
{
newAction = new QAction(QIcon(":/Image/NewFile.png"),tr("&New"),this);
newAction->setShortcut(tr("Ctrl+N"));
newAction->setToolTip(tr("Open new document"));
connect(newAction, SIGNAL(triggered(bool)), this, SLOT(fileNew()));
}
No subwindow is created even the SLOT is triggered. Subsequently, I find out that if I add document->show();, everything works well.
void MainWindow::fileNew()
{
DocumentWindows* document = new DocumentWindows;
workspace->addSubWindow(document);
document->show();
}
My question is: Why the show() function is needed in a SLOT but not in the constructor?
PS. DocumentWindows is just a class inherits QTextEdit.
This problem has nothing to do with the class of the widgets one is using. It is unrelated to documents, MDI, or the main window. After you add a child widget to a widget that is already visible, you must explicitly show it. Otherwise, the widget will remain hidden.
All widgets are hidden by default. When you initially show the MainWindow, all of its children are recursively shown too. When you later add a child MDI widget, it remains hidden. When widgets are added to layouts, they are shown by default - but your widget is managed by the MDI area, not by a layout.
This is a minimal test case demonstrating your issue:
// https://github.com/KubaO/stackoverflown/tree/master/questions/widget-show-32534931
#include <QtWidgets>
int main(int argc, char ** argv) {
QApplication app{argc, argv};
QWidget w;
w.setMinimumSize(200, 50);
QLabel visible{"Visible", &w};
w.show();
QLabel invisible{"Invisible", &w};
invisible.move(100, 0);
return app.exec();
}

Can't open Widget from the MainWindow

I want to open a Widget from my MainWindow. I thought this was easy to do, and all the tutorials I read do it like this:
void MainWindow::on_pushButton_Types_clicked()
{
m_typesWin = new TypesWindow(m_db, this);
m_typesWin->show();
this->hide();
}
However, this only works for me if I don't pass "this" into the constructor. When I add "this" to the constructor, I don't see the widget, the program just stops. If I don't hide "this", then I can see that parts of my widget are actually in my main window. I have no idea why.
EDIT: The classes are automatically created by QtCreator, so they should be alright.
If you want a QWidget to be displayed as a window, a parent widget should not be specified to that widget. Here, because you specify main window as the parent of TypesWindow, TypesWindow becomes embedded in main window. So when you hide main window, TypesWindow embedded in main window also gets hidden.
Since you want TypesWindow to be a separate window, don't pass parent widget to the QWidget constructor in TypesWindow constructor. If you want to access main window from TypesWindow, you can store main window pointer in a pointer field in TypesWindow.
To open a Mainwindows from the new Qwidget:
1)in the NEWWIDGET.CPP:
QWidget *w;
NEWWIDGET::NEWWIDGET(QWidget *parent,QWidget *win) :
QWidget(parent),
ui(new Ui::NEWWIDGET)
{
ui->setupUi(this);
w=win;
}
..
void NEWWIDGET::on_pushButton_clicked()
{
this->hide();
w->show();
}
2)In the NEWWIDGET.H
public:
explicit NEWWIDGET(QWidget *parent=nullptr,QWidget *win=nullptr);
~NEWWIDGET();

Extending an existing UI for QMainWindow by additional menus with Qt Designer

I have written a basic image viewing application using Qt and C++, i.e. I have a class
ImageViewApp : public QMainWindow, private Ui::ImageViewer {
Q_OBJECT
public:
ImageViewApp ( const char * InputFilename = NULL ) {
setupUi ( this );
}
};
with a .ui file created with Qt Designer, that generates a header file defining
class Ui_ImageViewer {
public:
void setupUi(QMainWindow *ImageViewer) { … }
};
namespace Ui {
class ImageViewer: public Ui_ImageViewer {};
} // namespace Ui
Now I would like to write another application ImageRegistrationApp, where I extend the QMenuBar of ImageViewApp by additional QMenus containing QActions for a certain purpose, say image registration. Additionally, I would like to change some other things, such as the windowTitle and add QActions to existing QMenus of ImageViewApp.
I am looking for a way where I don't need to touch or copy the the .ui file of ImageViewApp. I would like to do something like inheritance, where changes to the UI of ImageViewApp affect the UI of ImageRegistrationApp. Also I would like to be able to create and edit the additional QMenus and QActions for ImageRegistrationApp via Qt Designer.
Is this possible?
UPDATE:
I tried changing the title of ImageRegistrationApp and adding a QAction to an existing QMenu in ImageViewApp through inheritance within C++ as follows:
ImageRegistrationApp : public ImageViewApp {
Q_OBJECT
public:
QAction *actionTest;
ImageRegistrationApp ( const char * InputFilename = NULL )
: ImageViewApp ( InputFilename ) {
this->setWindowTitle(QApplication::translate("ImageViewer", "ImReg", 0, QApplication::UnicodeUTF8));
actionTest = new QAction(this);
actionTest->setObjectName(QString::fromUtf8("actionTest"));
actionTest->setText(QApplication::translate("ImageViewer", "Test", 0, QApplication::UnicodeUTF8));
this->menuTools->addAction(actionTest);
}
protected slots:
void on_actionTest_triggered() {
QMessageBox::information(NULL, "Test", "Hello world!" );
}
};
I also changed the inheritance in ImageViewApp to protected Ui::ImageViewer in order to be able to access the Ui elements.
The title of my ImageRegistrationApp changes as intended and also the QAction Test shows up in the menu Tools, but when I click it, nothing happens though I expect it to display the QMessageBox as defined in the slot on_actionTest_triggered.
Is there anything else I have to do, to connect the QAction with the slot?
I tried QObject::connect(actionTest, SIGNAL(triggered()), this, SLOT(actionTest)); but this did not change anything.
The problem with the slot not being executed when the QAction actionTest was triggered, was a misspelling. I had also tried QObject::connect(actionTest, SIGNAL(on_triggered()), this, SLOT(on_actionTest_triggered)); before, but there were the () missing at the end.
Adding the following line at the end of the constructor of ImageRegistrationApp solved the problem:
QObject::connect(actionTest, SIGNAL(on_triggered()), this, SLOT(on_actionTest_triggered()));
Instantiating ImageRegistrationApp now instantiates the entire GUI of ImageViewApp and adds the additionally defined QActions to the respective QMenus. This method should be capable of everything I asked for in the question, except for designing additional QMenus with Qt Designer. Instead, everything has to be coded in C++ source.

Qt: Change application QMenuBar contents on Mac OS X

My application uses a QTabWidget for multiple 'pages', where the top-level menu changes depending on what page the user is on.
My issue is that attempting to re-create the contents of the menu bar results in major display issues. It works as expected with the first and third style (haven't tested second, but I'd rather not use that style) on all platforms except for Mac OS X.
The first menu is created in the way I create most in the application, and they receive the correct title, but disappear as soon as the menu is re-created.
The second menu appears both on the initial population and the re-population of the menu bar, but in both cases has the label "Untitled". The style for the second menu was only created when trying to solve this, so it's the only way I've been able to have a menu stick around.
The third dynamic menu never appears, period. I use this style for dynamically populating menus that are about to show.
I have tried deleting the QMenuBar and re-creating one with
m_menuBar = new QMenuBar(0);
and using that as opposed to m_menuBar->clear() but it has the same behavior.
I don't have enough reputation to post images inline, so I'll include the imgur links:
Launch behavior: http://i.imgur.com/ZEvvGKl.png
Post button-click behavior: http://i.imgur.com/NzRmcYg.png
I have created a minimal example to reproduce this behavior on Mac OS X 10.9.4 with Qt 5.3.
mainwindow.cpp
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
m_menuBar = new QMenuBar(0);
m_dynamicMenu = new QMenu("Dynamic");
connect(m_dynamicMenu, SIGNAL(aboutToShow()), this, SLOT(updateDynamicMenu()));
changeMenuBar();
QPushButton *menuBtn = new QPushButton("Test");
connect(menuBtn, SIGNAL(clicked()), this, SLOT(changeMenuBar()));
setCentralWidget(menuBtn);
}
void MainWindow::changeMenuBar() {
m_menuBar->clear();
// Disappears as soon as this is called a second time
QMenu *oneMenu = m_menuBar->addMenu("One");
oneMenu->addAction("foo1");
oneMenu->addAction("bar1");
oneMenu->addAction("baz1");
// Stays around but has 'Untitled' for title in menu bar
QMenu *twoMenu = new QMenu("Two");
twoMenu->addAction("foo2");
twoMenu->addAction("bar2");
twoMenu->addAction("baz2");
QAction *twoMenuAction = m_menuBar->addAction("Two");
twoMenuAction->setMenu(twoMenu);
// Never shows up
m_menuBar->addMenu(m_dynamicMenu);
}
void MainWindow::updateDynamicMenu() {
m_dynamicMenu->clear();
m_dynamicMenu->addAction("foo3");
m_dynamicMenu->addAction("bar3");
m_dynamicMenu->addAction("baz3");
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtWidgets>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
private slots:
void changeMenuBar();
void updateDynamicMenu();
private:
QMenuBar *m_menuBar;
QMenu *m_dynamicMenu;
};
#endif // MAINWINDOW_H
All this looks like Qt bug on OS X. And it's very old bug, actually.
You can do workaround and don't work with QMenu via QMenuBar::addMenu function calls, as you do here:
m_menuBar->addMenu("One");
Instead of this work with QAction retrieved from QMenu by creation of the QMenu instance dynamically and then calling of QMenuBar::addAction for the QAction instance retrieved by QMenu::menuAction, as following:
m_menuBar->addAction(oneMenu->menuAction());
Beside QMenuBar::addAction you can use QMenuBar::removeAction and QMenuBar::insertAction if you want to make creation only of some specific menu items dynamically.
Based on your source code here it's modified version of it which deals with all menus dynamic creation on every button click (you do this in your source code) and the menu 'Dynamic' is populated with different count of items every time you click the button.
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtWidgets>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
private slots:
void changeMenuBar();
private:
QMenuBar *m_menuBar;
QMenu *m_dynamicMenu;
int m_clickCounter;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent),
m_clickCounter(1)
{
m_menuBar = new QMenuBar(this);
connect(m_dynamicMenu, SIGNAL(aboutToShow()), this, SLOT(updateDynamicMenu()));
changeMenuBar();
QPushButton *menuBtn = new QPushButton("Test");
connect(menuBtn, SIGNAL(clicked()), this, SLOT(changeMenuBar()));
setCentralWidget(menuBtn);
}
void MainWindow::changeMenuBar() {
++m_clickCounter;
m_menuBar->clear();
QMenu *oneMenu = new QMenu("One");
oneMenu->addAction("bar1");
oneMenu->addAction("baz1");
m_menuBar->addAction(oneMenu->menuAction());
QMenu *twoMenu = new QMenu("Two");
twoMenu->addAction("foo2");
twoMenu->addAction("bar2");
twoMenu->addAction("baz2");
m_menuBar->addAction(twoMenu->menuAction());
m_dynamicMenu = new QMenu("Dynamic");
for (int i = 0; i < m_clickCounter; ++i) {
m_dynamicMenu->addAction(QString("foo%1").arg(i));
}
m_menuBar->addAction(m_dynamicMenu->menuAction());
}
Additionally while developing menus logic for OS X it's good to remember:
It's possible to disable QMenuBar native behavior by using QMenuBar::setNativeMenuBar
Because of turned on by default QMenuBar native behavior, QActions with standard OS X titles("About","Quit") will be placed automatically by Qt in the predefined way on the screen; Empty QMenu instances will be not showed at all.
I think your problem is this line:
QMenu *oneMenu = m_menuBar->addMenu("One");
To add menus to a menubar you'd want code as follows:
QMenuBar *m = new QMenuBar;
m->addMenu( new QMenu("Hmmm") );
m->show();
To create menus, and then add actions, and then add the menu to the menu bar:
QMenu *item = new QMenu( "Test1" );
item->addAction( "action1" );
QMenuBar *t = new QMenuBar;
t->addMenu( item );
t->show();