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?
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.
Context: I am learning development of GUI using GTK+. I also wanted to draw lines and circles on the GUI. So I started with the tutorials and I am stuck with the part of GtkGLArea. I am following the code given in the GTK+ documentation
The error:
glTrial.cpp:32:13: error: variable or field ‘on_realize’ declared void
on_realize (GtkGLarea *area)
^
glTrial.cpp:32:13: error: ‘GtkGLarea’ was not declared in this scope
glTrial.cpp:32:24: error: ‘area’ was not declared in this scope
on_realize (GtkGLarea *area)
I believe I am not compiling properly and the compiler is not able to find correct headers.
Compilation:
g++ -std=c++14 \`pkg-config --cflags gtk+-3.0\` -o glTrial glTrial.cpp \`pkg-config --libs gtk+-3.0\`
The code:
#include <gtk/gtk.h>
#include <gtkgl-2.0/gtkgl/gdkgl.h>
#include <gtkgl-2.0/gtkgl/gtkglarea.h>
static void
print_hello (GtkWidget *widget,
gpointer user_data)
{
g_print ("Hello World\n");
}
static gboolean
render (GtkGLArea *area, GdkGLContext *context)
{
// inside this function it's safe to use GL; the given
// #GdkGLContext has been made current to the drawable
// surface used by the #GtkGLArea and the viewport has
// already been set to be the size of the allocation
// we can start by clearing the buffer
//glClearColor (0, 0, 0, 0);
// glClear (GL_COLOR_BUFFER_BIT);
// draw your object
// draw_an_object ();
// we completed our drawing; the draw commands will be
// flushed at the end of the signal emission chain, and
// the buffers will be drawn on the window
return TRUE;
}
static void
on_realize (GtkGLarea *area)
{
// We need to make the context current if we want to
// call GL API
gtk_gl_area_make_current (area);
// If there were errors during the initialization or
// when trying to make the context current, this
// function will return a #GError for you to catch
if (gtk_gl_area_get_error (area) != NULL)
return;
// You can also use gtk_gl_area_set_error() in order
// to show eventual initialization errors on the
// GtkGLArea widget itself
GError *internal_error = NULL;
init_buffer_objects (&error);
if (error != NULL)
{
gtk_gl_area_set_error (area, error);
g_error_free (error);
return;
}
init_shaders (&error);
if (error != NULL)
{
gtk_gl_area_set_error (area, error);
g_error_free (error);
return;
}
}
static void
activate (GtkApplication* app,
gpointer user_data)
{
GtkWidget *window;
GtkWidget *grid;
GtkWidget *button;
GtkWidget *gl_area =gtk_gl_area_new();
/* Create a new window, and set its title */
window = gtk_application_window_new(app);
gtk_window_set_title(GTK_WINDOW(window), "Window");
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
// gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);
/* Here we construct the container that is going to pack the buttons*/
grid = gtk_grid_new();
/* Pack the container in the window */
gtk_container_add (GTK_CONTAINER (window), grid);
button = gtk_button_new_with_label ("Quit");
g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_widget_destroy), window);
gtk_grid_attach (GTK_GRID (grid), button, 0, 0, 2, 1);
button = gtk_button_new_with_label ("Hello");
g_signal_connect_swapped (button, "clicked", G_CALLBACK (print_hello), NULL);
gtk_grid_attach (GTK_GRID (grid), button, 0, 1, 2, 1);
/* Trial for GL area*/
g_signal_connect (gl_area, "render", G_CALLBACK(render), NULL);
gtk_grid_attach (GTK_GRID (grid), gl_area, 0, 2, 10, 10);
gtk_widget_show_all (window);
}
int
main (int argc,
char **argv)
{
GtkApplication *app;
int status;
g_application_run (G_APPLICATION (app), argc, argv);
// app = gtk_application_new("org.gtk.example", G_APPLICATION_FLAGS_NONE);
g_signal_connect(app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return status;
}
According to a fast google, you are looking for GtkGLArea, note the uppercase A.
– derhass
you need:
sudo apt install *epoxy*
and
c++ t.c --target=arm-linux-gnu `pkg-config --libs --cflags gtk+-3.0 epoxy ` -o op
I am pretty new to GTK and I want to know how to resize the entry boxes size and and the spacing between label and boxes ?
Also, how to receive the input value from the entry box for further usage you know, like the C function "scanf". Thanks you and sorry for my bad English
#include <stdio.h>
#include <stdlib.h>
#include <gtk/gtk.h>
static void destroy(GtkWidget *widget, gpointer data){
gtk_main_quit ();
}
static void initialize_window(GtkWidget* window) {
gtk_window_set_title(GTK_WINDOW(window),"My Window");
gtk_window_set_default_size (GTK_WINDOW (window), 200, 100);
g_signal_connect (window, "destroy", G_CALLBACK (destroy), NULL);
}
int main (int argc, char *argv[]){
GtkWidget *window,*table,*label,*entry, *entry1, *label1, *label3, *label2, *entry2;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
initialize_window(window);
table = gtk_table_new(4, 2, TRUE);
gtk_container_add (GTK_CONTAINER (window), table);
label = gtk_label_new ("Circle with standard formula:(x-a)^2 + (y-b)^2 =r*r");
gtk_misc_set_alignment (GTK_MISC (label), 70, 70);
gtk_table_set_homogeneous(GTK_TABLE (table), TRUE);
gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 0, 1);
entry = gtk_entry_new ();
gtk_table_attach_defaults (GTK_TABLE (table), entry, 1, 2, 1, 2);
label1 = gtk_label_new ("Input value of a:");
gtk_table_set_homogeneous(GTK_TABLE (table), TRUE);
gtk_table_attach_defaults(GTK_TABLE (table), label1, 0, 1, 1, 2);
entry1 = gtk_entry_new();
gtk_table_attach_defaults(GTK_TABLE(table), entry1, 1, 2, 2, 3);
label2=gtk_label_new("Input the value of b:");
gtk_table_set_homogeneous(GTK_TABLE(table), TRUE);
gtk_table_attach_defaults(GTK_TABLE (table), label2, 0, 1, 2, 3);
label3=gtk_label_new("Input the value of r:");
gtk_table_set_homogeneous(GTK_TABLE(table), TRUE);
gtk_table_attach_defaults(GTK_TABLE (table), label3, 0, 1, 3, 4);
entry2 = gtk_entry_new();
gtk_table_attach_defaults(GTK_TABLE(table), entry2, 1, 2, 3, 4);
gtk_widget_show_all(window);
gtk_main ();
return 0;
}
In order to get the text within one of your entry boxes use the gtk_entry_get_text function found in more detail here: https://developer.gnome.org/gtk3/stable/GtkEntry.html#gtk-entry-get-text
For instance:
buffer = gtk_entry_get_text(&entry1);
As far as changing spacing between elements goes the Gtk documentation notes that if you do not need individual row spacing then GtkGrid is preferred as it appears many of the table functions are deprecated since Gtk 3.4.
To set the spacing between rows use gtk_table_set_row_spacing found here: https://developer.gnome.org/gtk3/stable/GtkTable.html#gtk-table-set-row-spacing
and just below that is listed the information on gtk_table_set_col_spacing.
Setting the padding within table cells is a bit different and would be controlled using x-padding and y-padding properties of the child elements, more information on this is listed at the bottom of the GtkTable page.
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.
I'm trying to get the input text from a text box in a callback function when the user changes something it it (on "changed").
The code goes as follow:
#include <stdio.h>
#include <gtk/gtk.h>
void enter_callback( GtkWidget *widget, GtkEditable *buffer)
{
printf("%s",gtk_editable_get_chars(buffer, 0, -1));
}
int main(int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *text;
GtkWidget *table;
gtk_init (&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
table = gtk_table_new (2, 2, TRUE);
gtk_container_add (GTK_CONTAINER (window), table);
text=gtk_text_new(NULL, NULL);
gtk_text_set_editable(text, TRUE);
gtk_signal_connect(GTK_OBJECT(text), "changed", GTK_SIGNAL_FUNC(enter_callback), (GtkEditable*)text);
gtk_table_attach_defaults(GTK_TABLE(table), text, 0, 1, 0, 1);
gtk_container_border_width (GTK_CONTAINER (window), 40);
gtk_window_set_default_size (GTK_WINDOW(window), 640, 200);
gtk_widget_show(text);
gtk_widget_show(window);
gtk_widget_show(table);
gtk_main();
return 0;
}
The code compiles just right, I'm compiling it on Code::Blocks on debug, checking output on the console by printf. The problem is I get <NULL> as a callback everytime I change something on the textbox. How can I get the correct output?
SOLUTION:
As noted by Washu, gtk_text is deprecated and gtk_text_view should be used instead.
According to the GTK documentation, GtkText is deprecated, buggy, and should not be used. You should instead be using the GtkTextView widget via gtk_text_view_new.
You can use GtkEntry widget too. And use gtk_entry_get_text () (which return const gchar * value) that to get text from GtkEntry, for instance.