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().
Related
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.
I'm trying to open a new window from an existing window by clicking on a button. The new window should display an image. When I click the button, the new window is shown, but the image is not displayed. There are no errors.
I made sure that my image is in the current directory and is readable. What is happening?
Code:
void ExampleWindow::on_button_clicked()
{
std::cout << "The Button was clicked." << std::endl;
Gtk::Window *window = new Gtk::Window();
Gtk::VBox mainLayout;
window->add(mainLayout);
Gtk::Image image("Vampire.png");
mainLayout.pack_start(image);
window->show_all();
}
The problem is that after your handler is called (end of scope), all local variables are destroyed, and that includes image.
Here is code that works:
#include <iostream>
#include <memory>
#include <gtkmm.h>
class ExampleWindow : public Gtk::Window
{
public:
ExampleWindow()
: m_image("Vampire.png")
{
add(m_btn);
m_btn.signal_clicked().connect([this](){OnButtonClicked();});
}
private:
void OnButtonClicked()
{
std::cout << __FUNCTION__ << " enter scope" << std::endl;
m_window = std::make_unique<Gtk::Window>();
m_window->add(m_image);
m_window->show_all();
std::cout << __FUNCTION__ << " leaving scope" << std::endl;
// With your code, image is destroyed here. The window still
// lives because it was newed, but you lost your reference to
// it and so the program will leak memory (i.e. you will be
// unable to call delete on it, unless it is a class member).
}
Gtk::Button m_btn("Show image");
Gtk::Image m_image;
std::unique_ptr<Gtk::Window> m_window;
};
int main(int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv, "so.question.q65011763");
ExampleWindow window;
window.show_all();
return app->run(window);
// With this code, the image and the window are both destroyed here.
// Since the window is in a unique_ptr, delete will be automatically
// called on it.
}
Note that I have made all variables class members, so that they outlive the end of scope of the handler. I have also stored the window containing the image inside a smart pointer so that I don't have to call delete myself.
I have a program that needs to check at startup if a path to project is set. For that I have subclassed QMessageBox, added some custom stuff (like spacer to make dialog wider) and I am calling QFileDialog::getExistingDirectory to get directory.
Thing is, user can click Cancel in QFileDialog. But I want user to be returned to QMessageBox to have another chance to set path or quit program altogether. To achieve that I wrote method loop():
CustomMessageBox::loop()
{
while (true) {
this->exec();
if (this->clickedButton() == setPathButton) {
path = QFileDialog::getExistingDirectory(...);
if (!path.isEmpty()) { break; }
} else if (this->clickedButton() == quitButton) {
break;
}
}
}
Then, I have a method getPath():
CustomMessageBox::getPath()
{
loop();
return path;
}
which I call in main.cpp:
CustomMessageBox box;
QString path = box.getPath();
if (!path.isEmpty()) {
// save path, bla, bla
} else {
exit(EXIT_FAILURE)
}
This works, but is this a good practice? I am specifically asking regarding this while inside which resides method exec().
IMO disadvantages of your decision:
Non-standard usage of dialog class. Put strange loop inside some GUI method and call standard exec() method inside it, when the method can easily be called in a standard way.
Hide exec() call from "user" (another programmer)
Dual, or even triple, purpose of the one getPath() method:
show dialog
flag that the dialog was accepted/rejected via empty - non-empty string
return directory path string
I suggest inherit QDialog (why message box?):
In Degister I put on form standard Push Button setPathButton and Dialog Button Box buttonBox. Then I removed "Ok" button from the box:
#include <QtWidgets/QDialog>
#include "ui_CustomDialog.h"
class CustomDialog : public QDialog
{
Q_OBJECT
public:
CustomDialog(QWidget *parent = nullptr);
QString path() const { return m_path; };
private slots:
void on_setPathButton_clicked();
void on_buttonBox_rejected(); // You can use any pushButton instead: on_closeButton_clicked()
private:
Ui::CustomDialogClass ui;
QString m_path;
};
...
#include "CustomDialog.h"
#include <QFileDialog>
CustomDialog::CustomDialog(QWidget *parent)
: QDialog(parent)
{
ui.setupUi(this);
}
void CustomDialog::on_setPathButton_clicked()
{
m_path.clear();
QString dir = QFileDialog::getExistingDirectory();
if (!dir.isEmpty())
{
m_path = dir;
done(QDialog::Accepted);
}
}
// You can use any pushButton instead: void on_closeButton_clicked()
void CustomDialog::on_buttonBox_rejected()
{
reject();
}
main.cpp
#include "CustomDialog.h"
#include <QtWidgets/QApplication>
#include <QDebug>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
CustomDialog box;
int code = box.exec();
if (code == QDialog::Accepted)
{
qDebug() << box.path();
// save path, bla, bla
}
else
{
exit(EXIT_FAILURE);
}
return a.exec();
}
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(¢ralWidget);
QVBoxLayout* layout = new QVBoxLayout(¢ralWidget);
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(), ¢ralWidget ) );
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.
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;
}