My method to add wxButton to wxGrid

If you have a cool piece of software to share, but you are not hosting it officially yet, please dump it in here. If you have code snippets that are useful, please donate!
Post Reply
liubl
Experienced Solver
Experienced Solver
Posts: 63
Joined: Sun Apr 11, 2010 11:25 am

My method to add wxButton to wxGrid

Post by liubl »

Hi all,

I found on the web the method on how to add wxButton to wxGrid but I did't find any detailed c++ examples. I do got a python version to do so http://hansir.javaeye.com/blog/460172.

Today I implement a c++ version by translate the python version. I pasted my code here because I think the codes is not perfect, I hope you can help to improve the code. And also I find there are others who blocked by this trick.

The tricks background I think are as follows (May not be right because I am not familiar with wxWidgets):
* wxGrid use wxGridCellRender to display each cell of the grid.
So to display the button, we must overwrite the Draw function of the render. But sadly, it is hard to draw button by wxDC and rectangle/text.

* If the wxGrid cell is editable, wxGrid will call wxGridCellEditor->Create to create the cell editor. Funtunaly, we can create a real wxButton here accept and bind the button event.
What is unfuntunate is that we draw button using wxDC to display the button and use the real button to response to user act. It is hard to make the two "button" looks like a single one.

* To make the button react to the user, the cell of the button must be editable.

* By default, wxGrid need two mouse click to get the button to function (one is to select the cell, the other is passed to wxButton). We must react to the mouse click event to use single mouse click.

the header file tmGridCell.h:

Code: Select all

#include <wx/wx.h>
#include <wx/grid.h>
#include <wx/renderer.h>
#include <wx/event.h>

class wxGridCellButtonRenderer : public wxGridCellRenderer
{
public:
	wxGridCellButtonRenderer(wxString label);
	virtual ~wxGridCellButtonRenderer(void);

    virtual void Draw(wxGrid &grid, wxGridCellAttr &attr, wxDC &dc, const wxRect &rect, int row, int col, bool isSelected);
    virtual wxSize GetBestSize(wxGrid &grid, wxGridCellAttr& attr, wxDC &dc, int row, int col);
    virtual wxGridCellRenderer *Clone() const;
private:
    wxString m_strLabel;
};


class wxGridCellButtonEditor : public wxEvtHandler, public wxGridCellEditor
{
public:
    wxGridCellButtonEditor(wxString label);
	virtual ~wxGridCellButtonEditor(void);

    virtual void Create(wxWindow *parent, wxWindowID id, wxEvtHandler* pEvtHandler);
	void OnButton(wxCommandEvent &evt);

    virtual void SetSize(const wxRect &rect);
	virtual void BeginEdit(int row, int col, wxGrid *pGrid);
    virtual bool EndEdit(int row, int col, wxGrid* grid);
    virtual void Reset();
	virtual wxString GetValue() const;
    virtual wxGridCellEditor *Clone() const;

private:
    wxString m_strLabel;
	wxButton *m_pButton;

    DECLARE_NO_COPY_CLASS(wxGridCellButtonEditor)
};

class tmGrid : public wxGrid
{
public:
	tmGrid(wxWindow *parent, wxWindowID id, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize, 
		long style = wxWANTS_CHARS, const wxString& name = wxPanelNameStr);
	virtual ~tmGrid();

	void OnLeftClick(wxGridEvent &evt);

	DECLARE_EVENT_TABLE()
};
The cpp file tmGridCell.cpp:

Code: Select all

#include "tmGridCell.h"

wxGridCellButtonRenderer::
wxGridCellButtonRenderer(wxString label)
{
	m_strLabel = label;
}

wxGridCellButtonRenderer::
~wxGridCellButtonRenderer(void)
{
}

void wxGridCellButtonRenderer::
Draw(wxGrid &grid, wxGridCellAttr &attr, wxDC &dc, const wxRect &rect, int row, int col, bool isSelected)
{
	wxRendererNative::Get().DrawPushButton(&grid, dc, rect, wxCONTROL_CURRENT);
	wxFont font = grid.GetFont();
	dc.SetFont(font);
	int x = rect.x + 10;
	int y = rect.y + 1;
	dc.DrawText(m_strLabel, x, y);
}

wxSize wxGridCellButtonRenderer::
GetBestSize(wxGrid &grid, wxGridCellAttr &attr, wxDC &dc, int row, int col)
{
	wxString text = grid.GetCellValue(row, col);
	dc.SetFont(attr.GetFont());
	return dc.GetTextExtent(text);
}

wxGridCellRenderer *wxGridCellButtonRenderer::
Clone() const
{
	return new wxGridCellButtonRenderer(m_strLabel);
}

////////////////////////////////////////////////////////////////////////////////////////////
wxGridCellButtonEditor::
wxGridCellButtonEditor(wxString label)
{
	m_strLabel = label;
}

wxGridCellButtonEditor::
~wxGridCellButtonEditor(void)
{
}

void wxGridCellButtonEditor::
Create(wxWindow *parent, wxWindowID id, wxEvtHandler* pEvtHandler)
{
	m_pButton = new wxButton(parent, id, m_strLabel);
	SetControl(m_pButton);
	m_pButton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxGridCellButtonEditor::OnButton));
}

void wxGridCellButtonEditor:: 
OnButton(wxCommandEvent &evt)
{
	
	evt.Skip();  
}

void wxGridCellButtonEditor::
SetSize(const wxRect &rect)
{
	m_pButton->SetSize(rect.x, rect.y, rect.width+2, rect.height+2, wxSIZE_ALLOW_MINUS_ONE);
}


void wxGridCellButtonEditor::
BeginEdit(int row, int col, wxGrid *pGrid)
{
	wxGridEvent evt(wxID_ANY, wxEVT_GRID_CELL_LEFT_CLICK, pGrid, row, col);
	wxPostEvent(m_pButton, wxCommandEvent(wxEVT_COMMAND_BUTTON_CLICKED));
}

bool wxGridCellButtonEditor::
EndEdit(int row, int col, wxGrid* grid)
{
	return true;
}

void wxGridCellButtonEditor::
Reset()
{
}

wxString wxGridCellButtonEditor::
GetValue() const
{
	return wxEmptyString;
}

wxGridCellEditor *wxGridCellButtonEditor::
Clone() const
{
	return new wxGridCellButtonEditor(m_strLabel);
}

//////////////////////////////////////////////////////////////////////////////////////////////////
BEGIN_EVENT_TABLE(tmGrid, wxGrid)
	EVT_GRID_CELL_LEFT_CLICK(tmGrid::OnLeftClick)
END_EVENT_TABLE()

tmGrid::
tmGrid(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style, const wxString &name)
		: wxGrid(parent, id, pos, size, style, name)
{
}

tmGrid::
~tmGrid()
{
}

void tmGrid::
OnLeftClick(wxGridEvent &evt)
{
	SetGridCursor(evt.GetRow(), evt.GetCol());
	evt.Skip();
}
Auria
Site Admin
Site Admin
Posts: 6695
Joined: Thu Sep 28, 2006 12:23 am
Contact:

Post by Auria »

Moved to the code dump section since this is not a question but an example.

Thanks for posting, I'm sure this will help others :)
"Keyboard not detected. Press F1 to continue"
-- Windows
punkermans
Earned a small fee
Earned a small fee
Posts: 16
Joined: Sat Jun 09, 2007 9:27 pm

Post by punkermans »

Hello, you can try to make your custom controls, if you want i can help you, this is a control that i develop some time ago.

Image
Tony0945
Earned some good credits
Earned some good credits
Posts: 105
Joined: Wed Oct 21, 2009 4:02 pm

Post by Tony0945 »

To avoid the two clicks, you can add a custom handler for the grid cell left click event. This handler can send a click event to the button. I don't have an example, but I have overridden the event handler to issue a nessagebox for debugging purposes.

Code: Select all

void TVGRidFrm::WxGrid1CellLeftClick0(wxGridEvent& event)
{
	// insert your code here
	ProgramInfo info = WxGrid1->GetInfo(event.GetRow(),event.GetCol());
    wxMessageBox(wxString::Format("ch %d ",info.channelnumber)+ info.channelname+" start "+info.start.Format()+"  slot "+info.slot.Format() );
}
GetInfo() is a method of the derived class, but GetRow() and GetCol() are base class methods. I haven't done it yet, but it seems like you can propogate events by manually calling ProcessEvent(). See http://biolpc22.york.ac.uk/wx/docs/html ... ocessevent

There's also an example in Cross-Platform GUI Programming with wxWidgets http://www.amazon.com/Cross-Platform-Pr ... 656&sr=1-1
dualband
Earned a small fee
Earned a small fee
Posts: 15
Joined: Fri Oct 16, 2020 11:53 pm

Re: My method to add wxButton to wxGrid

Post by dualband »

Hi

I tried to compile the original post tmGridCell.h and tmGridCell.cpp, but encountered compilation errors:

>>>>>>>>>>
tmgridcell.cpp(102): error C2259: 'wxGridCellButtonEditor': cannot instantiate abstract class
tmgridcell.cpp(102): note: due to following members:
tmgridcell.cpp(102): note: 'bool wxGridCellEditor::EndEdit(int,int,const wxGrid *,const wxString &,wxString *)': is abstract
\sdks\wx314\include\wx\generic\grid.h(414): note: see declaration of 'wxGridCellEditor::EndEdit'
tmgridcell.cpp(102): note: 'void wxGridCellEditor::ApplyEdit(int,int,wxGrid *)': is abstract
\sdks\wx314\include\wx\generic\grid.h(419): note: see declaration of 'wxGridCellEditor::ApplyEdit'
>>>>>>>>>>>

Does anyone have any comment?

Thanks.
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: My method to add wxButton to wxGrid

Post by doublemax »

The error messages are pretty clear. wxGridCellEditor now (10 years later) has two more abstract methods that needs to be implemented in any subclass.

As the class doesn't seem to modify any cell value directly, it might work to just keep them empty.
'bool wxGridCellEditor::EndEdit(int,int,const wxGrid *,const wxString &,wxString *)': is abstract
'void wxGridCellEditor::ApplyEdit(int,int,wxGrid *)': is abstract
Use the source, Luke!
dualband
Earned a small fee
Earned a small fee
Posts: 15
Joined: Fri Oct 16, 2020 11:53 pm

Re: My method to add wxButton to wxGrid

Post by dualband »

Thank you doublemax!

I updated the prototype: virtual bool EndEdit(int row, int col, const wxGrid * grid, const wxString & oldval, wxString * newval);
Added a new one: virtual void ApplyEdit(int row, int col, wxGrid*);
After I implemented the corresponding methods, it is now working!

Thanks again.
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7459
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: My method to add wxButton to wxGrid

Post by ONEEYEMAN »

Hi,
Can you post the updated code?

Thank you.
Post Reply