diff --git a/wxPython/CHANGES.txt b/wxPython/CHANGES.txt index 5dc2d47b9b..eb4362da3b 100644 --- a/wxPython/CHANGES.txt +++ b/wxPython/CHANGES.txt @@ -58,6 +58,9 @@ Added wxPython.lib.evtmgr by Rob Schecter, which is an easier, more "Pythonic" and more OO method of registering handlers for wxWindows events using the Publish/Subscribe pattern. +Added wxPython.lib.popupctl by Gerrit van Dyk which is a combobox-like +gizmo for poping up arbitrary controls. It is currently using +wxDialog because of some issues with wxPopupWindow... diff --git a/wxPython/demo/Main.py b/wxPython/demo/Main.py index 6f313ba328..f6c15939a4 100644 --- a/wxPython/demo/Main.py +++ b/wxPython/demo/Main.py @@ -27,6 +27,7 @@ _treeList = [ ('New since last release', [ 'wxRadioButton', 'Throbber', + 'wxPopupControl', ]), # managed windows == things with a caption you can close @@ -108,6 +109,7 @@ _treeList = [ 'PyCrustWithFilling', 'SplitTree', 'TablePrint', + 'Throbber', 'wxCalendar', 'wxCalendarCtrl', 'wxDynamicSashWindow', @@ -117,6 +119,7 @@ _treeList = [ 'wxIEHtmlWin', 'wxLEDNumberCtrl', 'wxMimeTypesManager', + 'wxPopupControl', 'wxRightTextCtrl', 'wxStyledTextCtrl_1', 'wxStyledTextCtrl_2', diff --git a/wxPython/demo/wxPopupControl.py b/wxPython/demo/wxPopupControl.py new file mode 100644 index 0000000000..e05a7800a1 --- /dev/null +++ b/wxPython/demo/wxPopupControl.py @@ -0,0 +1,94 @@ +from wxPython.wx import * +from wxPython.lib.popupctl import wxPopupControl +from wxPython.calendar import * + +class TestDateControl(wxPopupControl): + def __init__(self,*_args,**_kwargs): + apply(wxPopupControl.__init__,(self,) + _args,_kwargs) + + self.win = wxWindow(self,-1,pos = (0,0),style = 0) + self.cal = wxCalendarCtrl(self.win,-1,pos = (0,0)) + + bz = self.cal.GetBestSize() + self.win.SetSize(bz) + + # This method is needed to set the contents that will be displayed + # in the popup + self.SetPopupContent(self.win) + + # Event registration for date selection + EVT_CALENDAR_DAY(self.cal,self.cal.GetId(),self.OnCalSelected) + + # Method called when a day is selected in the calendar + def OnCalSelected(self,evt): + self.PopDown() + date = self.cal.GetDate() + + # Format the date that was selected for the text part of the control + self.SetValue('%02d/%02d/%04d' % (date.GetDay(), + date.GetMonth()+1, + date.GetYear())) + evt.Skip() + + # Method overridden from wxPopupControl + # This method is called just before the popup is displayed + # Use this method to format any controls in the popup + def FormatContent(self): + # I parse the value in the text part to resemble the correct date in + # the calendar control + txtValue = self.GetValue() + dmy = txtValue.split('/') + didSet = false + if len(dmy) == 3: + date = self.cal.GetDate() + d = int(dmy[0]) + m = int(dmy[1]) - 1 + y = int(dmy[2]) + if d > 0 and d < 31: + if m >= 0 and m < 12: + if y > 1000: + self.cal.SetDate(wxDateTimeFromDMY(d,m,y)) + didSet = true + if not didSet: + self.cal.SetDate(wxDateTime_Today()) + + +#--------------------------------------------------------------------------- + +class TestPanel(wxPanel): + def __init__(self, parent, log): + self.log = log + wxPanel.__init__(self, parent, -1) + date = TestDateControl(self, -1, pos = (30,30), size = (100,22)) + +#---------------------------------------------------------------------- + +def runTest(frame, nb, log): + win = TestPanel(nb, log) + return win + +#---------------------------------------------------------------------- + + + +overview = """ +

wxPopupControl

+ +wxPopupControl is a class that can display a value and has a button +that will popup another window similar to how a wxComboBox works. The +popup window can contain whatever is needed to edit the value. This +example uses a wxCalendarCtrl. + +

Currently a wxDialog is used for the popup. Eventually a +wxPopupWindow should be used... + + +""" + + + +if __name__ == '__main__': + import sys,os + import run + run.main(['', os.path.basename(sys.argv[0])]) + diff --git a/wxPython/wxPython/lib/popupctl.py b/wxPython/wxPython/lib/popupctl.py new file mode 100644 index 0000000000..fe4408a9b3 --- /dev/null +++ b/wxPython/wxPython/lib/popupctl.py @@ -0,0 +1,243 @@ +#---------------------------------------------------------------------- +# Name: popup +# Purpose: Generic popup control +# +# Author: Gerrit van Dyk +# +# Created: 2002/11/20 +# Version: 0.1 +# RCS-ID: $Id$ +# License: wxWindows license +#---------------------------------------------------------------------- + +from wxPython.wx import * +from wxPython.lib.buttons import wxGenButtonEvent + + +class PopButton(wxPyControl): + def __init__(self,*_args,**_kwargs): + apply(wxPyControl.__init__,(self,) + _args,_kwargs) + + self.up = true + self.didDown = false + + self.InitColours() + + EVT_LEFT_DOWN(self, self.OnLeftDown) + EVT_LEFT_UP(self, self.OnLeftUp) + EVT_MOTION(self, self.OnMotion) + EVT_PAINT(self, self.OnPaint) + + def InitColours(self): + faceClr = wxSystemSettings_GetSystemColour(wxSYS_COLOUR_BTNFACE) + self.faceDnClr = faceClr + self.SetBackgroundColour(faceClr) + + shadowClr = wxSystemSettings_GetSystemColour(wxSYS_COLOUR_BTNSHADOW) + highlightClr = wxSystemSettings_GetSystemColour(wxSYS_COLOUR_BTNHIGHLIGHT) + self.shadowPen = wxPen(shadowClr, 1, wxSOLID) + self.highlightPen = wxPen(highlightClr, 1, wxSOLID) + self.blackPen = wxPen(wxBLACK, 1, wxSOLID) + + def Notify(self): + evt = wxGenButtonEvent(wxEVT_COMMAND_BUTTON_CLICKED, self.GetId()) + evt.SetIsDown(not self.up) + evt.SetButtonObj(self) + evt.SetEventObject(self) + self.GetEventHandler().ProcessEvent(evt) + + def OnEraseBackground(self, event): + pass + + def OnLeftDown(self, event): + if not self.IsEnabled(): + return + self.didDown = true + self.up = false + self.CaptureMouse() + self.GetParent().textCtrl.SetFocus() + self.Refresh() + event.Skip() + + def OnLeftUp(self, event): + if not self.IsEnabled(): + return + if self.didDown: + self.ReleaseMouse() + if not self.up: + self.Notify() + self.up = true + self.Refresh() + self.didDown = false + event.Skip() + + def OnMotion(self, event): + if not self.IsEnabled(): + return + if event.LeftIsDown(): + if self.didDown: + x,y = event.GetPositionTuple() + w,h = self.GetClientSizeTuple() + if self.up and x=0 and y=0: + self.up = false + self.Refresh() + return + if not self.up and (x<0 or y<0 or x>=w or y>=h): + self.up = true + self.Refresh() + return + event.Skip() + + def DrawBezel(self, dc, x1, y1, x2, y2): + # draw the upper left sides + if self.up: + dc.SetPen(self.highlightPen) + else: + dc.SetPen(self.shadowPen) + for i in range(2): + dc.DrawLine(x1+i, y1, x1+i, y2-i) + dc.DrawLine(x1, y1+i, x2-i, y1+i) + + # draw the lower right sides + if self.up: + dc.SetPen(self.shadowPen) + else: + dc.SetPen(self.highlightPen) + for i in range(2): + dc.DrawLine(x1+i, y2-i, x2+1, y2-i) + dc.DrawLine(x2-i, y1+i, x2-i, y2) + + def DrawArrow(self,dc): + size = self.GetSize() + mx = size.x / 2 + my = size.y / 2 + dc.SetPen(self.highlightPen) + dc.DrawLine(mx-5,my-5,mx+5,my-5) + dc.DrawLine(mx-5,my-5,mx,my+5) + dc.SetPen(self.shadowPen) + dc.DrawLine(mx+4,my-5,mx,my+5) + dc.SetPen(self.blackPen) + dc.DrawLine(mx+5,my-5,mx,my+5) + + def OnPaint(self, event): + width, height = self.GetClientSizeTuple() + x1 = y1 = 0 + x2 = width - 1 + y2 = height - 1 + dc = wxBufferedPaintDC(self) + if self.up: + dc.SetBackground(wxBrush(self.GetBackgroundColour(), wxSOLID)) + else: + dc.SetBackground(wxBrush(self.faceDnClr, wxSOLID)) + dc.Clear() + self.DrawBezel(dc, x1, y1, x2, y2) + self.DrawArrow(dc) + + +#--------------------------------------------------------------------------- + + +# Tried to use wxPopupWindow but the control misbehaves on MSW +class wxPopupDialog(wxDialog): + def __init__(self,parent,content = None): + wxDialog.__init__(self,parent,-1,'', style = wxSTAY_ON_TOP) + + self.ctrl = parent + self.win = wxWindow(self,-1,pos = wxPoint(0,0),style = 0) + + if content: + self.SetContent(content) + + def SetContent(self,content): + self.content = content + self.content.Reparent(self.win) + self.content.Show(true) + self.win.SetClientSize(self.content.GetSize()) + self.SetSize(self.win.GetSize()) + + def Display(self): + pos = self.ctrl.ClientToScreen( (0,0) ) + dSize = wxGetDisplaySize() + selfSize = self.GetSize() + tcSize = self.ctrl.GetSize() + + pos.x -= (selfSize.x - tcSize.x) / 2 + if pos.x + selfSize.x > dSize.x: + pos.x = dSize.x - selfSize.x + if pos.x < 0: + pos.x = 0 + + pos.y += tcSize.height + if pos.y + selfSize.y > dSize.y: + pos.y = dSize.y - selfSize.y + if pos.y < 0: + pos.y = 0 + + self.MoveXY(pos.x,pos.y) + + self.ctrl.FormatContent() + + self.ShowModal() + + +#--------------------------------------------------------------------------- + + +class wxPopupControl(wxPyControl): + def __init__(self,*_args,**_kwargs): + if 'value' in _kwargs: + del _kwargs['value'] + apply(wxPyControl.__init__,(self,) + _args,_kwargs) + + self.textCtrl = wxTextCtrl(self,-1,'',pos = wxPoint(0,0)) + self.bCtrl = PopButton(self,-1) + self.pop = None + self.content = None + self.OnSize(None) + + EVT_SIZE(self,self.OnSize) + EVT_BUTTON(self.bCtrl,self.bCtrl.GetId(),self.OnButton) + + def OnSize(self,evt): + w,h = self.GetClientSizeTuple() + self.textCtrl.SetDimensions(0,0,w-17,h) + self.bCtrl.SetDimensions(w-17,0,17,h) + + def OnButton(self,evt): + if not self.pop: + if self.content: + self.pop = wxPopupDialog(self,self.content) + del self.content + else: + print 'No Content to pop' + if self.pop: + self.pop.Display() + + def Enable(self,flag): + wxPyControl.Enable(self,flag) + self.textCtrl.Enable(flag) + self.bCtrl.Enable(flag) + + def SetPopupContent(self,content): + if not self.pop: + self.content = content + self.content.Show(false) + else: + self.pop.SetContent(content) + + def FormatContent(self): + pass + + def PopDown(self): + if self.pop: + self.pop.EndModal(1) + + def SetValue(self,value): + self.textCtrl.SetValue(value) + + def GetValue(self): + return self.textCtrl.GetValue() + + +# an alias +wxPopupCtrl = wxPopupControl