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 » Sun Oct 03, 2010 1:09 pm

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 » Sun Oct 03, 2010 3:50 pm

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 » Thu Oct 07, 2010 4:38 am

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
Experienced Solver
Experienced Solver
Posts: 96
Joined: Wed Oct 21, 2009 4:02 pm

Post by Tony0945 » Wed Oct 13, 2010 11:30 pm

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

Post Reply