Interaction between QMainWindow class and QDialogue class - c++

I am developing a GUI app. I have a main window(QMainWindow) which pops up when the app is executed.
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
This window has some buttons. When I click a button here, another window which is of QDialog is popped up. The logic of the button is
void MainWindow::on_InsertButton_pressed()
{
libinsert lib ;
lib.setModal(false);
lib.exec();
}
Question: I have a socket in the QMainWindow class, whenever I receive a message there I need to send it to the QDialogue window and display it in a QLineEdit. With the above logic, even after I set QDialogue to nonmodal, I am not able to interact with the QMainWindow when the QDialogue window is open. I tried lib.show() instead of lib.exec(), but with this, when I click the button the QMainWindow class the QDialogue window does not pop up.
Please advice me what is the best method to communicate from the background window the the foreground window?

Your code can looks like
void MainWindow::on_InsertButton_pressed()
{
libinsert lib ;
connect( this, SIGNAL( messageReceived( QString ), &lib, SLOT( letsChangeLineEdit( QString ) ) ) );
lib.setModal(false);
lib.exec();
}
Of course you should implement your signal and slot. QString in signal slot arguments is target string to show in dialog.
Hope that will clear for you.

In your MainWindow::on_InsertButton_pressed()slot you are creating a local variable libinsert lib which gets destroyed when it gets out of scope, i.e. when the slot execution finishes. If you use lib.exec() the execution waits for the dialog execution to return so you see the dialog, but can't interact with the main window until you close it. When you use lib.show(), your dialog is shown, execution continues and exits the slot right away, at which point the lib gets destroyed and you don't see it any more (depending on your configuration this may be fast enough that it looks like it newer gets shown in the first place).
One solution is to have a pointer to your dialog as a member of MainWindow class (add libinsert* lib; in MainWindow.h), and initialize it in your Mainwindow constructor like this:
`MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
lib = new libinsert(this);
lib->setModal(false);
}`
Then in MainWindow::on_InsertButton_pressed() you only need to call QWidget::show() method, i.e. like this:
void MainWindow::on_InsertButton_pressed()
{
lib->show();
}
Now, if what you are trying to achieve is to have a dialog pop-up every time the message is received , and display the message, first you will need to have a public method(or slot) in your libinsert class which updates the text in your QLineEdit, for example
void libinsert::onMessageReceived(QString message)
{
// Update the text and show the dialog to avoid having to call show()
// explicitly from MainWindow.
ui->lineEdit->setText(message);
show();
}
and then either call this method directly, when you receive the message, with:
lib->onMessageReceived(message);
or connect a signal, emitted when message is received, to it with something like this (in MainWindow constructor):
connect( this, SIGNAL( messageReceived( QString ), &lib, SLOT( onMessageReceived( QString ) ) ) );

Related

How to recognize QMouseEvent inside child widgets?

EDIT and Some Self Critisicm
I tried both given solutions, which both solved my problem, and thus I thank you both! I marked the transparent solution as accepted because I thought it is was the easiest implementation when I only had one child widget, but I wish to share some insight for other beginners:
I first used QLabel, which apperently has enabled Qt::WA_TransparentForMouseEvents by default and thus obviously worked, but I also wanted the text to be selectable, by using QPlainTextEdit instead. Laughably, this is not possible in any way, because if you try to select the text (by clicking) you will close the window! I ended up keeping the transparancy, and neglecting the select-text-feature.
I'm guessing my following question has been answered somewhere before, but after an hour of searching I now post the question myself. I'm grateful if someone can point me to an already answered question that solves my problem.
Anyhow, I'm creating a popup window, using C++ and Qt. I've created the following PopupDialog class which works well and fine for all its purposes. However, I've removed its frame (including the bar containing the close button and window title) to make it look minimalistic, and now I want it to close if the user presses/releases the mouse button anywhere inside the popup window (dialog).
The below code works, however in such a way that I have to click and release the mouse exactly at the QDialog-window itself. It will not close when i click if the mouse hovers over the child widget(s) inside the QDialog, e.g. a QPlainTextEdit, which is displaying text.
Hence, I'm in need of a solution for QDialog to recognize QMouseEvents inside its child widgets. Please, don't hesitate to ask if something is unclear. I have not included my mainwindow.h/.cpp files, or popupdialog.ui file since I believe it would be a little too much to post here, but the .ui extremely simple: Just the QDialog window holding a QBoxLayout, containing a single widget, a QPlainTextEdit. I may posts these on request if it helps.
// popupdialog.h
#ifndef POPUPDIALOG_H
#define POPUPDIALOG_H
#include <QDialog>
#include <QString>
namespace Ui {class PopupDialog;}
class PopupDialog : public QDialog
{
Q_OBJECT
public:
explicit PopupDialog(QWidget *parent = 0, QString msgTxt="");
~PopupDialog();
private:
Ui::PopupDialog *ui;
QString messageText;
void mouseReleaseEvent(QMouseEvent*);
};
#endif //POPUPDIALOG_H
...
// popupdialog.cpp
#include "popupdialog.h"
#include "ui_popupdialog.h"
PopupDialog::PopupDialog(QWidget *parent, QString msgTxt) :
QDialog(parent),
ui(new Ui::PopupDialog),
messageText(msgTxt)
{
ui->setupUi(this);
setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
setModal(true);
ui->message_text_display->setText(messageText);
// The message_text_display is an instance of the class,
// "PlainTextEdit". Using "QLabel" partly solves my
// problem, but does not allow text selection.
}
PopupDialog::~PopupDialog()
{
delete ui;
}
void PopupDialog::mouseReleaseEvent(QMouseEvent *e)
{
this->close();
}
As you already noticed mouse events are handled from child widgets and propagated to parents if not accepted. You can read more about it here
To close your popup window when the click is done inside a child widget you can do two things. You could try looking into installEventFilter and set it up on each child widget to call close().
Another option would require you to have a kind of centralWidget (like the MainWindow usually has) - just to group all your child widgets. This way you could call setAttribute() on it to set Qt::WA_TransparentForMouseEvents property to simply skip handling mouse events on the widget and all of its children.
groupWidget->setAttribute(Qt::WA_TransparentForMouseEvents);
According to Qt docs:
When enabled, this attribute disables the delivery of mouse events to
the widget and its children. Mouse events are delivered to other
widgets as if the widget and its children were not present in the
widget hierarchy; mouse clicks and other events effectively "pass
through" them. This attribute is disabled by default.
Which basically means the event would be passed up the chain to the first widget which can handle the event. In your case it would be the PopupDialog and the already overriden mouseReleaseEvent slot.
in header file
class PopupDialog : public QDialog
{
Q_OBJECT
public:
explicit PopupDialog(QWidget *parent = 0, QString msgTxt="");
~PopupDialog();
//////////////////////////////////
protected:
bool eventFilter(QObject *obj, QEvent *event);
//////////////////////////////////////
private:
Ui::PopupDialog *ui;
QString messageText;
void mouseReleaseEvent(QMouseEvent*);
};
in cpp
PopupDialog::PopupDialog(QWidget *parent, QString msgTxt) :
QDialog(parent),
ui(new Ui::PopupDialog),
messageText(msgTxt)
{
ui->setupUi(this);
setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
setModal(true);
ui->message_text_display->setText(messageText);
// The message_text_display is an instance of the class,
// "PlainTextEdit". Using "QLabel" partly solves my
// problem, but does not allow text selection.
///////////////////////////////////////
foreach (QObject *child, children())
{
child->installEventFilter(this);
}
///////////////////////////////////////
}
///////////////////////////////////////
bool PopupDialog::eventFilter(QObject *obj, QEvent *event)
{
if(event->type() == QEvent::MouseButtonRelease)
{
this->close();
}
}

how to connect function between two classes

I have two different class in two files:
class Game: public QGraphicsView()
class Window: public QMainWindow()
{
public: Window();
Game *game;
public slots: void test() {game = new Game();};
}
in Window.cpp I start a new Game using test() function:
Window::Window() {test();}
now in Game.cpp I create a QMessageBox with two QPushButton
QMessageBox *box= new QMessageBox();
QPushButton *btYES = box->addButton(tr("YES"),QMessageBox::ActionRole);
QPushButton *btNO = box->addButton(tr("NO"),QMessageBox::ActionRole);
box->exec();
if (box->clickedButton() == btYES) {Window::test();}
if (box->clickedButton() == btNO) {close();}
As you can see I want to connect the function test() to btYES inside the Game.cpp but that function is inside Window.cpp and its function is to start a new game.
It is possible to do so?
QPushButton emits events when it its pressed/released
So you could connect the, lets say, released signal to a slot:
connect(button, SIGNAL(released()), windowClass, SLOT(handleButton()));
In your case you need to send it across classes, so you may need to do it in two steps.
In game:
// connect the button to a local slot
connect(btYES, SIGNAL(released()), this, SLOT(handleYesButton()));
// in the slot emit a signal - declare the signal in the header
game::handleYesButton()
{
emit userChoiceYes();
}
In window
// connect the signal in game to a your slot:
connect(game, SIGNAL(userChoiceYes()), this, SLOT(test()));
Then when btnYes is pressed/released the signal released is emitted - you pick that up in handleYesButton() and emit your own signal which your window class is connected to and handles it in test()
Based on #code_fodder answer, but you don't even need another slot, plus the basic signal for a QPushButton is clicked(). Here's the documentation :
A push button emits the signal clicked() when it is activated by the
mouse, the Spacebar or by a keyboard shortcut. Connect to this signal
to perform the button's action. Push buttons also provide less
commonly used signals, for example pressed() and released().
To start, instead of adding another slot in your class Game, just connect the button's signal clicked() to another signal :
connect(btYES, SIGNAL(clicked()), this, SIGNAL(btYesClicked()));
The signal from the class Game is now emitted when you press the button btYes. Now, you just have to connect this signal to a slot test() in your class Window :
connect(game, SIGNAL(btYesClicked()), this, SLOT(test()));

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();

How to programmatically click a QPushButton

I am using Qt. I have a button in the page added via the Qt Creator. It is connected to the method void MyPage::on_startButton_clicked().
I want to programmatically click this button. I tried ui->startButton->clicked(), it gives,
error C2248: 'QAbstractButton::clicked' : cannot access protected member declared in class 'QAbstractButton'
Please help. Thanks!
Use QAbstractButton::animateClick():
QPushButton* startButton = new QPushButton("Start");
startButton->animateClick();
RA's answer provides the way to do it so that it's visible that the button was clicked. If all you wish for is to emit the signal, what you're doing is correct in Qt 5, where the signals are public.
The error you're facing indicates that you're using Qt 4, where the signals are not public. You can work around this by invoking the signal indirectly:
QMetaObject::invokeMethod(ui->startButton, "clicked");
This calls the method immediately, i.e. the signal will be dispatched and the slots called by the time invokeMethod returns. Alas, most code (mostly your code!) assumes that the signal is emitted from event processing code close to the event loop - i.e. it's not reentrant, rather than from your own code. Thus you should defer the signal emission to the event loop:
// Qt 5.4 & up
QTimer::singleShot(0, ui->startButton, [this]{ ui->startButton->clicked(); });
// Qt 4/5
QTimer::singleShot(0, ui->startButton, "clicked");
The following is a complete example for Qt 5.4 & up:
#include <QtWidgets>
int main(int argc, char ** argv) {
bool clicked = {};
QApplication app{argc, argv};
QPushButton b{"Click Me"};
QObject::connect(&b, &QPushButton::clicked, [&]{ clicked = true; qDebug() << "clicked"; });
Q_ASSERT(!clicked);
b.clicked(); // immediate call
Q_ASSERT(clicked);
clicked = {};
// will be invoked second - i.e. in connect order
QObject::connect(&b, &QPushButton::clicked, &app, &QApplication::quit);
QTimer::singleShot(0, &b, [&]{ b.clicked(); }); // deferred call
Q_ASSERT(!clicked);
app.exec();
Q_ASSERT(clicked);
}
If you do not want the animation stuff, you can also just call the method:
on_startButton_clicked();

MDI window and QSignalMapper basics

First of all my apologies for big looking question but indeed it's not. I’m reading Foundation of qt development book and while reading fourth chapter author tells the basics of MDI window by showing this example :
MdiWindow::MdiWindow( QWidget *parent ) : QMainWindow( parent ) {
setWindowTitle( tr( "MDI" ) );
QWorkspace* workspace = new QWorkspace;
setCentralWidget( workspace );
connect( workspace, SIGNAL(windowActivated(QWidget *)), this, SLOT(enableActions()));
QSignalMapper* mapper = new QSignalMapper( this );
//my problem is in this line
connect( mapper, SIGNAL(mapped(QWidget*)), workspace, SLOT(setActiveWindow(QWidget*)) );
createActions();
createMenus();
createToolbars();
statusBar()->showMessage( tr("Done") );
enableActions();
}
His this para of explanation completely eluded me (is it me or others having problem understanding it too?) :
Next, a signal mapping object called QSignalMapper is created and
connected. A signal mapper is used to tie the source of the signal to
an argument of another signal. In this example, the action of the menu
item corresponding to each window in the Window menu is tied to the
actual document window. The actions are in turn connected to mapper.
When the triggered signal is emitted by the action, the sending action
has been associated with the QWidget* of the corresponding document
window. This pointer is used as the argument in the mapped(QWidget*)
signal emitted by the signal mapping object.
My question : I still don’t get what is signal mapper class, how it’s used and what's functionality it's doing in the example above?. Can anyone please explain the above para using easy terms? also It’d be awesome if you could please teach me about mapper class’s basics with simple example? possibly in layman’s term?
P.S : A confusion is when we have MDI window, do menu changes (though actions are disabled/enabled) e.g suppose for one particular document we have menu “File/close” and for other document we have “File/remaper” ?
The QSignalMapper is used to re-emit signals with optional parameters. In other words (from the documentation):
This class collects a set of parameterless signals, and re-emits them
with integer, string or widget parameters corresponding to the object
that sent the signal.
A good example (also from the doc - take a look at it) is set as follows:
Suppose we want to create a custom widget that contains a
group of buttons (like a tool palette). One approach is to connect
each button's clicked() signal to its own custom slot; but in this
example we want to connect all the buttons to a single slot and
parameterize the slot by the button that was clicked.
So imagine you have a number of buttons encapsulated in a class, say ButtonWidget, with a custom signal void clicked(const QString &text). Here is the definition:
class ButtonWidget : public QWidget {
Q_OBJECT
public:
ButtonWidget(QStringList texts, QWidget *parent = 0);
signals:
void clicked(const QString &text);
private:
QSignalMapper *signalMapper;
};
The constructor could then be defined like the following:
ButtonWidget::ButtonWidget(QStringList texts, QWidget *parent)
: QWidget(parent)
{
signalMapper = new QSignalMapper(this);
QGridLayout *gridLayout = new QGridLayout;
for (int i = 0; i < texts.size(); ++i) {
QPushButton *button = new QPushButton(texts[i]);
connect(button, SIGNAL(clicked()), signalMapper, SLOT(map()));
signalMapper->setMapping(button, texts[i]);
gridLayout->addWidget(button, i / 3, i % 3);
}
connect(signalMapper, SIGNAL(mapped(const QString &)),
this, SIGNAL(clicked(const QString &)));
setLayout(gridLayout);
}
So what happens here? We construct a grid layout and our buttons of type QPushButton. The clicked() signal of each of these is connected to the signal mapper.
One of the forces using QSignalMapper is that you can pass arguments to the re-emitted signals. In our example each of the buttons should emit a different text (due to the definition of our signal), so we set this using the setMapping() method.
Now all that's left to do is map the signal mapper to the signal of our class:
connect(signalMapper, SIGNAL(mapped(const QString &)),
this, SIGNAL(clicked(const QString &)));
Assume we have a testing class called TestClass then ButtonWidget can be used thusly:
TestClass::TestClass() {
widget = new ButtonWidget(QStringList() << "Foo" << "Bar");
connect(widget, SIGNAL(clicked(const QString &)),
this, SLOT(onButtonClicked(const QString &)));
}
void TestClass::onButtonClicked(const QString &btnText) {
if (btnText == "Foo") {
// Do stuff.
}
else {
// Or something else.
}
}
By using the signal mapper this way you don't have to declare and manage all the buttons and their clicked signals, just one signal pr. ButtonWidget.
The buttom line is that the signal mapper is great for bundling multiple signals and you can even set parameters when it re-emits them. I hope that gave some intuition about the usage of QSignalMapper.
Your example code
The explanation (your "para") states that all the actions are each individually mapped to a specific QWidget*. When triggering an action its respective QWidget* will be passed to the slot QWorkspace::setActiveWindow(QWidget*) of workspace, which in turn activates the widget.
Also note that the mapping from action to widget has to happen somewhere in your code. I assume it is done in createActions() or enableActions() perhaps.
A QSignalMapper allows you to add some information to a signal, when you need it. This object internally have a map like QMap<QObject*,QVariant>. Then you connect an object to it, and when the slot is called, it re-emit the signal with the associated value.
Workflow:
mySignalMapper:
[ obj1 -> 42 ]
[ obj2 -> "obiwan" ]
[ obj3 -> myWidget ]
connect(obj1,mySignal,mySignalMapper,SLOT(map())); // idem for obj2 and obj3
(obj2 emits "mySignal")
-> (mySignalMapper::map slot is called)
-> (sender() == obj2, associated data = "obiwan")
-> (mySignalMapper emits mapped("obiwan"))
I was going to add a more detailed example, but Morten Kristensen was faster than me ;)