Trouble with Gtk+ info bar - c++

I'm trying to add an info bar to a dialog when my program is not connected to the databse. The info bar shows, however, if I close the dialog and open it again, every time there is a new button. Here's my code:
Window.hpp
class Window: public Gtk::Window {
private:
/// more things
Gtk::InfoBar info;
Gtk::Container* infoBarContainer;
Gtk::Label info_label;
protected:
void onConnectedBody(void);
void onNotConnectedBody(void);
void onInfoBarResponse(int response);
void onMenuDbConnect(void);
void onMenuDbDisconnect(void);
public:
/// more things...
};
Window.cpp
Window::Window(
) :
infoBarContainer(0),
info_label("")
{
// more things
info.add_button(Gtk::Stock::OK, 0);
infoBarContainer = dynamic_cast<Gtk::Container*>(info.get_content_area());
if( infoBarContainer ) infoBarContainer->add(info_label);
}
Window::onMenuDbConnect(){
if( controller->Connected() ) {
onConnectedBody();
} else {
onNotConnectedBody();
}
}
Window::onConnectedBody(){
Gtk::Dialog dialog("Connected to database",true,true);
dialog.set_resizable( false );
dialog.set_has_separator(true);
Gtk::Button close;
Gtk::VBox* vbox = dialog.get_vbox();
Gtk::Label label;
label.set_markup("<b>Already connected.</b>");
vbox->pack_start(label,false,false);
close.set_label("Close");
close.signal_clicked().connect(sigc::mem_fun(*this,&Window::onButtonClose));
dialog.set_size_request(250,80);
vbox = 0;
close.set_can_default( true );
dialog.add_action_widget(close,1);
close.grab_default();
dialog.show_all_children();
dialog.run();
}
Window::onNotConnectedBody(){
Gtk::Dialog dialog("Connect to database",true,true);
dialog.set_resizable( false );
dialog.set_has_separator(true);
Gtk::Button close;
Gtk::VBox* b = dialog.get_vbox();
info.signal_response().connect(sigc::mem_fun(*this,&Window::onInfoBarResponse) );
info_label.set_text("Remember to fill all fields");
b->pack_start(info,Gtk::PACK_SHRINK);
Gtk::Label label;
label.set_markup("<b>Database:</b>");
Gtk::Label label_host;
label_host.set_markup("<b>Host:</b>");
Gtk::Label label_user;
label_user.set_markup("<b>User:</b>");
Gtk::Label label_pass;
label_pass.set_markup("<b>Pass:</b>");
label.set_justify(Gtk::JUSTIFY_LEFT);
label_host.set_justify(Gtk::JUSTIFY_LEFT);
label_user.set_justify(Gtk::JUSTIFY_LEFT);
label_pass.set_justify(Gtk::JUSTIFY_LEFT);
entry.set_max_length(10);
entry.set_text("Figures");
entry_host.set_text("localhost");
label.set_markup("<b>Database:</b>");
b->pack_start(label,false,false);
b->pack_start(entry,false,false);
b->pack_start(label_host,false,false);
b->pack_start(entry_host,false,false);
b->pack_start(label_user,false,false);
b->pack_start(entry_user,false,false);
b->pack_start(label_pass,false,false);
b->pack_start(entry_pass,false,false);
close.set_label("Connect");
close.signal_clicked().connect(sigc::mem_fun(*this,&Window::onButtonDbConnect));
close.set_can_default( true );
dialog.add_action_widget(close,1);
close.grab_default();
dialog.set_size_request(250,250);
dialog.show_all_children();
dialog.run();
}
void Window::onInfoBarResponse(int response) {
info.hide();
}
So, what I want is to show the info bar whenever I enter the onMenuDbConnect and is not yet connected to the database. If it is connected, no info bar is shown. The other components of my program, like the DB controller, works perfectly.
Any ideas? Thaanks.

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);
}

Trigger checkbutton hover event from another widget

I made a custom Right-to-Left check button widget using a Gtk::CheckButton without the label and a Gtk::Label inside Gtk::EventBox.
The reason I went this way instead of simply calling set_direction(Gtk::TEXT_DIR_RTL) is that I want to manually specify the alignment for these widgets.
I've figured out activating/deactivating checkbutton state when the label's clicked but I'm having trouble triggering the hover event from label to checkbutton. The default Gtk::CheckButton behavior triggers the hover effect on the clickable square button to indicate it's being activated when text is hovered too. How do I achieve that same behavior on my custom right-to-left checkbutton widget?
Below is the minimal reproducible code for testing:
#include <gtkmm.h>
using Gtk::Application;
using Gtk::Button;
using Gtk::CheckButton;
using Gtk::EventBox;
using Gtk::Grid;
using Gtk::Label;
using Gtk::Window;
class MyWindow : public Window {
Button button;
CheckButton checkbutton;
EventBox eventbox;
Grid grid;
Label label;
bool on_label_clicked(GdkEventButton *);
void arrange_content_layout();
public:
MyWindow();
};
bool MyWindow::on_label_clicked(GdkEventButton *event)
{
if (event->button == 1 && event->type == GDK_BUTTON_PRESS) {
checkbutton.set_active(!checkbutton.get_active());
return true;
}
return false;
}
void MyWindow::arrange_content_layout()
{
checkbutton.set_margin_start(6);
button.set_margin_top(24);
grid.child_property_width(button) = 2;
grid.set_margin_top(24);
grid.set_margin_start(24);
grid.set_margin_end(24);
grid.set_margin_bottom(24);
}
MyWindow::MyWindow()
: button("Click to steal focus")
, label ("Our custom RTL label")
{
eventbox.add(label);
eventbox.signal_button_press_event().connect(sigc::mem_fun(
*this, &MyWindow::on_label_clicked));
grid.add(eventbox);
grid.add(checkbutton);
grid.attach(button, 0, 1);
add(grid);
arrange_content_layout();
set_default_size(120, 60);
show_all();
}
int main()
{
auto app = Application::create("domain.reverse.sample.rtl-checkbutton");
MyWindow window;
return app->run(window);
}
One way is to use set_state_flags() and trick the visual output of your Right-to-Left checkbutton when the label is being hovered.
First, you need to enable these two masks for eventbox to capture enter and leave event:
Gdk::ENTER_NOTIFY_MASK
Gdk::LEAVE_NOTIFY_MASK
eventbox.add_events(Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK);
And connect eventbox signals
class MyWindow : public Window {
...
bool on_label_entered(GdkEventCrossing *);
bool on_label_exited(GdkEventCrossing *);
...
}
MyWindow::MyWindow()
...
{
...
eventbox.signal_enter_notify_event().connect(sigc::mem_fun(
*this, &MyWindow::on_label_entered));
eventbox.signal_leave_notify_event().connect(sigc::mem_fun(
*this, &MyWindow::on_label_exited));
...
And finally, use Gtk::STATE_FLAG_PRELIGHT to do the UI trick
// Enter event
bool MyWindow::on_label_entered(GdkEventCrossing *event)
{
if (event->mode != GDK_CROSSING_NORMAL) return false;
checkbutton.set_state_flags(checkbutton.get_state_flags() | Gtk::STATE_FLAG_PRELIGHT);
return true;
}
// Leave event
bool MyWindow::on_label_exited(GdkEventCrossing *event)
{
if (event->mode != GDK_CROSSING_NORMAL) return false;
checkbutton.unset_state_flags(Gtk::STATE_FLAG_PRELIGHT);
return true;
}

How to connect buttons with methods, which will be called on click

I started programming some custom gui application. But i need to know how to connect button with some method.
For example i have:
class Button
{
private:
string Text;
/*etc*/
public:
string GetLabel();
void SetLabel(string label);
void Connect(/*method name as param*/)
{
//TODO -this is that thing i need help here
/*this is method for connecting button with method, which will be
called on click, which im asking for help*/
}
};
class Window
{
private:
int sizeX,sizeY;
public:
void Put(Button * button,int _hpos,int _vpos);
void Show();
/*etc */
};
class MyWindow : public Window
{
public:
MyWindow()
{
InitializeComponents();
}
void Run()
{
this -> Put(btn1,10,10); //put button in window at position 10,10
this -> Show();
}
private:
Button * btn1;
void InitializeComponents()
{
btn1 = new Button();
btn1 -> Connect(on_btn1_click); //select which method will be called when user click this button, which I dont know how to do so pls help
}
void on_btn1_click() //this method is called when user click the button
{
//do something if user clicked btn1
}
};
int main()
{
MyWindow * win1 = new MyWindow();
win1 -> Run();
return 0;
}
So there is private method inside MyWindow class which will be called when user clicks the button (btn1). Method Connect() selects which method will be used for calling when user clicks the button.
You can check if the mouse is over a button/UI object, If it is than trigger a command/event.
const Button& GetObjectBelowMouse(int xMousePos, int yMousePos) // This would be equavalent to your Connect type
{
// all buttons would be your container of buttons / GUI objects.
for( const auto& button : allButtons)
{
// top,bottom,left,right would be the bounding rectangle that encapsulates the coordinates of the object
if( (xMousePos > button.left && xMousePos < button.right ) && (yMousePos > button.bottom && yMousePos < buttom.top))
{
return button;
}
}
// in this case we have a different constructor that creates a "empty" button that has no function
return Button(NO_OBJECT);
}
// the usage would be something like
int main()
{
/*
.. create button, set it's possition, etc
*/
// Option 1
if(GetObjectBelowMouse(mouse.x, mouse.y).type != NO_OBJECT)
{
// Do whatever you want, you know that the user has clicked a valid object
button.Foo();
}
// Option 2
GetObjectBelowMouse(mouse.x, mouse.y).Foo(); // You know that every button has a foo object, and that NO_OBJECT has a foo that does nothing, so you don't need to compare if it is NO_OBJECT or not.
}

Select an area from an image using a draggable rectangle using wxwidget in c++

I have written a program in c++ using wxwidgets.I am placing a rectangle on the image and want to select the part of image covered by rectangle for which the rectangle should be draggable. But the problem is when I click the mouse the image vanishes and the only rectangle (which can be dragged) remains and it happens vice-versa.
`
class BasicDrawPane : public wxPanel
{
public:
BasicDrawPane();
BasicDrawPane(wxFrame* parent);
void paintEvent(wxPaintEvent & evt);
void render(wxDC& dc);
void mouseMoved(wxMouseEvent& event);
void mouseDown(wxMouseEvent& event);
void mouseWheelMoved(wxMouseEvent& event);
void mouseReleased(wxMouseEvent& event);
void rightClick(wxMouseEvent& event);
void mouseLeftWindow(wxMouseEvent& event);
DECLARE_EVENT_TABLE()
};
class MyFrame: public wxFrame{
public:
MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
wxString path;
BasicDrawPane panel;
private:
void OnHello(wxCommandEvent& event);
void OnExit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
void OnOpen(wxCommandEvent& event);
void OnPaint(wxCommandEvent& event);
void OnRect(wxCommandEvent& event);
void OnSave(wxCommandEvent& event);
DECLARE_EVENT_TABLE();
wxBitmap bmp;
wxMemoryDC memDC;
};
enum
{
ID_Hello = 1, ID_PAINT = 2, ID_RECT = 3, ID_SAVE = 4
};
BEGIN_EVENT_TABLE( MyFrame, wxFrame )
EVT_MENU(ID_Hello,MyFrame::OnHello)
EVT_MENU(wxID_EXIT,MyFrame::OnExit)
EVT_MENU(wxID_ABOUT,MyFrame::OnAbout)
EVT_MENU(wxID_OPEN,MyFrame::OnOpen)
EVT_MENU(ID_PAINT,MyFrame::OnPaint)
EVT_MENU(ID_RECT,MyFrame::OnRect)
EVT_MENU(ID_SAVE,MyFrame::OnSave)
END_EVENT_TABLE()
void MyFrame::OnPaint(wxCommandEvent& event)
{
//wxPaintDC dc( this );
//dc.DrawBitmap( m_bitmap, 0, 0, true /* use mask */ );
//wxStaticBitmap *b1 = new wxStaticBitmap(this, -1, wxBitmap(wxImage(path)));
bmp.LoadFile((path),wxBITMAP_TYPE_ANY);
// bmp.LoadFile((path),wxBITMAP_TYPE_PNG);
memDC.SelectObject( bmp );
//memDC.SetBackground(*wxWHITE_BRUSH);
//memDC.Clear();
/* memDC.SetPen(*wxGREEN_PEN);
memDC.SetBrush(*wxTRANSPARENT_BRUSH);
memDC.DrawRectangle( m_x, m_y, WIDTH, HEIGHT );*/
//Check();
memDC.SelectObject(wxNullBitmap);
// wxSize sz(512,384);
// wxSize sz(900,600);
wxStaticBitmap *b1 = new wxStaticBitmap(/* dynamic_cast<wxFrame*>*/this, -1, bmp, wxDefaultPosition);
Refresh();
}
class MyApp: public wxApp
{
public:
virtual bool OnInit();
//MyFrame *frame;
BasicDrawPane * drawPane;
};
IMPLEMENT_APP(MyApp)
bool MyApp::OnInit()
{
// wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
//frame = new MyFrame((wxFrame *)NULL, -1, wxT("Hello wxDC"), wxPoint(50,50), wxSize(800,600));
MyFrame *frame = new MyFrame( _T("Hello World"), wxPoint(50, 50), wxSize(600, 600) );
// drawPane = new BasicDrawPane( (wxFrame*) frame );
// sizer->Add(drawPane, 1, wxEXPAND);
//frame->SetSizer(sizer);
// /* dynamic_cast<wxFrame*>(this)*/ frame-> SetAutoLayout(true);
/* dynamic_cast<wxFrame*>(this)*/frame -> Show();
return true;
}
BEGIN_EVENT_TABLE(BasicDrawPane, wxPanel)
EVT_MOTION(BasicDrawPane::mouseMoved)
EVT_LEFT_DOWN(BasicDrawPane::mouseDown)
EVT_LEFT_UP(BasicDrawPane::mouseReleased)
EVT_RIGHT_DOWN(BasicDrawPane::rightClick)
EVT_LEAVE_WINDOW(BasicDrawPane::mouseLeftWindow)
EVT_MOUSEWHEEL(BasicDrawPane::mouseWheelMoved)
EVT_PAINT(BasicDrawPane::paintEvent)
// catch paint events
END_EVENT_TABLE()
void BasicDrawPane::mouseDown(wxMouseEvent& event)
{
/* if (event.GetPosition().x >= m_x && event.GetPosition().x <= m_x + WIDTH &&
event.GetPosition().y >= m_y && event.GetPosition().y <= m_y + HEIGHT)
{
m_dragging = true;
m_previous_mouse_x = event.GetPosition().x;
m_previous_mouse_y = event.GetPosition().y;
}*/
}
void BasicDrawPane::mouseWheelMoved(wxMouseEvent& event) {}
void BasicDrawPane::mouseReleased(wxMouseEvent& event)
{
m_dragging = true;
}
void BasicDrawPane::mouseMoved(wxMouseEvent& event)
{
if (m_dragging && event.Dragging())
{
int delta_x = event.GetPosition().x - m_previous_mouse_x;
int delta_y = event.GetPosition().y - m_previous_mouse_y;
m_x += delta_x;
m_y += delta_y;
m_previous_mouse_x = event.GetPosition().x;
m_previous_mouse_y = event.GetPosition().y;
// trigger paint event
Refresh();
}
}
void BasicDrawPane::mouseLeftWindow(wxMouseEvent& event)
{
m_dragging = true;
}
void BasicDrawPane::rightClick(wxMouseEvent& event) {}
BasicDrawPane::BasicDrawPane(wxFrame* parent) :
wxPanel(parent)
{
// m_dragging = true;
// m_x = 100;
// m_y = 100;
}
/*
* Called by the system of by wxWidgets when the panel needs
* to be redrawn. You can also trigger this call by
* calling Refresh()/Update().
*/
void BasicDrawPane::paintEvent(wxPaintEvent & evt)
{
//wxCommandEvent w1(wxEVT_NULL, ID_PAINT);
//OnPaint(w1);
wxPaintDC dc(this);
render(dc);
}
void BasicDrawPane::render(wxDC& dc)
{
dc.SetPen(*wxGREEN_PEN);
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRectangle( m_x, m_y, WIDTH, HEIGHT );
}
`
There are several things to explain in order to answer this question, so I will take them one at a time. I think your basic idea is okay, so I won't go into a lot of detail on how the actual selection should take place etc.
Firstly, I would recommend to use either Connect() or Bind() instead of an event table. This allows you to connect child window events back to the parent window and handle them all in one place.
For example, if your main frame class is called MainFrame and you have a wxPanel member called m_DrawPanel, in the MainFrame ctor you could have:
MainFrame::MainFrame(wxWindow* parent)
{
// Connect mouse event handlers.
m_DrawPanel->Connect(wxEVT_LEFT_DOWN,
wxMouseEventHandler(MainFrame::OnPanelLDown), NULL, this);
m_DrawPanel->Connect(wxEVT_LEFT_UP,
wxMouseEventHandler(MainFrame::OnPanelLUp), NULL, this);
m_DrawPanel->Connect(wxEVT_MOTION,
wxMouseEventHandler(MainFrame::OnPanelMotion), NULL, this);
// Connect paint and erase handlers.
m_DrawPanel->Connect(wxEVT_PAINT,
wxPaintEventHandler(MainFrame::OnPanelPaint), NULL, this);
m_DrawPanel->Connect(wxEVT_ERASE_BACKGROUND,
wxEraseEventHandler(MainFrame::OnPanelErase), NULL, this);
// Load the bitmap and set the mode to 'not currently selecting'.
m_Picture.LoadFile ("wxwidgets.png", wxBITMAP_TYPE_PNG);
m_SelectionMode = false;
}
Note: I included a wxEVT_ERASE_BACKGROUND event override because otherwise panels tend to be cleared which leads to flicker (this is one simple approach).
The 3 mouse event handlers can implement your selection logic (I think this is basically what you are intending already):
void MainFrame::OnPanelLDown(wxMouseEvent& event)
{
m_SelectionMode = true;
m_SelectionRect = wxRect(event.GetPosition(), wxSize(0, 0));
}
void MainFrame::OnPanelLUp(wxMouseEvent& event)
{
m_SelectionMode = false;
// ... handle what to do with the selection here
// (selected area is defined by m_SelectionRect).
// ...
// Zero the selection rectangle for neatness (not really required).
m_SelectionRect = wxRect ();
}
void MainFrame::OnPanelMotion(wxMouseEvent& event)
{
m_SelectionRect = wxRect(m_SelectionRect.GetTopLeft(), event.GetPosition());
// Call Refresh() to trigger a paint event.
m_mainPanel->Refresh();
}
As mentioned earlier, override the panel's wxEVT_ERASE_BACKGROUND event to do nothing:
void MainFrame::OnPanelErase(wxEraseEvent& event)
{
}
Finally, I think this is the bit that you really are asking in your question (I included the others to help you build a working program):
void MainFrame::OnPanelPaint(wxPaintEvent& event)
{
// Obtain a wxPaintDC.
wxPaintDC pdc (m_mainPanel);
// Draw our image.
pdc.DrawBitmap(m_Picture, wxPoint(0, 0));
// If the user is currently selecting (left mouse button is down)
// then draw the selection rectangle.
if (m_SelectionMode)
{
pdc.SetPen(*wxRED_PEN);
pdc.SetBrush(*wxTRANSPARENT_BRUSH);
pdc.DrawRectangle(m_SelectionRect);
}
}
This is a paint event handler, so first we need to create a wxPaintDC context. Next, we paint the bitmap, this ensures it is refreshed every time and not damaged by mouse movements, resizing or other windows being dragged etc. Finally, if the user is currently moving the mouse with the left button pressed, then draw the selection rectangle.
There are many other ways of achieving the same thing. Some of them are possibly better, or more efficient, however this is a simple working way until you become more familiar with wxWidgets.

Why Does Gtk::Frame Force A Redraw & Resize?

In the included code I've created an application where I periodically update a label. When the application first starts up, updating the timeLabel results in redrawing the entire contents of the application. This can be observed by running the application with the --gtk-debug=updates argument.
When the button on the right side is clicked, the frame that encloses the contents of the window is removed from the widget hierarchy. This results in further updates to the timeLabel only redrawing the label, and not redrawing swapButton.
Why does a frame seem to want to redraw itself even if it doesn't need to?
#include <gtkmm.h>
class MyWindow
: public Gtk::Window
{
public:
MyWindow();
private:
bool timeout();
void toggleUseOfFrame();
Gtk::Frame frame;
Gtk::Label timeLabel;
Gtk::Button swapButton;
Gtk::Box box;
};
MyWindow::MyWindow()
{
// Layout widgets in initial configuration.
box.pack_start( timeLabel, true, true );
box.pack_start( swapButton, true, true );
box.set_homogeneous();
frame.add( box );
add( frame );
show_all();
set_size_request( 100, 50 );
// Setup signal handlers.
Glib::MainContext::get_default()->signal_timeout().connect(
sigc::mem_fun( *this, &MyWindow::timeout ), 1000 );
swapButton.signal_clicked().connect(
sigc::mem_fun( *this, &MyWindow::toggleUseOfFrame ) );
}
// Periodically update the label to force it to redraw.
bool MyWindow::timeout()
{
Glib::DateTime now = Glib::DateTime::create_now_local();
timeLabel.set_text( now.format( "%S" ) );
return true;
}
// If the frame is currently in use remove it. Otherwise add it back.
void MyWindow::toggleUseOfFrame()
{
if( frame.get_parent() ) {
remove();
box.reparent( *this );
}
else {
box.reparent( frame );
add( frame );
}
}
int main( int argc, char* argv[]) {
Glib::RefPtr<Gtk::Application> app =
Gtk::Application::create( argc, argv, "test" );
MyWindow myWindow;
return app->run( myWindow );
}