GTK uses cairo for drawing. So I'm trying to create a hello world app that writes to an image (svg, png, ...) instead of X11. I'm facing 2 problems:
- The image is empty
- When starting without X11 running (which is the actual goal) I get the error "** (a.out:9021): WARNING **: Could not open X display"
The code is draft!
#include <string>
#include <iostream>
#include <thread>
#include <chrono>
#include <cmath>
#include <cairo.h>
#include <cairommconfig.h>
#include <cairomm/context.h>
#include <cairomm/surface.h>
#include <gtk/gtk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
int main(int argc, char *argv[])
{
gtk_init(&argc, &argv);
GtkWidget *window;
GtkWidget *button;
// GtkWidget *main_window = gtk_initialize();
window = gtk_offscreen_window_new();
button = gtk_button_new_with_label ("Hello World");
gtk_container_add (GTK_CONTAINER (window), button);
gtk_widget_show (window);
GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
std::cout << "gdk window: " << gdk_window << std::endl;
cairo_surface_t * surfp = gdk_offscreen_window_get_surface(gdk_window);
std::cout << "Created Window will now draw to png" << std::endl;
std::string filename = "image.svg";
double width = 600;
double height = 400;
Cairo::SvgSurface srfobj(surfp);
Cairo::RefPtr<Cairo::SvgSurface> refptr(&srfobj);
Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(refptr);
cr->save(); // save the state of the context
cr->show_page();
std::cout << "Wrote SVG file \"" << filename << "\"" << std::endl;
std::chrono::milliseconds dura( 200 );
std::this_thread::sleep_for(dura);
return 0;
}
Why is this code not working?
Can I run a gtk app without X11 running, or should I just ignore the warning?
Here is my solution based on your example code - keep in mind it is a dirty solution and may not work using newer versions of GTK3. It works to save the UI of a window (only tested with the one button), but still requires (somewhere) a running X-server. It also ignores / don't use your settings for the picture size - you'll have to resize it at your own. I don't know if (and how) it is possible to cut this string (X-Server // X-Framebuffer) too (DirectFB seems not to be really supported anymore), but...
Have fun!
// Default
#include <string>
#include <iostream>
#include <thread>
#include <chrono>
// cairo / cairomm / gtk
#include <cairo.h>
#include <cairomm/context.h> //libcairomm-1.0-dev
#include <gtk/gtk.h>
int main(int argc, char *argv[]) {
// Init
gtk_init(&argc, &argv);
// Create window with a button
GtkWidget *window;
GtkWidget *button;
window = gtk_offscreen_window_new();
button = gtk_button_new_with_label("Hello World");
gtk_container_add(GTK_CONTAINER(window), button);
gtk_widget_show_all(window);
// Make a gdk window out of it, prepare cairo and draw it to it
GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
cairo_surface_t* surfp = gdk_offscreen_window_get_surface(gdk_window);
cairo_t* context = cairo_create(surfp);
gtk_widget_draw(GTK_WIDGET(window), context);
// Yay - begin the dump!
Cairo::SvgSurface srfobj(surfp);
std::string filename = "image.png";
srfobj.write_to_png(filename);
std::cout << "Done." << std::endl;
// Aaand a little sleep...
std::chrono::milliseconds dura(1000);
std::this_thread::sleep_for(dura);
return 0;
}
Try to use gtk_widget_draw (widget_ptr, cairo_ctx_ptr); to draw a widget (or a hierarchy of widgets) to a cario context?
The answer to both your questions is that you cannot run GTK+ applications without some sort of output. You're using gtk-x11 which requires an XServer. You might have some luck with the DirectFB backend, but I wouldn't hold your breath as I don't know if it's even maintained anymore.
Because Gtk doesn't run without an XServer the resulting image is empty.
Related
I have programed the GUI in C++.
#include <gtk/gtk.h>
#include <unistd.h>
#include <cstddef>
#include <iostream>
#include <string>
#include <stdio.h>
#include <stdlib.h>
int main(){
GtkWidget *_LinuxWindow, *_Box, *_Button;
int argC = 0;
char** argV;
// Setup the window and fixed grid
gtk_init(&argC,&argV);
_LinuxWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
_Box = gtk_fixed_new();
// Set the title
gtk_window_set_title(GTK_WINDOW(_LinuxWindow),"Title");
// Finish up
gtk_widget_show(_LinuxWindow);
g_signal_connect(G_OBJECT(_LinuxWindow),"destroy",G_CALLBACK(gtk_main_quit), NULL);
// Add controls
_Button = gtk_button_new_with_label("Click Me!");
gtk_fixed_put(GTK_FIXED(_Box),_Button,20,20);
gtk_fixed_move(GTK_FIXED(_Box),_Button,20,20);
gtk_widget_show(_Box);
gtk_widget_set_size_request(_Button,30,100);
// Create a dialog
GtkWidget *dialog;
dialog = gtk_message_dialog_new(GTK_WINDOW(_LinuxWindow), GTK_DIALOG_DESTROY_WITH_PARENT,GTK_MESSAGE_INFO,GTK_BUTTONS_OK_CANCEL,"OK or Cancel?",NULL,g_strerror(errno));
gint ret = gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(GTK_WIDGET(dialog));
printf("%i", ret);
// Add the fixed grid and go the to the main window loop
gtk_container_add(GTK_CONTAINER(_LinuxWindow),_Box);
gtk_widget_show(_Box);
gtk_widget_show_all(_LinuxWindow);
gtk_main();
return 0;
}
When I run it using
g++ -o out-withoutCross without-cross.cpp -lX11 `pkg-config --cflags gtk+-3.0`pkg-config --libs gtk+-3.0`
A message box and a window appears. The problem is the controls in the window do not appear while the message box is open. I think this may be because the message box is being ran on the same thread that the main window is. Is there any way to add multi-threading to GTK?
Also the return value of the message box printed to console is running after the main window is exited.
The problem is the structure of your code..\
#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
GtkWidget *_LinuxWindow, *_Box, *_Button;
int argC = 0;
char **argV;
// Setup the window and fixed grid
gtk_init(&argC, &argV);
_LinuxWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
_Box = gtk_fixed_new();
// Set the title
gtk_window_set_title(GTK_WINDOW(_LinuxWindow), "Title");
// Finish up
gtk_widget_show_all(_LinuxWindow);
g_signal_connect(G_OBJECT(_LinuxWindow), "destroy", G_CALLBACK(gtk_main_quit),
NULL);
// Add controls
_Button = gtk_button_new_with_label("Click Me!");
gtk_fixed_put(GTK_FIXED(_Box), _Button, 20, 20);
gtk_fixed_move(GTK_FIXED(_Box), _Button, 20, 20);
gtk_widget_set_size_request(_Button, 30, 100);
gtk_container_add(GTK_CONTAINER(_LinuxWindow), _Box);
gtk_widget_show_all(_LinuxWindow);
// Create a dialog
GtkWidget *dialog;
dialog = gtk_message_dialog_new(
GTK_WINDOW(_LinuxWindow), GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO, GTK_BUTTONS_OK_CANCEL, "OK or Cancel?", NULL);
gint ret = gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(GTK_WIDGET(dialog));
printf("%i", ret);
// Add the fixed grid and go the to the main window loop
gtk_main();
return 0;
}
gtk_dialog_run is a blocking function so it will wait until it gets a response from the dialog box.. to proceed further
In your code, you are adding the button to the window only after running the dialog box window (which is a blocking call) so after its execution, the button is being added to the window
I posted this on the Pi forum but the only answer I received is that I posted it in the wrong place.
Posting on Pi forum
I managed to create a program that will display some buttons and an Image. Now I would like to update the image when a button is clicked. How do I do that?
Here is what I have so far, it all works but missing code to update the displayed image. The 3 second delay before camrea.grab is missing from this code but has been add to the code that I am using.
#include <iostream>
#include <raspicam/raspicam.h>
#include <wiringPi.h>
#include <gtk/gtk.h>
#include <fstream>
#include <vector>
#include <unistd.h>
using namespace std;
void Capture_Image(void){
//camera setup
raspicam::RaspiCam camera; //Camera object
if (!camera.open())
{
cerr << "Could not open the camera" << endl;
//return 1;
}
camera.grab();
std::vector<unsigned char> buf;
buf.resize(camera.getImageTypeSize(raspicam::RASPICAM_FORMAT_RGB));
camera.retrieve(buf.data(), raspicam::RASPICAM_FORMAT_RGB);
std::ofstream outFile("/home/pi/s.jpg", std::ios::binary);
outFile << "P6\n" << camera.getWidth() << " " << camera.getHeight() << " 255\n";
outFile.write((char*)buf.data(), buf.size());
cout << "Image saved to output.ppm" << endl;
}
int main(int argc, char **argv)
{
wiringPiSetup();
gtk_init (&argc, &argv);
//Main Box
GtkWidget *mainBox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
gtk_container_add (GTK_CONTAINER (win), mainBox);
//camera image container
GtkWidget *cameraBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
gtk_container_add (GTK_CONTAINER (mainBox), cameraBox);
GtkWidget *cameraView = gtk_image_new_from_file("/home/pi/s.jpg");
gtk_box_pack_start (GTK_BOX (cameraBox), cameraView, TRUE, TRUE, 0);
//resize image
GdkPixbuf *pixBuf = gtk_image_get_pixbuf(GTK_IMAGE(cameraView));
pixBuf = gdk_pixbuf_scale_simple(pixBuf, 300,200,GDK_INTERP_BILINEAR);
gtk_image_set_from_pixbuf(GTK_IMAGE(cameraView),pixBuf);
//image capture button
GtkWidget *imageCapture = gtk_button_new_with_label ("Image Capture");
g_signal_connect (imageCapture, "clicked", G_CALLBACK (Capture_Image), NULL);
gtk_box_pack_start (GTK_BOX (cameraBox), imageCapture, TRUE, TRUE, 0);
//show the new window
gtk_widget_show_all (win);
//Start the GTK main loop
gtk_main ();
return 0;
}
I did a lot of research and found the solution.
I updated my function like this
void Capture_Image(GtkWidget *widget, gpointer image){
And added this code in my function after the image was saved.
GtkWidget *cameraView = (GtkWidget *) image;
gtk_image_set_from_file(GTK_IMAGE(cameraView),"/home/pi/s.jpg");
//resize image
GdkPixbuf *pixBufUpdate = gtk_image_get_pixbuf(GTK_IMAGE(cameraView));
pixBufUpdate = gdk_pixbuf_scale_simple(pixBufUpdate, 320,240,GDK_INTERP_BILINEAR);
gtk_image_set_from_pixbuf(GTK_IMAGE(cameraView),pixBufUpdate);
When I call with the button I used this
g_signal_connect (imageCapture, "clicked", G_CALLBACK (Capture_Image), cameraView);
I am trying to create a program in fltk and I followed this example
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>
int main(int argc, char **argv) {
Fl_Window *window = new Fl_Window(340,180);
Fl_Box *box = new Fl_Box(20,40,300,100,"Hello, World!");
box->box(FL_UP_BOX);
box->labelfont(FL_BOLD+FL_ITALIC);
box->labelsize(36);
box->labeltype(FL_SHADOW_LABEL);
window->end();
window->show(argc, argv);
return Fl::run();
}
from FLTK docs. The problem is I'm trying to draw multiple rectangle shapes to the window and it seem very tedious to create multiple boxed in order to have multiple rectangles. I tried looking up a lot of tutorials on drawing shapes on FLTK but I can't find anything simple enough to show me.
My code looks like this so far
#include <FL/Fl.H>
#include <FL/Fl_Widget.H>
#include <FL/Fl_Window.H>
// #include <FL/fl_draw.H>
#include <iostream>
int main() {
Fl_Window *window = new Fl_Window(900, 600);
window->position(0, 0);
window->color(FL_BLACK);
window->end();
window->show();
while (1) {
int ev = Fl::event();
if (ev == FL_SHORTCUT) {
if (Fl::event_key() == FL_Escape)
break;
}
Fl::check();
}
return 0;
}
and I would like to implement the drawing inside the loop (continuously).
As an important side note: You should really consider using Fl::run() instead of your custom while loop, I ran into many problems with a similar approach like yours.
Now, to answer your question:
Take the example and wrap the Box creation inside a loop. You can take for example an index for assigning different positions to each Box.
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>
int main(int argc, char **argv) {
Fl_Window *window = new Fl_Window(340,500);
// 3 Fl_Boxes in a loop
for (int i = 0; i < 3; ++i) {
Fl_Box *box = new Fl_Box(20, 40 + i*120, 300, 100,"Hello, World!");
box->box(FL_UP_BOX);
box->labelfont(FL_BOLD+FL_ITALIC);
box->labelsize(36);
box->labeltype(FL_SHADOW_LABEL);
}
window->end();
window->show(argc, argv);
return Fl::run();
}
You control the layout with the formula 20, 40 + i*120, 300, 100 in the box Constructor. You have to adjust that to your requirements.
With FLTK one basic rule is: All widgets you initialize between the window constructor and window->end() or any other widget that works the same way, for example Fl_Group, will become children of the surrounding element and will show, if their parent is shown.
I am not entirely sure, if Fl_Box is what you are looking for. I found Erco's FLTK Cheat Page extremely helpful. You might find inspiration and other approaches to your problem there.
I have a program here that performs face detection and I would like to use these coordinates to move a window created with GTK+ 3.22 using GTK's
gtk_window_move function. I would like the window to remain open the entire time while it is moving similar to OpenCV's moveWindow function.
I just downloaded the GTK+ packages yesterday so I am not all too familiar.
The program will perform a loop 100 times, tracking a face the entire time. Currently, the face tracking works, but the window does not appear until the loop is complete. Why is this? I believe the gtk_move_window function is working, but the window does not stay open. I have tried reopening the window each time in the loop, or just opening once before the loop. If you are familiar with OpenCV's moveWindow function that is exactly what I am looking for. Here is the sample code.
By the way, if you know how a GTK+ function that will bring the window to the very top layer on top of all the other windows open when called, that is helpful information for me as well.
#include "FlyCapture2.h"
#include <opencv2/core/core.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/cuda.hpp>
#include <opencv2/cudaobjdetect.hpp>
#include <math.h>
#include <thread>
#include <iostream>
#include <vector>
#include <gtk-3.0/gtk/gtk.h>
using namespace FlyCapture2;
cv::Ptr<cv::cuda::CascadeClassifier> face_detect;
void detect_faces(cv::Mat img, cv::cuda::GpuMat buf,GtkWidget *win)
{
std::vector<cv::Rect>faces;
cv::cuda::GpuMat image_gpu(img);
//Face detection here
...
if (faces.size() > 0)
{
float x = faces[0].x;
float y = faces[0].y;
int new_x = roundf(x*40/51);
int new_y = roundf(y*135/256);
gtk_window_move(GTK_WINDOW (win),new_x,new_y);
gtk_widget_show (win); //Should this go here?
std::cout<<faces[0]<<std::endl;
}
}
int main( int argc, char *argv[])
{
//Camera connect here
...
//face detect variables
face_detect = cv::cuda::CascadeClassifier::create("/home/nvidia/opencv/data/haarcascades_cuda/haarcascade_frontalface_default.xml");
cv::cuda::GpuMat objbuf;
//GTK+ Params
GtkWidget *window;
GdkRGBA *color;
gtk_init (&argc, &argv);
gdk_rgba_parse(color,"(0,0,0)");
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_decorated(GTK_WINDOW (window),FALSE);
gtk_window_set_position(GTK_WINDOW (window), GTK_WIN_POS_CENTER);
gtk_widget_override_background_color(window, GTK_STATE_FLAG_NORMAL, color);
gtk_widget_show (win); //Should this go here?
// capture loop
for (int i=0;i<100;i++)
{
// Get the image
Image rawImage;
camera.RetrieveBuffer( &rawImage );
// convert to rgb
Image rgbImage;
rawImage.Convert( FlyCapture2::PIXEL_FORMAT_MONO8, &rgbImage );
// convert to OpenCV Mat
unsigned int rowBytes = (double)rgbImage.GetReceivedDataSize()/(double)rgbImage.GetRows();
cv::Mat image = cv::Mat(rgbImage.GetRows(), rgbImage.GetCols(), CV_8UC1, rgbImage.GetData(),rowBytes);
//Detect Faces
detect_faces(image,objbuf,window);
}
//Disconnect Camera
camera.StopCapture();
camera.Disconnect();
gtk_main();
return 0;
}
The code in your capture loop should be in an event handler callback.
You first need to call g_timeout_add or g_idle_add to register your callback.
The callback you registered is a GSourceFunc that will be called after gtk_main is run. The return value (G_SOURCE_CONTINUE or G_SOURCE_REMOVE) controls if you want to have the callback be called again.
I want to customize Qt's GUI
Here is the Default Qt OpenCV GUI
I want to place createButton on Menu.
How can I do that?
Below is my code
#include "mainwindow.h"
#include <QApplication>
#include <opencv2/core/core.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
void callbackButton2(int state, void* userData){
}
int main(int argc, char *argv[])
{
//QApplication a(argc, argv);
//MainWindow w;
//w.show();
cv::VideoCapture vc(0);
if(!vc.isOpened()) perror("Can't Open WebCam");
const char* winname = "WebCam";
cv::namedWindow(winname,CV_GUI_EXPANDED);
cv::createButton("button6",callbackButton2,NULL,CV_PUSH_BUTTON,1);
for(;;){
cv::Mat frm;
vc>>frm;
if(!frm.empty()) imshow(winname,frm);
if(cv::waitKey(20)==27) break;
}
vc.release();
cv::destroyWindow(winname);
return 0;
// return a.exec();
}
The code above simply opens webcam and display it on the screen. But for my next application, I need to customize gui and place it on to the menu.
BTW I searched through the fllowing link, and I couldn't find how can I customize the GUI. http://docs.opencv.org/2.4/modules/highgui/doc/qt_new_functions.html
I'm not sure about if you can customize the OpenCv Qt windows but you can code your own window and then show the openCv image, using something similiar to this, you can retrive the cv:Mat immage and convert it to a Qimage object then show the object in a Qt graphicsview using a QPixmap.