Launch a QWidget from a QMain window - c++

I'm doing my first C++ - Qt4 application and I'm having some trouble "connecting" my different uis.
I have a main window with several buttons and when I click on one, I want another window to open.
The MyMainWindowClass inherits from QMainWindow and the other from QWidget.
Here is the code I have written so far :
#include <iostream>
#include "MyWidgetClass.hpp"
#include "MyMainWindowClass.hpp"
#include "ui_MyMainWindowClassUi.h"
MyMainWindowClass::MyMainWindowClass(QWidget *parent) :
QMainWindow(parent),
m_ui(new Ui::MyMainWindowClassUi)
{
m_ui->setupUi(this);
initConnect();
}
void MyMainWindowClass::initConnect()
{
QObject::connect(m_ui->SomeBtn,
SIGNAL(clicked()),
this,
SLOT(SomeBtnClicked()));
// Some other QObject::connect calls
return;
}
void MyMainWindowClass::SomeBtnClicked()
{
std::cout << "Some Btn has been clicked" << std::endl;
this->setEnabled(false);
MyWidgetClass mwc(this);
mwc.show();
return;
}
This calls the Ctor and the Dtor from MyWidgetClass, disables the MyMainWindowClassUi, but doesn't show my other GUI. What am I missing to have the window showed when I click on the button ?

Try this instead of your SomeBtnClicked method:
MyWidgetClass *mwc;
void MyMainWindowClass::SomeBtnClicked()
{
std::cout << "Some Btn has been clicked" << std::endl;
this->setEnabled(false);
if (!mwc)
mwc = new MyWidgetClass(this);
mwc->show();
mwc->raise();
mwc->setActiveWindow(); // Qt 4: activateWindow()
return;
}

Related

How to inject an event like button press programmatically in gtkmm C++?

I am very new to C++ gtkmm (Linux) programming. I developing a program where I need a button to be clicked in the callback function of another button on the gui.
I have tried
button.activate()
But it only animates the button click but the callback function is not called. When I click the button manually, the callback function is called.
Please explain how to inject event into the gtkmm C++ coding. Events may include button press, key press etc.
Here is an example that works with Gtkmm 3.24 for a button click:
#include <iostream>
#include <gtkmm.h>
class MainWindow : public Gtk::ApplicationWindow
{
public:
MainWindow();
private:
Gtk::Grid m_layout;
Gtk::Label m_label;
Gtk::Button m_buttonA;
Gtk::Button m_buttonB;
};
MainWindow::MainWindow()
: m_buttonA{"A"}
, m_buttonB{"B"}
{
m_label.set_text("Click a button...");
m_buttonA.signal_clicked().connect(
[this](){
std::cout << "Button A clicked!" << std::endl;
// Emits "clicked" on button B, just like when
// a user clicks it:
m_buttonB.clicked();
m_buttonB.activate_action("clicked");
}
);
m_buttonB.signal_clicked().connect(
[this](){
std::cout << "Button B clicked!" << std::endl;
}
);
m_layout.attach(m_buttonA, 0, 0, 1, 1);
m_layout.attach(m_buttonB, 1, 0, 1, 1);
add(m_layout);
}
int main(int argc, char *argv[])
{
std::cout << "Gtkmm version : " << gtk_get_major_version() << "."
<< gtk_get_minor_version() << "."
<< gtk_get_micro_version() << std::endl;
auto app = Gtk::Application::create(argc, argv, "org.gtkmm.examples.base");
MainWindow window;
window.show_all();
return app->run(window);
}
With Gtkmm 4 however, the clicked() method seems to have been removed from Gtk::Button's interface. By looking at the new interface, there is a activate_action method (inherited from Gtk::Widget) that, maybe, could work. However, I don't have Gtkmm 4 here, so I could not try it.

Close main window ffrom messagebox using gtkmm / c++

From a message box i'd like to close the main window if i click on Ok button.
class usb_boot : public Gtk::Window{
public:
usb_boot();
and from message box
i tried this
void usb_boot::creation(){
//Gtk::MessageDialog dialog(*this, dropdownList.get_active_text());
std::string message("Format : " + type);
Gtk::MessageDialog *dialog = new Gtk::MessageDialog("Resume", true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO);
dialog->set_title("Resume");
dialog->set_message(dropdownList.get_active_text());
dialog->set_secondary_text(message);
dialog->set_default_response(Gtk::RESPONSE_YES);
int result = dialog->run();
switch(result){
case(Gtk::RESPONSE_YES):{
std::cout << "next program" << std::endl;
delete dialog;// ok work
usb_boot().close();//compile but doesn't close main window
break;
}
How to close the main window ?
You should avoid using raw new/delete whenever you can (like here, for instance). For message dialogs, you can use simple scopes:
#include <iostream>
#include <gtkmm.h>
class MainWindow : public Gtk::ApplicationWindow
{
public:
MainWindow() = default;
};
int main(int argc, char **argv)
{
auto app = Gtk::Application::create(argc, argv, "so.question.q63872817");
MainWindow w;
w.show_all();
int result;
// Here we put the dialog inside a scope so that it is destroyed
// automatically when the user makes a choice (you could do it
// inside a function instead of a free scope):
{
Gtk::MessageDialog dialog(w, "Message dialog", true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO);
dialog.set_title("Title");
dialog.set_message("Primary message");
dialog.set_secondary_text("Secondary message");
dialog.set_default_response(Gtk::RESPONSE_YES);
result = dialog.run();
} // Here the dialog is destroyed and closed.
if(result == Gtk::RESPONSE_YES)
{
std::cout << "Closing main window..." << std::endl;
//MainWindow().close(); // Will not work!
w.close();
}
return app->run(w);
}
Also, in your code, you call usb_boot().close(), but notice the extra parenthesis after usb_boot. This constructs a new usb_boot object (since you call the constructor) and immediately closes it. In the example above, I called w.close(), instead of MainWindow().close().

qt Program crush with segmentation fault while switching cental widget

I tried maybe everything but I just don't see a mistake in this code. I made two windows inherited from QMainWindow: one with qt designer and another just in constructor. I'm trying to switch these windows in the MainWindow class depending on what buttons you press.
LoginWindow.cpp
LoginWindow::LoginWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::LoginWindow) {
ui->setupUi(this);
}
LoginWindow::~LoginWindow() {
delete ui;
}
void LoginWindow::on_confirmButton_clicked() {
emit confirmButton_clicked();
}
RegistrationWindow.hpp
RegistrationWindow::RegistrationWindow(QWidget* parent): QMainWindow(parent) {
label = new QLabel("Fast Typing"); //variable in .hpp
label->setAlignment(Qt::AlignCenter);
label->setFont(QFont("Lucida Console", 12, 2));
QVBoxLayout * l = new QVBoxLayout();
l->addWidget(label);
l->addWidget(new QLabel("Login", this));
l->addWidget(new QLineEdit(this));
l->addWidget(new QLabel("Password", this));
l->addWidget(new QLineEdit(this));
p = new QPushButton("Confirm", this); //variable in .hpp
l->addWidget(p);
w = new QWidget(); //variable in .hpp
w->setLayout(l);
setCentralWidget(w);
connect(p, &QPushButton::clicked, this, &RegistrationWindow::on_confirmButton_clicked);
}
void RegistrationWindow::on_confirmButton_clicked() {
emit confirmButton_clicked();
}
RegistrationWindow::~RegistrationWindow() { }
MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
//variables in .hpp
logWindow = new LoginWindow();
regWindow = new RegistrationWindow();
goToReg();
connect(logWindow, SIGNAL(confirmButton_clicked()), this, SLOT(goToReg()));
connect(regWindow, SIGNAL(confirmButton_clicked()), this, SLOT(goToLog()));
}
void MainWindow::goToReg() {
qDebug() << "goToReg";
resize(regWindow->size());
qDebug() << "goToReg";
setCentralWidget(regWindow);
setWindowTitle("Registration");
qDebug() << "registration: " << regWindow;
qDebug() << "login: " << logWindow;
}
void MainWindow::goToLog() {
qDebug() << "goToLog";
resize(logWindow->size());
qDebug() << "goToLog";
setCentralWidget(logWindow);
setWindowTitle("Login");
qDebug() << "registration: " << regWindow;
qDebug() << "login: " << logWindow;
}
All I could say is it switches windows no matter what I make a starting window and for second time it's crashes on the line setCentralWidget() in slot with SEGV runtime error.
From the documentation of QMainWindow::setCentralWidget:
Note: QMainWindow takes ownership of the widget pointer and deletes it at the appropriate time.
The widgets that logWindow and regWindow point to start life unowned. Then you call goToReg(), which calls setCentralWidget(regWindow) - now the main window owns *regWindow. Then you call goToLog(), which calls setCentralWidget(logWindow) - now the main window takes ownership of *logWindow and destroys *regWindow, so regWindow becomes a dangling pointer. Next time you attempt to use it, the program exhibits undefined behavior.

qserialport crashes when called from slot

I am writing a gui in QT for controlling usb relays. I wrote a relay class which has turn on/off member functions. also wrote a custom widget which has radio buttons for controlling, a relay pointer, as well as a switchStatus() slot in which the relay turn on/off functions were called. The mainwindow has relay members. When I call the the ui->widget->switchStatus() from the mainwindow constructor, everything works fine, the relay can be turn on and off well. However, if I connect the radio button signal to switchStatus(), the program crashes whenever I click the radio button. It crashes at the line serialPort->write. But it's not about write, whatever code related to serialPort pointer it first come to will cause crash. even I want to get the port name or port address.
myWidget::myWidget(QWidget *parent) : QWidget(parent), m_ui(new Ui::Form)
{ status = 0;
m_ui->setupUi(this);
m_ui->statusIndicator->status = &status; // status in ui pointing to null before this
m_ui->turnOffButton->setChecked(true);
connect(m_ui->turnOnButton, SIGNAL(clicked(bool)), this, SLOT(switchStatus()));
//crashes when click ratioButton,
//compare to last line in the mainwindow construtor
connect(m_ui->turnOffButton, SIGNAL(clicked(bool)), this, SLOT(switchStatus()));
}
void myWidget::switchStatus()
{
qDebug() << "swithcing";
if(status)
{
setStatus(false);
}
else
{
setStatus(true);
}
m_relay->switchStatus();
}
void relay::switchStatus()
{ if(status) turnOff();
else turnOn();
}
bool relay::turnOn(){
qDebug() << writeDataOn; // test if string is correct
qDebug() << serialPort; // crashes whenever serialPort address is called
const qint64 bytesWritten = serialPort->write(writeDataOn);
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
QList<QSerialPortInfo> infos = QSerialPortInfo::availablePorts();
qDebug() << infos[0].description();
QSerialPort serialPort;
serialPort.setPortName(infos[0].portName());
serialPort.setBaudRate(QSerialPort::Baud9600);
if (!serialPort.open(QIODevice::ReadWrite)) {
qDebug() << QObject::tr("Failed to open port %1, error: %2").arg(serialPort.portName()).arg(serialPort.error()) ;
}
ui->setupUi(this);
for(int i=0;i<4;i++)
{
relays[i].setRelayNumber(i);
relays[i].setPort(&serialPort);
relays[i].setStatus(relays[i].getRealStatus());
}
ui->widget->m_relay = relays;
qDebug() << ui->widget->m_relay;
qDebug() << ui->widget->m_relay->getPort();
ui->widget->switchStatus(); // this line runs well
}
Make your serial port a member of the MainWindow class. At the moment it is local to your constructor, so it is deallocated when the constructor finishes, hence the crashing. If you have declared a 'serialPort' member, you are hiding it by redeclaring it as a local scope variable inside the constructor.

How to embed one qApplication's GUI into another qApplication's mainWindow?

There are two qApplications A & B, they can be executed separately with their own main window.
I would like to achieve the following:
1) //Open Application B.
//Inside App B's code
QProcess* proA = new QProcss();
proA->start(A.exe) //Under Windows7
2) //Instead of showing app A in a separate window.
//I would like to show it as a widget of app B's main window.
Sort of like google chrome. A similar post here:QT How to embed an application into QT widget talked about the similar problem. But it involves implement your own window management system. Is there simpler solutions as both my app are Qt's qApp and both uses QWindow.
It's definitely possible if the two QApplications are in different processed.
Create two process each with it's QApplication and QWidget
From one process, find the winId of the other processe's QWidget and reparent it to your own widget.
To manage the widget from the other process reparented to yours, you may use qtwinmigrate. Originally this was meant to embed a MFC widget (with its own CWinApp) in a Qt widget, but it can also help embedding a QWidget from a separate process.
Here is a piece of working code:
Child process:
#include <QLabel>
#include <QWidget>
#include <QVBoxLayout>
#include <QApplication>
#include <sstream>
int main(int argc, char **argv)
{
QApplication app(argc,argv);
QWidget widget;
widget.setWindowTitle( "CHILD WINDOW" );
std::stringstream str;
str << "QWidget ID: " << widget.winId() << std::endl;
str << "Process Name: " << argv[0];
Qt::WindowFlags flags = widget.windowFlags();
flags |= Qt::FramelessWindowHint;
flags |= Qt::MSWindowsFixedSizeDialogHint;
flags |= Qt::SubWindow;
widget.setWindowFlags( flags );
widget.setLayout(new QVBoxLayout(&widget));
QLabel label;
widget.layout()->addWidget(&label);
label.setText(str.str().c_str());
widget.setStyleSheet( "background: red" );
widget.show();
QEvent e(QEvent::EmbeddingControl);
QApplication::sendEvent(&label, &e);
app.processEvents();
return app.exec();
}
Parent process:
#include <QMainWindow>
#include <QApplication>
#include <QMessageBox>
#include <QVBoxLayout>
#include <QLabel>
#include <QProcess>
#include "windows.h"
#include "qtwinmigrate5/qwinhost.h"
#include <iostream>
#include <sstream>
/* The EnumChildProc callback */
static HWND hWndHandle = NULL;
static qint64 childProcessID = 0;
BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam)
{
DWORD dwProcessID = 0;
if (GetWindowThreadProcessId(hwnd, &dwProcessID) &&
dwProcessID == childProcessID )
{
char strTemp[256] = "";
GetWindowText(hwnd, strTemp, 256);
std::string str = strTemp;
if (str == "CHILD WINDOW") // sanity check
hWndHandle = hwnd;
}
return ( hWndHandle == NULL ); // must return TRUE; If return is FALSE it stops the recursion
}
void* GetChildWindowHandle( qint64 pid )
{
hWndHandle = NULL;
childProcessID = pid;
EnumWindows(EnumChildProc, 0);
return hWndHandle;
}
int main(int argc, char **argv)
{
QApplication app(argc,argv);
QMainWindow wnd;
wnd.setWindowTitle("That's the parent window!");
// Create child process:
QProcess process;
process.start("test_3rdparty_inprg_qtwinmigrate_child.exe");
if (process.state() != QProcess::Running)
{
QMessageBox::critical(NULL, "ERROR", "Unable to create child process");
return 1;
}
// Create qtwinmigrate widget container:
QWinHost* host = new QWinHost( &wnd );
// Get child process wiindow handle
HWND hChildWnd = NULL;
int timeout = 20;
while ((hChildWnd = (HWND)GetChildWindowHandle(process.processId())) == NULL)
{
// let child process more time to create and show its widget....
Sleep(200);
--timeout;
if (timeout == 0)
break;
}
int res = 1;
if (hChildWnd != NULL)
{
// attach child window handle to qtwinmigrate widget container
host->setWindow(hChildWnd);
char strTemp[256] = "";
GetWindowText(hChildWnd, strTemp, 256);
QWidget centralWidget(&wnd);
wnd.setCentralWidget(&centralWidget);
QVBoxLayout* layout = new QVBoxLayout(&centralWidget);
std::stringstream str;
str << "Attached data window " << std::showbase << std::hex << hChildWnd << std::endl;
str << "Window title: " << strTemp << std::endl;
str << "Widget below runs in a separate process:" << std::endl;
layout->addWidget( new QLabel( str.str().c_str(), &centralWidget ) );
layout->addWidget(host);
wnd.resize(400, 200);
wnd.show();
res = app.exec();
}
else
{
QMessageBox::critical(NULL, "ERROR", "Unable to find child process widget");
}
// kill child process
process.kill();
return res;
}
1- Compile child process into an executable named test_3rdparty_inprg_qtwinmigrate_child.exe.
2- Compile parent process into an executable
3- Run parent process, this one will start the child process, find it's top level widget window handle and insert it within its own QMainWindow as a child. When done, it will kill the child process.
As a end user, it's hard to tell the widgets are not part of the same process, they are perfectly embedded (the red part of the widget comes from the child process):
well, that's the idea of the whole QWidget approach: Everything that can be put in a container can be part of another application.
However, putting a complete, unmodified Qt application into another one will not be possible: There can only be one QApplication instance, and only one global event loop.