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.
Related
I'm really stuck on one problem that I want to solve. the problem is that I have a Class for QMainWindow which holds the Ui variable for that form. Now I want to be able to edit that Form using the Ui variable in that class on a QDialog cpp file. I probably sound really stupid and I really have no idea how I should explain this, but I have code which maybe can help.
MainWindow.h:
#include "ui_mainwindow.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
Ui::MainWindow *ui;
}
MainWindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "dialog.h"
Dialog *dialog;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
dialog = new Dialog(this);
dialog->show();
}
QDialog.cpp:
#include "ui_mainwindow.h"
#include "mainwindow.h"
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
}
Dialog::~Dialog()
{
delete ui;
}
Ui::MainWindow *mainui;
void Dialog::on_pushbutton_clicked(){
mainui->label->setText("test");
}
So as you can see from the above code, it shows that I have a pointer to the Ui variable however its uninitialised, therefore it would lead to a SIGSEGV error, so how to do Initialize this pointer? any help here is highly appreciated, and even though this is probably really simple I just don't know what to do. (I have looked at other questions but I couldn't quite grasp what to do, so please explain what I am to do before linking me to a similar question. Also, I have left out the Dialog.h file as I didn't think it was needed, please tell me if I need to show it, thanks!).
Generally in C++ you should practice what is called encapsulation - keep data inside a class hidden from others that don't need to know about it. It's not good to have multiple pointers to the UI object as now all those other objects have to know how the main window UI is implemented.
In this case, what I would recommend is to use Qt's signals and slots mechanism to allow the dialog to tell the main window what you need it to do. That has the advantage that if you add more dialogs, or change how things are implemented in the main window, you don't need to alter the signal slot mechanism, and the details are hidden cleanly.
So - for your dialog, add a signal like this in the header file
class Dialog : QDialog
{
Q_OBJECT
signals:
void setTextSignal(QString text);
}
and in your main window header, add a slot.
class MainWindow : public QMainWindow
{
Q_OBJECT
public slots:
void setTextSlot(const QString &text);
}
now in your method where the button is pressed,
void Dialog::on_pushbutton_clicked()
{
emit setTextSignal("test");
}
and in your main window
void MainWindow::setTextSlot(const QString &text)
{
mainUi->label->setText(text);
}
The final part is to connect the signal and slot together, which you would do in your main window function where you create the dialog:
void MainWindow::on_pushButton_clicked()
{
dialog = new Dialog(this);
connect(dialog, SIGNAL(setTextSignal(QString)), this, SLOT(setTextSlot(QString)));
dialog->show();
}
You can see there are many advantages to this; the Dialog no longer needs a pointer to the main window UI, and it makes your code much more flexible (you can have other objects connected to the signals and slots as well).
Short answere - your can't! If you want to create a new instance of the ui, you would have to do:
MainWindow::Ui *ui = new MainWindow::UI();
ui->setupUi(this);
However, the this-pointer for a UI created for a QMainWindow based class must inherit QMainWindow - thus, you can't.
In general, it is possible if you create your Ui based on a QWidget instead of a QMainWindow, since both inherit QWidget.
Alternativly, you could try the following:
QMainWindow *subWindow = new QMainWindow(this);
subWindow->setWindowFlags(Qt::Widget);
MainWindow::Ui *ui = new MainWindow::UI();
ui->setupUi(subWindow );
//... add the mainwindow as a widget to some layout
But I would guess the result will look weird and may not even work in the first place.
There is one QPushButton in a QWidget, click the button should open another QWidget, as coded below:
project.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = untitled
TEMPLATE = app
CONFIG(debug, debug|release) {
DESTDIR = debug
} else {
DESTDIR = release
}
INCLUDEPATH += ..
SOURCES += ../main.cpp\
../widget.cpp \
../secondwidget.cpp
HEADERS += ../widget.h \
../secondwidget.h
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include <QVBoxLayout>
#include <QPushButton>
#include "secondwidget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
resize(400, 300);
QVBoxLayout *vLayout = new QVBoxLayout(this);
QPushButton *bt = new QPushButton("Open 2nd Widget");
vLayout->addWidget(bt, 1, Qt::AlignRight);
SecondWidget *secondWidget = new SecondWidget();
// SecondWidget *secondWidget = new SecondWidget(this);
connect(bt, &QPushButton::clicked, secondWidget, &SecondWidget::show);
}
Widget::~Widget()
{
}
secondwidget.h
#ifndef SECONDWIDGET_H
#define SECONDWIDGET_H
#include <QWidget>
class SecondWidget : public QWidget
{
Q_OBJECT
public:
explicit SecondWidget(QWidget *parent = 0);
signals:
public slots:
};
#endif // SECONDWIDGET_H
secondwidget.cpp
#include "secondwidget.h"
SecondWidget::SecondWidget(QWidget *parent) : QWidget(parent)
{
setAttribute( Qt::WA_QuitOnClose, false );
}
Passing this for the SecondWidget constructor (as in the commented line) will brake my logic at some point. So the SecondWidget doesn't show anymore, when the button is clicked.
What's going on?
In addition to the problems with your destructor and constructor (and the memory leak you have because of the second), and the project file here are a few things that you may want to know in order to understand the whole parent situation:
You don't need to pass this. The purpose of assigning a parent is to be able to simplify the cleanup procedure for QObject instances (including all classes inheriting from the QObject class). Qt parent-child model follows the C++ standard that is destructors are invoked in the reversed order of their constructors. In simple terms this means the following:
Imagine you create widget A, B and C. Using the parent property you assign B and C to be children of A. So here we have the order of creation:
A (parent)
B,C (children)
Now at some point you want to destroy your widgets (example: closing the application). The standard C++ way is to destroy them in the following (reversed to the construction) order:
B, C (children)
A (parent)
And this is indeed what happens if we go for the parent first. However we are dealing with Qt here so we have to consider one additional feature the library provides - slots and signals.
Whenever a QObject gets destroyed it emits a signal. Based on the role the object plays in the parent-child relationship the outcome is one of the following:
only the QObject that is destroyed gets destroyed - this is what happens when a child gets destroyed before we destroy its parent
all children of that QObject get destroyed first followed by the destruction of the QObject itself - this is what happens when a parent gets destroyed. The emitted signal is caught by all its children which in return also get eliminated.
However assigning a parent is not mandatory. You can do the cleanup manually yourself. Read more about the parent-child model in the Qt documentation.
In addition to all that ownership transfer (a widget becomes a child of another widget) often happens automatically so it is not necessary to explicitly specify a widget's parent. Again taking things out of the Qt documentation an exception is made here. If you add a widget to a layout, the ownership is NOT transferred to the layout itself but to the QWidget it is part of.
Finally there is one important case when not assigning a parent makes things very, very different. Here I shall quote the Qt documentation on QObject:
Setting parent to 0 constructs an object with no parent. If the object is a widget, it will become a top-level window.
So if you have a QWidget and don't add it to some layout (and indirectly to the QWidget that has that layout) for example it will automatically become a separate window.
EDIT:
Check your constructor and in particular the way you work with your secondWidget object. As I've mentioned above in case you don't want to assign it to a parent widget you need to take care of the cleaning.
You dynamically allocate the memory for it
SecondWidget *secondWidget = new SecondWidget();
and even connect it to your button
connect(bt, &QPushButton::clicked, secondWidget, &SecondWidget::show);
however you never release the allocated memory using delete or assigning it to a parent widget, which will take care of it automatically. Hence you create a memory leak. Connecting a QObject via signals and slots doesn't have anything to do with transfer of ownership.
I personally assume that you actually want a secondWidget to be an extra window shown on the screen. In this case you need to create a class member of type SecondWidget
SecondWidget *secondWidget;
then allocate it inside your constructor and connect whichever slots and signals you want
Widget::Widget(QWidget *parent) : QWidget(parent)
{
//...
secondWidget = new SecondWidget();
connect(bt, &QPushButton::clicked, secondWidget, &SecondWidget::show);
}
and finally release the memory inside your constructor:
Widget::~Widget()
{
delete secondWidget;
}
Otherwise as I said you are basically creating a reference to a memory block and right after you leave your constructor that reference gets destroyed since it runs out of scope.
EDIT 2:
Here is a small example how to do it if you want secondWidget as a child to your main widget:
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
widget.hpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "secondwidget.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
SecondWidget *secondWidget = new SecondWidget(this); # secondWidget is now officially adopted by Widget
# If you skip the parent assignment inside SecondWidget you can call secondWidget->setParent(this) here
connect(ui->pushButton, SIGNAL(clicked(bool)), secondWidget, SLOT(show()));
}
Widget::~Widget()
{
delete ui;
}
secondwidget.hpp
#ifndef SECONDWIDGET_H
#define SECONDWIDGET_H
#include <QObject>
#include <QDialog>
class SecondWidget : public QDialog
{
Q_OBJECT
public:
explicit SecondWidget(QWidget *parent = 0);
~SecondWidget();
};
#endif // SECONDWIDGET_H
secondwidget.cpp
#include "secondwidget.h"
#include <QFormLayout>
#include <QLabel>
SecondWidget::SecondWidget(QWidget *parent) : QDialog(parent)
{
# If you don't call the constructor of your superclass you can still assign a parent by invoking setParent(parent) here
QFormLayout *layout = new QFormLayout();
QLabel *label = new QLabel("SecondWidget here");
layout->addWidget(label); # Transfer ownership of label to SecondWidget
setLayout(layout);
}
SecondWidget::~SecondWidget()
{
}
Note the setParent(parent) inside the SecondWidget's constructor. You either have to invoke the superclass constructor (as you have done) or manually call setParent(parent). If you don't do that yoursecondWidgetwill not be assigned as a child to your main widget where you create it and thus you will produce a memory leak. You can also invokesecondWidget->setParent(this)` from within the constructor of your main widget in order to set the parent.
Here is how you can check if the parent-child hierarchy is okay:
To each QObject that you have (QWidget, QLayout etc. are all subclasses of QObject) assign an object name using QObject::setObjectName("some name")
At the end of both of your constructors add:
for(int i = 0; i < this->children().count(); i++)
std::cout << this->children()[i]->objectName().toStdString() << std::endl; // Add #include <iostream> to get the output
which basically traverses all the children of this (Widget or SecondWidget) and displays its children. In my case I got
label // Printing out children of SecondWidget
formLayout // Printing out children of SecondWidget
gridLayout // Printing out children of Widget
pushButton // Printing out children of Widget
main second widget // Printing out children of Widget
once I launched my application.
EDIT 3: Ah, I didn't notice that you are calling the QWidget(parent) in you SecondWidget constructor. This also does the trick so you don't need setParent(parent). I have altered my second EDIT.
This usually happens if
classes are linked
destructor calls aren't properly being called
Makefile needs some changes.
Actual Error?
Makefile needs to be remade using the cmd prompt or Terminal
Make sure that your destructor doesn't call itself so this change the remedy of execution.
I will start off by explaining my main goal. I have a main window with 7 buttons on it(amongst other things), when you hit each button, it closes out the current window and opens up a new window. All the windows will have the same 7 buttons, so you can go between each window. With all windows having the exact same 7 buttons, I wanted to set up a function that each class can call to set up each button and connect to a slot() in my mainwindow.cpp(called setupSubsystemButtons in example below). However, I can't seem to get the window to close using the standard "this->close()"...it works when I go from the main window to another window(the main window closes) but when I go from a different window to say the home window, the different window doesn't close. Suggestions would be greatly appreciated. My guess is that my understanding of "this" when it comes to calling slots in another class is wrong.
mainwindow.cpp( the parts that are relevant)
void MainWindow::ECSgeneralScreen()
{
ECSgeneralCommand *ECSgeneral = new ECSgeneralCommand;
this->close();
ECSgeneral->show();
//opens up the ECS screen
}
void MainWindow::homeScreen()
{
MainWindow *home = new MainWindow;
this->close();
home->show();
//opens up the ECS screen
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::setupSubsystemButtons(QGridLayout *layout)
{
//Push Button Layout
homeScreenButton = new QPushButton("Home");
layout->addWidget(homeScreenButton, 3, 11);
connect(homeScreenButton, SIGNAL(clicked()), this, SLOT(homeScreen()));
ECSgeneralScreenButton = new QPushButton("General");
layout->addWidget(ECSgeneralScreenButton,5,11);
connect(ECSgeneralScreenButton, SIGNAL(clicked()), this, SLOT(ECSgeneralScreen()));
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtWidgets>
#include <QDialog>
namespace Ui {
class MainWindow;
}
class MainWindow : public QDialog
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
QWidget *window;
void setupSubsystemButtons(QGridLayout *layout);
~MainWindow();
private slots:
public slots:
void ECSgeneralScreen();
void homeScreen();
};
#endif // MAINWINDOW_H
ecsgeneralcommandWindow
include "ecsgeneralcommand.h"
#include "mainwindow.h"
#include <QtWidgets>
#include <QtCore>
ECSgeneralCommand::ECSgeneralCommand(MainWindow *parent) : QDialog(parent)
{
QGridLayout *layout = new QGridLayout;
QWidget::setFixedHeight(600);
QWidget::setFixedWidth(650);
...
//Setup Subsystem Buttons
test.setupSubsystemButtons(layout);
setLayout(layout);
}
ecsgeneralcommandWindow header
#ifndef ECSGENERALCOMMAND_H
#define ECSGENERALCOMMAND_H
#include <QDialog>
#include <QMainWindow>
#include <QtWidgets>
#include <QObject>
#include "mainwindow.h"
class ECSgeneralCommand : public QDialog
{
Q_OBJECT
public:
explicit ECSgeneralCommand(MainWindow *parent = 0);
private:
MainWindow test;
public slots:
};
#endif // ECSGENERALCOMMAND_H
Slots are just normal functions. When Qt invokes a slot, it ends up calling the appropriate receiver's method. In other words, this equals to the value of the 3rd argument of your connect statements. You passed this there, so the receiver is MainWindow object. E.g. MainWindow::homeScreen method always tries to close MainWindow. If it is already hidden, this action takes no effect.
You should either have a slot in each window class and connect buttons to appropriate receivers, or use a pointer to the currently active window instead of this when calling close(). But your architecture is strange in the first place. Why would you need to create these buttons for each window? It is reasonable to create them once and use in all windows. Also hiding and showing windows is not necessary. You can create one main window with buttons and a QStackedWidget that will contain the content of all other windows. Maybe you can even use QTabWidget instead of these buttons.
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.
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