Qt signal forwarding - what am I doing wrong? - c++

I am writing a test Qt application in which everything works fine until I try to receive a signal by slot and then emit another signal (MyWidget::setValue doesn't cause an emission of MyWidget::valueChanged). What am I doing wrong?
#include <QApplication>
#include <QVBoxLayout>
#include <QSpinBox>
#include <QSlider>
class MyWidget: public QWidget {
Q_OBJECT
signals:
void valueChanged(int value);
public slots:
void setValue(int value){
emit valueChanged(value);
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MyWidget* win = new MyWidget;
QVBoxLayout* layout = new QVBoxLayout(win);
QSpinBox* spin =new QSpinBox();
QSlider* slider = new QSlider(Qt::Horizontal);
spin->setMinimum(-100);
spin->setMaximum(100);
slider->setMinimum(-100);
slider->setMaximum(100);
layout->addWidget(spin);
layout->addWidget(slider);
QObject::connect(spin, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int)));
QObject::connect(slider, SIGNAL(valueChanged(int)), win, SLOT(setValue(int)));
QObject::connect(win, SIGNAL(valueChanged(int)), spin, SLOT(setValue(int)));
win->show();
return app.exec();
}

What am I doing wrong?
You've created a binding loop and would overflow the stack if Qt didn't protect you from yourself by forcibly breaking the loop. As this is only a protection mechanism, its only job is to prevent a freeze and a crash. It's not supposed to make your application functional.
You have to avoid creating the loop in the first place.
You could do so e.g. by not invoking the setValue slot of the originating widget - recall that your setValue slot is called from within the valueChanged signal's body.

Related

Closing Dialog quits Application if QMainWindow is hidden

I have the following Problem:
I have a Qt MainWindow which starts several Dialogs. Through an external source, I can hide and show the MainWindow. If a dialog is open, only the MainWindow is hidden, but the dialog is still visible. This is not nice but not my main problem. The main problem is, if i close the dialog while the MainWindow is hidden, the whole Application terminates. I do not want that, because I can make my main window visible again by external source.
I know it has something to do with QApplication quitOnLastWindowClosed. But if i set it true, my Application doesn't terminate if i normaly press "X".
Here is an example:
// MainApp.h
#include <QMainWindow>
#include "ui_MainWindow.h"
class MainApp : public QObject {
Q_OBJECT
public:
MainApp(QObject *parent = nullptr);
private slots:
void slotOpenDialog();
private:
QMainWindow mMainWindow;
Ui::MainWindow mUi;
};
// MainApp.cpp
#include "MainApp.h"
#include <QTimer>
#include <QMessageBox>
MainApp::MainApp(QObject *parent) {
mUi.setupUi(&mMainWindow);
mMainWindow.show();
connect(mUi.pushButton, &QPushButton::clicked, this, &MainApp::slotOpenDialog);
// simulate external hide and show mechanism
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout,
[=] {
if(mMainWindow.isHidden()) mMainWindow.show();
else mMainWindow.hide();
});
timer->start(3000);
}
void MainApp::slotOpenDialog() {
QMessageBox::information(nullptr, "Info", "text");
}
// main.cpp
#include <QApplication>
#include "MainApp.h"
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
MainApp* mApp = new MainApp;
// if set true, I can't exit the application with "X"
//a.setQuitOnLastWindowClosed(false);
int error = a.exec();
delete mApp;
return error;
}
How can I prevent the program from quitting when it is hidden and a still visible dialog has been closed and how can I make it exit normally when the window is visible?
QApplication emits a signal "lastWindowClosed" and it has a quit() slot. As mentioned in the comments, the problem can be solved by connecting your own slot onLastWindowClosed() to that signal and quitting only when you want to.

Qt: Force child window to have its own task bar entry

I'm using Qt 5 and C++ and I want to force some of my child windows to have their own task bar entries. Right now, I am able to create parentless QWidgets and use the signal-slot mechanism to close those windows when a main window (QMainWindow) is closed.
However as I add more and more parents and childs this whole signal-slot technique will get tedious (won't it?) and I am sure that Qt already has a feature I can use for this. I've seen this; Primary and Secondary Windows section talks about what I'm trying to do. It says:
In addition, a QWidget that has a parent can become a window by
setting the Qt::Window flag. Depending on the window management system
such secondary windows are usually stacked on top of their respective
parent window, and not have a task bar entry of their own.
I can see that I need to set my QWidgets as Primary Windows but I don't exactly know how.
So I tried:
// when a QMainWindow's (this) push button is clicked:
QWidget* newWindow = new QWidget(this, Qt::Window);
newWindow->show();
It doesn't give me the behavior I want. How can I set newWindow as a Primary Window while still keeping this as its parent?
I don't now native method to do it in Qt. But you can use for it signal/slot it's definitely not create a serious burden on the processor until you have at least a thousand windows. For it you can implement your own child parent mechanizme.
For example:
class myOwnWidget: public QWidget{
Q_OBJECT
public:
myOwnWidget(myOwnWidget* parent = 0):
QWidget(){
if(parent){
connect(parent,SIGNAL(close()), this,SLOT(deleteLater()));
}
}
void closeEvent(QCloseEvent* e){
emit close();
QWidget::closeEvent(e);
}
signals:
void close();
};
class PrimaryWindow : public myOwnWidget{
PrimaryWindow(myOwnWidget *parent):
myOwnWidget(parent)
{
//your constructor here
}
}
using:
PrimaryWindow * rootWindows = new PrimaryWindow();
PrimaryWindow * childWin = new PrimaryWindow(rootWindows);
In code PrimaryWindow create without Qt-parent, but for closing child windows you should inherit all your windows from myOwnWidget.
Hope it help.
Heavily based on posters Konstantin T.'s and Mike's ideas, I have developed two classes, MyWidget and MyMainWindow, which can use themselves and each other in their constructors to create child windows which will have their own task bar entries while still acting as children to their parent windows (i.e. getting automatically closed and destroyed upon termination of the parent window). MyWidget replaces QWidget and MyMainWindow replaces QMainWindow if such a behavior is desired.
my_widget.h:
#ifndef MY_WIDGET_H
#define MY_WIDGET_H
#include <QWidget>
#include <QMainWindow>
class MyMainWindow;
class MyWidget : public QWidget{
Q_OBJECT
public:
MyWidget(MyWidget* parent = 0){
if(parent){
connect(parent, SIGNAL(window_closed()),
this, SLOT(close()));
connect(parent, SIGNAL(destroyed(QObject*)),
this, SLOT(deleteLater()));
}
}
MyWidget(MyMainWindow* parent);
void closeEvent(QCloseEvent* event){
emit window_closed();
QWidget::closeEvent(event);
}
signals:
void window_closed();
};
class MyMainWindow : public QMainWindow{
Q_OBJECT
public:
MyMainWindow(MyMainWindow* parent = 0){
if(parent){
connect(parent, SIGNAL(window_closed()),
this, SLOT(close()));
connect(parent, SIGNAL(destroyed(QObject*)),
this, SLOT(deleteLater()));
}
}
MyMainWindow(MyWidget* parent){
connect(parent, SIGNAL(window_closed()),
this, SLOT(close()));
connect(parent, SIGNAL(destroyed(QObject*)),
this, SLOT(deleteLater()));
}
void closeEvent(QCloseEvent* event){
emit window_closed();
QMainWindow::closeEvent(event);
}
signals:
void window_closed();
};
#endif // MY_WIDGET_H
my_widget.cpp:
#include "my_widget.h"
MyWidget::MyWidget(MyMainWindow* parent){
connect(parent, SIGNAL(window_closed()),
this, SLOT(close()));
connect(parent, SIGNAL(destroyed(QObject*)),
this, SLOT(deleteLater()));
}
Example main.cpp:
#include <QApplication>
#include "my_widget.h"
int main(int argc, char* argv){
QApplication a(argc, argv);
MyWidget mw1{new MyWidget};
mw1.setWindowTitle("ctor: MyWidget(MyWidget*)");
mw1.show();
MyMainWindow mmw1{&mw1};
mmw1.setWindowTitle("ctor: MyMainWindow(MyWidget*)");
mmw1.show();
MyMainWindow mmw2{&mmw1};
mmw2.setWindowTitle("ctor: MyMainWindow(MyMainWindow*)");
mmw2.show();
MyWidget mw2{&mmw2};
mw2.setWindowTitle("ctor: MyWidget(MyMainWindow*)");
mw2.show();
return a.exec();
}
So the window chain is: mw1 -> mmw1 -> mmw2 -> mw2 where any window that is closed will also destroy all windows to its right and all windows in the chain will have their own task bar entries.
I'm sure there are more elegant ways of defining the constructors in my_widget.h, but as a newbie this worked for me to obtain the exact behavior I needed. I'd appreciate to see better practices on my_widget.h.

undefined reference to function send

I'm a very beginnner at C++ /Qt programming. I've made this simple dialog box that check the QLineEdit, if the text entered is "bob" should enable the OK button.
I can't get it to compile successfully, it gives me:
dialog.cpp|31|undefined reference to `Dialogmio::send()'
What am I doing wrong?
This is dialog.h:
//dialog.h
#ifndef DIALOG_H_INCLUDED
#define DIALOG_H_INCLUDED
#include <QDialog>
class QPushButton;
class QLineEdit;
class Dialogmio : public QWidget
{
public:
Dialogmio(QWidget *parent =0);
signals:
void send ();
public slots:
void recip(QString &text);
private:
QLineEdit *linedit;
QPushButton *buttonOK;
};
#endif
This is dialog.cpp:
//dialog.cpp
#include <QtGui>
#include "dialog.h"
Dialogmio::Dialogmio(QWidget *parent)
: QWidget(parent)
{
linedit = new QLineEdit();
buttonOK = new QPushButton("OK");
buttonOK->setEnabled(FALSE);
connect( linedit, SIGNAL( textChanged(const QString &) ), this, SLOT( recip(const QString &) ));
connect (this,SIGNAL( send()), this, SLOT( buttonOK->setEnabled(true)) );
QHBoxLayout *layout = new QHBoxLayout();
layout->addWidget(linedit);
layout->addWidget(buttonOK);
setLayout(layout);
}
void Dialogmio::recip(QString &text)
{
QString a = linedit->text();
if (a == "bob"){
emit send(); //here it gives me the error
}
}
This is main.cpp:
#include <QApplication>
#include "dialog.h"
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
Dialogmio *dialog = new Dialogmio;
dialog->show();
return app.exec();
}
I've inserted the Q_OBJECT macro as suggested, now I get one more errors on line 7:
dialog.cpp|7|undefined reference to `vtable for Dialogmio'|
You start by including the Qt file for QDialog, but then go on to inherit from QWidget. While inheriting from QWidget is not a problem, was your intention to actually inherit from QDialog(?), in which case you should define your class this way: -
class Dialogmio : public QDialog
{
Q_OBJECT
public:
Dialog(QWidget* parent);
private slots:
void aSlotFunction();
}
The signals and slots mechanism are C++ extensions that are unique to Qt and in order for a class to use it, the class must include the Q_OBJECT macro, which adds all the necessary functionality. During the build stages, Qt parses the header and creates the code required for the extensions, including run-time type information, the dynamic property system and of-course the signals and slots.
As you state you're using codeblocks as the IDE, if it doesn't automatically run qmake before building, you'll need to do that whenever you add any signals or slots to a class in order for the moc (meta-object-compiler) to see them.
Another thing is that the call to connect signals and slots is wrong: -
connect (this,SIGNAL( send()), this, SLOT( buttonOK->setEnabled(true)) );
The parameter in the SLOT macro takes a slot function, so you need to create a slot and connect it to the send signal: -
connect(this, SIGNAL(send()), this, SLOT(aSlotFunction());
Inside the aSlotFunction, you can then call the set enabled for the button: -
void Dialogmio::aSlotFunction()
{
buttonOK->setEnabled(true);
}
If you're using Qt 5, there's an easier syntax of handling the connection: -
connect(this, &Dialogmio::send, this, &Dialogmio::aSlotFunction);
As this syntax accepts pointers to the functions that will be called, they don't actually have to be declared as slots to work. In addition, you do not provide the arguments, so if they change, you won't have to update the connect calls too.

QProcess saving to QTextEdit

What I'm trying to do is launch a program within another program using QProcess and then save the output from the launched program into a QTextEdit of the launcher program. Every time I launch this program I want it to add more text to the QTextEdit. Now I get the program to launch but then after the text is supposed to be written it crashes. Here is the code:
#include <QWidget>
#include <QPushButton>
#include <QTextEdit>
#include <QProcess>
#include <QVBoxLayout>
#include <QApplication>
class Widget : public QWidget
{
Q_OBJECT
QTextEdit* text;
public:
Widget() : text(new QTextEdit) {
QPushButton* addBtn = new QPushButton("Add Module");
text->setReadOnly(true);
QVBoxLayout* layout = new QVBoxLayout(this);
layout->addWidget(addBtn,0);
layout->addWidget(text);
connect(addBtn,SIGNAL(clicked()),SLOT(launchModule()));
}
Q_SLOT void launchModule() {
QString program = "C:/A2Q2-build-desktop/debug/A2Q1.exe";
QProcess *myProcess = new QProcess(this);
connect(myProcess, SIGNAL(finished(int)), SLOT(finished()));
connect(myProcess, SIGNAL(error(QProcess::ProcessError)), SLOT(finished()));
myProcess->start(program);
}
Q_SLOT void finished() {
QProcess *process = qobject_cast<QProcess*>(sender());
QString out = process->readAllStandardOutput(); // will be empty if the process failed to start
text->append(out);
delete process;
}
};
int main(int argc, char **argv)
{
QApplication app(argc, argv);
Widget w;
w.show();
app.exec();
}
#include "main.moc"
It's crashing because you're deleting the sender object while inside a slot. Instead of delete process, you should
process->deleteLater();
For logging purposes you should be using QPlainTextEdit instead of a QTextEdit. The former is faster. You're prematurely pessimizing by using the latter. Alas, even QPlainTextEdit becomes abysmally slow if you're sending about 100 lines/s (at least on Qt 4.8). If you want a really fast log view, you'll need to use QListWidget, with a caveat, or roll your own.
I have a complete example of how to send to and receive from a process in another answer.
The process is crashing because you're deleting the parent from within the finished slot.
Also, it's probably easier to do something like this:
QObject::connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(getOutput()));
instead of using the finished() slot. But that's more personal preference than anything.

Getting text from QLineEdit to append to QTextEdit upon QLineEdit returnpressed()

I'm trying to write a simple Qt program which takes text inside a QLineEdit and appends it into a QTextEdit object when the return key is pressed.
Here is the code for my program:
#include <QApplication>
#include <QtGui>
#define WIDTH 640
#define HEIGHT 480
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
QTextEdit textArea;
textArea.setReadOnly(true);
QLineEdit lineEdit;
QPushButton quit("Quit");
QObject::connect(&quit, SIGNAL(clicked()), qApp, SLOT(quit()));
QHBoxLayout hLayout;
hLayout.addWidget(&lineEdit);
hLayout.addWidget(&quit);
QVBoxLayout vLayout;
vLayout.addWidget(&textArea);
vLayout.addLayout(&hLayout);
QWidget window;
window.setBaseSize(WIDTH, HEIGHT);
window.setLayout(&vLayout);
window.show();
//This is the line I can not get to work
QObject::connect(&lineEdit, SIGNAL(returnPressed()), &textArea, SLOT(append(lineEdit.text())));
return app.exec();
}
Essentially, the problem is connecting the QLineEdit returnPressed() SIGNAL to the QTextEdit append() SLOT. I am hoping someone can point out what is wrong with my code.
Thank you very much in advance for your time.
When you run your program, you should notice on the console the following Qt error output..
Object::connect: No such slot QTextEdit::append(lineEdit.text()) in ..
You would need to qualify the append reference in your call to connect with the QTextEdit variable name textArea.
But that's not going to help much because you can only specify signal and slot method names and parameter types when calling connect so you can't specify lineEdit.text() in there.
Since the append() slot expects a QString, ideally you would want to connect a signal that includes a QString but there is no such signal for QLineEdits.
You pretty much have to write a slot yourself that you can connect to returnPressed() and call textArea.append(lineEdit.text()) from there. You will need to subclass a QObject of some kind to write a slot which would usually mean subclassing QWidget and putting all of your UI building code in its constructor.
You might also notice that your program crashes when you close it. Since Qt likes to manage the destruction of most QObjects itself, it is usually best to allocate all QObject instances on the heap with new. This isn't technically necessary all the time but it is much easier :)
QObject::connect(&lineEdit, SIGNAL(returnPressed()), &textArea, SLOT(append(lineEdit.text())));
returnPressed() doesn't take any arguments, but append(QString) does take one argument; a QString. Thus, if this would work, you would theoretically call append(""), meaning you wouldn't append anything. Using lineEdit.text() wouldn't work either at this place.
I would recommend you to create a class for the widget:
class Widget : public QWidget
{
public:
Widget(QWidget parent = 0);
//Other public functions
private:
//Private functions and variables
public slots:
void boom();
};
Then you can just use
Widget w(0);
w.show();
in your main function.
void boom() would be called by returnPressed(), and it would take lineEdit.text() and append it to the QTextEdit.
I hope this helps.
here is the code it might be helpful.....
#include "hwidget.h"
Hwidget::Hwidget(QWidget *parent) :
QWidget(parent)
{
}
void Hwidget::mainform_init(void)
{
lineeditp = new QLineEdit;
quitp = new QPushButton("&Exit");
hboxlayoutp = new QHBoxLayout;
hboxlayoutp->addWidget(lineeditp);
hboxlayoutp->addWidget(quitp,0,0);
vboxlayoutp = new QVBoxLayout;
texteditp = new QTextEdit;
texteditp->setReadOnly(true);
vboxlayoutp->addWidget(texteditp,0,0);
vboxlayoutp->addLayout(hboxlayoutp);
QWidget *mywin = new QWidget;
mywin->setLayout(vboxlayoutp);
mywin->setWindowTitle("My Sig and Slot");
mywin->show();
lineeditp->setFocus();
}
void Hwidget::mcopy(void)
{
qDebug() <<"i am your copy slot";
texteditp->setText(lineeditp->text());
lineeditp->clear();
}
#include <QApplication>
#include "hwidget.h"
int main (int argc, char *argv[])
{
QApplication app(argc,argv);
Hwidget *hwin = new Hwidget;
hwin->mainform_init();
hwin->connect(hwin->quitp,SIGNAL(pressed()),
qApp,SLOT(quit()));
hwin->connect(hwin->lineeditp,SIGNAL(returnPressed()),
hwin,SLOT(mcopy()));
return app.exec();
return 0;
}
#ifndef HWIDGET_H
#define HWIDGET_H
#include <QWidget>
#include <QTextEdit>
#include <QLineEdit>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPushButton>
#include <QObject>
#include <QString>
#include <QDebug>
class Hwidget : public QWidget
{
Q_OBJECT
public:
explicit Hwidget(QWidget *parent = 0);
void mainform_init(void);
signals:
public slots:
void mcopy(void);
private:
QHBoxLayout *hboxlayoutp;
QVBoxLayout *vboxlayoutp;
public:
QPushButton *quitp;
QLineEdit *lineeditp;
QTextEdit *texteditp;
};
#endif // HWIDGET_H