Divide Gtkmm program into functions (part 1) - c++

I know that this will seem a very basic question, but actually it is not something obvious because of the use of pointers, scopes and GTK especific types of variables and others. I really was not able to find an answer.
I have got to divide the GUI related part of a Gtkmm program into functions, but something seems to be wrong.
To make it clear, here is an example, There is the WORKING code in CODE1.cpp, and it must be divided into something similar to CODE2.cpp (not yet working).
The first one is a window containing only a label, the second is the same, but the label is created inside a function.
Where is the error? What is missing? Any tip or help would be appreciated.
Codes mentioned are the following:
CODE1.cpp:
#include <gtkmm.h>
int main (int argc, char *argv[])
{
Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "Ejemplo");
Gtk::Window ventana;
Gtk::Label labela;
labela.set_text("perrito");
ventana.add (labela);
ventana.show_all ();
return app->run(ventana);
}
CODE2.cpp:
#include <gtkmm.h>
Gtk::Label etiqueta (string x)
{
Gtk::Label labela;
labela.set_text(x);
return ( labela );
}
int main (int argc, char *argv[])
{
Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "Ejemplo");
Gtk::Window ventana;
etiqueta("perrito");
ventana.add (labela);
ventana.show_all ();
return app->run(ventana);
}

I guess your problem is that the Gtk::Label does not appear. That's because:
a) You are creating it as a local variable in the scope of the function and it is then released at the end of the function. Maybe you mean to use new (with Gtk::manage()) and return a Gtk::Label* instead of a Gtk::Label.
b) You don't use the return value from your function. There is no labela variable in your main() function.
I don't mean to sound harsh, but you need to read your compiler warnings and you need to read a beginner's C++ book. It's hard to learn C++ just by guessing or by hoping that it's like other languages such as Java.

Related

How do I pack a Gtk::Entry into a Gtk::HeaderBar so the entry completely fills the header bar?

I'm making a program in gtkmm-3.0 which has a Gtk::HeaderBar as the title bar.
I'm trying to pack a Gtk::Entry into it using this code:
Gtk::HeaderBar headerBar;
Gtk::Entry entry;
headerBar.set_hexpand();
headerBar.set_halign((Gtk::Align)GTK_ALIGN_FILL);
entry.set_hexpand();
entry.set_halign((Gtk::Align)GTK_ALIGN_FILL);
headerBar.pack_start(uriEntry);
headerBar.set_show_close_button();
The entry is correctly packed, but it only fills half the space of the header bar, which is very confusing. Using headerBar.add(entry) or headerBar.pack_end(entry) does not help the slightest (The entry still fills half the space it's supposed to take).
Also, using headerBar.pack_start() with a Gtk::Button before the headerBar.pack_start(entry) line will put the button in its place, but the entry will stop the expansion at the same point that it stopped before, being shorter than before.
How can I make the entry fill the whole header bar?
The problem is that Gtk::HeaderBar also has a "title" widget taking space. You could set a title, resulting in this:
An you see why only half the screen was given to the entry. One workaround is to define your own, custom, header bar. Here is an extremely minimal example:
#include <gtkmm.h>
class MainWindow : public Gtk::ApplicationWindow
{
public:
MainWindow();
private:
Gtk::Box m_customHeaderBar;
Gtk::Entry m_entry;
};
MainWindow::MainWindow()
{
m_entry.set_hexpand_set(true);
m_entry.set_hexpand();
m_customHeaderBar.pack_start(m_entry);
set_titlebar(m_customHeaderBar);
}
int main(int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv, "org.gtkmm.examples.base");
MainWindow window;
window.show_all();
return app->run(window);
}
Which results in this:
Of course, you will have to add a close button and everything yourself (I would recommend making a class). I will leave this part to you.

Open multiple different windows in Gtkmm

I've only just started learning how to use gtkmm, and I'm trying to create an application which can have more than one window open at the same time (think, main window and a control panel).
I decided to create the layout in Glade, if that's of any relevance to this question.
My method of displaying two bottles is more or less this:
Gtk::Window* main_window = nullptr;
Gtk::Window* servsettings = nullptr;
int main(int argc, char* argv[]) {
auto app = ...
auto builder = ... // these are exluded for brevity
builder->add_from_file("../src/design.glade");
builder->get_widget("main", main_window);
builder->get_widget("servsettings", servsettings);
app->run(*servsettings);
app->run(*main_window);
}
Instead of opening two windows, this instead opens servsettings, then segfaults when I close that.
Now forgetting about the segfault (I'm utterly confused about that,) I think I can see why it only opens servsettings - I assume this is because I'm running the app, and then that call only exits when the window dies?
The problem is, I can't think of any other way to do it. I experimented with multithreading but decided that it would be better to ask here first.
Before anyone suggests it, this answer does not help me. This is because they had a scope-based issue. I don't.
Use Gtk::Application::add_window(Gtk::Window&).
If all the windows managed by Gtk::Application are closed (hidden) or removed from the application then the call to run() will return.
#include <gtkmm.h>
Gtk::Window* window1, *window2;
int main()
{
auto app = Gtk::Application::create();
Gtk::Button button1("Quit"), button2("Quit");
window1 = new Gtk::Window();
window2 = new Gtk::Window();
button1.signal_clicked().connect(sigc::mem_fun(window1, &Gtk::Window::close));
button2.signal_clicked().connect(sigc::mem_fun(window2, &Gtk::Window::close));
window1->set_default_size(200, 200);
window1->add(button1);
window1->show_all();
window2->set_default_size(200, 200);
window2->add(button2);
window2->show_all();
app->signal_startup().connect([&]{
app->add_window(*window2);
});
return app->run(*window1);
}
I found the answer. For those wondering, I replaced the two app->runs with:
servsettings->show();
main_window->show();
app->run(*main_window);

Qt GUI Easiest way to access MainWindow from another class

I am doing a blackjack program and I am keeping track of the cards in the players hand in another class ("hand.h") than the main window class.
In the hand class, for every card that I collect, I am also creating a QLabel that grabs the proper card image for the card and also sets the coordinates for where the card should appear on the main window.
The problem is that I am not able to create the QLabel based on the MainWindows object that is originally created at the main function. Is there any easy way that I am able to get that information fairly easily? Thanks for your help!
I have tried using QGuiApplication::topLevelWindows(), but haven't came to luck with using that. Here is my function that I am using.
#include <QRect>
#include <QApplication>
#include <iostream>
#include <QLabel>
#include "mainwindow.h"
#include <QMainWindow>
#include <QWindowList>
#include <QWidgetList>
#include "ui_mainwindow.h"
void Test() {
QList<QWindow*> Main_Window = QGuiApplication::topLevelWindows();
for (int i = 0; i < Main_Window.size(); ++i) {
if(Main_Window.objectName() == "mainWindow") // name is OK
break;
}
QMainWindow* mainWindow = static_cast<QMainWindow*>(Main_Window);
QLabel* temp;
temp = new QLabel(Main_Window);
temp->setPixmap(QString("Ten of Clubs.png"));
temp->setGeometry(290, 300, 350, 390);
temp->show();
}
Here is the main.cpp file that creates the mainwindow
int main(int argc, char *argv[])
{
srand(time(NULL));
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
I found the iterating code online and have been having issues from it.
I am having issues while trying to iterate through the list, but I have no idea how to identify the list and the error says that there is no objectName() function. Also, in the static cast line, there is an error that says that I cannot convert an QList to type QMainWindow. Any help would be greatly appreciated.
No way in general, because some applications may have several (toplevel) QMainWindow-s (and their list could change with time). So for that case you'll better pass the pointer to it (the particular QMainWindow you want to deal with) explicitly....
A possible way might be to have your specific subclass of QApplication (which is a singleton class, see QCoreApplication::instance to get its sole instance) and in your application subclass put, as fields, the explicit windows you want to deal with (maybe you even want to add some new signal or slot to your application class).
However, you could use QGuiApplication::topLevelWindows() or QGuiApplication::allWindows() to get the list of all such windows. Notice that a QWindowList is just a QList<QWindow *>. So see QList for how to traverse or iterate on that list.
Once you have found which QMainWindow you want, adding a QLabel into it is usual practice (but again, signals & slots could be helpful).
BTW, each (displayed) widget has its window, see QWidget::window()
About your code:
Your Main_Window is really poorly named (and the name is so confusing that I cannot use that). It is a list not a window. So code first:
QMainWindow* mainWindow = nullptr;
{
QList<QWindow*> topwinlist = QGuiApplication::topLevelWindows();
int nbtopwin = topwinlist.size();
for (int ix=0; ix<nbtopwin; ix++) {
QWindow*curwin = topwinlist.at(ix);
if (curwin->objectName() == "mainWindow")
mainWindow = dynamic_cast<QMainWindow*>(curwin);
}
}
I did not test the above code and I am not sure it is correct or even can compile. But why don't you just have a global pointer to your main window:
MainWindow*mymainwinp = nullptr;
and initialize it appropriately in your main body:
int main(int argc, char *argv[]) {
srand(time(NULL));
QApplication a(argc, argv);
MainWindow w;
mymainwinp = &w;
w.show();
int r = a.exec();
mymainwinp = nullptr;
return r;
}
then use mymainwinp elsewhere (e.g. in your Test)? If you want more elegant code, define your own subclass of QApplication and have mymainwinp be a field in it.

GTKMM Error: Void Value Not Ignored As it Ought to Be

I am trying to create a simple window in GTKMM that contains a box. I've got the window part working, but I can't get my box code to work. I am following along with this tutorial
I think the tutorial is a little bit dated because Anjuta (the IDE I'm using) generated some different code. Here is my code that should add a box:
#include <gtkmm.h>
#include <iostream>
#include "config.h"
using namespace Gtk;
int main (int argc, char *argv[])
{
Gtk::Main kit(argc, argv);
Gtk::Window *main_win = new Gtk::Window (Gtk::WINDOW_TOPLEVEL);
main_win->set_title ("Image-Viewer");
Gtk::Box *box = Gtk::manage (new Gtk::Box());
box ->set_orientation (ORIENTATION_VERTICAL);
box->set_spacing(6);
*main_win -> add(*box);
if (main_win)
{
kit.run(*main_win);
}
return 0;
}
In the code on the tutorial the window is not created in the same way. As you can see below, the window in my code is being created so that it is in the heap, rather than the stack. (or at least I think [I am new to C++]). I know that items in the heap should be used like a pointer, so for the add function I did that, (rather than using the dot notation described in the tutorial). When I run this code, I get an error stating the following:
error:void value not ignored as it out to be
The error is pertaining to the add method being called on the window. Can somone tell me what I'm doing incorrectly? Thanks
This instruction:
Gtk::Window *main_win = new Gtk::Window (Gtk::WINDOW_TOPLEVEL);
Declares a pointer to Gtk::Window. Later, you do:
*main_win -> add(*box);
This is incorrect, because you basically try to apply operator -> after you already dereferenced the main_win pointer - and the result of this dereferencing is not a pointer itself, but a reference to an object of type Gtk::Window.
To fix the problem, remove the extra dereferencing:
main_win -> add(*box);
NOTE:
I do not know Gtk::Window and its member function add(), but if it is the case that add() accepts a pointer as its argument, then you also shouldn't dereference box.

C++ How to access an object initialized in main() from outside of main()?

My main() looks like this:
int main(int argc, char** argv)
{
// Initialize GLUT
glutInit(&argc, argv);
...
glutDisplayFunc(display);
...
// Set robot's parameters
Robot robot; // Initialize global object robot
robot.setSize(50);
robot.setColor('G');
robot.setLocation(50,100);
glutMainLoop();
return EXIT_SUCCESS;
}
Then I have another function, which I would like to have access to the methods of the robot:
// This function is constantly "looped"
void display() {
...
robot.draw();
...
}
What is the legit way to do it in C++?
For anyone interested, the question changed, so my old answer is lost to the edits.
If your display function is required to have a specific signature (void()), you can use std::bind, presuming you have access to C++11:
void display(Robot &robot){...}
//in main
Robot robot;
glutDisplayFunc(std::bind(std::ref(display), robot));
If you don't have C++11, boost::bind works just as well:
glutDisplayFunc(boost::bind(boost::ref(display), robot));
If you have neither, you'll have to store robot more globally.
Since the glut display callback doesn't take parameters, you will have to use a global variable (Robot * gRobot; ) or a singleton pattern.