I have a simple frame which is containing some buttons. My aim is that, after clicking the GetMousePosition button, getting the first mouse click position. I try to capture mouse click, even if I click outside of the running application.
This is a desktop application running on Windows. I tried some mouse events that wxwidgets provides, but I couldn't handle the next click event. I tried to find a solution with following code, but if there is some different solution, I can throw that code in the trash.
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_BUTTON(BUTTON_GetPos, MyFrame::OnButtonClick)
EVT_MOUSE_EVENTS(MyFrame::OnMouseEvent)
EVT_MOUSE_CAPTURE_LOST(MyFrame::OnMouseCaptureLost)
END_EVENT_TABLE()
//some more code
void MyFrame::OnButtonClick(wxCommandEvent & WXUNUSED(event))
{
//Start Capturing for next mouse left-click
if (!HasCapture())
CaptureMouse();
}
void MyFrame::OnMouseEvent(wxMouseEvent &event)
{
if (event.LeftDown()) {
//GetMousePosition
if (HasCapture())
ReleaseMouse();
}
}
void MyFrame::OnMouseCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(event))
{
}
I expect to get the mouse position, in the first left click after the button is pressed.
The code you posted looks like it should work. If there's a problem, it might be in the code you omitted. Anyway, here's a small example application showing the behavior you want. The underlying logic of this example is the same as the code you posted, except this example uses dynamic binding instead of event tables.
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
class MyFrame : public wxFrame
{
public:
MyFrame(wxWindow* parent);
protected:
void OnButtonClick(wxCommandEvent& event);
void OnMouseCapLost(wxMouseCaptureLostEvent& event);
void OnLeftDown(wxMouseEvent&);
void CleanUp();
private:
wxTextCtrl* m_textCtrl;
};
class MyApp : public wxApp
{
public:
virtual bool OnInit() wxOVERRIDE;
};
MyFrame::MyFrame(wxWindow* parent)
:wxFrame(parent, wxID_ANY, "Demo", wxDefaultPosition, wxDefaultSize,
wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL)
{
wxPanel* panel = new wxPanel(this, wxID_ANY );
wxButton* button = new wxButton(panel, wxID_ANY, "Click Me");
m_textCtrl = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxTE_DONTWRAP|wxTE_MULTILINE|wxTE_READONLY);
wxBoxSizer* bSizer = new wxBoxSizer(wxVERTICAL);
bSizer->Add(button, 0, wxALL, 5);
bSizer->Add(m_textCtrl, 1, wxALL|wxEXPAND, 5);
panel->SetSizer(bSizer);
Layout();
button->Bind(wxEVT_BUTTON,&MyFrame::OnButtonClick,this);
}
void MyFrame::OnButtonClick(wxCommandEvent& event)
{
if ( !HasCapture() )
{
CaptureMouse();
m_textCtrl->AppendText("Mouse captured.\n");
Bind(wxEVT_LEFT_DOWN, &MyFrame::OnLeftDown, this);
Bind(wxEVT_MOUSE_CAPTURE_LOST, &MyFrame::OnMouseCapLost, this);
}
}
void MyFrame::CleanUp()
{
if ( HasCapture() )
ReleaseMouse();
Unbind(wxEVT_LEFT_DOWN, &MyFrame::OnLeftDown, this);
Unbind(wxEVT_MOUSE_CAPTURE_LOST, &MyFrame::OnMouseCapLost, this);
}
void MyFrame::OnMouseCapLost(wxMouseCaptureLostEvent& event)
{
m_textCtrl->AppendText("Mouse cap lost.\n");
CleanUp();
}
void MyFrame::OnLeftDown(wxMouseEvent& event)
{
m_textCtrl->AppendText("Click recorded.\n");
CleanUp();
}
bool MyApp::OnInit()
{
MyFrame* frame = new MyFrame(NULL);
frame->Show();
return true;
}
wxIMPLEMENT_APP(MyApp);
I hope this helps.
edit: Here's a version using event tables as well:
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#define BUTTON_ID 101
class MyFrame : public wxFrame
{
public:
MyFrame(wxWindow* parent);
protected:
void OnButtonClick(wxCommandEvent& event);
void OnMouseCapLost(wxMouseCaptureLostEvent& event);
void OnMouseEvent(wxMouseEvent&);
void CleanUp();
private:
wxTextCtrl* m_textCtrl;
wxDECLARE_EVENT_TABLE();
};
class MyApp : public wxApp
{
public:
virtual bool OnInit() wxOVERRIDE;
};
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_BUTTON(BUTTON_ID, MyFrame::OnButtonClick)
EVT_MOUSE_EVENTS(MyFrame::OnMouseEvent)
EVT_MOUSE_CAPTURE_LOST(MyFrame::OnMouseCapLost)
wxEND_EVENT_TABLE()
MyFrame::MyFrame(wxWindow* parent)
:wxFrame(parent, wxID_ANY, "Demo", wxDefaultPosition, wxDefaultSize,
wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL)
{
wxPanel* panel = new wxPanel(this, wxID_ANY );
wxButton* button = new wxButton(panel, BUTTON_ID, "Click Me");
m_textCtrl = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxTE_DONTWRAP|wxTE_MULTILINE|wxTE_READONLY);
wxBoxSizer* bSizer = new wxBoxSizer(wxVERTICAL);
bSizer->Add(button, 0, wxALL, 5);
bSizer->Add(m_textCtrl, 1, wxALL|wxEXPAND, 5);
panel->SetSizer(bSizer);
Layout();
}
void MyFrame::OnButtonClick(wxCommandEvent& event)
{
if ( !HasCapture() )
{
CaptureMouse();
m_textCtrl->AppendText("Mouse captured.\n");
}
}
void MyFrame::CleanUp()
{
if ( HasCapture() )
ReleaseMouse();
}
void MyFrame::OnMouseCapLost(wxMouseCaptureLostEvent& event)
{
m_textCtrl->AppendText("Mouse cap lost.\n");
CleanUp();
}
void MyFrame::OnMouseEvent(wxMouseEvent& event)
{
if( HasCapture() && event.LeftIsDown() )
{
m_textCtrl->AppendText("Click recorded.\n");
CleanUp();
}
}
bool MyApp::OnInit()
{
MyFrame* frame = new MyFrame(NULL);
frame->Show();
return true;
}
wxIMPLEMENT_APP(MyApp);
Related
I have made an application using wxWidgets 3.1.5 in C++ and everything is working fine except a test button that I have on my main window.
Here's a pic:
The menubar, menus and their functions work perfectly but the button covers the entire client area.
Here's the code:
main.h
#pragma once
#include <wx\wx.h>
#include "mainFrame.h"
class main : public wxApp
{
private:
mainFrame* frame;
public:
virtual bool OnInit();
};
main.cpp
#include "main.h"
wxIMPLEMENT_APP(main);
bool main::OnInit()
{
frame = new mainFrame("Kill Me", wxPoint(15, 10), wxSize(640, 480));
frame->Show();
return true;
}
mainFrame.h
#pragma once
#include "About.h"
using str = std::string;
class mainFrame : public wxFrame
{
public:
mainFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
~mainFrame();
private:
About* abtF = NULL;
wxButton* hewwo = NULL;
wxMenuBar* mbar = NULL;
wxMenu* sett = NULL;
wxMenu* quitApp = NULL;
wxMenu* abt = NULL;
void onHewwo(wxCommandEvent& evt);
void onSett(wxCommandEvent& evt);
void quit(wxCommandEvent& evt);
void about(wxCommandEvent& evt);
wxDECLARE_EVENT_TABLE();
};
enum {
ID_SETT = 1,
ID_BTN = 2
};
mainFrame.cpp
#include "mainFrame.h"
wxBEGIN_EVENT_TABLE(mainFrame, wxFrame)
EVT_BUTTON(ID_BTN, onHewwo)
EVT_MENU(ID_SETT, onSett)
EVT_MENU(wxID_EXIT, quit)
EVT_MENU(wxID_ABOUT, about)
wxEND_EVENT_TABLE()
mainFrame::mainFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
:
wxFrame(nullptr, wxID_ANY, title, pos, size) {
hewwo = new wxButton(this, ID_BTN, "Hewwo World", wxPoint(15, 15), wxSize(70, 20));
sett = new wxMenu();
sett->AppendSeparator();
sett->Append(ID_SETT, "&Settings");
quitApp = new wxMenu();
quitApp->AppendSeparator();
quitApp->Append(wxID_EXIT, "&Quit this crap");
abt = new wxMenu();
abt->AppendSeparator();
abt->Append(wxID_ABOUT, "&About");
mbar = new wxMenuBar();
mbar->Append(sett, "&Settings");
mbar->Append(abt, "&About");
mbar->Append(quitApp, "&Quit");
SetMenuBar(mbar);
}
void mainFrame::onHewwo(wxCommandEvent& evt) {
wxMessageBox("Hewwo", "Hewwo", wxOK | wxICON_INFORMATION, this);
}
void mainFrame::onSett(wxCommandEvent& evt) {
wxMessageBox("Settings", "Settings", wxOK | wxICON_INFORMATION, this); // Just a test
}
void mainFrame::about(wxCommandEvent& evt) {
abtF = new About(wxPoint(10, 10), wxSize(480, 320));
abtF->Show();
}
void mainFrame::quit(wxCommandEvent& evt) {
Close(true);
}
mainFrame::~mainFrame() {
delete abtF;
}
I'm using Visual Studio 2019.
(I followed OneLoneCoder's (javidx9) youtube video on wxWidgets)
That is how a wxFrame with only one child behaves.
If you don't want that, use a wxSizer to layout your button (position, align, expand etc).
Reference:
if the frame has exactly one child window, not counting the status and toolbar, this child is resized to take the entire frame client area. If two or more windows are present, they should be laid out explicitly either by manually handling wxEVT_SIZE or using sizers
wxFrame docs -> Default event processing -> wxEVT_SIZE
I use wxWidget-3.1.4 under windows 10
I try to process mouse events from my class.
Header class:
#include "wx/wxprec.h"
#include <wx/wfstream.h>
class MainWindow : public wxFrame
{
public:
MainWindow(const wxString& title);
void OnQuit(wxCommandEvent& event);
void OnOpenImage(wxCommandEvent& WXUNUSED);
void OnEditImage(wxCommandEvent& WXUNUSED);
//will redirect mouse event to class ImageDrawing
void OnMouseWheel(wxMouseEvent& event);
void OnMouseMove(wxMouseEvent& event);
void OnMouseDown(wxMouseEvent& event);
void OnMouseUp(wxMouseEvent& event);
private:
//wx Panel for drawing image
wxPanel* m_background;
wxImage* m_Image;
...
};
Cpp file:
MainWindow::MainWindow(const wxString& title): wxFrame(NULL, wxID_ANY, title)
{
m_background = new wxPanel(this, wxID_ANY);
m_background->Bind(wxEVT_PAINT, &MainWindow::OnPaint, this);
}
void MainWindow::OnQuit(wxCommandEvent& WXUNUSED(event))
{
Close(true);
}
void MainWindow::OnOpenImage(wxCommandEvent& WXUNUSED(event))
{
wxString file = OpenFileDialog("Open Image file", "png files (*.png)|*.png");
openImage(file, true);
}
void MainWindow::OnEditImage(wxCommandEvent& WXUNUSED(event))
{
wxString file = OpenFileDialog("Open Image file", "png files (*.png)|*.png");
// some logic
}
void MainWindow::OnMouseWheel(wxMouseEvent& event)
{
std::cout<<"OnMouseWheel(event)";
}
void MainWindow::OnMouseMove(wxMouseEvent& event)
{
std::cout<<"OnMouseMove(event)";
}
void MainWindow::OnMouseDown(wxMouseEvent& event)
{
std::cout<<"OnMouseDown(event)";
}
void MainWindow::OnMouseUp(wxMouseEvent& event)
{
std::cout<<"OnMouseUp(event)";
}
wxBEGIN_EVENT_TABLE(MainWindow, wxFrame)
EVT_MENU(Minimal_Quit, MainWindow::OnQuit)
EVT_MENU(Open_Image, MainWindow::OnOpenImage)
EVT_MENU(Edit_Image, MainWindow::OnEditImage)
EVT_MOTION(MainWindow::OnMouseMove)
EVT_MOUSEWHEEL(MainWindow::OnMouseWheel)
EVT_LEFT_DOWN(MainWindow::OnMouseDown)
EVT_LEFT_UP(MainWindow::OnMouseUp)
EVT_RIGHT_DOWN(MainWindow::OnMouseDown)
EVT_RIGHT_UP(MainWindow::OnMouseUp)
wxEND_EVENT_TABLE()
the problem is that event menu works fine, also OnMouseWheel works, but all other mouse events are not working. Googling problem shows that a reason for this is because a parent component, in my case wxFrame is included some child, like wxPanel, and this child component could intercept events.
So why OnMouseWheel is working and all other is not. the same behaviour if I change wxFrame to wxPanel in event table and if I didn't use m_background.
I was able to solve this problem by applying the following changes in constructor:
MainWindow::MainWindow(const wxString& title): wxFrame(NULL, wxID_ANY, title)
{
m_background = new wxPanel(this, wxID_ANY);
m_background->Bind(wxEVT_PAINT, &MainWindow::OnPaint, this);
m_background->Connect(wxEVT_MOTION, wxMouseEventHandler(MainWindow::OnMouseMove));
m_background->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(MainWindow::OnMouseDown));
m_background->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(MainWindow::OnMouseUp));
}
in each function that if you want to access to some class member or parameter you need to do the following:
void MainWindow::OnMouseMove(wxMouseEvent& event)
{
MainWindow* pointer = dynamic_cast<MainWindow*>(this->GetParent());
if(pointer->m_path.size() > 0)
pointer->m_drawing->OnMove(event);
}
and instead of this, you need to use a pointer, the reason for this because this now point to m_background.
Last step is to delete this function from wx_EVENT_TABLE
I am a novice in C++ and wxWidgets, and use wxFormBuilder to help me create the GUI.
I like the idea to put similar functions together in 1 module, which means that my apps have multiple modules.
One of the modules is for the GUI (it now contains code for the main Frame (with only a menu and taskbar) and
a Panel (with 2 StaticText controls and a button).
The button increments a counter and the value is shown in one of the StaticTexts on the Panel.
Sofar so good. (compiles without errors)
I can make the Panel show/Hide, but I seem to miss an essential piece of knowledge to make the Panel button work.
The ways I tried are all similar to the way the Main module is coded, but that is not working.
I understand how it works with GUI class elements in the same file.
However, I like to keep all GUI code in one module (GUIFrame.h/.cpp),
and all the 'function' code in e.g. the Panel module (MyPanel.h/.cpp).
Just because I am not sure where I make my mistake, I present all code in this post.
My aplologies if it is too much.
Hopefully someone can help me bridge my gap in knowledge about this way of working.
Regards,
Ruud
=== GUIFrame.h
#pragma once
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/string.h>
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/menu.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/statusbr.h>
#include <wx/frame.h>
#include <wx/stattext.h>
#include <wx/button.h>
#include <wx/sizer.h>
#include <wx/panel.h>
///////////////////////////////////////////////////////////////////////////
#define idMenuQuit 1000
///////////////////////////////////////////////////////////////////////////////
/// Class GUIFrame
///////////////////////////////////////////////////////////////////////////////
class GUIFrame : public wxFrame
{
private:
protected:
wxMenuBar* mbar;
wxMenu* mainMenu;
wxStatusBar* statusBar;
// Virtual event handlers, overide them in your derived class
virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
virtual void m_menuItemPanelMainOnMenuSelection( wxCommandEvent& event ) { event.Skip(); }
virtual void OnQuit( wxCommandEvent& event ) { event.Skip(); }
public:
GUIFrame( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Test application"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 500,500 ), long style = wxDEFAULT_FRAME_STYLE );
~GUIFrame();
};
///////////////////////////////////////////////////////////////////////////////
/// Class PanelMAIN
///////////////////////////////////////////////////////////////////////////////
class PanelMAIN : public wxPanel
{
private:
protected:
wxStaticText* m_staticText3;
// Virtual event handlers, overide them in your derived class
virtual void m_btn_Counter_OnButtonClick( wxCommandEvent& event ) { event.Skip(); }
public:
wxStaticText* m_staticTextPMain;
wxButton* m_buttonPMain;
PanelMAIN( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxPoint( 3,3 ), const wxSize& size = wxSize( 350,300 ), long style = wxBORDER_RAISED, const wxString& name = wxEmptyString );
~PanelMAIN();
};
==== GUIFrame.cpp
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif //__BORLANDC__
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif //WX_PRECOMP
#include "GUIFrame.h"
///////////////////////////////////////////////////////////////////////////
GUIFrame::GUIFrame( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxDefaultSize, wxDefaultSize );
mbar = new wxMenuBar( 0 );
mainMenu = new wxMenu();
wxMenuItem* m_menuItemPanelMain;
m_menuItemPanelMain = new wxMenuItem( mainMenu, wxID_ANY, wxString( wxT("To Main Panel") ) , wxEmptyString, wxITEM_NORMAL );
mainMenu->Append( m_menuItemPanelMain );
wxMenuItem* menuFileQuit;
menuFileQuit = new wxMenuItem( mainMenu, idMenuQuit, wxString( wxT("&Quit") ) + wxT('\t') + wxT("Alt+F4"), wxT("Quit the application"), wxITEM_NORMAL );
mainMenu->Append( menuFileQuit );
mbar->Append( mainMenu, wxT("&Menu") );
this->SetMenuBar( mbar );
statusBar = this->CreateStatusBar( 2, wxSTB_SIZEGRIP, wxID_ANY );
// Connect Events
this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( GUIFrame::OnClose ) );
mainMenu->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( GUIFrame::m_menuItemPanelMainOnMenuSelection ), this, m_menuItemPanelMain->GetId());
mainMenu->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( GUIFrame::OnQuit ), this, menuFileQuit->GetId());
}
GUIFrame::~GUIFrame()
{
// Disconnect Events
this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( GUIFrame::OnClose ) );
}
PanelMAIN::PanelMAIN( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name ) : wxPanel( parent, id, pos, size, style, name )
{
this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_APPWORKSPACE ) );
wxBoxSizer* bSizer1;
bSizer1 = new wxBoxSizer( wxVERTICAL );
m_staticTextPMain = new wxStaticText( this, wxID_ANY, wxT("We are on PanelMAIN now"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextPMain->Wrap( -1 );
bSizer1->Add( m_staticTextPMain, 0, wxALL, 5 );
m_buttonPMain = new wxButton( this, wxID_ANY, wxT("Counter++"), wxDefaultPosition, wxDefaultSize, 0 );
bSizer1->Add( m_buttonPMain, 0, wxALL, 5 );
m_staticText3 = new wxStaticText( this, wxID_ANY, wxT("0"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText3->Wrap( -1 );
bSizer1->Add( m_staticText3, 0, wxALL, 5 );
this->SetSizer( bSizer1 );
this->Layout();
// Connect Events
m_buttonPMain->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PanelMAIN::m_btn_Counter_OnButtonClick ), NULL, this );
}
PanelMAIN::~PanelMAIN()
{
// Disconnect Events
m_buttonPMain->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PanelMAIN::m_btn_Counter_OnButtonClick ), NULL, this );
}
==== wxPanelAPP.h
#ifndef WXPANELAPP_H
#define WXPANELAPP_H
#include <wx/app.h>
class wxPanelApp : public wxApp
{
public:
virtual bool OnInit();
};
#endif // WXPANELAPP_H
==== wxPanelAPP.cpp
#ifdef WX_PRECOMP
#include "wx_pch.h"
#endif
#ifdef __BORLANDC__
#pragma hdrstop
#endif //__BORLANDC__
#include "wxPanelApp.h"
#include "wxPanelMain.h"
IMPLEMENT_APP(wxPanelApp);
bool wxPanelApp::OnInit()
{
wxPanelFrame* frame = new wxPanelFrame(0L);
frame->SetIcon(wxICON(aaaa)); // To Set App Icon
frame->Show();
return true;
}
==== wxPanelMain.h
#ifndef WXPANELMAIN_H
#define WXPANELMAIN_H
#include "wxPanelApp.h"
#include "GUIFrame.h"
#include "MyPanel.h"
#include <iostream>
class wxPanelFrame: public GUIFrame
{
public:
wxPanelFrame(wxFrame *frame);
~wxPanelFrame();
wxPanel *m_wxpMain = new PanelMAIN(this, wxID_ANY);
private:
virtual void OnClose(wxCloseEvent& event);
virtual void OnQuit(wxCommandEvent& event);
void m_menuItemPanelMainOnMenuSelection( wxCommandEvent& event );
};
#endif // WXPANELMAIN_H
==== wxPanelMain.cpp
#ifdef WX_PRECOMP
#include "wx_pch.h"
#endif
#ifdef __BORLANDC__
#pragma hdrstop
#endif //__BORLANDC__
#include "wxPanelMain.h"
wxPanelFrame::wxPanelFrame(wxFrame *frame) : GUIFrame(frame)
{
#if wxUSE_STATUSBAR
statusBar->SetStatusText(_("Simple Panel Test"), 0);
#endif
// Hide the panel
m_wxpMain->Show(false);
}
wxPanelFrame::~wxPanelFrame()
{
}
void wxPanelFrame::OnClose(wxCloseEvent &event)
{
Destroy();
}
void wxPanelFrame::OnQuit(wxCommandEvent &event)
{
Destroy();
}
void wxPanelFrame::m_menuItemPanelMainOnMenuSelection( wxCommandEvent& event )
{
// Show the panel
m_wxpMain->Show(true);
}
==== MyPanel.h
#ifndef MYPANEL_H
#define MYPANEL_H
#include "GUIFrame.h"
#include <iostream>
class MyPanel : public PanelMAIN
{
public:
MyPanel(wxPanel *panel);
virtual ~MyPanel();
int i{0};
protected:
private:
void m_btn_Counter_OnButtonClick( wxCommandEvent& event );
};
#endif // MYPANEL_H
==== MyPanel.cpp
#include "MyPanel.h"
MyPanel::MyPanel(wxPanel *panel) : PanelMAIN(panel)
{
std::cout << "MyPanel::MyPanel /*CONSTRUCTOR*/\n";
}
MyPanel::~MyPanel()
{
std::cout << "MyPanel::~MyPanel /*DESTRUCTOR*/\n";
}
void MyPanel::m_btn_Counter_OnButtonClick( wxCommandEvent& event )
{
wxString wxs_Count{};
i++;
wxs_Count << i;
m_staticText3->SetLabelText(wxs_Count);
}
You define your event handler in your derived class MyPanel, but you never create and instance of it. The line
wxPanel *m_wxpMain = new PanelMAIN(this, wxID_ANY);
only creates on object of the base class.
You should change this to
MyPanel *m_wxpMain = new MyPanel(this);
You also have an problem with the constructor for MyPanel. The parent needs to be a wxWindow not wxPanel. So the declaration should be something like
MyPanel(wxWindow *panel);
and the body should look something like
MyPanel::MyPanel(wxWindow *panel) : PanelMAIN(panel)
{
std::cout << "MyPanel::MyPanel /*CONSTRUCTOR*/\n";
}
I'm triying to color each tab on wxWidgets with a different color, like when you tag an Excel Sheet, is there a way to do that in the C++ version of wxWidgets, with or without AUI?
I don't think there is anything that will let you do this out of the box; but with an Aui notebook, you can write a custom tab art to color the tabs as you see fit. Here's a hideous example that I just threw together to demonstrate one way to do this:
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include <wx/aui/auibook.h>
#include <map>
class MyTabArt:public wxAuiGenericTabArt
{
public:
MyTabArt():wxAuiGenericTabArt(){}
wxAuiTabArt* Clone()
{
return new MyTabArt(*this);
}
void AddTabColor(wxWindow* w, const wxColor& c)
{
m_tabColors[w] = c;
}
virtual void DrawTab(wxDC& dc, wxWindow* wnd, const wxAuiNotebookPage& page,
const wxRect& rect, int closeButtonState,
wxRect* outTabRect, wxRect* outButtonRect,
int* xExtent) wxOVERRIDE
{
wxSize tabSize = GetTabSize(dc, wnd, page.caption, page.bitmap,
page.active, closeButtonState, xExtent);
wxCoord tabHeight = m_tabCtrlHeight;
wxCoord tabWidth = tabSize.x;
wxCoord tabX = rect.x;
wxCoord tabY = rect.y + rect.height - tabHeight;
wxRect tabRect(tabX, tabY, tabWidth, tabHeight);
wxDCClipper clipper(dc, tabRect);
auto it = m_tabColors.find(page.window);
if ( it != m_tabColors.end() )
{
wxDCBrushChanger bchanger(dc, it->second);
wxDCPenChanger pchanger(dc, it->second);
dc.DrawRectangle(tabRect);
}
else
{
wxDCBrushChanger bchanger(dc, *wxGREEN);
wxDCPenChanger pchanger(dc, *wxGREEN);
dc.DrawRectangle(tabRect);
}
dc.DrawText(page.caption,tabRect.x,tabRect.y);
*outTabRect = tabRect;
}
private:
std::map<wxWindow*,wxColor> m_tabColors;
};
class MyFrame: public wxFrame
{
public:
MyFrame();
private:
};
MyFrame::MyFrame()
:wxFrame(NULL, wxID_ANY, "AUI Tab", wxDefaultPosition, wxSize(600, 400))
{
wxAuiNotebook * auiNotebook =
new wxAuiNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
wxPanel* panel1 = new wxPanel( auiNotebook, wxID_ANY );
wxPanel* panel2 = new wxPanel( auiNotebook, wxID_ANY );
auiNotebook->AddPage(panel1, "Page 1");
auiNotebook->AddPage(panel2, "Page 2");
MyTabArt* art = new MyTabArt();
art->AddTabColor(panel1, *wxRED);
art->AddTabColor(panel2, *wxBLUE);
auiNotebook->SetArtProvider(art);
}
class MyApp : public wxApp
{
public:
virtual bool OnInit()
{
::wxInitAllImageHandlers();
MyFrame* frame = new MyFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(MyApp);
On windows, this monstrosity looks like this:
You can look at the source wxWidgets source for the other tab arts to see an example of how to make this prettier.
I want to view an image in one of wxSplitterWindow using wxScrolledWindow.
I derived class from wxPanel, which I add into wxScrolledWindow. But the result is not what i want.(
class ImagePanel : public wxPanel
{
public:
//ImagePanel(wxFrame* parent);
ImagePanel(wxWindow* parent);
void paintEvent(wxPaintEvent& event);
void paintNow();
void setBitmap(wxBitmap& imBitmap);
void setBitmap(wxImage& img);
int GetHeight();
int GetWidth();
void render(wxDC& dc);
DECLARE_EVENT_TABLE()
private:
wxBitmap curBitmap;
};
Paint event:
void ImagePanel::paintEvent(wxPaintEvent& event)
{
wxBufferedPaintDC dc(this);
render(dc);
}
This is how I have written the interface into main frame constructor:
topSizer = new wxBoxSizer(wxVERTICAL);
topSplitter = new wxSplitterWindow(this,ID_TOPSPLITTER,wxDefaultPosition,wxDefaultSize,wxSP_LIVE_UPDATE|wxSP_3D);
topSizer->Add(topSplitter,1,wxALL|wxEXPAND,0);
imagePanel = new wxPanel(topSplitter,ID_IMAGEPANEL);
imageSizer = new wxBoxSizer(wxVERTICAL);
imagePanel->SetSizer(imageSizer);
optionPanel = new wxPanel(topSplitter,ID_OPTIONPANEL);
optionSizer = new wxBoxSizer(wxVERTICAL);
optionPanel->SetSizer(optionSizer);
topSplitter->SplitVertically(imagePanel, optionPanel);
topSplitter->SetMinimumPaneSize(200);
//===========================================
//image panel interface
imageScWindow=new wxScrolledWindow(imagePanel,ID_IMAGESCWINDOW,wxDefaultPosition,wxDefaultSize,
wxVSCROLL|wxHSCROLL|wxALL|wxALWAYS_SHOW_SB|wxFULL_REPAINT_ON_RESIZE);
imgArea = new ImagePanel(imageScWindow);
imageSizer->Add(imageScWindow,1,wxEXPAND,0);
scrollSizer = new wxBoxSizer(wxVERTICAL);
imageScWindow->SetSizer(scrollSizer);
scrollSizer->Add(imgArea,1,wxEXPAND,0);
imageScWindow->SetTargetWindow(imgArea);
And this is the function that loads image:
void MainFrame::Setpic(wxCommandEvent& event)
{
wxString path = GetCurrentWorkingDir()+wxT("\\")+tmptxt1->GetValue();
wxImage img(path);
wxBitmap imBitmap(img,-1);
imgArea->setBitmap(imBitmap);
imageScWindow->SetScrollbars(1,1,imgArea->GetWidth(),imgArea->GetHeight());
imageScWindow->Refresh();
}
Thats what i get:
https://i.imgur.com/x1GIlrl.png
I have also tried to do like this in the constructor, without SetTargetArea:
//===========================================
//image panel interface
imageScWindow=new wxScrolledWindow(imagePanel,ID_IMAGESCWINDOW,wxDefaultPosition,wxDefaultSize,
wxVSCROLL|wxHSCROLL|wxALL|wxALWAYS_SHOW_SB|wxFULL_REPAINT_ON_RESIZE);
imgArea = new ImagePanel(imageScWindow);
imageSizer->Add(imageScWindow,1,wxEXPAND,0);
scrollSizer = new wxBoxSizer(wxVERTICAL);
imageScWindow->SetSizer(scrollSizer);
scrollSizer->Add(imgArea,1,wxEXPAND,0);
Then image shows properly, but if I change the frame size or move splitter, scrollbars disappear, and I have to set them again somwhere.
Then i tried to do like this:
int prvx,prvy;
void ImagePanel::paintEvent(wxPaintEvent& event)
{
reinterpret_cast<wxScrolledWindow*>(this->GetParent())->GetViewStart(&prvx,&prvy);
reinterpret_cast<wxScrolledWindow*>(this->GetParent())->SetScrollbars(1,1,curBitmap.GetWidth(),curBitmap.GetHeight());
reinterpret_cast<wxScrolledWindow*>(this->GetParent())->Scroll(wxPoint(prvx,prvy));
wxBufferedPaintDC dc(this);
render(dc);
}
It shows properly, but lags while scrolling, even without wxFULL_REPAINT_ON_RESIZE in wxScrolledWindow. The reason why it is in paint event: if I add this into OnSize event, program crashes. What to do?
I'm not sure where to begin answering your question. Here's a minimal sample of a frame that has a splitter with a bitmap on the left and a panel on the right. I hope you can adapt this sample to do what you need.
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include <wx/scrolwin.h>
#include <wx/splitter.h>
#include <wx/dcbuffer.h>
class MyFrame : public wxFrame
{
public:
MyFrame( wxWindow* parent, int id = wxID_ANY, wxString title = "Demo",
wxPoint pos = wxDefaultPosition, wxSize size = wxDefaultSize,
int style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL );
private:
void OnScrollPaint( wxPaintEvent& event );
wxScrolledCanvas* m_canvas;
wxBitmap m_bitmap;
};
MyFrame::MyFrame( wxWindow* parent, int id, wxString title, wxPoint pos,
wxSize size, int style )
:wxFrame( parent, id, title, pos, size, style )
{
m_bitmap=wxBitmap ("test.png", wxBITMAP_TYPE_PNG );
wxSplitterWindow* m_splitter1
= new wxSplitterWindow( this, wxID_ANY, wxDefaultPosition,
wxDefaultSize, wxSP_LIVE_UPDATE );
m_canvas = new wxScrolledCanvas( m_splitter1, wxID_ANY, wxDefaultPosition,
wxDefaultSize, wxSTATIC_BORDER|wxHSCROLL|wxVSCROLL );
m_canvas->SetScrollRate( 5, 5 );
m_canvas->SetVirtualSize(m_bitmap.GetWidth(), m_bitmap.GetHeight());
m_canvas->SetBackgroundStyle(wxBG_STYLE_PAINT);
m_canvas->Bind( wxEVT_PAINT, &MyFrame::OnScrollPaint , this );
wxPanel* m_panel2 = new wxPanel( m_splitter1, wxID_ANY, wxDefaultPosition,
wxDefaultSize, wxSTATIC_BORDER|wxTAB_TRAVERSAL );
m_splitter1->SplitVertically( m_canvas, m_panel2, GetSize().x/2 );
}
void MyFrame::OnScrollPaint( wxPaintEvent& event )
{
wxAutoBufferedPaintDC dc(m_canvas);
m_canvas->DoPrepareDC(dc);
dc.Clear();
dc.DrawBitmap(m_bitmap,0,0);
}
class MyApp : public wxApp
{
public:
virtual bool OnInit()
{
wxInitAllImageHandlers();
MyFrame* frame = new MyFrame(NULL);
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(MyApp);
To run this, be sure to change "test.png" in line 35 to the name of an actual image file on your computer (and change wxBITMAP_TYPE_PNG if the image file isn't a png).
The important parts are:
Set the background style of the canvas to wxBG_STYLE_PAINT as shown in line 46.
In the paint handler, call the DoPrepareDC method on the paintdc.
By the way, the paint handler shown in my sample isn't very good. It draws the entire bitmap every time. A better approach would be to get the update region and only redraw the parts that are needed, but I wanted to keep this simple.