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.
Related
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.
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().
I'm coding in c++ with Qt creator. I can get all windows on the screen but I would like to detect their movement directly. Is there any way to detect the movement thanks to a signal emitted by the window ?
You can try creating a QWindow from the target window and then wrap it in a QWidget using QWidget::createWindowContainer().
You can have a look at this QTBUG thread : https://bugreports.qt.io/browse/QTBUG-40320
It needs some effort to make it work properly. The captured window doesn't keep its initial dimensions, and releasing the window behave weirdly. Read the last QTBUG comments to find an improvement.
I added an event filter to this code to capture the window position in real time. But that might be unsatisfying.
class EventFilter : public QObject
{
Q_OBJECT
public:
EventFilter(){}
virtual ~EventFilter(){}
protected:
bool eventFilter(QObject *obj, QEvent *event);
} ;
bool EventFilter::eventFilter(QObject *obj, QEvent *event)
{
qDebug() << event->type() ;
if (event->type() == QEvent::Move) {
QMoveEvent *moveEvent = static_cast<QMoveEvent *>(event);
qDebug() << "position" << moveEvent->pos() ;
return true;
} else {
// standard event processing
return QObject::eventFilter(obj, event);
}
}
I removed part 3 from the QTBUG snippet and installed the event handler on the inner widget. You can also remove the timers.
// From https://bugreports.qt.io/browse/QTBUG-40320
int main(int argc, char *argv[])
{
// Windows: Find HWND by window title
WId id = (WId)FindWindow(NULL, L"Calculator");
if (!id)
return -1;
QApplication a(argc, argv);
// Optional
QTimer t;
t.start(2500);
// Part 1
QWindow* window = QWindow::fromWinId(id);
window->show();
window->requestActivate();
// Optional
QObject::connect(&t, &QTimer::timeout, [=]
{
qDebug() << "=== Inner QWindow ===";
qDebug() << "Geometry:" << window->geometry();
qDebug() << "Active?:" << window->isActive();
qDebug() << "Flags:" << window->flags();
});
// Part 2
QWidget* widget = QWidget::createWindowContainer(window);
widget->show();
// Optional
QObject::connect(&t, &QTimer::timeout, [=]
{
qDebug() << "=== Outer QWidget ===";
qDebug() << "Geometry:" << widget->geometry();
qDebug() << "Active?" << widget->isActiveWindow();
qDebug() << "Flags:" << widget->windowFlags();
});
// Realtime position
EventFilter filter ;
widget->installEventFilter( &filter ) ;
return a.exec();
}
Output:
=== Inner QWindow ===
Geometry: QRect(0,0 640x480)
Active?: true
Flags: QFlags<Qt::WindowType>(ForeignWindow)
=== Outer QWidget ===
Geometry: QRect(2489,29 640x480)
Active? true
Flags: QFlags<Qt::WindowType>(Window|WindowTitleHint|WindowSystemMenuHint|WindowMinMaxButtonsHint|WindowCloseButtonHint)
QEvent::Type(Move)
position QPoint(2484,29)
QEvent::Type(Move)
position QPoint(2481,30)
QEvent::Type(Move)
position QPoint(2478,31)
QEvent::Type(Move)
position QPoint(2474,31)
See also http://blog.qt.io/blog/2013/02/19/introducing-qwidgetcreatewindowcontainer/
I've been working on a project recently which is a simple game. I've written the following code in my main function (file main.cpp):
ending_note = "Draw.";
End_Page end(ending_note, a);
end.show();
(*a).exec();
if(end.flag == 1)
{
return 1;
} //end if
where a is a Qapplication object. The class End_Page is defined as follows (file end_page.cpp):
End_Page::End_Page(string _winner, QApplication* _a, QWidget *parent):QWidget(parent){
a = _a;
this->setFixedSize(900, 675);
this->move(350, 50);
flag = 0;
//------------------- background label
background = new QLabel(this);
QMovie* movie2 = new QMovie("..\\project\\Data\\pic\\7.jpeg");
movie2->setScaledSize(QSize(this->width(), 600));
background->setMovie(movie2);
background->setGeometry(0, 0, this->width(), 600);
movie2->start();
//-------------------- set label
QString s;
label = new QLabel(s.fromStdString(_winner), this);
label->setStyleSheet("QLabel { color : rgb(200, 0, 30); qproperty-alignment: AlignCenter; }");
QFont f( "MV Boli", 32, QFont::Bold);
label->setFont(f);
label->setGeometry(0,this->height() - 400, this->width(), 160);
question = new QLabel("Do you want to play again?\n", this);
question->setStyleSheet("QLabel { color : black;}");
question->setGeometry(375, 610, 200, 30);
accept = new QPushButton("Yes", this);
accept->setGeometry(300, 630, 80, 40);
decline = new QPushButton("No", this);
decline->setGeometry(500, 630, 80, 40);
//-------------------- connect
connect(this,SIGNAL(closeSignal()), this, SLOT(closeProgram()));
connect(decline, SIGNAL(clicked()), this, SLOT(closeProgram()));
connect(accept, SIGNAL(clicked()), this, SLOT(restartProgram()));
}
End_Page::~End_Page(){}
void End_Page::closeEvent(QCloseEvent* event){
emit closeSignal();
event->accept();
}
void End_Page::EndGame(){
a->exit();
}
void End_Page::closeProgram(){
exit(0);
}
void End_Page::restartProgram(){
flag = 1;
a->exit();
}
My problem is that, after the program executes the statement (*a).exec();, if the user click on the push button labeled Yes, the program executes the function restartProgram to the end, but after that it doesn't continue back in function main (in other words it gets stuck there). How can I solve this problem?
Try calling quit() or exit() as static class members (you don't need to pass your QApplication around):
For any GUI application using Qt, there is precisely one QApplication object, no matter whether the application has 0, 1, 2 or more windows at any given time. For non-GUI Qt applications, use QCoreApplication instead, as it does not depend on the QtGui library.
The QApplication object is accessible through the instance() function that returns a pointer equivalent to the global qApp pointer.
void End_Page::restartProgram(){
flag = 1;
QApplication::quit();
}
But, the main problem in your application is that you are emmiting closeSignal() in your closeEvent() and the slot connected to it will call exit(0); system call, which is, I think, completely unnecessary, and which will "kill" the current process.
Here is a completely working example:
#include <QApplication>
#include <qtimer>
#include <iostream>
/* Move this into h file and moc it! */
class Window:public QObject
{
Q_OBJECT
public slots:
void closeApp(){ QApplication::quit(); flag = 500; }
public:
int flag;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Window win;
QTimer::singleShot(5000, &win, SLOT(closeApp()));
a.exec();
std::cout << "Flag: " << win.flag << std::endl;
return 0;
}
Edit
Why are you doing this:
if(end.flag == 1) // flag is set to 1 in restartProgram slot
{
return 1;
} //end if
This will exit your main() function, and it will not restart program.
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;
}