Hi I am new to the Qt and C++. I have one requirement where I have to access signal of Base class using derived class object. Whether is it possible and if yes then how? I tried to implement it using one simple application, but it is not working.
//Base Class
//Which is emitting signal temp() in it's constructor
class IPCBase : public QWidget
{
Q_OBJECT
public:
explicit IPCBase(QWidget *parent = 0);
~IPCBase();
signals:
void temp();
private:
Ui::IPCBase *ui;
};
//cpp
IPCBase::IPCBase(QWidget *parent) :
QWidget(parent),
ui(new Ui::IPCBase)
{
ui->setupUi(this);
qDebug()<<"coming to base cpp";
emit temp();
}
I have created one derived class named IPCReceiver and using the object of IPCReceiver(derived class) I am trying to use the connect statement in another class(Form) and call the SLOT.It is not giving compilation error but the SLOT is not getting called.
Form::Form(QWidget *parent) :
QWidget(parent),
ui(new Ui::Form)
{
ui->setupUi(this);
IPCReceiver *receiver = new IPCReceiver;
connect(receiver, SIGNAL(temp()), this, SLOT(debug()));
}
Thanks in advance.
Your signal temp() emits in constructor of a class. That's why your connection is unusable. In your logic: at first emits signal in constructor, then you create connection. You must emit signal after creation of the connection.
You generally shouldn't be emitting any signals from constructors of any class deriving from QObject as it makes the class much harder to use. You need to emit the signal from the event loop, after the constructor has finished. There are at least two idioms that express that:
Using a Connection to QObject::destroyed
IPCBase::IPCBase(QWidget *parent) :
QWidget(parent),
ui(new Ui::IPCBase)
{
ui->setupUi(this);
QObject src;
connect(&src, &QObject::destroyed, this, &IPCBase::temp, Qt::QueuedConnection);
}
The src object is used only as a source of a generic signal.
Using QMetaObject::invokeMethod
IPCBase::IPCBase(QWidget *parent) :
QWidget(parent),
ui(new Ui::IPCBase)
{
ui->setupUi(this);
QMetaObject::invokeMethod(this, "temp", Qt::QueuedConnection);
}
As an aside, you don't need to allocate Ui dynamically, you can make it a member variable instead; then the compiler-generated default destructor is sufficient.
Related
I'm learning qt framework for c++ and I'm not able to understand what this statement does:
Window::Window (QWidget *parent) : QWidget(parent)
{
....
}
// (QWidget *parent) : QWidget(parent) <---
This statement means calling base class constructor. In your context it is widget class. It is base class for window. Actually parent widget will be passed to this constructor
I have a pushbutton on my main form named pushButton_Tar1ex, it has some text on it. I have another pushbutton on my main form named pushButton_RenameTargets.
When I push pushButton_RenameTargets, the following code executes:
void MainWindow::on_pushButton_RenameTargets_clicked()
{
RenameTargets renametargets;
renametargets.target1NameCurrent = ui->pushButton_Tar1ex->text();
renametargets.setModal(true);
renametargets.exec();
}
So my second window is called RenameTargets and the first line of code in the function creates an object renametargets. Then I set renametargets.target1NameCurrent = ui->pushButton_Tar1ex->text();which target1NameCurrent is in the public: portion of renametargets.h. Then in RenameTargets I set the label to the pushbuttontext by:
RenameTargets::RenameTargets(QWidget *parent) :
QDialog(parent),
ui(new Ui::RenameTargets)
{
ui->setupUi(this);
ui->label_currentNameTarget1->setText(target1NameCurrent);
}
Yet nothing appears in the label label_currentNameTarget1.
I have run some qDebug stuff and what happens is when RenameTargets renametargets; is run when I push the button it runs through the RenameTargets ui(new Ui::RenameTargets) part before the renametargets.target1NameCurrent = ui->pushButton_Tar1ex->text(); is declared.
What am I doing wrong? any help would be appreciated.
you set renametargets.target1NameCurrent member variable after constructing your object renametargets... that's the problem, since you use target1NameCurrent in your constructor to set your label.
One solution is to pass target1NameCurrent through the constructor.
void MainWindow::on_pushButton_RenameTargets_clicked()
{
RenameTargets renametargets(this, ui->pushButton_Tar1ex->text());
renametargets.setModal(true);
renametargets.exec();
}
RenameTargets.cpp
RenameTargets::RenameTargets(QWidget *parent, const QString & target1NameCurrent):
QDialog(parent),
ui(new Ui::RenameTargets),
target1NameCurrent(target1NameCurrent)
{
ui->setupUi(this);
ui->label_currentNameTarget1->setText(target1NameCurrent);
}
RenameTargets.h
class RenameTargets{
public:
RenameTargets(QWidget *parent, const QString & target1NameCurrent);
};
Also, you can just initialize label_currentNameTarget1 in another function in RenameTargets after constructing renametargets and initializing renametargets.target1NameCurrent.
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.
I am trying to create a thread for a Scanner class which handles all the events for this particular class, thereby freeing the GUI thread. I have an exit button on my GUI which simply calls qApp->quit() to exit the application, but I am not sure how to deal with the thread in my Scanner class. I am seeing the following errors in the debug log when the application is exited.
QThread::wait: Thread tried to wait on itself
QThread::wait: Thread tried to wait on itself
QThread: Destroyed while thread is still running
In Scanner.cpp (Omitted other functions)
Scanner::Scanner() :
{
this->moveToThread(&m_thread);
connect(&m_thread, &QThread::finished, this, &QObject::deleteLater);
connect(this, SIGNAL(StartEnroll()), this, SLOT(StartEnrollment()));
m_thread.start();
}
Scanner::~Scanner()
{
m_thread.quit(); // Not sure if this is the correct
m_thread.wait();
}
In main Window.cpp (Omitted other functions)
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->ExitButton, SIGNAL(released()), this, SLOT(Quit()));
connect(&m_scanner, SIGNAL(FinishedEnroll(bool)), this, SLOT(EnrollDone(bool)));
}
void MainWindow::Quit()
{
close();
qApp->quit();
}
Any pointers on how to quit safely quit the application in a multi-threaded application.
You need to let the Scanner class know that the application is exiting.
Add the following line to the constructor of MainWindow
connect(qApp, SIGNAL(aboutToQuit()), &m_scanner, SLOT(deleteLater()));
UPDATE:
connect(&m_thread, &QThread::finished, this, &QObject::deleteLater);
Should not be in the constructor of Scanner
and
m_thread.quit();
m_thread.wait();
should not be in the destructor of Scanner
In fact, m_thread should not be part of Scanner in any way. The QThread class does not represent a thread, it is a thread manager and should be owned and controlled from the thread where it was created.
There are a number of methods of using threads in Qt, many not documented very well. If you want to use the
workerObject->moveToThread(&thread);
thread.start();
way of using threads, then m_thread should be a member of MainWindow class and these function calls should be made in it's constructor.
Thanks for clarifying and for the solutions posted above. Here is what I did based on what was posted before.
ScannerThread.h
#include <QThread>
class ScannerThread : public QThread
{
public:
ScannerThread();
~ScannerThread();
};
ScannerThread.cpp
#include "scannerthread.h"
ScannerThread::ScannerThread()
{
connect(this, &QThread::finished, this, &QObject::deleteLater);
}
ScannerThread::~ScannerThread()
{
quit();
wait();
}
In MainWindow.h
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void Quit();
private:
Ui::MainWindow *ui;
Scanner m_scanner;
ScannerThread m_scannerThread;
};
In MainWindow.cpp (Omitting other functions)
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_scanner.moveToThread(&m_scannerThread);
m_scannerThread.start();
connect(ui->ExitButton, SIGNAL(released()), this, SLOT(Quit()));
connect(qApp, SIGNAL(aboutToQuit()), &m_scanner, SLOT(deleteLater()));
}
void MainWindow::Quit()
{
close();
qApp->quit();
}
This seemed to work fine for me. If you see any errors please correct, and thanks for helping with this.
First you should move your QThread out of the Scanner class. A QThread manages a thread, so you can't call functions that are related to the thread management inside that thread itself. That's the reason you're getting the message about the thread waiting on itself.
You should rather have something like that:
m_scanner.moveToThread(&m_thread);//make the thread a member of your window
m_thread.start();
Then, in your quit function, do as you want, either waiting on the thread (better) or terminating it (worse) before exiting, or a tradeoff, such as:
void MainWindow::Quit()
{
close();
//Wait maximum 1 second
if(!m_thread.wait(1000) {
m_thread.terminate();
}
qApp->quit();
}
I have a MainWindow class and Another class. Another class has method createView that create new QGraphicsView. This method I call from MainWindow and I also want to layout this view on my form. It looks like:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)) {
...
AnotherClass object(this);
object.createView();
...
}
...
void AnotherClass::createView() {
QGraphicsView *gv= new QGraphicsView(mainWindow); // mainWindow - pointer to MainWindow object
gv->show();
}
But it doesn't work so good... actually it does't work at all. And yes, I save pointer on MainWindow object in my Another class as mainWindow, that I take from Another class constructor.
If use
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)) {
...
QGraphicsView *gv= new QGraphicsView(this);
gv->show();
...
}
It will work fine, but this solution doesn't satisfied me.
Problem was in creating variable on stack.