How to change Gtk::Image after object creation when PixBuf is given - c++

I am attempting to change Gtk::Image-derived object by giving it pixbuf, but i cannot figure out how to approach that.
The simple setup can be mimicked as:
#include <gtkmm.h>
#include <iostream>
class MyImage : public Gtk::Image
{
public:
void setPixBuf(Glib::RefPtr<Gdk::Pixbuf> pixbuf);
};
void MyImage::setPixBuf(Glib::RefPtr<Gdk::Pixbuf> pixbuf)
{
// How can i override the existing pixbuf here?
}
void freeImagePixelData(const guint8* data)
{
delete[] data;
}
Glib::RefPtr<Gdk::Pixbuf> generateTestImage()
{
guint8 *data = new guint8[40*40*4];
for(int i=0; i<40*40*4; )
{
data[i++] = (guint8)255; // R
data[i++] = (guint8)255; // G
data[i++] = (guint8)0; // B
data[i++] = (guint8)255; // A
}
Glib::RefPtr<Gdk::Pixbuf> pixbuf = Gdk::Pixbuf::create_from_data(
data, Gdk::Colorspace::COLORSPACE_RGB, true, 8, 40, 40, 40*4, sigc::ptr_fun(&freeImagePixelData));
return pixbuf;
}
int main(int argc, char** argv)
{
Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "com.almost-university.gtkmm.image.pixbuf");
Gtk::Window window;
MyImage im1;
im1.setPixBuf(generateTestImage());
window.add(im1);
window.show_all_children();
app->run(window);
return 0;
}
(Please note that this is an oversimplified version of what i am trying to figure out, i do know that i should be using Gtk::manage and not add things directly to the window without another container, this is just a mock-up).
I know that if i were to generate the image using a constructor as so:
Gtk::Image im2(generateTestImage());
window.add(im2);
then i would in fact be getting a yellow square.
Somehow i refuse to believe that one can only use pixbuf at the time of object creation. There must be a way to set the image data somehow, and i just cannot find the needed function.

To set Pixbuf in an Gtk::Image you can use Gtk::Image::set(const Glib::RefPtr< Gdk::Pixbuf >& pixbuf) method:
void MyImage::setPixBuf(Glib::RefPtr<Gdk::Pixbuf> pixbuf)
{
set(pixbuf);
}

Related

GTKmm popover menu items not highlighting when used with treeview

Good afternoon,
I'm trying to integrate a popover context menu to a treeview widget in GTKmm 4.
I've been successful in getting the menu to be displayed and for the respective actions to be called when clicking on the context menu options, however, I'm finding that the menu items are not being highlighted when the mouse hovers over them.
A GIF showing what I'm seeing is here:
If however, using the same code, I attach the menu and action group to another widget (such as a button or the window itself), all works as expected and the options are highlighted correctly.
Below is code for a minimal reproducible example.
Could someone help as I'm going round in circles with this??
#include <gtkmm.h>
class Window : public Gtk::Window {
public:
Window() {
list_store_ = Gtk::ListStore::create(model_);
auto row = *(list_store_->append());
row[model_.id] = 1;
row[model_.name] = "Example 1";
row = *(list_store_->append());
row[model_.id] = 2;
row[model_.name] = "Example 2";
treeview_.set_hexpand(true);
treeview_.set_vexpand(true);
treeview_.set_model(list_store_);
treeview_.append_column("ID", model_.id);
treeview_.append_column("Name", model_.name);
Glib::RefPtr<Gio::Menu> gmenu = Gio::Menu::create();
gmenu->append("_Edit", "popup.edit");
gmenu->append("_Remove", "popup.remove");
menu_.set_parent(treeview_);
menu_.set_menu_model(gmenu);
menu_.set_has_arrow(false);
Glib::RefPtr<Gio::SimpleActionGroup> action_group = Gio::SimpleActionGroup::create();
action_group->add_action("edit", sigc::mem_fun(*this, &Window::on_popup_edit));
action_group->add_action("remove", sigc::mem_fun(*this, &Window::on_popup_remove));
treeview_.insert_action_group("popup", action_group);
Glib::RefPtr<Gtk::GestureClick> gesture = Gtk::GestureClick::create();
gesture->set_button(GDK_BUTTON_SECONDARY);
gesture->signal_pressed().connect(sigc::mem_fun(*this, &Window::on_popup_button_pressed));
treeview_.add_controller(gesture);
set_child(treeview_);
}
~Window() override {
}
private:
class ExampleModel : public Gtk::TreeModel::ColumnRecord {
public:
ExampleModel() {
add(id);
add(name);
}
Gtk::TreeModelColumn<int> id;
Gtk::TreeModelColumn<Glib::ustring> name;
};
void on_popup_button_pressed(int, double x, double y) {
int cx, cy;
treeview_.convert_widget_to_bin_window_coords(x, y, cx, cy);
Gtk::TreeModel::Path path;
treeview_.get_path_at_pos(cx, cy, path);
if (!path) {
return;
}
const Gdk::Rectangle rect(x, y, 1, 1);
menu_.set_pointing_to(rect);
menu_.popup();
}
void on_popup_edit() { /* Implementation here */ }
void on_popup_remove() { /* Implementation here */ }
Gtk::TreeView treeview_;
ExampleModel model_;
Glib::RefPtr<Gtk::ListStore> list_store_;
Gtk::PopoverMenu menu_;
};
int main(int argc, char** argv) {
auto app = Gtk::Application::create("com.example.treeview");
return app->make_window_and_run<Window>(argc, argv);
}
Okay, so I recently had time to come back to this project.
This time I thought I'd attempt to re-write it in Rust using the gtk4-rs crate and see if the same happens and it does!
I've managed to work around this issue by wrapping the TreeView inside of a ScrolledWindow and then setting up the menu against the ScrolledView instead of the TreeView.
Inside the on_popup_button_pressed function, I'm still able to determine the path/item that was clicked.
Updated code (untested though - as the project is now written in Rust instead) is below:
#include <gtkmm.h>
class Window : public Gtk::Window {
public:
Window() {
list_store_ = Gtk::ListStore::create(model_);
auto row = *(list_store_->append());
row[model_.id] = 1;
row[model_.name] = "Example 1";
row = *(list_store_->append());
row[model_.id] = 2;
row[model_.name] = "Example 2";
treeview_.set_hexpand(true);
treeview_.set_vexpand(true);
treeview_.set_model(list_store_);
treeview_.append_column("ID", model_.id);
treeview_.append_column("Name", model_.name);
scrolled_window_.set_child(treeview_);
Glib::RefPtr<Gio::Menu> gmenu = Gio::Menu::create();
gmenu->append("_Edit", "popup.edit");
gmenu->append("_Remove", "popup.remove");
menu_.set_parent(scrolled_window_);
menu_.set_menu_model(gmenu);
menu_.set_has_arrow(false);
Glib::RefPtr<Gio::SimpleActionGroup> action_group = Gio::SimpleActionGroup::create();
action_group->add_action("edit", sigc::mem_fun(*this, &Window::on_popup_edit));
action_group->add_action("remove", sigc::mem_fun(*this, &Window::on_popup_remove));
scrolled_window.insert_action_group("popup", action_group);
Glib::RefPtr<Gtk::GestureClick> gesture = Gtk::GestureClick::create();
gesture->set_button(GDK_BUTTON_SECONDARY);
gesture->signal_pressed().connect(sigc::mem_fun(*this, &Window::on_popup_button_pressed));
scrolled_window_.add_controller(gesture);
set_child(scrolled_window_);
}
~Window() override {
}
private:
class ExampleModel : public Gtk::TreeModel::ColumnRecord {
public:
ExampleModel() {
add(id);
add(name);
}
Gtk::TreeModelColumn<int> id;
Gtk::TreeModelColumn<Glib::ustring> name;
};
void on_popup_button_pressed(int, double x, double y) {
int cx, cy;
treeview_.convert_widget_to_bin_window_coords(x, y, cx, cy);
Gtk::TreeModel::Path path;
treeview_.get_path_at_pos(cx, cy, path);
if (!path) {
return;
}
const Gdk::Rectangle rect(x, y, 1, 1);
menu_.set_pointing_to(rect);
menu_.popup();
}
void on_popup_edit() { /* Implementation here */ }
void on_popup_remove() { /* Implementation here */ }
Gtk::TreeView treeview_;
Gtk::ScrolledWindow scrolled_window_;
ExampleModel model_;
Glib::RefPtr<Gtk::ListStore> list_store_;
Gtk::PopoverMenu menu_;
};
int main(int argc, char** argv) {
auto app = Gtk::Application::create("com.example.treeview");
return app->make_window_and_run<Window>(argc, argv);
}

Fl_Window subclass doesn't work

As titled above, i'm trying to get an extremely simple FLTK 1.3.3 application working.
But, even with only a simple Fl_Window and 1 Fl_Button, nothing seems to work. Can anyone help?
class MainEditorWindow : public Fl_Window
{
public:
MainEditorWindow(int _width, int _height, std::string _title);
~MainEditorWindow();
virtual void draw();
virtual int handle(int _event);
private:
Fl_Button* m_btnExit;
};
And here is the Implementation
MainEditorWindow::~MainEditorWindow()
{
}
int MainEditorWindow::handle(int _event)
{
return 1;
}
void MainEditorWindow::draw()
{
m_btnExit->redraw();
}
MainEditorWindow::MainEditorWindow(int _width, int _height, std::string _title) : Fl_Window(_width, _height, _title.c_str())
{
this->begin();
m_btnExit = new Fl_Button(0, 0, 40, 40, "EXIT");
m_btnExit->color(FL_RED);
this->color(FL_WHITE);
this->end();
}
But when simply running the application like this:
int main(int argc, char** argv)
{
MainEditorWindow* mw = new MainEditorWindow(800, 600, "SHIP Editor");
mw->show(argc,argv);
return Fl::run();
}
The window shows up fine, its resizable movable etc, the draw() - function is being called and all that. But the window itself is just blank. It simply shows nothing, especially not the Fl_Button. Can anybody tell me why this occurs? As far as i can tell, there should be nothing particularily wrong with my code.
You need to call Fl_Window::draw()
void MainEditorWindow::draw()
{
m_btnExit->redraw();
Fl_Window::draw();
}
And maybe you want the button is clickable too
int MainEditorWindow::handle(int _event)
{
//return 1;
return(Fl_Window::handle(_event));
}
Try this in your MainEditorWindow constructor:
MainEditorWindow(int _width, int _height, const std::string& _title)
: Fl_Window(_width, _height, _title.c_str()) {
// begin grouped GUI object creation
Fl_Group::begin();
// alter x,y coords of button as necessary
m_btnExit = new Fl_Button(0,0,40,40,"EXIT");
m_btnExit->color(FL_RED);
// end grouped GUI object creation
Fl_Group::end();
// defines resizable widget for group
Fl_Group::resizable(this);
this->color(FL_WHITE);
// display window
Fl_Window::show();
}
Then in main:
int main(int argc, char** argv) {
MainEditorWindow mw(800, 600, "SHIP Editor");
return Fl::run();
}
Here we have added the button to a group and then invoked Fl_Window::show() inside the constructor to display it.
Note there is no need to make mw a pointer to MainEditorWindow in main.

Basic animation doesn't fire

I'm trying to follow this example to create an animation:
http://seriss.com/people/erco/fltk/#Animate
Except for the fact that instead of changing the image I'm moving it. There's a car image that should move in a down-right direction every half second, for 10 times:
void func(void* data)
{
static int counter=0;
counter++;
Fl_PNG_Image* image= static_cast<Fl_PNG_Image*>(data);
Fl_Box* box= new Fl_Box(counter*10, counter*10,100,100);
box->image(image);
//delete box;
window->redraw();
if(counter==10)
{
Fl::remove_timeout(func,data);
}
else
{
Fl::repeat_timeout(.5,func,data);
}
}
int main(int argc, char **argv)
{
window = new Fl_Double_Window(width, height);
Fl_PNG_Image* image= new Fl_PNG_Image("car-down.png");
Fl_Box* box= new Fl_Box(0,0,100,100);
box->image(image);
Fl::add_timeout(.5, func, image);
//delete box;
window->end();
window->show(argc, argv);
return Fl::run();
}
I have two problems:
I can't understand when I should delete the box. I'm using it to draw the image, and if I delete it immediately after having drawn the image, the image disappears.
The image doesn't move, and it always stays on the initial position.
Instead of creating new boxes and images, move the box. The box will be deleted when the window is closed
void func(void* data)
{
static int counter=0;
counter++;
//Fl_PNG_Image* image= static_cast<Fl_PNG_Image*>(data);
Fl_Box* box= static_cast<Fl_Box*) data;
box->position(counter*10, counter*10);
window->redraw();
...
}
int main(int argc, char **argv)
{
...
Fl::add_timeout(.5, func, box);
window->end();
window->show(argc, argv);
return Fl::run();
}

How to refresh gtk widget (gtk_widget_queue_draw error)

I have a simple gtk window with one image in it. After making some modification on that image (taken from OpenCV) I want to make window refresh by expose-event. I use gtk2 and it is not possible to change to gtk3. There is no errors but image is not redrawn, the old one still persist.
class TestApp {
public:
GtkWidget *frameWindow;
GInputStream *inStr;
GtkWidget *image;
GdkPixbuf *pixBuff;
cv::Mat *frame;
TestApp(int argc, char *argv[]) : frameWindow(NULL), image(NULL), pixBuff(NULL), inStr(NULL),frame(NULL){
gtk_init(&argc, &argv);
}
int refresh(cv::Mat *f){
frame=f;
int sz = f->dataend - f->datastart;
memcpy((uchar*)gdk_pixbuf_get_pixels(pixBuff),f->datastart,sz);
gtk_widget_queue_draw(frameWindow);
return 0;
}
void imshow(cv::Mat *im){
/* main window */
frame = im;
frameWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_container_set_border_width(GTK_CONTAINER(frameWindow), 1);
gtk_window_set_title(GTK_WINDOW(frameWindow), "image");
gtk_window_set_default_size(GTK_WINDOW(frameWindow), 1280, 720);
gtk_window_set_position(GTK_WINDOW(frameWindow), GTK_WIN_POS_CENTER);
gtk_window_fullscreen(GTK_WINDOW(frameWindow));
pixBuff=gdk_pixbuf_new_from_data((guchar*)im->datastart,GDK_COLORSPACE_RGB,FALSE,8,im->size().width,im->size().height,(im->channels()*im->cols), NULL,NULL);
image = gtk_image_new_from_pixbuf(pixBuff);
gtk_container_add(GTK_CONTAINER(frameWindow), image);
g_object_ref_sink(G_OBJECT(frameWindow));
g_signal_connect(G_OBJECT(frameWindow), "expose_event", G_CALLBACK((void*)exposeCb), (gpointer)this);
g_signal_connect(G_OBJECT(frameWindow), "key_press_event", G_CALLBACK((void*)buttonCb), (gpointer)this);
gtk_widget_realize(frameWindow);
gtk_widget_show_all(frameWindow);
gtk_main();
}
static gboolean buttonCb(GtkWidget *eventBox, GdkEventKey *event, gpointer data){
char c = event->keyval;
switch(c){
case 'q':
gtk_widget_destroy(eventBox);
gtk_main_quit();
break;
}
return true;
}
static gboolean exposeCb(GtkWidget *eventBox, GtkWidget *event, gpointer data){
return false;
}
};
int main (int argc, char *argv[]) {
TestApp gtkObj(argc,argv);
cv::Mat im=cv::imread("colour256.png");
cv::Mat imOld=im.clone();
cv::cvtColor(im,im,CV_BGR2RGB);
gtkObj.imshow(&im);
gtkObj.refresh(&imOld);
return 0;
}
Should I redraw image somehow?
FALSE is correct return value. Returning TRUE means "I have dealt with this event" so no further callbacks for particular event ("signal" in GTK+ parlance) are called, in particular default callback is not called so window is not painted. Returning FALSE doesn't stop other callbacks.
I don't think there's much difference. Id' leave it in refresh in order to avoid doing too much work in callbacks.
Quoted warning means that pointer passed to gtk_widget_queue_draw does not contain a GtkWidget. Maybe it hot corrupt? Maybe refresh is called prior to imshow?

Adding an OpenGL window into a GTK builder

I am writing a C++ application using GTK and OpenGL. I have a GTK builder which reads an XML file and builds itself. I need to add an "OpenGL window area" into it. How do I do this?
The way I do it (gtkmm / C++ code) is like this:
#include <gtkmm.h>
#include <gtkglmm.h>
class GLWidget : public Gtk::GL::DrawingArea {
public:
GLWidget(Glib::RefPtr<Gdk::GL::Config> glconfig)
: Gtk::GL::DrawingArea(glconfig) {}
~GLWidget() {}
virtual bool on_expose_event(GdkEventExpose* event);
};
bool GLWidget::on_expose_event(GdkEventExpose* event)
{
Glib::RefPtr<Gdk::GL::Drawable> d = get_gl_drawable();
d->gl_begin(get_gl_context());
// make this as complex as you need
glClear(GL_COLOR_BUFFER_BIT);
d->swap_buffers();
d->gl_end();
return true;
}
int main(int argc, char **argv)
{
Gtk::Main kit(argc, argv);
Gtk::GL::init(argc, argv);
Glib::RefPtr<Gtk::Builder> builder = Gtk::Builder::create_from_file("ui.glade");
Gtk::Window* mainWindow;
Gtk::Alignment* container;
builder->get_widget("mainWindow", mainWindow);
builder->get_widget("Box", container);
if (mainWindow == NULL || container == NULL) {
g_critical("Gtk Builder failed to load mainWindow and/or container !\n");
return -1;
}
Glib::RefPtr<Gdk::GL::Config> glconfig;
glconfig = Gdk::GL::Config::create(Gdk::GL::MODE_RGBA | Gdk::GL::MODE_DOUBLE);
if (!glconfig)
glconfig = Gdk::GL::Config::create(Gdk::GL::MODE_RGB);
if (!glconfig) {
g_critical("Cannot create OpenGL-capable config\n");
return -1;
}
GLWidget drawingArea(glconfig);
drawingArea.set_size_request(640, 480);
drawingArea.show();
container->add(drawingArea);
kit.run(*mainWindow);
return 0;
I.e. I'm simply loading a UI that contains an empty container widget, get the handle to that by name, then create the GL-enabled drawing area in code and add it to the empty container. Supposedly, it's possible to "load-time" substitute an ordinary Gtk DrawingArea for a GtkGL one (see this posting) but that method didn't work for me; the above, programmatically creating it, always did.
You need gtkglext / gtkglextmm for the GL Drawingarea widget.