Related
this code show me on xubuntu 21.04 form with size (400px width + 400px height) and with ONE button.
How can I add to this code two buttons?
#include <gtk/gtk.h>
static void activate(GtkApplication *app, void *user_data) {
GtkWidget *window = gtk_application_window_new(app);
gtk_window_set_child(GTK_WINDOW(window), gtk_label_new("Hello World!"));
gtk_window_present(GTK_WINDOW(window));
gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
// Create a new button
GtkWidget *button = gtk_button_new_with_label ("press 123");
gtk_window_set_child (GTK_WINDOW (window), button);
// When the button is clicked, close the window passed as an argument
g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_window_close), window);
}
int main(int argc, char *argv[]) {
g_autoptr(GtkApplication) app = gtk_application_new(NULL, G_APPLICATION_FLAGS_NONE);
g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
return g_application_run(G_APPLICATION(app), argc, argv);
}
In GTK4, I have found that a window (GtkWindow or GtkApplicationWindow) can only have one child. So to include multiple widgets within a window (such as a label and three buttons) one usually has to first create a grid object (GtkGrid), place the widgets within the grid at specified rows and columns, and then set the grid as the child of the window. Using your sample code above, I revised the code to look like the following:
#include <gtk/gtk.h>
static void activate(GtkApplication *app, void *user_data)
{
GtkWidget *window = gtk_application_window_new(app);
GtkWidget *grid = gtk_grid_new();
GtkWidget *label = gtk_label_new("Hello World");
//gtk_window_set_child(GTK_WINDOW(window), gtk_label_new("Hello World!"));
gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
gtk_grid_set_column_spacing(GTK_GRID(grid),10);
gtk_grid_set_row_spacing(GTK_GRID(grid), 6);
// Create a new button
GtkWidget *button1 = gtk_button_new_with_label ("Press 1");
GtkWidget *button2 = gtk_button_new_with_label ("Press 2");
GtkWidget *button3 = gtk_button_new_with_label ("Press 3");
gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 3, 1);
gtk_grid_attach(GTK_GRID(grid), button1, 0, 1, 1, 1);
gtk_grid_attach(GTK_GRID(grid), button2, 1, 1, 1, 1);
gtk_grid_attach(GTK_GRID(grid), button3, 2, 1, 1, 1);
gtk_window_set_child (GTK_WINDOW (window), grid);
// When the button is clicked, close the window passed as an argument
g_signal_connect_swapped (button1, "clicked", G_CALLBACK (gtk_window_close),
window);
g_signal_connect_swapped (button2, "clicked", G_CALLBACK (gtk_window_close),
window);
g_signal_connect_swapped (button3, "clicked", G_CALLBACK (gtk_window_close),
window);
gtk_window_present(GTK_WINDOW(window));
}
int main(int argc, char *argv[])
{
g_autoptr(GtkApplication) app = gtk_application_new(NULL,
G_APPLICATION_FLAGS_NONE);
g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
return g_application_run(G_APPLICATION(app), argc, argv);
}
This results in establishing a window with the two additional buttons you wanted. I did not know what type of functions those buttons should trigger, so for my revisions to your sample code, I just hooked the two additional buttons to the same closure signal. Following is a sample of the window when I run the program.
I hope that helps you out.
Regards.
Additional Notes:
Regarding the request as how to add "label2" and then have that label's text updated to "Network Connections", the following additions to your sample program could provide a way to do this.
First, a callback function for updating the new label's text would be added to the program (usually at the beginning of the program).
void on_button1_clicked (GtkLabel *lbl)
{
gtk_label_set_text(lbl, "Network Connections");
}
Then, within the activation function, the new label would be defined.
GtkWidget *label2 = gtk_label_new("");
Next, the new label widget would be added to the grid (in this example, it was added next to the "label" widget in the first row).
gtk_grid_attach(GTK_GRID(grid), label2, 1, 0, 2, 1);
Finally, since the request in the comment was to have the label's text updated to "Network Connections", the signal connection for the first button would be revised to call the new "on_button1_clicked" callback function and passing the "label2" widget instead of the "window" widget.
g_signal_connect_swapped (button1, "clicked", G_CALLBACK (on_button1_clicked), label2);
The result should net the desired behavior.
Hopefully, that addresses your comment.
Regards.
I'm working on a project that is supposed to translate the user input but that isn't the issue. Indeed I'm new to libmotif and I want a toggle button to chose the way of translation (french to italian or italian to french). Therefore, I already have a cascade button on this menu bar and I got this Warning :
Warning:
Name: main_list
Class: XmRowColumn
Attempt to add wrong child type to a homogeneous RowColumn widget
Warning: Cannot find callback list in XtAddCallback
Moreover, can I put the toggle button in parameter of other function to look his state?
//CrĂ©ation de la fenĂȘtre principal
Widget main_window = XtVaCreateManagedWidget("main_window", xmMainWindowWidgetClass, top_widget, NULL) ;
//Menu
Widget menu_bar = XmCreateMenuBar(main_window, (String) "main_list", NULL, 0);
XtManageChild(menu_bar);
Widget quit = XtVaCreateManagedWidget("Quit", xmCascadeButtonWidgetClass, menu_bar,NULL);
Widget toggle = XtVaCreateManagedWidget("Fr vers It", xmToggleButtonWidgetClass, menu_bar, NULL);
//Callbacks
XtAddCallback(quit, XmNactivateCallback, quit_call, NULL);
XtAddCallback(toggle,XmNactivateCallback, toggle_call, NULL);
You may only attach a CascadeButton or CascadeButtonGadget to a Menubar. You cannot put a toggle button on it. You may create a PullDown and attach the Toggle buttun to it
#include <Xm/RowColumn.h>
#include <Xm/MainW.h>
#include <Xm/CascadeB.h>
#include <Xm/SeparatoG.h>
#include <Xm/PushBG.h>
#include <Xm/ToggleBG.h>
main(argc, argv)
int argc;
char *argv[];
{
Widget toplevel, MainWindow, menuBar, FilePullDown;
XmString label_str;
XtAppContext app;
XtSetLanguageProc (NULL, NULL, NULL);
toplevel = XtVaAppInitialize (&app, "Program", NULL, 0,
&argc, argv, NULL, NULL);
MainWindow = XtVaCreateManagedWidget ("mainWindow",
xmMainWindowWidgetClass, toplevel,
XmNscrollingPolicy, XmAUTOMATIC,
NULL);
menuBar = XmCreateMenuBar (MainWindow, "menuBar", NULL, 0);
FilePullDown = XmCreatePulldownMenu (menuBar, "Language", NULL, 0);
label_str = XmStringCreateLocalized ("Language or Exit");
XtVaCreateManagedWidget ("File",
xmCascadeButtonWidgetClass, menuBar,
XmNlabelString, label_str,
XmNmnemonic, 'F',
XmNsubMenuId, FilePullDown,
NULL);
XmStringFree (label_str);
/* Now add the menu items */
XtVaCreateManagedWidget ("Italian or French",
xmToggleButtonGadgetClass, FilePullDown, NULL);
XtVaCreateManagedWidget ("separator",
xmSeparatorGadgetClass, FilePullDown, NULL);
XtVaCreateManagedWidget ("Exit",
xmPushButtonGadgetClass, FilePullDown, NULL);
XtManageChild (menuBar);
XtRealizeWidget (toplevel);
XtAppMainLoop (app);
}
so i am currently trying to achieve the following: Create a gui for the user were the user can see a livefeed of the webcam and also have two buttons left of the livefeed to start and stop the recording. all this has to run on a raspberry pi.
I am using a basler camera so i can not use the videocapture object but using the pylon sdk (sdk for basler cameras) i managed to grab an image and convert it into a opencv::mat.
however im stuck on two points:
first i do not know how to display an opencv::mat on a gtk widget at all.
the second problem is that i do not know how to get a live stream. I tried to use the g_timeout_add function but then the ui gets unresponsive. it calls the function in background but since i call it every 100ms it seems to be stuck forever in the function and therefore i can not click on any buttons.
i have included some of my code below.
#include <gtk/gtk.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/video/video.hpp>
#include <pylon/PylonIncludes.h>
using namespace Pylon;
using namespace cv;
using namespace std;
void callback(GtkWidget *widget, gpointer labelstatus) {}
gint delete_event(GtkWidget *widget, GdkEvent *event) {
gtk_main_quit ();
}
static gboolean livefeed_func(gpointer data) {
Pylon::PylonAutoInitTerm autoInitTerm;
CInstantCamera camera(CTlFactory::GetInstance().CreateFirstDevice());
camera.Open();
GenApi::INodeMap& nodemap = camera.GetNodeMap();
.
. Some other pylon related stuff
.
CImageFormatConverter formatConverter;
formatConverter.OutputPixelFormat= PixelType_BGR8packed;
CPylonImage pylonImage;
Mat openCvImage;
cv::Size frameSize = Size((int)width->GetValue(), (int)height->GetValue());
.
. Some other pylon related stuff
.
formatConverter.Convert(pylonImage, ptrGrabResult);
openCvImage = cv::Mat(ptrGrabResult->GetHeight(), ptrGrabResult->GetWidth(), CV_8UC3, (uint8_t *) pylonImage.GetBuffer());
imwrite("tmp_name.jpg", openCvImage);
}
int main(int argc, char *argv[]) {
//GTK
GtkWidget *window;
GtkWidget *table;
GtkWidget *labelstatus;
GtkWidget *labellog;
GtkWidget *buttonexit;
GtkWidget *buttonstart;
GtkWidget *buttonstop;
GtkWidget *livefeed;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "Table");
gtk_container_set_border_width (GTK_CONTAINER (window), 20);
gtk_signal_connect (GTK_OBJECT (window), "delete_event", GTK_SIGNAL_FUNC (delete_event), NULL);
table = gtk_table_new (6, 6, TRUE);
gtk_container_add (GTK_CONTAINER (window), table);
labelstatus = gtk_label_new ("Status: Idle");
gtk_table_attach_defaults (GTK_TABLE(table), labelstatus, 0, 2, 0, 1);
gtk_widget_show (labelstatus);
buttonstart = gtk_button_new_with_label ("Start");
gtk_signal_connect (GTK_OBJECT (buttonstart), "clicked", GTK_SIGNAL_FUNC (callback), (gpointer) labelstatus);
gtk_table_attach_defaults (GTK_TABLE(table), buttonstart, 0, 1, 1, 2);
gtk_widget_show (buttonstart);
buttonstop = gtk_button_new_with_label ("Stop");
gtk_signal_connect (GTK_OBJECT (buttonstop), "clicked", GTK_SIGNAL_FUNC (callback), NULL);
gtk_table_attach_defaults (GTK_TABLE(table), buttonstop, 1, 2, 1, 2);
gtk_widget_show (buttonstop);
labellog = gtk_label_new ("11:31:54: Started Recording\n11:36:54: Stopped Recording");
gtk_table_attach_defaults (GTK_TABLE(table), labellog, 0, 2, 2, 3);
gtk_widget_show (labellog);
buttonexit = gtk_button_new_with_label ("Quit");
gtk_signal_connect (GTK_OBJECT (buttonexit), "clicked",GTK_SIGNAL_FUNC (delete_event), NULL);
gtk_table_attach_defaults (GTK_TABLE(table), buttonexit, 0, 2, 3, 4);
gtk_widget_show (buttonexit);
livefeed = gtk_drawing_area_new();
gtk_table_attach_defaults (GTK_TABLE(table), livefeed, 2, 5, 0, 4);
gtk_widget_show (livefeed);
gtk_widget_show (table);
gtk_widget_show (window);
g_timeout_add(100, livefeed_func, livefeed);
gtk_main ();
}
so if i run this code it will display the gtk window and then be unresponsive and write images until i cancel the process.
So to sum it up:
is there a way to execute background tasks in gtk without the ui becoming unresponsive
how can i display an opencv::Mat in a gtk window/widget?
int main(int argc, char **argv)
{
GtkWidget *pWindow;
GtkWidget *pVBox;
GtkWidget *pEntry;
GtkWidget *pButton;
GtkWidget *pLabel;
GtkWidget *text_view;
GtkWidget *scrollbar;
gtk_init(&argc, &argv);
pWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(pWindow), "My IRC");
gtk_window_set_default_size(GTK_WINDOW(pWindow), 800, 600);
g_signal_connect(G_OBJECT(pWindow), "destroy", G_CALLBACK(gtk_main_quit), NULL);
pVBox = gtk_vbox_new(TRUE, 0);
pEntry = gtk_entry_new();
pLabel = gtk_label_new(NULL);
text_view = gtk_text_new(NULL, NULL);
scrollbar = gtk_scrolled_window_new(NULL, NULL);
gtk_container_add(GTK_CONTAINER(pWindow), pVBox);
gtk_box_pack_start(GTK_BOX(pVBox), scrollbar, TRUE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(pVBox), pEntry, TRUE, FALSE, 0);
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrollbar), text_view);
g_signal_connect(G_OBJECT(pEntry), "activate", G_CALLBACK(on_activate_entry), (GtkWidget*) text_view);
gtk_widget_show_all(pWindow);
gtk_main();
return EXIT_SUCCESS;
}
I want to make the text_view box bigger than the other one. I couldn't find any solutions in the GTK documentation.
PS: It's GTK 2.0.
The two boolean arguments of gtk_box_pack_start() are expand and fill. When expand is true, the widget gets extra space after allocating other widgets. When fill is true, the widget is resized to fill that space. So what you want to do instead is
// expand AND fill - fills all available space
gtk_box_pack_start(GTK_BOX(pVBox), scrollbar, TRUE, TRUE, 0);
// NO expand AND NO fill - only uses what it needs
gtk_box_pack_start(GTK_BOX(pVBox), pEntry, FALSE, FALSE, 0);
Here's a page with more information. Note that you can use the [hv](expand|align) properties with boxes as well as grids (and use gtk_container_add(), which acts like gtk_box_pack_start()).
I've coded a very simple GUI with GTK3, with three buttons:
Start: calls OpenCV VideoCapture
Stop: stops the VideoCapture
Quit: destroys the window
The problem arises when I hit "Stop", the OpenCV process blocks the GUI and the command is executed after many seconds (typically, a minute).
this is my main:
bool stop = false;
static void start_webcam (GtkWidget *widget, gpointer data)
{
cout<<"Webcam On"<<endl;
int err = 0;
**err = opencv_webcam(); // this function loads the OpenCV Webcam**
// troubles?
if (err != 0)
{
cout<<"Unable to load the webcam. Error Code # "<<err<<endl;
}
}
static void stop_webcam (GtkWidget *widget, gpointer data)
{
stop = true;
cout<<"Webcam Off"<<endl;
}
int main (int argc, char *argv[])
{
// Widgets
GtkWidget *window;
GtkWidget *grid;
GtkWidget *button;
// GTK Init
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "WEBCAM CONTROL PANEL");
g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
grid = gtk_grid_new ();
gtk_container_add (GTK_CONTAINER (window), grid);
/// START WEBCAM
button = gtk_button_new_with_label ("Start");
g_signal_connect (button, "clicked", G_CALLBACK (start_webcam), NULL);
gtk_grid_attach (GTK_GRID (grid), button, 0, 0, 1, 1);
/// STOP WEBCAM
button = gtk_button_new_with_label ("Stop");
g_signal_connect (button, "clicked", G_CALLBACK (stop_webcam), NULL);
gtk_grid_attach (GTK_GRID (grid), button, 1, 0, 1, 1);
/// QUIT
button = gtk_button_new_with_label ("Quit");
g_signal_connect (button, "clicked", G_CALLBACK (gtk_main_quit), NULL);
gtk_grid_attach (GTK_GRID (grid), button, 0, 1, 2, 1);
/// GO!
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
I would like to set some high priority to the Quit button. I tried by setting the highest priority in my main by SetPriorityClass() in Windows, but I was unsuccessful.
Thank you very much!
EDIT
this is my openCV code:
extern bool stop;
int opencv_webcam()
{
/// Various stuff
cv::Mat frame;
cv::namedWindow("Webcam Session");
/// Webcam Opening
cv::VideoCapture clip(0);
while(stop != true)
{
// frames extraction
clip.grab();
clip.retrieve(frame);
// troubles?
if ((frame.empty()==1))
{
// errore!
return -1;
}
/// SHOW the frames
cv::imshow("Webcam Session", frame);
if (waitKey(30)>= 0)
{
break;
}
}
return 0;
}
GTK+ is NOT multithreaded. When one of your signal handlers is running, all other GUI processing stops. Your opencv_webcam() function is running on the GUI thread, and runs its main loop while processing the Start button signal. The reason why your Stop and Quit buttons aren't working is because they never get a chance to run (at least not until opencv_webcam() errors out).
I don't know what the appropriate solution is; I don't know enough about OpenCV to suggest a solution that lets you have stable timing while playing nice with GTK+. I know of two possibilities, though:
Doing all OpenCV processing in a gdk_threads_add_idle() callback (stable timing will be harder)
Running OpenCV on a different thread (you will need to communicate frame images across threads if you want to show them in your window)
You will need to search around and figure out what the best solution is. Try Googling for "gtk opencv" and seeing what you get.
Alternatively, if you're doing strict webcam work, you might want to look at libcheese, which is a GTK+ complement specifically intended for webcams.
As for button priorities, there's no signal prioritization that I know of. Given the diagnosis of your bug above, you should be able to see why prioritization wouldn't have helped anyway. In fact, I don't know of any GUI toolkit for any platform that has that ability. You should instead fix threading bugs that prevent the GUI from being snappy.