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.
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 make a simple software with Gtkmm3.
I want to have a window with a grid inside. At a click on a button inside that grid, a method of the window should be triggered to delete the current grid and replace it with another one.
I'm able to use a method of the grid like this :
button.signal_clicked().connect(sigc::mem_fun(*this, &MyGrid::someMethod));
"this" being MyGrid.
I would like to do something like this :
button.signal_clicked().connect(sigc::mem_fun(*this->get_parent(), &MyWindow::someMethod));
where this->get_parent() would be an instance of MyWindow
My .h:
#ifndef MINIPROJECT_GUI_H
#define MINIPROJECT_GUI_H
#include <gtkmm/button.h>
#include <gtkmm/window.h>
#include <gtkmm/grid.h>
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <gtkmm/label.h>
class WelcomeGrid: public Gtk::Grid
{
Gtk::Label message;
Gtk::Button nextButton; // This button should be connected to Fenetre::infoView()
public:
WelcomeGrid();
void display();
};
class InfoGrid : public Gtk::Grid
{
Gtk::Button button2;// This button should be connected to Fenetre::welcomeView()
Gtk::Label label2;
public:
InfoGrid();
void display();
};
class Fenetre : public Gtk::Window
{
public:
Fenetre();
virtual ~Fenetre(); // Setup window
void welcomeView();
protected:
//Member widgets:
WelcomeGrid welcome;
InfoGrid info;
void infoView(); // Remove the current grid from the window and replace it by infoGrid
void welcomeView(); // Remove the current grid from the window and replace it by WelcomeGrid
};
#endif //MINIPROJECT_GUI_H
My .cpp :
#include "GUI.h"
Fenetre::Fenetre()
{
// Sets the border width of the window.
set_border_width(10);
this->add(welcome);
}
Fenetre::~Fenetre()
{
}
void Fenetre::welcomeView() {
this->remove();
this->add(welcome);
}
void Fenetre::infoView() {
this->remove();
this->add(info);
}
InfoGrid::InfoGrid() {
button2.set_label("Hello.");
button2.signal_clicked().connect(sigc::mem_fun(*this,
&InfoGrid::display));
label2.set_label("Welcome on the Vampire creation interface.");
this->attach(label2, 0, 0, 1, 1);
this->attach(button2,1,1,1,1);
button2.show();
this->show_all();
}
WelcomeGrid::WelcomeGrid() {
nextButton.set_label("Create new character.");
auto a = this->get_parent();
nextButton.signal_clicked().connect(sigc::mem_fun(*this,
&WelcomeGrid::display));
message.set_label("Welcome on the Vampire creation interface.");
this->attach(message, 0, 0, 1, 1);
this->attach(nextButton,1,1,1,1);
// This packs the button into the Window (a container);
this->show_all();
}
void WelcomeGrid::display() {
auto a = this->get_parent();
std::cout << typeid(a).name();
}
void InfoGrid::display() {
std::cout << "coucou";
}
Without any code, it is hard to know what exactly you are looking for. Here is how I would do it: I would keep a reference to the parent Window inside the grid. For example:
#include <iostream>
#include <memory>
#include <sstream>
#include <gtkmm.h>
class MyWindow : public Gtk::Window
{
public:
MyWindow()
: m_grid{std::make_unique<MyGrid>(*this, m_count)}
{
add(*m_grid);
}
// This is called when the grid's button is pressed:
void ReplaceGrid()
{
++m_count;
// Remove the grid from the window:
remove();
// Destroy current grid:
m_grid = nullptr;
// Create a new grid:
m_grid = std::make_unique<MyGrid>(*this, m_count);
// Add it to the window:
add(*m_grid);
show_all();
}
private:
class MyGrid : public Gtk::Grid
{
public:
MyGrid(MyWindow& p_parent, int p_count)
: m_parent{p_parent}
{
// Create button:
std::ostringstream ss;
ss << "Replace me #" << p_count;
m_replaceButton = Gtk::Button(ss.str());
// Attach it to the grid:
attach(m_replaceButton, 0, 0, 1, 1);
// Connect replacement signal, using the parent window:
m_replaceButton.signal_clicked().connect([this]()
{
// Call the parent (the window):
m_parent.ReplaceGrid();
});
}
~MyGrid()
{
std::cout << "Grid destroyed" << std::endl;
}
private:
Gtk::Button m_replaceButton;
// Keep a reference to the parent window in the grid:
MyWindow& m_parent;
};
int m_count = 0;
std::unique_ptr<MyGrid> m_grid;
};
int main(int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv, "so.question.q64594709");
MyWindow w;
w.show_all();
return app->run(w);
}
If you run this code, you will see a window with a grid containing one button. Whenever you click the button, the window:
updates a counter
destroys the current grid
creates a new grid with the updated counter value
You will see the counter value updated on the new grid's button label. In the terminal, the grids destructor will print a message, proving grids have really been switched.
Notice I have used lambdas here to clean up the syntax. I would suggest you do so as well. If you really want to use sigc::men_fun, you can encapsulate the lambda's content into the MyGrid::someMethod method that you mentioned in your question.
Notice also that the grid is a private nested class of the window (no one else needs to know...).
Compiled with GCC:
g++ main.cpp -o example.out `pkg-config gtkmm-3.0 --cflags --libs`
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().
Per design requirements, I am developing a GUI in FLTK that must have a main (parent) window, a child window, and an X Window that is the child of the child window. This is in Ubuntu 16.04.
Running my original project with this setup produces the XLib error BadWindow (invalid Window parameter) (Details: serial 7 error_code 3 request_code 1 minor_code 0).
I have the following test program with only the features relevant to the error that reproduces a similar error:
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/x.H>
#include <iostream>
class ChildWindow : public Fl_Window {
public:
ChildWindow() : Fl_Window(100, 100, 300, 200, "Child"){
this->end();
this->show();
Display * dis = XOpenDisplay(NULL);
std::cout << "Child XID: " << (void *)fl_xid(this) << '\n';
std::cout << "XWindow XID: " << dis << '\n';
Window win = XCreateSimpleWindow(dis, fl_xid(this),
5, 5, 100, 100, 0, 0, 0);
XMapRaised(dis, win);
XSync(dis, false);
}
};
class ParentWindow : public Fl_Window {
public:
static ChildWindow * child;
ParentWindow() : Fl_Window(100, 100, 400, 300, "Parent"){
Fl_Button * b = new Fl_Button(10, 10, 70, 20, "Make Child");
b->callback(CallbackMakeChild, NULL);
this->add(b);
this->end();
this->show();
std::cout << "Parent XID: " << (void *)fl_xid(this) << '\n';
}
static void CallbackMakeChild(Fl_Widget * w, void * o){
child = new ChildWindow();
child->show();
}
};
ChildWindow * ParentWindow::child = NULL;
with the main function of
int main(){
ParentWindow parent;
Fl::run();
}
When run, the above code should create a window with a button. When the button is pressed, a child window will be created and the X Window will be attempted to be created. Here is the console output:
Parent XID: 0x4400002
Child XID: 0x4400005
XWindow XID: 0xe03140
X_CreateWindow: BadWindow (invalid Window parameter) 0x4400005
X_ConfigureWindow: BadWindow (invalid Window parameter) 0x4600001
X_MapWindow: BadWindow (invalid Window parameter) 0x4600001
The child window and X Window by themselves behave fine:
int main(){
ChildWindow child;
Fl::run();
}
This code (creating two non-hierarchical windows) also fails:
int main(){
ChildWindow child1; // This window works
ChildWindow child2; // This causes similar errors as above
Fl::run();
}
I have also tried various combinations of fl_open_display and Fl_Window::make_current without success.
(All code, being in one file, compiles by running ./fltk-config --compile myprog.cpp)
The pattern here appears that X windows initialize fine for the first FLTK window, but do not for windows created after that. My guess is that it has something to do with how FLTK interfaces/initializes with XLib, but I have not been able to find specifics in the documentation.
Does anyone have an explanation/solution for why X Window initialization only works when using the first-created FLTK window as a parent?
The problem was that XOpenDisplay was being called twice. FLTK opens the display as part of the initialization process, and calling it again with initializing the X window messed up the system.
This was solved by removing the line that said dis = XOpenDisplay(NULL) and replacing references to dis, with the FLTK display located at fl_display.
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;
}