I am using wxWidgets 2.8, together with wxPropertyGrid 1.4 in an application.
For float values, I would like to use a slider to edit them. However, a slider editor is not provided by default, so I have resorted to implementing my own editor, following the guidelines mentioned in the documentation.
However, with this new editor, even though I set it as the editor for my float property, it's not actually appearing until the property grid cell is in any way interacted with (e.g. clicked). Until then, the classic, textbox based controller is still visible.
Apparently, the actual CreateControl method for the slider editor isn't called when the propgrid gets generated - it's only called when the cell itself is somehow interacted with.
Here's my custom property editor:
wxpgslider.h
class WXDLLIMPEXP_PG wxPGSliderEditor : public wxPGEditor
{
#ifndef SWIG
WX_PG_DECLARE_EDITOR_CLASS(wxPGSliderEditor)
#endif
public:
wxPGSliderEditor (int p = 10000)
: precision(p)
{
}
~wxPGSliderEditor ()
{}
// Macro for the CreateControls method stub
wxPG_DECLARE_CREATECONTROLS
void UpdateControl ( wxPGProperty* property, wxWindow* wnd) const;
bool OnEvent ( wxPropertyGrid* propgrid, wxPGProperty* property, wxWindow* wnd, wxEvent& event) const;
bool GetValueFromControl ( wxVariant& variant, wxPGProperty* property, wxWindow* wnd) const;
void SetValueToUnspecified ( wxPGProperty* property, wxWindow* wnd) const;
//void DrawValue ( wxDC& dc, const wxRect& rect, wxPGProperty* property, const wxString& text) const;
private:
int precision;
};
wxpgslider.cpp
#include "cseditor/wxpgslider.h"
//----------------- wxPGSliderEditor ---------------------
WX_PG_IMPLEMENT_EDITOR_CLASS(SliderEditor, wxPGSliderEditor, wxPGEditor)
wxPGWindowList wxPGSliderEditor::CreateControls( wxPropertyGrid* propgrid,
wxPGProperty* property,
const wxPoint& pos,
const wxSize& size ) const
{
double v_d = property->GetValue().GetDouble();
if ( v_d 1 )
v_d = 1;
wxSlider *ctrl = new wxSlider();
#ifdef __WXMSW__
ctrl->Hide();
#endif
ctrl->Create ( propgrid->GetPanel(),
wxPG_SUBID2,
(int)(v_d * precision),
0,
precision,
pos,
size,
wxSL_HORIZONTAL );
return wxPGWindowList(ctrl);
}
void wxPGSliderEditor::UpdateControl ( wxPGProperty* property, wxWindow* wnd ) const
{
wxSlider* ctrl = wxDynamicCast ( wnd, wxSlider );
if ( ctrl )
{
double val;
if (wxPGVariantToDouble (property->DoGetValue(), &val))
{
if ( val 1 )
val = 1;
ctrl->SetValue ( (int)(val * precision) );
//static_cast(property)->GetLabel()
// ->SetValue( wxString::Format(wxT("%ld"), val * precision) );
}
}
}
bool wxPGSliderEditor::OnEvent ( wxPropertyGrid* propgrid,
wxPGProperty* property,
wxWindow* wnd,
wxEvent& event ) const
{
if(event.GetEventType() == wxEVT_SCROLL_CHANGED)
{
// Update the value
event.Skip();
propgrid->EditorsValueWasModified();
return true;
}
return false;
}
bool wxPGSliderEditor::GetValueFromControl ( wxVariant& variant,
wxPGProperty* property,
wxWindow* wnd ) const
{
wxSlider* ctrl = wxDynamicCast ( wnd, wxSlider );
if ( ctrl )
{
variant = wxVariant ( (double)(ctrl->GetValue())/(double)(precision) );
property->SetValue ( variant );
}
return true;
}
void wxPGSliderEditor::SetValueToUnspecified ( wxPGProperty* property, wxWindow* wnd) const
{
wxSlider* ctrl = wxDynamicCast ( wnd, wxSlider );
if ( ctrl )
{
ctrl->SetValue (0);
}
}
And this is the code I'm using to generate the slider, in a Populate function:
double value = variant->GetFloat();
// Generate a homebrewed slider
wxFloatProperty* fp = new wxFloatProperty(translatedName, originalName, value);
wxPGEditor* rHandle = wxPropertyGrid::RegisterEditorClass(new wxPGSliderEditor(), wxT("SliderEditor"));
fp->SetEditor(rHandle);
page->AppendIn(categoryID, fp);
I register the class, in case it's not been registered before, then set the property's editor. Then I add the property to the grid. Why is the slider not showing up until the cell gets interacted with?
Is calling pgMan->GetGrid()->SelectProperty(fp, false); the only way to make it get drawn?
you are using
#ifdef __WXMSW__
ctrl->Hide();
#endif
what's about
#ifdef __WXMSW__
ctrl->Show();
#endif
sample:
....
wxSlider* ctrl = new wxSlider();
#ifdef __WXMSW__
ctrl->Hide();
#endif
....
ctrl->Create ( propgrid->GetPanel(),
wxPG_SUBID2,
(int)(v_d * precision),
0,
precision,
pos,
size,
wxSL_HORIZONTAL );
#ifdef __WXMSW__
ctrl->Show();
#endif
return wxPGWindowList(ctrl);
}
EDIT
I can not see an OnCustomEditorEvent in your code.
with wxEVT_SCROLL_THUMBTRACK or wxEVT_SCROLL_CHANGED.
....
propgrid->Connect( wxPG_SUBID2, wxEVT_SCROLL_CHANGED,
(wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction)
&wxPropertyGrid::OnCustomEditorEvent, NULL, propgrid );
#ifdef __WXMSW__
ctrl->Show();
#endif
return wxPGWindowList(ctrl);
}
Related
I came across the following MRE that does not work anymore as it generates the error given below. The user who wrote this MRE at the time claimed that this works but after trying this example now, i get the error given below. My question is that how can I make the MRE working again. That is how to get rid of this error. Also, I want to know why it doesn't work anymore.
Note also that this example was written and provided here by #NewPagodi.
#ifdef WX_PRECOMP
#include "wx_pch.h"
#endif
#ifdef __BORLANDC__
#pragma hdrstop
#endif //__BORLANDC__
#include <wx/dcmemory.h>
#include <wx/renderer.h>
#include <wx/vlbox.h>
#include <wx/sizer.h>
#include <wx/panel.h>
#include <wx/statusbr.h>
#include <wx/frame.h>
#include <wx/app.h>
#include <set>
#include "lock.xpm"
#include "trash.xpm"
wxDECLARE_EVENT(myEVT_VLIST_BUTTON_CLICK, wxCommandEvent);
wxDECLARE_EVENT(myEVT_VLIST_CHECK_CLICK, wxCommandEvent);
class myVListBox:public wxVListBox
{
public:
myVListBox(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=0, const wxString &name=wxVListBoxNameStr);
virtual void OnDrawItem (wxDC &dc, const wxRect &rect, size_t n) const;
virtual wxCoord OnMeasureItem (size_t n) const;
private:
void OnVListLeftUp( wxMouseEvent& event );
wxBitmap CheckPlainBitmap;
wxBitmap CheckCheckedBitmap;
wxBitmap TrashBitmap;
wxBitmap LockBitmap;
//configurable
int CheckSide;
int CheckTextSide;
int ButtonSide;
//computed
int TextStart;
int CheckEnd;
int ButtonWidth;
int BoxHeight;
int CheckGap;
int TextGap;
int ButtonGap;
std::set<int> selected;
};
wxDEFINE_EVENT(myEVT_VLIST_BUTTON_CLICK, wxCommandEvent);
wxDEFINE_EVENT(myEVT_VLIST_CHECK_CLICK, wxCommandEvent);
myVListBox::myVListBox(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style, const wxString &name)
:wxVListBox(parent, wxID_ANY, pos, size, style, name)
{
wxDelegateRendererNative rn = wxRendererNative::Get();
wxSize sz = rn.GetCheckBoxSize(this);
CheckPlainBitmap = wxBitmap(sz);
CheckCheckedBitmap = wxBitmap(sz);
wxMemoryDC temp_dc;
temp_dc.SelectObject(CheckPlainBitmap);
rn.DrawCheckBox(this, temp_dc, wxRect(0,0,sz.GetWidth(),sz.GetHeight()),wxCONTROL_NONE );
temp_dc.SelectObject(CheckCheckedBitmap);
rn.DrawCheckBox(this, temp_dc, wxRect(0,0,sz.GetWidth(),sz.GetHeight()),wxCONTROL_CHECKED );
temp_dc.SelectObject(wxNullBitmap);
CheckSide=5;
CheckTextSide=5;
ButtonSide=5;
TrashBitmap=wxBitmap(trash_xpm);
LockBitmap=wxBitmap(lock_xpm);
//computed
CheckEnd=CheckSide+sz.GetX();
TextStart=CheckEnd+CheckTextSide;
ButtonWidth=TrashBitmap.GetWidth();
wxSize textSz = GetTextExtent("Test");
//Compute the box heights and the gaps
BoxHeight = sz.GetHeight();
if(textSz.GetHeight()>BoxHeight)
{
BoxHeight=textSz.GetHeight();
}
if(TrashBitmap.GetHeight()>BoxHeight)
{
BoxHeight=TrashBitmap.GetHeight();
}
BoxHeight+=4;
CheckGap=(BoxHeight-sz.GetHeight())/2;
TextGap=(BoxHeight-textSz.GetHeight())/2;
ButtonGap=(BoxHeight-TrashBitmap.GetHeight())/2;
Bind( wxEVT_LEFT_UP, &myVListBox::OnVListLeftUp , this );
}
wxCoord myVListBox::OnMeasureItem (size_t n) const
{
return BoxHeight;
}
void myVListBox::OnDrawItem(wxDC &dc, const wxRect &rect, size_t n) const
{
int textLeft=rect.GetLeft()+TextStart;
int textTop=rect.GetTop()+TextGap;
int checkLeft=rect.GetLeft()+CheckSide;
int checkTop=rect.GetTop()+CheckGap;
int buttonLeft=rect.GetRight()-ButtonWidth-ButtonSide;
int buttonTop=rect.GetTop()+ButtonGap;
if(selected.find(n) == selected.end())
{
dc.DrawBitmap( CheckPlainBitmap, checkLeft, checkTop );
}
else
{
dc.DrawBitmap( CheckCheckedBitmap, checkLeft, checkTop );
}
if(IsSelected(n) )
{
wxColour c= dc.GetTextForeground();
dc.SetTextForeground(*wxWHITE);
dc.DrawText( wxString::Format("entry %d",n), textLeft, textTop );
dc.SetTextForeground(c);
}
else
{
dc.DrawText( wxString::Format("entry %d",n), textLeft, textTop );
}
if(n%3==1)
{
dc.DrawBitmap( TrashBitmap, buttonLeft,buttonTop, true );
}
else
{
dc.DrawBitmap( LockBitmap, buttonLeft,buttonTop, true );
}
}
void myVListBox::OnVListLeftUp( wxMouseEvent& event )
{
int item = VirtualHitTest(event.GetY());
if(item==wxNOT_FOUND)
{
return;
}
wxRect r=GetItemRect(item);
bool OnButton(false);
if( CheckSide<=event.GetX() && event.GetX()<=CheckEnd )
{
if(r.GetTop()+CheckGap <= event.GetY() &&event.GetY() <=r.GetBottom()-CheckGap )
{
std::set<int>::iterator it= selected.find(item);
if( it == selected.end() )
{
selected.insert(item);
}
else
{
selected.erase(it);
}
RefreshRow(item);
wxCommandEvent event2(myEVT_VLIST_CHECK_CLICK,GetId());
event2.SetEventObject(this);
event2.SetInt(item);
event2.SetExtraLong(it == selected.end());
ProcessWindowEvent(event2);
}
}
else if( r.GetRight()-ButtonSide-ButtonWidth<=event.GetX() && event.GetX()<=r.GetRight()-ButtonSide )
{
if(r.GetTop()+ButtonGap <= event.GetY() &&event.GetY() <=r.GetBottom()-ButtonGap )
{
wxCommandEvent event2(myEVT_VLIST_BUTTON_CLICK,GetId());
event2.SetEventObject(this);
event2.SetInt(item);
ProcessWindowEvent(event2);
}
}
event.Skip();
}
class vlistFrame : public wxFrame
{
public:
vlistFrame( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("myVListBox demo"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 481,466 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL );
private:
void OnVListButton(wxCommandEvent& event);
void OnVListCheck(wxCommandEvent& event);
myVListBox* m_vlist;
};
vlistFrame::vlistFrame( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style )
{
wxPanel* m_panel1 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* bSizer2 = new wxBoxSizer( wxVERTICAL );
m_vlist=new myVListBox(m_panel1);
m_vlist->SetItemCount (20);
bSizer2->Add( m_vlist, 1, wxALL|wxEXPAND, 5 );
m_panel1->SetSizer( bSizer2 );
m_panel1->Layout();
bSizer2->Fit( m_panel1 );
CreateStatusBar( 2, wxST_SIZEGRIP, wxID_ANY );
// Connect Events
m_vlist->Bind( myEVT_VLIST_BUTTON_CLICK, &vlistFrame::OnVListButton , this );
m_vlist->Bind( myEVT_VLIST_CHECK_CLICK, &vlistFrame::OnVListCheck , this );
}
void vlistFrame::OnVListButton(wxCommandEvent& event)
{
SetStatusText( wxString::Format("Item %d button clicked",event.GetInt()),1);
}
void vlistFrame::OnVListCheck(wxCommandEvent& event)
{
SetStatusText( wxString::Format("Item %d check clicked %s",event.GetInt(),
(event.GetExtraLong()?"true":"false")),1);
}
class vlistApp : public wxApp
{
public:
virtual bool OnInit()
{
vlistFrame* frame = new vlistFrame(0L);
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(vlistApp);
The above produces the following error:
/home/user/Documents/folder/somefo/anj/sno/vlistMain.cpp: In constructor ‘myVListBox::myVListBox(wxWindow*, wxWindowID, const wxPoint&, const wxSize&, long int, const wxString&)’:
/home/user/Documents/folder/somefo/anj/sno/vlistMain.cpp:10:57: error: ‘wxDelegateRendererNative::wxDelegateRendererNative(const wxDelegateRendererNative&)’ is private within this context
wxDelegateRendererNative rn = wxRendererNative::Get();
^
In file included from /usr/local/include/wx-3.1/wx/memory.h:14:0,
from /usr/local/include/wx-3.1/wx/object.h:19,
from /usr/local/include/wx-3.1/wx/dc.h:18,
from /usr/local/include/wx-3.1/wx/dcmemory.h:14,
from /home/user/Documents/folder/somefo/anj/sno/../../headers/sno/vlistMain.h:14,
from /home/user/Documents/folder/somefo/anj/sno/vlistMain.cpp:2:
/usr/local/include/wx-3.1/wx/renderer.h:594:5: note: declared private here
wxDECLARE_NO_COPY_CLASS(wxDelegateRendererNative);
^
/home/user/Documents/folder/somefo/anj/sno/vlistMain.cpp:10:57: error: use of deleted function ‘wxDelegateRendererNative::wxDelegateRendererNative(const wxDelegateRendererNative&)’
wxDelegateRendererNative rn = wxRendererNative::Get();
^
In file included from /usr/local/include/wx-3.1/wx/memory.h:14:0,
from /usr/local/include/wx-3.1/wx/object.h:19,
from /usr/local/include/wx-3.1/wx/dc.h:18,
from /usr/local/include/wx-3.1/wx/dcmemory.h:14,
from /home/user/Documents/folder/somefo/anj/sno/../../headers/sno/vlistMain.h:14,
from /home/user/Documents/folder/somefo/anj/sno/vlistMain.cpp:2:
/usr/local/include/wx-3.1/wx/renderer.h:594:5: note: declared here
wxDECLARE_NO_COPY_CLASS(wxDelegateRendererNative);
^
In file included from /home/user/Documents/folder/somefo/anj/sno/../../headers/sno/vlistMain.h:15:0,
from /home/user/Documents/folder/somefo/anj/sno/vlistMain.cpp:2:
/usr/local/include/wx-3.1/wx/renderer.h:428:5: note: after user-defined conversion: wxDelegateRendererNative::wxDelegateRendererNative(wxRendererNative&)
wxDelegateRendererNative(wxRendererNative& rendererNative)
The problem is that wxRendererNative::Get() returns a reference (see documentation) to the renderer you intend to use for drawing, but you try to create a new renderer as local variable. Moreover, it returns wxRendererNative&, but you try to create wxDelegateRendererNative (which is derived from wxRendererNative), so the type of new renderer is different, and in its documentation you can see that it doesn't have appropriate constructors to be constructed from wxRendererNative. And even if such constructor was available, the copied renderer could behave differently than what you wanted (e.g. draw something in its own state/buffer and not copy that to original one). So, you should use the returned reference instead by, e.g., saving it to local reference variable and using it to access original native renderer:
wxRendererNative& rn = wxRendererNative::Get();
// ...
P.S. Note that the previous version of this answer was to save returned reference to wxDelegateRendererNative& rn, which was wrong (thanks VZ.) because renderer could have different dynamic type than wxDelegateRendererNative, and wouldn't even compile since initializing derived lvalue reference to base object is not allowed in reference initialization (except when conversion from base to derived lvalue reference is defined, which is not the case here). Also, note that delegate renderer is needed only when you try to define your own renderer with limited additional functionality and delegate everything else (via inheritance from wxDelegateRendererNative) to other renderer (which is passed as constructor parameter to delegate; see example). If your renderer was implemented this way and you know dynamic type of renderer at compile time, you can do explicit reference cast using static_cast and actually use wxDelegateRendererNative& (beware of UB if cast is unsuccessful), but in this case using actual type of renderer (e.g. MyRendererNative&) will be more likely to help optimizer 'devirtualize' virtual method calls through this reference (which is the only reason I can think of to do downcast in this situation).
So I wanted to add a texture to a map, but the problem with it is that I can't quite get why it doesn't get to the correct place while zooming with different zoom sizes.
I'm normally trying to achieve setting the texture position on the background to my position, keeping myself centered into the frame: for example my texture size is 1500x1600 and I'm located at X140, Y590 in that picture ( yes, the coordinates are retrieved correctly as I've checked with the console ), zooming in with some value and scaling the texture and setting it's position to where I'm at.
The code is the following:
if (!areTexturesInit) {
InitiateTextures();
areTexturesInit = true;
}
wxBitmap bit(imageTest);
wxPaintDC dc(this);
double zoomSize = 0.9; // here I'm applying the zooming proportions ( 0.1 - bigger size of the texture, 0.9 - more zoomed in )
this->SetSize(wxSize(386, 386)); // size of the main frame
backgroundSize.x = GetSize().x; // get the size of the main frame
backgroundSize.y = GetSize().y;
middlePoint.x = (backgroundSize.x / 2); // calculate the middle point of the frame
middlePoint.y = (backgroundSize.y / 2);
mapSizeX = 25600 / -zoomSize; // scale vs zoom size
mapSizeY = 25600 / zoomSize;
Vector3 myPosition;
GetPlayerPosition(&myPosition); // gets my location
float TextureCoordinateX = middlePoint.x + (myPosition.x / mapSizeX) * backgroundSize.x;
float TextureCoordinateY = middlePoint.y - (myPosition.y / mapSizeY) * backgroundSize.y;
dc.DrawBitmap(bit, TextureCoordinateX, TextureCoordinateY);
Vector3 myPosOnMap = PositionToMapPosition(myPosition, myPosition); // calculates my position on the map vs mapSizeX and Y & rotates vector
dc.SetPen(wxPen(wxColor(255, 0, 0), 4));
dc.DrawRectangle(wxRect(myPosOnMap.x, myPosOnMap.y, 2, 2)); // draws me on the map with a red square
The problem is that I think I've messed up the zooming part somewhere.
I've attached some demos so you can see what I'm talking about:
"zoomSize" of 0.9:
"zoomSize" of 0.67 - which kind of works, but I need to change it to different zoomSizes, there being the problem:
Panning and zooming a dc is surprisingly complicated. It requires working with 3 separate coordinate systems and it's really easy to accidentally work in the wrong coordinate system.
Here's an example I wrote a while ago that shows how to do the calculations that allow a dc to be pan and zoomed.
It sounds like you're not interested in the pan part, so you can ignore all the stuff that allows a user to set their own pan. However, it's still necessary to to use a pan vector just for the zoom in order to center the zoom at the correct location.
// 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/graphics.h>
#include <wx/dcbuffer.h>
class PanAndZoomCanvas:public wxWindow
{
public:
PanAndZoomCanvas(wxWindow *parent,
wxWindowID id = wxID_ANY,
const wxPoint &pos=wxDefaultPosition,
const wxSize &size=wxDefaultSize,
long style=0,
const wxString &name="PanAndZoomCanvas");
wxRect2DDouble GetUntransformedRect() const;
protected:
void DoDrawCanvas(wxGraphicsContext*);
private:
void OnPaint(wxPaintEvent&);
void OnMouseWheel(wxMouseEvent&);
void OnLeftDown(wxMouseEvent&);
void OnMotion(wxMouseEvent&);
void OnLeftUp(wxMouseEvent&);
void OnCaptureLost(wxMouseCaptureLostEvent&);
void ProcessPan(const wxPoint&,bool);
void FinishPan(bool);
int m_zoomFactor;
wxPoint2DDouble m_panVector;
wxPoint2DDouble m_inProgressPanVector;
wxPoint m_inProgressPanStartPoint;
bool m_panInProgress;
};
PanAndZoomCanvas::PanAndZoomCanvas(wxWindow *parent, wxWindowID id,
const wxPoint &pos, const wxSize &size,
long style, const wxString &name)
:wxWindow(parent, id, pos, size, style, name)
{
Bind(wxEVT_PAINT,&PanAndZoomCanvas::OnPaint,this);
Bind(wxEVT_MOUSEWHEEL,&PanAndZoomCanvas::OnMouseWheel,this);
Bind(wxEVT_LEFT_DOWN,&PanAndZoomCanvas::OnLeftDown,this);
SetBackgroundStyle(wxBG_STYLE_PAINT);
m_zoomFactor = 100;
m_panVector = wxPoint2DDouble(0,0);
m_inProgressPanStartPoint = wxPoint(0,0);
m_inProgressPanVector = wxPoint2DDouble(0,0);
m_panInProgress = false;
}
void PanAndZoomCanvas::DoDrawCanvas(wxGraphicsContext* gc)
{
gc->SetPen(*wxBLACK_PEN);
wxGraphicsPath path = gc->CreatePath();
path.MoveToPoint(100,100);
path.AddLineToPoint(300,100);
path.AddLineToPoint(300,300);
path.CloseSubpath();
gc->StrokePath(path);
}
void PanAndZoomCanvas::OnPaint(wxPaintEvent& WXUNUSED(event))
{
wxAutoBufferedPaintDC dc(this);
dc.Clear();
wxGraphicsContext* gc = wxGraphicsContext::Create(dc);
if ( gc )
{
double a = m_zoomFactor / 100.0;
wxPoint2DDouble totalPan = m_panVector + m_inProgressPanVector;
gc->Translate(-totalPan.m_x, -totalPan.m_y);
gc->Scale(a, a);
DoDrawCanvas(gc);
delete gc;
}
}
void PanAndZoomCanvas::OnMouseWheel(wxMouseEvent& event)
{
if ( m_panInProgress )
{
FinishPan(false);
}
int rot = event.GetWheelRotation();
int delta = event.GetWheelDelta();
int oldZoom = m_zoomFactor;
m_zoomFactor += 10*(rot/delta);
if ( m_zoomFactor<10 )
{
m_zoomFactor = 10;
}
if ( m_zoomFactor>800)
{
m_zoomFactor = 800;
}
double a = oldZoom / 100.0;
double b = m_zoomFactor / 100.0;
// Set the panVector so that the point below the cursor in the new
// scaled/panned cooresponds to the same point that is currently below it.
wxPoint2DDouble uvPoint = event.GetPosition();
wxPoint2DDouble stPoint = uvPoint + m_panVector;
wxPoint2DDouble xypoint = stPoint/a;
wxPoint2DDouble newSTPoint = b * xypoint;
m_panVector = newSTPoint - uvPoint;
Refresh();
}
void PanAndZoomCanvas::ProcessPan(const wxPoint& pt, bool refresh)
{
m_inProgressPanVector = m_inProgressPanStartPoint - pt;
if ( refresh )
{
Refresh();
}
}
void PanAndZoomCanvas::FinishPan(bool refresh)
{
if ( m_panInProgress )
{
SetCursor(wxNullCursor);
if ( HasCapture() )
{
ReleaseMouse();
}
Unbind(wxEVT_LEFT_UP, &PanAndZoomCanvas::OnLeftUp, this);
Unbind(wxEVT_MOTION, &PanAndZoomCanvas::OnMotion, this);
Unbind(wxEVT_MOUSE_CAPTURE_LOST, &PanAndZoomCanvas::OnCaptureLost, this);
m_panVector += m_inProgressPanVector;
m_inProgressPanVector = wxPoint2DDouble(0,0);
m_panInProgress = false;
if ( refresh )
{
Refresh();
}
}
}
wxRect2DDouble PanAndZoomCanvas::GetUntransformedRect() const
{
double a = m_zoomFactor / 100.0;
wxSize sz = GetSize();
wxPoint2DDouble zero = m_panVector/a;
return wxRect2DDouble(zero.m_x, zero.m_y, sz.GetWidth()/a, sz.GetHeight()/a);
}
void PanAndZoomCanvas::OnLeftDown(wxMouseEvent& event)
{
wxCursor cursor(wxCURSOR_HAND);
SetCursor(cursor);
m_inProgressPanStartPoint = event.GetPosition();
m_inProgressPanVector = wxPoint2DDouble(0,0);
m_panInProgress = true;
Bind(wxEVT_LEFT_UP, &PanAndZoomCanvas::OnLeftUp, this);
Bind(wxEVT_MOTION, &PanAndZoomCanvas::OnMotion, this);
Bind(wxEVT_MOUSE_CAPTURE_LOST, &PanAndZoomCanvas::OnCaptureLost, this);
CaptureMouse();
}
void PanAndZoomCanvas::OnMotion(wxMouseEvent& event)
{
ProcessPan(event.GetPosition(), true);
}
void PanAndZoomCanvas::OnLeftUp(wxMouseEvent& event)
{
ProcessPan(event.GetPosition(), false);
FinishPan(true);
}
void PanAndZoomCanvas::OnCaptureLost(wxMouseCaptureLostEvent&)
{
FinishPan(true);
}
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 );
};
MyFrame::MyFrame( wxWindow* parent, int id, wxString title, wxPoint pos
, wxSize size, int style )
:wxFrame( parent, id, title, pos, size, style )
{
PanAndZoomCanvas* canvas = new PanAndZoomCanvas(this);
}
class myApp : public wxApp
{
public:
virtual bool OnInit()
{
MyFrame* frame = new MyFrame(NULL);
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(myApp);
On windows, this looks like this:
My original aim was to feed items (QStandardItem) of a specific column with rich text, therefore I implemented a subclass delegate as suggested.
Everything looked fine except one thing: when I moved the mouse pointer over these items, they were not highlighted at all. (The other items in the row - where the original paint method is used - were highlighted.) Item selection worked fine although. Then I added line
if ( optionV4.state & QStyle::State_MouseOver )
in which I was able to handle the item text highlight, but I have no idea how to highlight the background too. It is still white. Any ideas?
Here is the relevant code:
class MStyledItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
protected:
void paint ( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
QSize sizeHint ( const QStyleOptionViewItem& option, const QModelIndex& index ) const;
};
void MStyledItemDelegate::paint( QPainter* aPainter, const QStyleOptionViewItem& aOption, const QModelIndex& aIndex ) const
{
// ...
QStyleOptionViewItemV4 optionV4 = aOption;
initStyleOption( &optionV4, aIndex );
QStyle* style = optionV4.widget ? optionV4.widget->style() : QApplication::style();
QTextDocument doc;
doc.setHtml( optionV4.text );
optionV4.text = QString();
style->drawControl( QStyle::CE_ItemViewItem, &optionV4, aPainter );
// highlight text
QAbstractTextDocumentLayout::PaintContext ctx;
if ( optionV4.state & QStyle::State_MouseOver )
{
ctx.palette.setColor( QPalette::Text, Qt::blue );
}
// draw
aPainter->save();
QRect textRect = style->subElementRect( QStyle::SE_ItemViewItemText, &optionV4 );
aPainter->translate( textRect.topLeft() );
aPainter->setClipRect( textRect.translated( - textRect.topLeft() ) );
doc.documentLayout()->draw( aPainter, ctx );
aPainter->restore();
}
I suppose you use it with a QTableView, call the method setMouseTracking(true).
when Using Qt Graphics View to simulate traffic situation.
I have :
2 global class TrafficLight inherited from QGraphicsItem
class Car inherited from QGraphicsItem
class TrafficLight : public QGraphicsItem
{
public:
TrafficLight( int sec );
QColor getLightStatus( ELightDirct ) volatile;
QRectF boundingRect() const;
void paint( QPainter* painter, const QStyleOptionGraphicsItem* option,
QWidget* Widget );
QPainterPath shape() const;
protected:
void advance( int step );
private:
volatile int ticker;
public:
volatile bool ableNS;
};
static TrafficLight* pLightAtCr1 = new TrafficLight( 0 );
static TrafficLight* pLightAtCr2 = new TrafficLight( 125 );
And in the advance() function of TrafficLight, counting and change ticker and ableNS status:
/*---------------------------------------------------------------------------*/
/* advance */
/*---------------------------------------------------------------------------*/
void TrafficLight::advance( int step )
{
if ( !step )
return;
if ( ticker == 140 * freq / speedUp - 1)
{
ticker = 0;
ableNS = false;
}
else if ( ticker == 120 * freq -1 )
{
++ticker;
ableNS = true;
}
else
{
++ticker;
}
}
Then the Car class :
class Car: public QGraphicsItem
{
public:
Car();
Car( EPlace, EPlace, qreal );
QRectF boundingRect() const;
void paint( QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget );
QPainterPath shape() const;
void setStartPos();
bool ableToForward( TrafficLight*, ELightDirct ) const;
void checkLightForward() ;
protected:
void advance( int step );
public:
EPlace genPlace;
EPlace dist;
qreal speed;
};
void Car::checkLightForward() function check global traffciLight's status and determine stop to wait or moving at every advance.
/*---------------------------------------------------------------------------*/
/* checkLightForward */
/*---------------------------------------------------------------------------*/
void Car::checkLightForward()
{
QPointF curLoc = mapToScene( 0, 0 );
if ( curLoc == lightCr1BSN || curLoc == lightCr1BSS ) {
if ( !pLightAtCr1->ableNS ) {
}
else {
setPos( mapToParent( 0, 0.5 ) );
}
}
else {
setPos( mapToParent( 0, 0.5 ) );
}
}
at the Car::advance(), invoke the checkLightForward().
and added them to QGraphicsScene.
Then the main()
QTimer timer;
QObject::connect( &timer, SIGNAL(timeout()), &scene, SLOT(advance()) );
timer.start( 1000/freq );
But what make me confused:
TrafficLight pLightAtCr1 status changed that can be seen on QGraphicsView, but cars can not
received the TrafficLight's status changes, ignoring red Light.
does compiler optimization lead to this confused result, that storing TrafficLight's status in the register. Or other reason? How can I fix this problem!
and merry christmas~
thanks so much!
I have a QStyledItemDelegate derived object for a QTableView derived view. I further delegate the painting and editor creation depending on the model index data type. For bools I wanted to represent the state via a checkbox - but the check box never appears.
Here is the base delegate paint function:
void Sy_QtPropertyDelegate::paint( QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index ) const
{
painter->save();
if ( index.column() == 0 ) {
...
} else {
QVariant var = index.data();
bool modified = index.data( Sy_QtPropertyModel::ModifiedRole ).toBool();
// If the data type is one of our delegates, then push the work onto
// that.
auto it = delegateMap_.find( var.type() );
if ( it != delegateMap_.end() ) {
( *it )->paint( painter, option, index );
} else if ( var.type() != QVariant::UserType ) {
...
} else {
...
}
}
painter->restore();
}
And the bool sub delegate paint function:
void Sy_boolPD::paint( QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index ) const
{
painter->save();
bool checked = index.data().toBool();
bool modified = index.data( Sy_QtPropertyModel::ModifiedRole ).toBool();
QStyle* style = Sy_application::style();
if ( modified ) {
QStyleOptionViewItemV4 bgOpt( option );
bgOpt.backgroundBrush = QBrush( Sy_QtPropertyDelegate::ModifiedColour );
style->drawControl( QStyle::CE_ItemViewItem, &bgOpt, painter );
}
QStyleOption butOpt( option );
butOpt.state = QStyle::State_Enabled;
butOpt.state |= checked ? QStyle::State_On : QStyle::State_Off;
style->drawControl( QStyle::CE_CheckBox, &butOpt, painter );
painter->restore();
}
If I force modified to be true, the background is colour of the table is appropriately changed, and couting butOpt's rect and state members show that they are correct - but no check box is shown! Setting QStyle::CE_CheckBox to any other type also causes nothing to render.
I've worked with Qt's MVC framework a lot, but I cannot see where I have gone wrong here.
All I had to do was look in the source code...
case CE_CheckBox:
if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
...
}
The QStyleOption I pass to the method is cast to the type specific for drawing the control, CE_CheckBox requires a QStyleOptionButton, and if the cast fails the drawing operation silently skips.