I've tried to emit a custom signal login() from my loginmanager class to the mainwindow. The signal is fired on the loginButtonClicked slot, and to my understand on the signal/slot mechanism, it should be able to capture any signal fired event and "look" for the corresponding slot to be execute. But it doesn't work as what I've think.
The connect function returns 1, which means it is able to be implemented in the moc file, and it DOES work if i run the m_LoginManager->setLogin() which fires the login() signal.
But what I prefer is the signal is emitted by the loginButton, and pass to the mainwindow to be process (in this case, init()).
Please correct me if I'm wrong.
Below are the code.
loginmanager.cpp
LoginManager::LoginManager(QWidget * parent) : QWidget(parent)
{
ui.setupUi(this);
connect(ui.loginButton, SIGNAL(clicked()), this, SLOT(loginButtonClicked());
}
LoginManager::~LoginManager()
{
}
void LoginManager::setLogin()
{
emit login();
}
void LoginManager::loginButtonClicked()
{
setLogin();
}
loginmanager.hpp
#include <QWidget>
#include "ui_loginmanager.h"
class DatabaseManager;
class SettingManager;
class LoginManager : public QWidget
{
Q_OBJECT
public:
LoginManager(QWidget * parent = Q_NULLPTR);
~LoginManager();
void setLogin();
signals:
void login();
public slots:
void loginButtonClicked();
private:
Ui::LoginManager ui;
};
mainwindow.hpp
#include <QtWidgets/QMainWindow>
#include "ui_safeboxmanager.h"
class SafeboxManager : public QMainWindow
{
Q_OBJECT
public:
SafeboxManager(QWidget *parent = 0);
~SafeboxManager();
public slots:
void init();
private:
Ui::SafeboxManagerClass ui;
LoginManager* m_LoginManager;
};
#endif // SAFEBOXMANAGER_H
mainwindow.cpp
#include "safeboxmanager.hpp"
#include "loginmanager.hpp"
SafeboxManager::SafeboxManager(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
m_LoginManager = new LoginManager();
ui.mainToolBar->setEnabled(false);
ui.tableWidget->setEnabled(false);
connect(m_LoginManager, SIGNAL(login()), this, SLOT(init()));
//m_LoginManager->setLogin() << this work
}
SafeboxManager::~SafeboxManager()
{
}
void SafeboxManager::init()
{
ui.mainToolBar->setEnabled(true);
ui.tableWidget->setEnabled(true);
}
SafeboxManager and LoginManager objects must live long enough. Check life times.
Related
I have 2 UIs and I am trying to update one from the other. I have the classes phone, user_ui, test_ui, and user_transmitter. The user_ui is the one that a user can interact with such as if the user pressed the toggle button on the UI of the user_ui then the status should be displayed on the UI of the test_ui. The user_transmitter transmits the inputs and outputs and that's its only job, there is no UI to it. The phone class just contains the variables and nothing else.
user_ui.h:
#ifndef USER_UI_H
#define USER_UI_H
#include <QMainWindow>
#include <QTimer>
#include <phone.h>
#include "user_transmitter.h"
#include "test_ui.h"
using namespace std;
QT_BEGIN_NAMESPACE
namespace Ui { class user_ui; }
QT_END_NAMESPACE
class user_ui : public QMainWindow
{
Q_OBJECT
public:
user_ui(QWidget *parent = nullptr);
~user_ui();
Phone phone;
QTimer *timer;
private:
Ui::user_ui *ui;
user_transmitter user_t;
void updateUI();
void connections();
signals:
void toggeButton(int);
private slots:
void toggleButton1();
void update();
};
#endif // USER_UI_H
user_ui.cpp:
#include "user_ui.h"
#include "ui_user_ui.h"
#include <QMessageBox>
user_ui::user_ui(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::user_ui)
{
ui->setupUi(this);
update();
connections();
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
user_ui::~user_ui()
{
delete ui;
}
void user_ui::update()
{
ui->status->setText((phone.status) ? "On" : "Off");
}
void user_ui::connections() {
QObject::connect(this, &user_ui::toggleButton, &user_t, &user_transmitter::toggleButton);
connect(ui->button, SIGNAL(released()), this, SLOT(toggleButton1()));
}
void user_ui::toggleButton1() {
phone.status= !phone.status;
emit toggleButton(1);
}
user_transmitter.h:
#ifndef USER_TRANSMITTER_H
#define USER_TRANSMITTER_H
#include <vector>
#include "phone.h"
#include "test_ui.h"
class user_transmitter : public QObject
{
Q_OBJECT
public:
explicit user_transmitter(QObject *parent = nullptr);
Phone phones;
public slots:
void toggleButton(int);
private:
void setUpSignals();
test_ui test;
signals:
void button(int,bool);
};
#endif // USER_TRANSMITTER_H
user_transmitter.cpp:
#include "user_transmitter.h"
user_transmitter::user_transmitter(QObject *parent) : QObject(parent)
{
setUpSignals();
}
void user_transmitter::setUpSignals(){
QObject::connect(this, &user_transmitter::button, &test, &test_ui::button);
}
void user_transmitter::button(int index)
{
phones.status = !phones.status;
cout << "From transmitter: " << phones.status<< endl;
emit button(index, phones.status);
}
test_ui.h:
#ifndef TEST_UI_H
#define TEST_UI_H
#include <QWidget>
namespace Ui {
class test_ui;
}
class test_ui : public QWidget
{
Q_OBJECT
public:
explicit test_ui(QWidget *parent = nullptr);
~test_ui();
QTimer *timer;
public slots:
void button(int,bool);
private:
Ui::test_ui *ui;
signals:
};
#endif // TEST_UI_H
test_ui.cpp:
#include "test_ui.h"
#include "ui_test_ui.h"
test_ui::test_ui(QWidget *parent) :
QWidget(parent),
ui(new Ui::test_ui)
{
ui->setupUi(this);
}
test_ui::~test_ui()
{
delete ui;
}
void test_ui::button(int index, bool status){
qDebug() << "From test: " << status;
ui->status->setText((status) ? "On" : "Off");
}
My problem is that when I press the toggle button of user_ui's UI, the "on" or "off" status should display on the user UI and the test_ui. It displays on the user UI but not the test_ui. The qdebug line in the transmitter and the test class print but the UI of the test does not get updated so I am guessing that the function is getting called. So, I am not sure if it's a signal slot problem since the qdebug line is printing or if it's a timing problem.
I tested out my test UI by trying to print something in the QLineEdit box from a different function in that class and it works so there is no problem there.
I was able to solve my problem by simply adding my Signal and Slot connections from the 2 classes (user_ui & user_transmitter) to my main.cpp.
I have a QMainWindow Application which also includes an QStackedWidget.
The pages of the QstackedWidget are promoted to ui widgets for example heating_widget.ui
If I use a button slot on my QMainWindow I can use this to get my application to fullscreen:
void SmartHome::on_fullscreen_on_clicked()
{
SmartHome::setWindowState(Qt::WindowFullScreen);
}
But how can I do this from a button which is in the heating_widget.cpp file?
Using:
void heating_widget::on_fullscreen_on_clicked()
{
SmartHome::setWindowState(Qt::WindowFullScreen);
}
obviously doesn't work and throws this error at me:
cannot call member function 'void
QWidget::setWindowState(Qt::WindowStates)' without object
SmartHome::setWindowState(Qt::WindowFullScreen);
I know this has something to do with parent() but I can't get it to work.
Do you have any idea?
My smarthome.h file:
#ifndef SMARTHOME_H
#define SMARTHOME_H
#include <QTime>
#include <QMainWindow>
namespace Ui {
class SmartHome;
}
class SmartHome : public QMainWindow
{
Q_OBJECT
public:
explicit SmartHome(QWidget *parent = 0);
~SmartHome();
private slots:
void on_Info_Button_clicked();
void on_News_Button_clicked();
void on_Heating_clicked();
void timerslot();
void on_Config_clicked();
void on_About_clicked();
public slots:
void setFullscreen();
private:
Ui::SmartHome *ui;
QTimer* myTimer;
};
#endif // SMARTHOME_H
My heating_widget.h :
#ifndef HEATING_WIDGET_H
#define HEATING_WIDGET_H
#include "smarthome.h"
#include <QWidget>
namespace Ui {
class heating_widget;
class SmartHome;
}
class heating_widget : public QWidget
{
Q_OBJECT
public:
explicit heating_widget(QWidget *parent = 0);
~heating_widget();
private slots:
void on_fullscreen_on_clicked();
private:
Ui::heating_widget *ui;
};
#endif // HEATING_WIDGET_H
and my heating.widget.cpp:
#include "heating_widget.h"
#include "ui_heating_widget.h"
#include "smarthome.h"
#include "iostream"
heating_widget::heating_widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::heating_widget)
{
ui->setupUi(this);
QObject::connect(ui->fullscreen_on, SIGNAL(clicked()), this , SLOT(SmartHome::setFullscreen()));
}
heating_widget::~heating_widget()
{
delete ui;
}
void heating_widget::on_fullscreen_on_clicked()
{
parentWidget()->setWindowState(Qt::WindowFullScreen);
std::cout<<"clicked"<<std::endl;
}
I would do it in the following way:
void heating_widget::on_fullscreen_on_clicked()
{
foreach(QWidget *widget, QApplication::topLevelWidgets()) {
if (auto mainWindow = qobject_cast<SmartHome *>(widget)) {
mainWindow->setWindowState(Qt::WindowFullScreen);
}
}
}
The idea is finding your main window among application top level widgets and change its state. This code can be called from anywhere in your application regardless of the windows hierarchy.
The core of my project is independent of GUI framework that's why I prefer std::thread. But Qt gives me an error when thread is using.
The inferior stopped because it received a signal from the operating system.
Signal name: SIGSEGV
Signal meaning: Segmentation fault
//MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <thread>
#include <mutex>
#include <QMainWindow>
namespace Ui { class MainWindow; }
struct Observer
{
virtual void notify() = 0;
};
class Core
{
public:
std::thread *run()
{
std::thread thread(&Core::runP, this);
thread.detach();
return &thread;
}
void setObserver(Observer *observer) { _observer = observer; }
int ii() const { return _ii; }
void nextIi() { _ii++; }
void lock() { _mutex.lock(); }
bool tryLock() { return _mutex.try_lock(); }
void unlock() { _mutex.unlock(); }
private:
void runP()
{
for (int i = 1; i <= 1000; i++) {
if (i % 10 == 0) {
lock();
nextIi();
unlock();
notify();
}
}
}
void notify() { _observer->notify(); } //!!!
Observer *_observer;
int _ii;
std::mutex _mutex;
};
struct MwObserver : public Observer
{
explicit MwObserver(struct MainWindow *mainWindow) { _mainWindow = mainWindow; }
virtual void notify();
MainWindow *_mainWindow;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow() { delete _ui; }
void upd();
public slots:
void run() { _core.run(); }
private:
Ui::MainWindow *_ui;
MwObserver _observer;
Core _core;
};
inline void MwObserver::notify() { _mainWindow->upd(); }
#endif
-
//MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
_ui(new Ui::MainWindow),
_observer(this)
{
_ui->setupUi(this);
connect(_ui->pushButtonRun, SIGNAL(clicked(bool)), this, SLOT(run()));
}
void MainWindow::upd()
{
_core.lock();
setWindowTitle(QString::number(_core.ii()));
_core.unlock();
}
There are multiple problems here, first and most obvious was already noted by perencia. You are returning a pointer to stack variable. In c++ terms it's unacceptable.
Secondly. The crash comes from not using std::thread, but from race condition. The Qt event loop does not know about you mutex, so your setWindowTitle call is introducing a race, that leads to crash.
You need to use QMetaObject::invokeMethod to post function to the Qts event loop.
Example:
change
inline void MwObserver::notify() { _mainWindow->upd(); }
to
inline void MwObserver::notify() {
if(!QMetaObject::invokeMethod(_mainWindow, "upd", Qt::QueuedConnection))
std::cerr << " Failed to invoke method" << std::endl;
}
additional includes may apply
This updates the GUI from a thread different then the GUI thread! Which is not allowed.
Why not to use QThread and a signal/slot mechanism to update your window title. The Qt framework does the thread switching automatically.
class Core : public QObject
{
Q_OBJECT
public:
explicit Core(QObject * parent = 0) : QObject(parent) {}
signals:
void notify();
public slots:
void nextIi() { _ii++; }
void runP()
{
for (int i = 1; i <= 1000; i++) {
if (i % 10 == 0) {
nextIi();
notify();
}
}
}
private:
Q_DISABLE_COPY(Core);
int _ii;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void run() {_th.start();}
void upd(int ii) {setWindowTitle(QString::number(ii));}
private:
Ui::MainWindow *_ui;
Core _core;
QThread _th;
};
//MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
_ui(new Ui::MainWindow),
_observer(this)
{
_ui->setupUi(this);
connect(_ui->pushButtonRun, SIGNAL(clicked(bool)), this, SLOT(run()));
connect(&_core, SIGNAL(notify(int)), this, SLOT(upd(int)));
_core.moveToThread(&_th);
}
MainWindow::~MainWindow()
{
delete _ui;
_th.quit();
_th.wait(1000);
}
You are creating thread on the stack and returning a pointer to that. After run() that pointer is no longer valid.
Aside from returning pointer to stack variable and updating GUI from thread object that is not known for QT. I don't see from your code, where you set up _observer member of Core class. There is no setObserver call for _core member of MainWindow class.
So consructor of MainWindow class calls consructor of _core member, but after that _core._observer contains garbage. I think this is the cause of your Segmentaion Fault in call of notify method of Core class.
The answers to all the problems have already been given, let me summarize.
The program crash has nothing to do with the threading, The problem is that the _observer in the _core member of MainWindowis not set. A call to setObserver must be added.
explicit MainWindow( QWidget *parent = nullptr ) :
QMainWindow( parent ),
_observer( this )
{
_core.setObserver( &_observer );
}
This will lead to the next problem, that the observer actually calls the udp message from another thread, causing a UI update in a different thread context. To solve this, it is easiest to use Qt's Qt::QueuedConnection. To enable this we must make upt() a slot.
public slots:
void run();
void upd();
Then we can either call it using QMetaObject::invokeMethod in
inline void MwObserver::notify()
{
QMetaObject::invokeMethod( _mainWindow, "upd", Qt::QueuedConnection );
}
or use a signal / slot connection by deriving MwObserver from QObject, giving it a signal, and connect that signal to the upd slot and raising the signal in notify.
struct MwObserver
: public QObject
, public Observer
{
Q_OBJECT;
signals:
void sigUpd();
public:
explicit MwObserver( MainWindow *mainWindow );
virtual void notify()
MainWindow *_mainWindow;
};
void MwObserver::notify()
{
sigUpd();
}
MwObserver::MwObserver( MainWindow *mainWindow )
{
_mainWindow = mainWindow;
connect( this, SIGNAL(sigUpd()), _mainWindow, SLOT(upd()) )
}
Disclaimer: I haven't used Qt in some time but with X/XMotif on Linux/UNIX the GUI MUST run in the 'main-thread', not spawned threads. Maybe this applies to your situation. Just a thought, have your GUI code run in the main-thread.
The best approach is to wrap pure C++ code with QObejct instance and fire signals when this objects receive some notification from pure C++ code.
SO in your case:
class MwObserver : public QObject, public Observer
{
Q_OBJECT
public:
explicit MwObserver(QObject *parent)
: QObject(parent)
{}
signals:
void SomeEvent();
protected:
// Observer
void notify() {
emit SomeEvent();
}
};
Now MainWindow should connect some slot to signal provided this way and everything should work out of the box (Qt will do thread jumping behind the scenes).
In your code form comment the crash is caused by invalid use of temporary object. This is INVALID C++ code no mater what kind of object is returned:
std::thread *run()
{
std::thread thread(&Core::runP, this);
thread.detach();
return &thread;
}
You cant return a pointer to local object of the function method, since this object becomes invalid immediately when you return a function. This is basic C++ knowledge.
What I'm trying to do is to call an time consuming operation (MockClamWrapper::loadDatabase()) in a separate thread at the moment of creation of my window and then to update my window once the operation is completed. Here is the code that I have.
MockClamWrapper.h
class MockClamWrapper : QObject
{
Q_OBJECT
public:
MockClamWrapper();
~MockClamWrapper();
bool loadDatabase(unsigned int *signatureCount=NULL);
Q_SIGNALS:
void databaseLoaded();
};
MockClamWrapper.cpp
bool MockClamWrapper::loadDatabase(unsigned int *signatureCount){
QThread::currentThread()->sleep(10);
databaseLoaded();
return true;
}
MainWindow.h
#include <QMainWindow>
#include <QFileDialog>
#include "mockclamwrapper.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public slots:
void enableWindow();
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
MockClamWrapper *clam;
void initWindow();
};
MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect((QObject*)clam, SIGNAL(databaseLoaded()),(QObject*)this,SLOT(enableWindow()));
QFuture<void> fut = QtConcurrent::run(this,&MainWindow::initWindow);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::initWindow(){
clam->loadDatabase(NULL);
}
void MainWindow::enableWindow(){
ui->checkFileButton->setEnabled(true);
}
The program compiles, but it crashes right after start. I assume that I do something wrong with slots and signals, but can't find my mistake.
The reason for crash is that you are not making any instance of the class MockClamWrapper. In the connect statement, you are referencing a pointer that points to nothing. Make a new object and then connect :
clam = new MockClamWrapper();
connect(clam, SIGNAL(databaseLoaded()), this, SLOT(enableWindow()));
In my code I want to reroute a signal-slot-connection, i.e. first the slot is connected to signal 1, and after the rerouting it should only be connected to signal 2. For that I used disconnect(this), with this referring to the class which owns the slots (it is in a class function). This command should disconnect all signals from extern from the class. Unfortunately, I get false as return value of disconnect(). Why can't I disconnect the signals? Is there a possibility to get more information?
Strangely, in my example below the disconnect and reconnect does not work, too, I get the same error code from disconnect.
If I remove the line connect(this, &MainWindow::writeLine, class1, &TXClass::emit_signal); from the on_PushButton2_clicked()-function, disconnect() still returns "false", but I get the expected result.
Example code:
Mainwindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
class1 = new TXClass("Class 1");
class2 = new TXClass("Class 2");
connect(this, SIGNAL(writeLine()), class1, SLOT(emit_signal()));
connect(class1, SIGNAL(signal(QString)), this, SLOT(newText(QString)));
}
MainWindow::~MainWindow()
{
delete class1;
delete class2;
delete ui;
}
void MainWindow::newText(QString text)
{
ui->lineEdit->setText(text);
}
void MainWindow::on_pushButton_clicked()
{
emit writeLine();
}
void MainWindow::on_pushButton_2_clicked()
{
qDebug() << "Disconnect result: " << disconnect(this);
connect(this, &MainWindow::writeLine, class2, &TXClass::emit_signal);
connect(this, &MainWindow::writeLine, class1, &TXClass::emit_signal);
connect(class2, &TXClass::signal, this, &MainWindow::newText);
//The onliest signal I want to get now is from class2.
}
MainWindow.h:
#include <QDebug>
#include <txclass.h>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
signals:
void writeLine(void);
public slots:
void on_pushButton_clicked();
void newText(QString text);
void on_pushButton_2_clicked();
private:
Ui::MainWindow *ui;
TXClass *class1, *class2;
};
#endif // MAINWINDOW_H
TXClass.cpp:
#include "txclass.h"
TXClass::TXClass(QString name)
{
TXClass::name = name;
}
void TXClass::emit_signal()
{
emit signal(name);
}
TXClass.h:
#ifndef TXCLASS_H
#define TXCLASS_H
#include <QObject>
class TXClass : public QObject
{
Q_OBJECT
private:
QString name;
signals:
void signal(QString string);
public slots:
void emit_signal(void);
public:
TXClass(QString name);
};
#endif // TXCLASS_H
I've checked how QObject::disconnect is implemented and I don't see how this is supposed to work if you only specify receiver. QMetaObjectPrivate::disconnect will return immediately with false when sender is not specified. This means that second part of QObject::disconnect will no set res to true. The only other place you could get true out of is call to QInternal::activateCallbacks. But it doesn't look like it's instance specific, rather some global stuff (I admit I have no idea what exactly is this suppose to do :/).
I solution that works and seems good enough is this:
void MainWindow::second()
{
qDebug() << "Disconnect result: " << class1->disconnect();
connect(this, &MainWindow::writeLine, class2, &TXClass::emit_signal);
connect(this, &MainWindow::writeLine, class1, &TXClass::emit_signal);
connect(class2, &TXClass::signal, this, &MainWindow::newText);
//The onliest signal I want to get now is from class2.
}