From d63ae92c2097121b5102cffbeb56bd53fb143b20 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Mon, 25 Jul 2016 21:25:09 -0700 Subject: [PATCH 01/33] Various demo tweaks and fixes --- demo/Calendar.py | 2 +- demo/CalendarCtrl.py | 28 +++++++++++++++------------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/demo/Calendar.py b/demo/Calendar.py index d374cdbd..e4a54ccf 100644 --- a/demo/Calendar.py +++ b/demo/Calendar.py @@ -416,7 +416,7 @@ class PrintCalend: printout2 = SetPrintout(self) self.preview = wx.PrintPreview(printout, printout2, self.printData) - if not self.preview.Ok(): + if not self.preview.IsOk(): wx.MessageBox("There was a problem printing!", "Printing", wx.OK) return diff --git a/demo/CalendarCtrl.py b/demo/CalendarCtrl.py index 9e6f5e70..d9776cb9 100644 --- a/demo/CalendarCtrl.py +++ b/demo/CalendarCtrl.py @@ -2,7 +2,7 @@ import wx import wx.adv -from wx.adv import CalendarCtrl +from wx.adv import CalendarCtrl, GenericCalendarCtrl, CalendarDateAttr #---------------------------------------------------------------------- @@ -28,13 +28,13 @@ class TestPanel(wx.Panel): txt = wx.StaticText(self, -1, description) txt.Wrap(300) - # cal = self.cal = GenericCalendarCtrl(self, -1, wx.DateTime().Today(), - # style = wx.adv.CAL_SHOW_HOLIDAYS - # | wx.adv.CAL_SUNDAY_FIRST - # | wx.adv.CAL_SEQUENTIAL_MONTH_SELECTION - # ) + cal = GenericCalendarCtrl(self, -1, wx.DateTime().Today(), + style = wx.adv.CAL_SHOW_HOLIDAYS + | wx.adv.CAL_SUNDAY_FIRST + | wx.adv.CAL_SEQUENTIAL_MONTH_SELECTION + ) - # cal2 = wxcal.GenericCalendarCtrl(self, -1, wx.DateTime().Today()) + cal2 = GenericCalendarCtrl(self, -1, wx.DateTime().Today()) # Track a few holidays @@ -43,9 +43,9 @@ class TestPanel(wx.Panel): # bind some event handlers to each calendar - for c in [native]:#, cal, cal2 + for c in [native, cal, cal2]: c.Bind(wx.adv.EVT_CALENDAR, self.OnCalSelected) - ## c.Bind(wx.adv.EVT_CALENDAR_MONTH, self.OnChangeMonth) + c.Bind(wx.adv.EVT_CALENDAR_MONTH, self.OnChangeMonth) c.Bind(wx.adv.EVT_CALENDAR_SEL_CHANGED, self.OnCalSelChanged) c.Bind(wx.adv.EVT_CALENDAR_WEEKDAY_CLICKED, self.OnCalWeekdayClicked) @@ -53,15 +53,17 @@ class TestPanel(wx.Panel): fgs = wx.FlexGridSizer(cols=2, hgap=50, vgap=50) fgs.Add(native) fgs.Add(txt) - # fgs.Add(cal) - # fgs.Add(cal2) + fgs.Add(cal) + fgs.Add(cal2) box = wx.BoxSizer() box.Add(fgs, 1, wx.EXPAND|wx.ALL, 25) self.SetSizer(box) def OnCalSelected(self, evt): - self.log.write('OnCalSelected: %s\n' % evt.GetDate()) + self.log.write('OnCalSelected: %s\n' % evt.Date) + if evt.Date.month == wx.DateTime.Aug and evt.Date.day == 14: + self.log.write("HAPPY BIRTHDAY!") def OnCalWeekdayClicked(self, evt): self.log.write('OnCalWeekdayClicked: %s\n' % evt.GetWeekDay()) @@ -86,7 +88,7 @@ class TestPanel(wx.Panel): # August 14th is a special day, mark it with a blue square... if cur_month == 8: - attr = wxcal.CalendarDateAttr(border=wx.adv.CAL_BORDER_SQUARE, + attr = CalendarDateAttr(border=wx.adv.CAL_BORDER_SQUARE, colBorder="blue") cal.SetAttr(14, attr) else: From 1bd3704e814f73611e1bc0aedba306d3fc8ae317 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Tue, 26 Jul 2016 16:59:46 -0700 Subject: [PATCH 02/33] Add a note that Python's XML is better than wx's XML for most things. --- etg/_xml.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/etg/_xml.py b/etg/_xml.py index 771f91fa..69322fba 100644 --- a/etg/_xml.py +++ b/etg/_xml.py @@ -15,6 +15,9 @@ MODULE = "_xml" NAME = "_xml" # Base name of the file to generate to for this script DOCSTRING = """\ Some simple XML classes for use with XRC. + +For more advanced XML needs it would be better to use one of the XML packages +provided by Python. """ # The classes and/or the basename of the Doxygen XML files to be processed by From aa9e47e0f540af1d404b6af066bf82a4943085f0 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Sat, 30 Jul 2016 23:45:22 -0700 Subject: [PATCH 03/33] More demo tweaks and fixes --- demo/ColourSelect.py | 4 ++-- demo/ComboCtrl.py | 2 +- demo/CommandLinkButton.py | 17 +++++++++-------- demo/ContextHelp.py | 11 +++++++++-- wx/lib/colourselect.py | 6 +++--- wx/lib/combotreebox.py | 37 +++++++++++++++++++++---------------- 6 files changed, 45 insertions(+), 32 deletions(-) diff --git a/demo/ColourSelect.py b/demo/ColourSelect.py index 401a4144..c1519518 100644 --- a/demo/ColourSelect.py +++ b/demo/ColourSelect.py @@ -36,7 +36,7 @@ class TestColourSelect(wx.Panel): "and wxColourDialog Classes. Click Button to get Colour Values") mainSizer.Add(t, 0, wx.ALL, 3) - b = wx.Button(self, -1, "Show All Colours") + b = wx.Button(self, -1, "Log All Current Colours") self.Bind(wx.EVT_BUTTON, self.OnShowAll, id=b.GetId()) mainSizer.Add(b, 0, wx.ALL, 3) @@ -55,7 +55,7 @@ class TestColourSelect(wx.Panel): # build several examples of buttons with different colours and sizes buttonData = [ ("Default Size", (255, 255, 0), wx.DefaultSize, ""), - ("Another Size", (255, 0, 255), (60, 20), ""), + ("Another Size", (255, 0, 255), (60, 22), ""), ("Another Colour", (0, 255, 0), wx.DefaultSize, ""), ("Larger Size", (0, 0, 255), (60, 60), ""), ("With a Label", (127, 0, 255), wx.DefaultSize, "Colour..."), diff --git a/demo/ComboCtrl.py b/demo/ComboCtrl.py index 3d6f3e35..23074c98 100644 --- a/demo/ComboCtrl.py +++ b/demo/ComboCtrl.py @@ -112,7 +112,7 @@ class TestPanel(wx.Panel): self.log = log wx.Panel.__init__(self, parent, -1) - comboCtrl = wx.ComboCtrl(self, wx.ID_ANY, "") + comboCtrl = wx.ComboCtrl(self, wx.ID_ANY, "", (20,20)) popupCtrl = ListCtrlComboPopup() diff --git a/demo/CommandLinkButton.py b/demo/CommandLinkButton.py index 9c49971b..fdbc4a4d 100644 --- a/demo/CommandLinkButton.py +++ b/demo/CommandLinkButton.py @@ -2,6 +2,7 @@ import wx import wx.adv +from textwrap import dedent #---------------------------------------------------------------------- @@ -10,14 +11,14 @@ class TestPanel(wx.Panel): self.log = log wx.Panel.__init__(self, parent, -1) - cmd = wx.adv.CommandLinkButton(self, -1, - "wx.CommandLinkButton", - """\ -This type of button includes both a main label and a 'note' that is meant to -contain a description of what the button does or what it is used for. On -Windows 7 it is a new native widget type, on the other platforms it is -implemented generically.""", - pos=(25,25)) + cmd = wx.adv.CommandLinkButton(self, -1, "wx.CommandLinkButton", + dedent("""\ + This type of button includes both a main label and a 'note' that + is meant to contain a description of what the button does or + what is used for. On Windows 7 it is a new native widget type, + on the other platforms it is implemented generically. + """), + pos=(25,25), size=(500,-1)) #---------------------------------------------------------------------- diff --git a/demo/ContextHelp.py b/demo/ContextHelp.py index aa4d7d95..1566de80 100644 --- a/demo/ContextHelp.py +++ b/demo/ContextHelp.py @@ -25,6 +25,7 @@ class TestPanel(wx.Panel): # Init the context help button. # And even include help text about the help button :-) cBtn = wx.ContextHelpButton(self) + cBtn.Bind(wx.EVT_BUTTON, self.OnCtxHelpButton) cBtn.SetHelpText("wx.ContextHelpButton") cBtnText = wx.StaticText(self, -1, @@ -63,7 +64,6 @@ class TestPanel(wx.Panel): sizer.Add(text) text.Bind(wx.EVT_HELP, self.OnCtxHelp2, text) - border = wx.BoxSizer(wx.VERTICAL) border.Add(sizer, 0, wx.ALL, 25) @@ -72,9 +72,16 @@ class TestPanel(wx.Panel): self.Layout() + def OnCtxHelpButton(self, evt): + # This starts a nested event loop which exits when an item has been + # clicked on, its help message shown and dismissed. + cshelp = wx.ContextHelp(self) + + # On the second text control above, we intercept the help event. This is where # we process it. Anything could happen here. In this case we're just printing - # some stuff about it, then passing it on, at which point we see the help tip. + # some stuff about it, then passing it on with Skip(), at which point we + # see the help tip. def OnCtxHelp(self, evt): self.log.write("OnCtxHelp: %s" % evt) evt.Skip() diff --git a/wx/lib/colourselect.py b/wx/lib/colourselect.py index 3126cc5a..7d1cfe24 100644 --- a/wx/lib/colourselect.py +++ b/wx/lib/colourselect.py @@ -180,10 +180,10 @@ class ColourSelect(wx.lib.buttons.GenBitmapButton): if label: mdc = wx.MemoryDC(wx.Bitmap(1,1)) w, h = mdc.GetTextExtent(label) - w += 6 - h += 6 + w += 8 + h += 8 else: - w, h = 20, 20 + w, h = 22, 22 size.width = size.width if size.width != -1 else w size.height = size.height if size.height != -1 else h diff --git a/wx/lib/combotreebox.py b/wx/lib/combotreebox.py index 670863ba..8c42d908 100644 --- a/wx/lib/combotreebox.py +++ b/wx/lib/combotreebox.py @@ -213,7 +213,7 @@ class BasePopupFrame(wx.Frame): def _layoutInterior(self): frameSizer = wx.BoxSizer(wx.HORIZONTAL) frameSizer.Add(self._tree, flag=wx.EXPAND, proportion=1) - self.SetSizerAndFit(frameSizer) + self.SetSizerAndFit(frameSizer) #**** def _bindEventHandlers(self): self._tree.Bind(wx.EVT_CHAR, self.OnChar) @@ -870,21 +870,6 @@ class MSWComboTreeBox(NativeComboTreeBox): super(MSWComboTreeBox, self).NotifyNoItemSelected(*args, **kwargs) -class MACComboTreeBox(NativeComboTreeBox): - def _createPopupFrame(self): - return MACPopupFrame(self) - - def _createButton(self): - return self.GetChildren()[0] # The choice button - - def _keyShouldNavigate(self, keyEvent): - return False # No navigation with up and down on wxMac - - def _keyShouldPopUpTree(self, keyEvent): - return super(MACComboTreeBox, self)._keyShouldPopUpTree(keyEvent) or \ - keyEvent.GetKeyCode() == wx.WXK_DOWN - - class GTKComboTreeBox(BaseComboTreeBox, wx.Panel): """ The ComboTreeBox widget for wxGTK. This is actually a work @@ -917,6 +902,26 @@ class GTKComboTreeBox(BaseComboTreeBox, wx.Panel): self.SetSizerAndFit(panelSizer) +# class MACComboTreeBox(NativeComboTreeBox): +# def _createPopupFrame(self): +# return MACPopupFrame(self) +# +# def _createButton(self): +# return self.GetChildren()[0] # The choice button +# +# def _keyShouldNavigate(self, keyEvent): +# return False # No navigation with up and down on wxMac +# +# def _keyShouldPopUpTree(self, keyEvent): +# return super(MACComboTreeBox, self)._keyShouldPopUpTree(keyEvent) or \ +# keyEvent.GetKeyCode() == wx.WXK_DOWN + + +# The MAC implementation based on the NativeComboTreeBox is no longer working, +# so let's use the GTKComboTreeBox instead. +MACComboTreeBox = GTKComboTreeBox + + # --------------------------------------------------------------------------- From 23fa4115ca652e09256be344096dc3ec29b3625f Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Mon, 1 Aug 2016 19:53:37 -0700 Subject: [PATCH 04/33] More tweaks and fixes from testing the demo modules --- demo/ContextHelp.py | 7 ------ demo/Cursor.py | 2 +- demo/DelayedResult.py | 51 ++++++++++++++++++++++++--------------- demo/DialogUnits.py | 17 +++++++------ etg/cshelp.py | 2 +- etg/window.py | 26 ++++++++++++++++++-- etgtools/tweaker_tools.py | 2 +- unittests/test_cshelp.py | 5 +++- 8 files changed, 72 insertions(+), 40 deletions(-) diff --git a/demo/ContextHelp.py b/demo/ContextHelp.py index 1566de80..f13f218c 100644 --- a/demo/ContextHelp.py +++ b/demo/ContextHelp.py @@ -25,7 +25,6 @@ class TestPanel(wx.Panel): # Init the context help button. # And even include help text about the help button :-) cBtn = wx.ContextHelpButton(self) - cBtn.Bind(wx.EVT_BUTTON, self.OnCtxHelpButton) cBtn.SetHelpText("wx.ContextHelpButton") cBtnText = wx.StaticText(self, -1, @@ -72,12 +71,6 @@ class TestPanel(wx.Panel): self.Layout() - def OnCtxHelpButton(self, evt): - # This starts a nested event loop which exits when an item has been - # clicked on, its help message shown and dismissed. - cshelp = wx.ContextHelp(self) - - # On the second text control above, we intercept the help event. This is where # we process it. Anything could happen here. In this case we're just printing # some stuff about it, then passing it on with Skip(), at which point we diff --git a/demo/Cursor.py b/demo/Cursor.py index 812bb8e4..0ad2818c 100644 --- a/demo/Cursor.py +++ b/demo/Cursor.py @@ -94,7 +94,7 @@ class TestPanel(wx.Panel): image.SetOption(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 1) # make the image into a cursor - cursor = wx.CursorFromImage(image) + cursor = wx.Cursor(image) else: # create one of the stock (built-in) cursors diff --git a/demo/DelayedResult.py b/demo/DelayedResult.py index c6db2e28..def625bb 100644 --- a/demo/DelayedResult.py +++ b/demo/DelayedResult.py @@ -25,20 +25,19 @@ class FrameSimpleDelayedBase(wx.Frame): def __init__(self, *args, **kwds): wx.Frame.__init__(self, *args, **kwds) pnl = wx.Panel(self) - self.checkboxUseDelayed = wx.CheckBox(pnl, -1, "Using delayedresult") + self.textUseDelayed = wx.StaticText(pnl, -1, "NOT using delayedresult") self.buttonGet = wx.Button(pnl, -1, "Get") self.buttonAbort = wx.Button(pnl, -1, "Abort") self.slider = wx.Slider(pnl, -1, 0, 0, 10, size=(100,-1), style=wx.SL_HORIZONTAL|wx.SL_AUTOTICKS) self.textCtrlResult = wx.TextCtrl(pnl, -1, "", style=wx.TE_READONLY) - self.checkboxUseDelayed.SetValue(1) - self.checkboxUseDelayed.Enable(False) self.buttonAbort.Enable(False) + vsizer = wx.BoxSizer(wx.VERTICAL) vsizer = wx.BoxSizer(wx.VERTICAL) hsizer = wx.BoxSizer(wx.HORIZONTAL) - vsizer.Add(self.checkboxUseDelayed, 0, wx.ALL, 10) + vsizer.Add(self.textUseDelayed, 0, wx.ALL, 10) hsizer.Add(self.buttonGet, 0, wx.ALL, 5) hsizer.Add(self.buttonAbort, 0, wx.ALL, 5) hsizer.Add(self.slider, 0, wx.ALL, 5) @@ -52,7 +51,6 @@ class FrameSimpleDelayedBase(wx.Frame): - class FrameSimpleDelayed(FrameSimpleDelayedBase): """This demos simplistic use of delayedresult module.""" @@ -61,14 +59,19 @@ class FrameSimpleDelayed(FrameSimpleDelayedBase): self.jobID = 0 self.abortEvent = delayedresult.AbortEvent() self.Bind(wx.EVT_CLOSE, self.handleClose) + self.textUseDelayed.SetLabel("USING delayedresult") + def setLog(self, log): self.log = log + def handleClose(self, event): - """Only needed because in demo, closing the window does not kill the + """ + Only needed because in demo, closing the window does not kill the app, so worker thread continues and sends result to dead frame; normally - your app would exit so this would not happen.""" + your app would exit so this would not happen. + """ if self.buttonAbort.IsEnabled(): self.log( "Exiting: Aborting job %s" % self.jobID ) self.abortEvent.set() @@ -88,9 +91,11 @@ class FrameSimpleDelayed(FrameSimpleDelayedBase): def _resultProducer(self, jobID, abortEvent): - """Pretend to be a complex worker function or something that takes + """ + Pretend to be a complex worker function or something that takes long time to run due to network access etc. GUI will freeze if this - method is not called in separate thread.""" + method is not called in separate thread. + """ import time count = 0 while not abortEvent() and count < 50: @@ -112,7 +117,7 @@ class FrameSimpleDelayed(FrameSimpleDelayedBase): assert jobID == self.jobID try: result = delayedResult.get() - except Exception, exc: + except Exception as exc: self.log( "Result for job %s raised exception: %s" % (jobID, exc) ) return @@ -125,6 +130,7 @@ class FrameSimpleDelayed(FrameSimpleDelayedBase): self.buttonAbort.Enable(False) + class FrameSimpleDirect(FrameSimpleDelayedBase): """This does not use delayedresult so the GUI will freeze while the GET is taking place.""" @@ -132,15 +138,17 @@ class FrameSimpleDirect(FrameSimpleDelayedBase): def __init__(self, *args, **kwargs): self.jobID = 1 FrameSimpleDelayedBase.__init__(self, *args, **kwargs) - self.checkboxUseDelayed.SetValue(False) - + + def setLog(self, log): self.log = log - + + def handleGet(self, event): - """Use delayedresult, this will compute result in separate - thread, and will affect GUI response because a thread is not - used.""" + """ + Not using delayedresult, this will compute result in the same thread, + and will affect GUI response because a thread is not used. + """ self.buttonGet.Enable(False) self.buttonAbort.Enable(True) @@ -148,18 +156,23 @@ class FrameSimpleDirect(FrameSimpleDelayedBase): result = self._resultProducer(self.jobID) self._resultConsumer( result ) + def _resultProducer(self, jobID): - """Pretend to be a complex worker function or something that takes + """ + Pretend to be a complex worker function or something that takes long time to run due to network access etc. GUI will freeze if this - method is not called in separate thread.""" + method is not called in separate thread. + """ import time time.sleep(5) return jobID + def handleAbort(self, event): """can never be called""" pass - + + def _resultConsumer(self, result): # output result self.log( "Got result for job %s: %s" % (self.jobID, result) ) diff --git a/demo/DialogUnits.py b/demo/DialogUnits.py index 1dd7161f..5acda83b 100644 --- a/demo/DialogUnits.py +++ b/demo/DialogUnits.py @@ -32,25 +32,26 @@ class MyFrame(wx.Frame): panel = wx.Panel(self, -1) wx.StaticText(panel, -1, "Size:", - wx.DLG_PNT(panel, (4, 4)), wx.DefaultSize + panel.DLG_UNIT((4, 4)), wx.DefaultSize ) wx.StaticText(panel, -1, "Pos:", - wx.DLG_PNT(panel, (4, 16)), wx.DefaultSize + panel.DLG_UNIT((4, 20)), wx.DefaultSize ) self.sizeCtrl = wx.TextCtrl(panel, -1, "", - wx.DLG_PNT(panel, (24, 4)), - wx.DLG_SZE(panel, (36, -1)), + panel.DLG_UNIT((24, 4)), + panel.DLG_UNIT((36, -1)), wx.TE_READONLY) self.posCtrl = wx.TextCtrl(panel, -1, "", - wx.DLG_PNT(panel, (24, 16)), - wx.DLG_SZE(panel, (36, -1)), + panel.DLG_UNIT((24, 20)), + panel.DLG_UNIT((36, -1)), wx.TE_READONLY) - #print(wx.DLG_PNT(panel, (24, 4)), wx.DLG_SZE(panel, (36, -1))) - #print(wx.DLG_PNT(panel, (24, 16)),wx.DLG_SZE(panel, (36, -1))) + #print(wx.DLG_UNIT(panel, (24, 4)), wx.DLG_UNIT(panel, (36, -1))) + #print(panel.DLG_UNIT((24, 16)), panel.DLG_UNIT((36, -1))) + # This method is called automatically when the CLOSE event is # sent to this window diff --git a/etg/cshelp.py b/etg/cshelp.py index 9debeefc..237d5351 100644 --- a/etg/cshelp.py +++ b/etg/cshelp.py @@ -38,7 +38,7 @@ def run(): c = module.find('wxContextHelpButton') assert isinstance(c, etgtools.ClassDef) tools.fixWindowClass(c) - + c = module.find('wxHelpProvider') c.abstract = True diff --git a/etg/window.py b/etg/window.py index 222c805b..832e930f 100644 --- a/etg/window.py +++ b/etg/window.py @@ -194,8 +194,16 @@ def run(): self.Hide() wx.GetApp().ScheduleForDestruction(self) """) - - + + c.addPyMethod('DLG_UNIT', '(self, dlg_unit)', + doc="""\ + A convenience wrapper for :meth:`ConvertDialogToPixels`. + """, + body="""\ + return self.ConvertDialogToPixels(dlg_unit) + """) + + # MSW only. Do we want them wrapped? c.find('GetAccessible').ignore() c.find('SetAccessible').ignore() @@ -333,6 +341,20 @@ def run(): def __exit__(self, exc_type, exc_val, exc_tb): self._win.Thaw() ''') + + module.addPyCode('''\ + def DLG_UNIT(win, dlg_unit, val2=None): + """ + Convenience function for converting a wx.Point, wx.Size or + (x,y) in dialog units to pixels. + """ + if val2 is not None: + dlg_unit = (dlg_unit, val2) + return win.ConvertDialogToPixels(dlg_unit) + + DLG_PNT = wx.deprecated(DLG_UNIT, "Use DLG_UNIT instead.") + DLG_SZE = wx.deprecated(DLG_UNIT, "Use DLG_UNIT instead.") + ''') # Add a wrapper for wxWindowList and a new iterator class for it that diff --git a/etgtools/tweaker_tools.py b/etgtools/tweaker_tools.py index 9596eadc..1b99145a 100644 --- a/etgtools/tweaker_tools.py +++ b/etgtools/tweaker_tools.py @@ -239,7 +239,7 @@ def fixWindowClass(klass, hideVirtuals=True, ignoreProtected=True): parent.transferThis = True # if there is an id param give it a default id = func.findItem('id') or func.findItem('winid') - if id: + if id and not id.default: id.default = 'wxID_ANY' # if there is a pos or size parameter without a default then give it one. diff --git a/unittests/test_cshelp.py b/unittests/test_cshelp.py index 6eb6910a..bb4e4600 100644 --- a/unittests/test_cshelp.py +++ b/unittests/test_cshelp.py @@ -13,7 +13,10 @@ class cshelp_Tests(wtc.WidgetTestCase): pnl = wx.Panel(self.frame) pnl.SetHelpText("HelpMe!") cBtn = wx.ContextHelpButton(pnl) - + + # Make sure we haven't borked the magic ID + assert cBtn.GetId() == wx.ID_CONTEXT_HELP + def test_cshelp2(self): wx.wxEVT_HELP From 287501a4f8b5825f99bd98743c062f02d8363269 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Tue, 2 Aug 2016 13:06:00 -0700 Subject: [PATCH 05/33] Return True from the drop methods that need it. --- demo/DragAndDrop.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/demo/DragAndDrop.py b/demo/DragAndDrop.py index 2fdb41bd..297a27fe 100644 --- a/demo/DragAndDrop.py +++ b/demo/DragAndDrop.py @@ -134,9 +134,9 @@ class MyFileDropTarget(wx.FileDropTarget): self.window.SetInsertionPointEnd() self.window.WriteText("\n%d file(s) dropped at %d,%d:\n" % (len(filenames), x, y)) - for file in filenames: self.window.WriteText(file + '\n') + return True class MyTextDropTarget(wx.TextDropTarget): @@ -147,6 +147,7 @@ class MyTextDropTarget(wx.TextDropTarget): def OnDropText(self, x, y, text): self.window.WriteText("(%d, %d)\n%s\n" % (x, y, text)) + return True def OnDragOver(self, x, y, d): return wx.DragCopy From 97947ba176c1bf3fa35f8d8a775df18dab0c8dc7 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Tue, 2 Aug 2016 15:08:44 -0700 Subject: [PATCH 06/33] DnD of text or files to wx.TextCtrl doesn't work on OSX (Apple doesn't let us override the build-in behavior I think) so don't use a wx.TextCrtl for the drop target. --- demo/DragAndDrop.py | 38 ++++++++++++++------------------------ ext/wxWidgets | 2 +- unittests/test_dataobj.py | 2 ++ 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/demo/DragAndDrop.py b/demo/DragAndDrop.py index 297a27fe..b2d0be57 100644 --- a/demo/DragAndDrop.py +++ b/demo/DragAndDrop.py @@ -15,8 +15,6 @@ class ClipTextPanel(wx.Panel): wx.Panel.__init__(self, parent, -1) self.log = log - #self.SetFont(wx.Font(10, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False)) - sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add( wx.StaticText( @@ -42,7 +40,6 @@ class ClipTextPanel(wx.Panel): self.Bind(wx.EVT_BUTTON, self.OnPaste, id=ID_PasteBtn) self.Bind(wx.EVT_BUTTON, self.OnCopyBitmap, id=ID_BitmapBtn) - self.SetAutoLayout(True) self.SetSizer(sizer) @@ -71,6 +68,7 @@ class ClipTextPanel(wx.Panel): "Error" ) + def OnCopyBitmap(self, evt): dlg = wx.FileDialog(self, "Choose a bitmap to copy", wildcard="*.bmp") @@ -120,7 +118,7 @@ class OtherDropTarget(wx.DropTarget): def OnData(self, x, y, d): self.log.WriteText("OnData: %d, %d, %d\n" % (x, y, d)) self.GetData() - self.log.WriteText("%s\n" % self.do.GetFilenames()) + self.log.SetLabel("%s\n" % self.do.GetFilenames()) return d @@ -131,11 +129,9 @@ class MyFileDropTarget(wx.FileDropTarget): self.log = log def OnDropFiles(self, x, y, filenames): - self.window.SetInsertionPointEnd() - self.window.WriteText("\n%d file(s) dropped at %d,%d:\n" % - (len(filenames), x, y)) - for file in filenames: - self.window.WriteText(file + '\n') + txt = "\n%d file(s) dropped at %d,%d:\n" % (len(filenames), x, y) + txt += '\n'.join(filenames) + self.window.SetLabel(txt) return True @@ -146,7 +142,7 @@ class MyTextDropTarget(wx.TextDropTarget): self.log = log def OnDropText(self, x, y, text): - self.window.WriteText("(%d, %d)\n%s\n" % (x, y, text)) + self.window.SetLabel("(%d, %d)\n%s\n" % (x, y, text)) return True def OnDragOver(self, x, y, d): @@ -165,38 +161,32 @@ class FileDropPanel(wx.Panel): 0, wx.EXPAND|wx.ALL, 2 ) - self.text = wx.TextCtrl( - self, -1, "", - style = wx.TE_MULTILINE|wx.HSCROLL|wx.TE_READONLY - ) + self.text = wx.StaticText(self, -1, "", style=wx.ST_NO_AUTORESIZE|wx.BORDER_SIMPLE) + self.text.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) dt = MyFileDropTarget(self, log) self.text.SetDropTarget(dt) - sizer.Add(self.text, 1, wx.EXPAND) + sizer.Add(self.text, 1, wx.EXPAND|wx.ALL, 5) sizer.Add( wx.StaticText(self, -1, " \nDrag some text here:"), 0, wx.EXPAND|wx.ALL, 2 ) - self.text2 = wx.TextCtrl( - self, -1, "", - style = wx.TE_MULTILINE|wx.HSCROLL|wx.TE_READONLY - ) + self.text2 = wx.StaticText(self, -1, "", style=wx.ST_NO_AUTORESIZE|wx.BORDER_SIMPLE) + self.text2.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) dt = MyTextDropTarget(self.text2, log) self.text2.SetDropTarget(dt) - sizer.Add(self.text2, 1, wx.EXPAND) + sizer.Add(self.text2, 1, wx.EXPAND|wx.ALL, 5) self.SetAutoLayout(True) self.SetSizer(sizer) - def WriteText(self, text): - self.text.WriteText(text) + def SetLabel(self, text): + self.text.SetLabel(text) - def SetInsertionPointEnd(self): - self.text.SetInsertionPointEnd() #---------------------------------------------------------------------- diff --git a/ext/wxWidgets b/ext/wxWidgets index 24d5ff7d..73fca4c3 160000 --- a/ext/wxWidgets +++ b/ext/wxWidgets @@ -1 +1 @@ -Subproject commit 24d5ff7dc1f6c9c14f001cf4004e342f52a19a3a +Subproject commit 73fca4c37d1ee2e9e495aaa68442cdfcb4243b52 diff --git a/unittests/test_dataobj.py b/unittests/test_dataobj.py index c657c51c..5a1c45d4 100644 --- a/unittests/test_dataobj.py +++ b/unittests/test_dataobj.py @@ -117,6 +117,7 @@ class DataObjTests(wtc.WidgetTestCase): def test_DataObject2(self): + # More-or-less a duplicate of the above, but with a custom data format class MyDataObject(wx.DataObject): def __init__(self, value=''): wx.DataObject.__init__(self) @@ -211,6 +212,7 @@ class DataObjTests(wtc.WidgetTestCase): def test_DataObjectSimple3(self): + # More-or-less a duplicate of the above, but with a custom data format class MyDataObject(wx.DataObjectSimple): def __init__(self, value=''): wx.DataObjectSimple.__init__(self) From a0d5f596f21736c5b1e428aedf2b99ea14fbc526 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Tue, 2 Aug 2016 21:53:21 -0700 Subject: [PATCH 07/33] Hide the DynamicSashWindow sample for now --- TODO.rst | 2 +- demo/demodata.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TODO.rst b/TODO.rst index c4c7b13f..da750f9c 100644 --- a/TODO.rst +++ b/TODO.rst @@ -209,5 +209,5 @@ Other Dev Stuff self-contained instead of some relying on files generated by others. This won't work if we want to run tests in parallel. - + * Port the old DynamicSashWindow from wxCode to a pure-python implementation? diff --git a/demo/demodata.py b/demo/demodata.py index bc6b0788..5853f102 100644 --- a/demo/demodata.py +++ b/demo/demodata.py @@ -172,7 +172,7 @@ _treeList = [ 'ComboCtrl', 'ContextHelp', 'DatePickerCtrl', - 'DynamicSashWindow', + #'DynamicSashWindow', # TODO 'EditableListBox', 'ExpandoTextCtrl', 'FancyText', From 1bdac0af636eb30c69ceca1531465a5017f01de7 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Tue, 2 Aug 2016 21:54:34 -0700 Subject: [PATCH 08/33] wx.lib.editor fixes for Python3 --- wx/lib/editor/__init__.py | 6 +++--- wx/lib/editor/editor.py | 27 +++++++++++++-------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/wx/lib/editor/__init__.py b/wx/lib/editor/__init__.py index 6eea9d6f..f48798ec 100644 --- a/wx/lib/editor/__init__.py +++ b/wx/lib/editor/__init__.py @@ -1,11 +1,11 @@ #---------------------------------------------------------------------- # Name: wx.lib.editor -# Purpose: A package containing a colourizable text editror +# Purpose: A package containing a colourizable text editor # # Author: Robin Dunn # # Created: 30-Dec-1999 -# Copyright: (c) 1999 by Total Control Software +# Copyright: (c) 1999-2016 by Total Control Software # Licence: wxWindows license #---------------------------------------------------------------------- # 12/14/2003 - Jeff Grimmett (grimmtooth@softhome.net) @@ -21,4 +21,4 @@ # import the main classes into the package namespace. -from editor import Editor +from .editor import Editor diff --git a/wx/lib/editor/editor.py b/wx/lib/editor/editor.py index c9c30502..b73901ae 100644 --- a/wx/lib/editor/editor.py +++ b/wx/lib/editor/editor.py @@ -16,7 +16,7 @@ # # # Created: 15-Dec-1999 -# Copyright: (c) 1999 by Dirk Holtwick, 1999 +# Copyright: (c) 1999-2016 by Dirk Holtwick, 1999 # Licence: wxWindows license #---------------------------------------------------------------------- # 12/14/2003 - Jeff Grimmett (grimmtooth@softhome.net) @@ -33,8 +33,8 @@ import time import wx -import selection -import images +from . import selection +from . import images #---------------------------- @@ -133,8 +133,6 @@ class Editor(wx.ScrolledWindow): font = wx.Font(10, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL) else: font = wx.Font(12, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) - if wx.Platform == "__WXMAC__": - font.SetNoAntiAliasing() return font def UnixKeyHack(self, key): @@ -162,18 +160,18 @@ class Editor(wx.ScrolledWindow): self.bw, self.bh = self.GetClientSize() if wx.Platform == "__WXMSW__": - self.sh = self.bh / self.fh - self.sw = (self.bw / self.fw) - 1 + self.sh = int(self.bh / self.fh) + self.sw = int(self.bw / self.fw) - 1 else: - self.sh = self.bh / self.fh + self.sh = int(self.bh / self.fh) if self.LinesInFile() >= self.sh: self.bw = self.bw - wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X) - self.sw = (self.bw / self.fw) - 1 + self.sw = int(self.bw / self.fw) - 1 - self.sw = (self.bw / self.fw) - 1 + self.sw = int(self.bw / self.fw) - 1 if self.CalcMaxLineLen() >= self.sw: self.bh = self.bh - wx.SystemSettings.GetMetric(wx.SYS_HSCROLL_Y) - self.sh = self.bh / self.fh + self.sh = int(self.bh / self.fh) def UpdateView(self, dc = None): @@ -246,6 +244,7 @@ class Editor(wx.ScrolledWindow): dc.SetBackgroundMode(wx.SOLID) dc.SetTextBackground(self.bgColor) dc.SetTextForeground(self.fgColor) + dc.SetBackground(wx.Brush(self.bgColor)) dc.Clear() for line in range(self.sy, self.sy + self.sh): self.DrawLine(line, dc) @@ -291,7 +290,7 @@ class Editor(wx.ScrolledWindow): szy = self.fh x = xp * szx y = yp * szy - dc.Blit(x,y, szx,szy, dc, x,y, wx.SRC_INVERT) + dc.Blit(x,y, szx,szy, dc, x,y, wx.XOR) self.sco_x = xp self.sco_y = yp @@ -448,7 +447,7 @@ class Editor(wx.ScrolledWindow): ##------------------------ mousing functions def MouseToRow(self, mouseY): - row = self.sy + (mouseY/ self.fh) + row = self.sy + int(mouseY / self.fh) if self.AboveScreen(row): self.HandleAboveScreen(row) elif self.BelowScreen(row): @@ -457,7 +456,7 @@ class Editor(wx.ScrolledWindow): self.cy = min(row, self.LinesInFile() - 1) def MouseToCol(self, mouseX): - col = self.sx + (mouseX / self.fw) + col = self.sx + int(mouseX / self.fw) if self.LeftOfScreen(col): self.HandleLeftOfScreen(col) elif self.RightOfScreen(col): From 65959b71d16b8a70016107557c1664ab1649b3bc Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Tue, 2 Aug 2016 21:55:15 -0700 Subject: [PATCH 09/33] more minor demo tweaks --- demo/EditableListBox.py | 2 +- demo/EventManager.py | 10 ++++++---- demo/FileCtrl.py | 12 +++++++----- demo/FileHistory.py | 11 +++++------ 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/demo/EditableListBox.py b/demo/EditableListBox.py index df96d407..0db17e8e 100644 --- a/demo/EditableListBox.py +++ b/demo/EditableListBox.py @@ -13,7 +13,7 @@ class TestPanel(wx.Panel): self.elb = wx.adv.EditableListBox( self, -1, "List of Stuff", (50,50), (250, 250), style=wx.adv.EL_DEFAULT_STYLE | - wx.adv.EL_NO_REORDER | + #wx.adv.EL_NO_REORDER | wx.adv.EL_ALLOW_NEW | wx.adv.EL_ALLOW_EDIT | wx.adv.EL_ALLOW_DELETE) diff --git a/demo/EventManager.py b/demo/EventManager.py index 16c0f5d4..6f0b3c3f 100644 --- a/demo/EventManager.py +++ b/demo/EventManager.py @@ -2,12 +2,13 @@ #--------------------------------------------------------------------------- # Name: EventManager.py -# Purpose: A module to demonstrate wxPython.lib.evtmgr.EventManager. +# Purpose: A module to demonstrate wx.lib.evtmgr.EventManager. # # Author: Robb Shecter (robb@acm.org) # # Created: 16-December-2002 -# Copyright: (c) 2002 by Robb Shecter (robb@acm.org) +# Copyright: (c) 2002-2016 by Robb Shecter (robb@acm.org), +# Total Control Software # Licence: wxWindows license #--------------------------------------------------------------------------- @@ -136,7 +137,7 @@ class InnerTile(wx.Window): FINAL_COLOR = wx.Colour( 20, 80,240) OFF_COLOR = wx.Colour(185,190,185) # Some pre-computation. - DELTAS = map(lambda a,b: b-a, START_COLOR.Get(), FINAL_COLOR.Get()) + DELTAS = list(map(lambda a,b: b-a, START_COLOR.Get(), FINAL_COLOR.Get())) START_COLOR_TUPLE = START_COLOR.Get() """ @@ -185,7 +186,8 @@ class InnerTile(wx.Window): self.makeColorFromTuple(mouseEvent.GetPosition()) - def makeColorFromTuple(self, (x, y)): + def makeColorFromTuple(self, xy): + x, y = xy MAX = 180.0 scaled = min((x + y) * self.factor, MAX) # In range [0..MAX] percent = scaled / MAX diff --git a/demo/FileCtrl.py b/demo/FileCtrl.py index 85e0e0cc..1682361e 100644 --- a/demo/FileCtrl.py +++ b/demo/FileCtrl.py @@ -18,9 +18,7 @@ class FileCtrl(wx.FileCtrl): pos=wx.DefaultPosition, size=wx.DefaultSize, name="filectrl", log=None): wx.FileCtrl.__init__(self, parent, id, defaultDirectory, defaultFilename, wildCard, style, pos, size, name) - - self.BackgroundColour = 'pink' - + self.log = log self.Bind(wx.EVT_FILECTRL_FILEACTIVATED, self.OnFileActivated) self.Bind(wx.EVT_FILECTRL_SELECTIONCHANGED, self.OnSelectionChanged) @@ -49,8 +47,12 @@ class TestPanel(wx.Panel): self.log = log wx.Panel.__init__(self, parent) - fc = FileCtrl(self, pos=(15,15), log=log) - + wx.StaticText(self, -1, + "This is a generic control with features like a file dialog", + pos=(10,10)) + fc = FileCtrl(self, pos=(10,35), log=log) + fc.SetSize((500,350)) + fc.BackgroundColour = 'sky blue' #--------------------------------------------------------------------------- diff --git a/demo/FileHistory.py b/demo/FileHistory.py index 014d4234..eb9428a5 100644 --- a/demo/FileHistory.py +++ b/demo/FileHistory.py @@ -6,12 +6,11 @@ import wx #---------------------------------------------------------------------- text = """\ -Right-click on the panel above the line to get a menu. This menu will -be managed by a FileHistory object and so the files you select will -automatically be added to the end of the menu and will be selectable -the next time the menu is viewed. The filename selected, either via the -Open menu item, or from the history, will be displayed in the log -window below. +Right-click on this panel to get a menu. This menu will be managed by a +FileHistory object and so the files you select will automatically be added to +the end of the menu and will be selectable the next time the menu is viewed. +The filename selected, either via the Open menu item, or from the history, +will be displayed in the log window below. """ #---------------------------------------------------------------------- From ed9f191d3bb1e3095ad125e17e7f82a87076f6e0 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Wed, 3 Aug 2016 13:30:19 -0700 Subject: [PATCH 10/33] GIFAnimationCtrl has been moved to AnimationCtrl --- demo/GIFAnimationCtrl.py | 61 ---------------------------------------- 1 file changed, 61 deletions(-) delete mode 100644 demo/GIFAnimationCtrl.py diff --git a/demo/GIFAnimationCtrl.py b/demo/GIFAnimationCtrl.py deleted file mode 100644 index b80fa87d..00000000 --- a/demo/GIFAnimationCtrl.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python - -import wx -from wx.animate import GIFAnimationCtrl - -from Main import opj - -GIFNames = [ - 'bitmaps/AG00178_.gif', - 'bitmaps/BD13656_.gif', - 'bitmaps/AG00185_.gif', - 'bitmaps/AG00039_.gif', - 'bitmaps/AG00183_.gif', - 'bitmaps/AG00028_.gif', - ] - -#---------------------------------------------------------------------- - -class TestPanel(wx.Panel): - def __init__(self, parent, log): - self.log = log - wx.Panel.__init__(self, parent, -1) - - sizer = wx.FlexGridSizer(cols=3, hgap=5, vgap=5) - for name in GIFNames: - ani = GIFAnimationCtrl(self, -1, opj(name)) - ani.GetPlayer().UseBackgroundColour(True) - ani.Play() - sizer.Add(ani, 0, wx.ALL, 10) - - border = wx.BoxSizer() - border.Add(sizer, 1, wx.EXPAND|wx.ALL, 20) - self.SetSizer(border) - - -#---------------------------------------------------------------------- - -def runTest(frame, nb, log): - win = TestPanel(nb, log) - return win - -#---------------------------------------------------------------------- - - - -overview = """ -

wx.animate.GIFAnimationCtrl

- -wx.animate.GIFAnimationCtrl is like a wx.StaticBitmap but is able to -display an animation by extracing frames from a multi-images GIF file. - - -""" - - - -if __name__ == '__main__': - import sys,os - import run - run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:]) - From 579952f2077e52af00a7fa05a56593c429428e1f Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Wed, 3 Aug 2016 13:31:29 -0700 Subject: [PATCH 11/33] Pop event handlers, and change deprecated MoveXY method calls --- wx/lib/gridmovers.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/wx/lib/gridmovers.py b/wx/lib/gridmovers.py index b4ff6b00..937a584d 100644 --- a/wx/lib/gridmovers.py +++ b/wx/lib/gridmovers.py @@ -121,7 +121,7 @@ class ColDragWindow(wx.Window): if x == pos: self.Refresh() # Need to display insertion point else: - self.MoveXY(pos,y) + self.Move(pos,y) def GetMoveColumn(self): return self.moveColumn @@ -184,7 +184,7 @@ class RowDragWindow(wx.Window): if y == pos: self.Refresh() # Need to display insertion point else: - self.MoveXY(x,pos) + self.Move(x,pos) def GetMoveRow(self): return self.moveRow @@ -248,6 +248,13 @@ class GridColMover(wx.EvtHandler): self.Bind(wx.EVT_MOTION, self.OnMouseMove) self.Bind(wx.EVT_LEFT_DOWN, self.OnPress) self.Bind(wx.EVT_LEFT_UP, self.OnRelease) + self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy) + + + def OnDestroy(self, evt): + assert self.lwin.GetEventHandler() is self + self.lwin.PopEventHandler(True) + def OnMouseMove(self,evt): if not self.isDragging: @@ -373,6 +380,13 @@ class GridRowMover(wx.EvtHandler): self.Bind(wx.EVT_MOTION, self.OnMouseMove) self.Bind(wx.EVT_LEFT_DOWN, self.OnPress) self.Bind(wx.EVT_LEFT_UP, self.OnRelease) + self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy) + + + def OnDestroy(self, evt): + assert self.lwin.GetEventHandler() is self + self.lwin.PopEventHandler(True) + def OnMouseMove(self,evt): if not self.isDragging: From 89795ee0d32c829b5dcb86f0617a8b4ed8536111 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Wed, 3 Aug 2016 13:34:33 -0700 Subject: [PATCH 12/33] Use triple-double quotes for docstrings --- wx/lib/itemspicker.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/wx/lib/itemspicker.py b/wx/lib/itemspicker.py index c5d53a78..e57ec784 100644 --- a/wx/lib/itemspicker.py +++ b/wx/lib/itemspicker.py @@ -10,7 +10,7 @@ # Licence: wxPython license # Tags: phoenix-port #---------------------------------------------------------------------------- -''' +""" Created on Oct 3, 2010 @authors: Daphna Rosenbom,Gitty Zinger,Moshe Cohavi and Yoav Glazner @@ -22,7 +22,8 @@ items_picker.ItemsPicker: - De-Selection is done by the Remove button or Double Click, Derived from wxPanel -''' +""" + import wx __version__ = 0.1 @@ -48,18 +49,18 @@ class IpSelectionChanged(wx.PyCommandEvent): class ItemsPicker(wx.Panel): - ''' + """ ItemsPicker is a widget that allows the user to form a set of picked items out of a given list - ''' + """ def __init__(self, parent, id=wx.ID_ANY, choices = [], label = '', selectedLabel = '', ipStyle = IP_DEFAULT_STYLE, *args, **kw): - ''' + """ ItemsPicker(parent, choices = [], label = '', selectedLabel = '', ipStyle = IP_DEFAULT_STYLE) - ''' + """ wx.Panel.__init__(self, parent, id, *args, **kw) self._ipStyle = ipStyle sizer = wx.BoxSizer(wx.HORIZONTAL) @@ -72,14 +73,14 @@ class ItemsPicker(wx.Panel): def SetItems(self, items): - '''SetItems(self, items)=> None - items - Sequence of strings that the user can pick from''' + """SetItems(self, items)=> None + items - Sequence of strings that the user can pick from""" return self._source.SetItems(items) def GetItems(self): - '''GetItems(self)=> items - returns list of strings that the user can pick from''' + """GetItems(self)=> items + returns list of strings that the user can pick from""" return self._source.GetItems() @@ -89,16 +90,16 @@ class ItemsPicker(wx.Panel): def GetSelections(self): - '''GetSelections(self)=>items + """GetSelections(self)=>items returns list of strings that were selected - ''' + """ return self._dest.GetItems() def SetSelections(self, items): - '''SetSelections(self, items)=>None + """SetSelections(self, items)=>None items - Sequence of strings to be selected - The items are displayed in the selection part of the widget''' + The items are displayed in the selection part of the widget""" assert len(items)==len(set(items)),"duplicate items are not allowed" if items != self._dest.GetItems(): self._dest.SetItems(items) From 000c3cac9e8349e5a5cdeb7d2576253de77100d6 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Wed, 3 Aug 2016 13:35:03 -0700 Subject: [PATCH 13/33] Add uppercase X and Y properties too --- etg/mousestate.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/etg/mousestate.py b/etg/mousestate.py index a5b3a1ec..06cf5817 100644 --- a/etg/mousestate.py +++ b/etg/mousestate.py @@ -36,6 +36,8 @@ def run(): c.addProperty("x GetX SetX") c.addProperty("y GetY SetY") + c.addProperty("X GetX SetX") + c.addProperty("Y GetY SetY") c.addProperty("leftIsDown LeftIsDown SetLeftDown") c.addProperty("middleIsDown MiddleIsDown SetMiddleDown") c.addProperty("rightIsDown RightIsDown SetRightDown") From 0c94741b204800c85ab8f0598b89c817faf3e82f Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Wed, 3 Aug 2016 13:37:04 -0700 Subject: [PATCH 14/33] OnDropFiles needs to return a boolean value --- demo/GridDragAndDrop.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/demo/GridDragAndDrop.py b/demo/GridDragAndDrop.py index d1b9ba68..4ff3582c 100644 --- a/demo/GridDragAndDrop.py +++ b/demo/GridDragAndDrop.py @@ -32,6 +32,8 @@ class GridFileDropTarget(wx.FileDropTarget): self.grid.AutoSizeColumn(col) self.grid.Refresh() + return True + class FooTable(gridlib.GridTableBase): From ef87041ac3ec713cb32bb4d6130ed93e17e56999 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Wed, 3 Aug 2016 13:38:25 -0700 Subject: [PATCH 15/33] The table's GetValue needs to return a string --- demo/GridDragable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/GridDragable.py b/demo/GridDragable.py index 1a80d6dc..4fa2b3c6 100644 --- a/demo/GridDragable.py +++ b/demo/GridDragable.py @@ -63,7 +63,7 @@ class CustomDataTable(gridlib.GridTableBase): def GetValue(self, row, col): id = self.identifiers[col] - return self.data[row][id] + return str(self.data[row][id]) def SetValue(self, row, col, value): id = self.identifiers[col] From a4ecd56a7d808d692a84546b0b7f518643bbb13b Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Wed, 3 Aug 2016 13:39:09 -0700 Subject: [PATCH 16/33] More minor demo tweaks --- demo/Gauge.py | 13 +++++++------ demo/ItemsPicker.py | 17 +++++++++++++---- demo/data/widgetTest.htm | 4 ++-- demo/widgetTest.py | 2 +- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/demo/Gauge.py b/demo/Gauge.py index e749b8d3..34df7677 100644 --- a/demo/Gauge.py +++ b/demo/Gauge.py @@ -12,11 +12,11 @@ class TestPanel(wx.Panel): wx.StaticText(self, -1, "This example shows the wx.Gauge control.", (45, 15)) - self.g1 = wx.Gauge(self, -1, 50, (110, 50), (250, 25)) - self.g2 = wx.Gauge(self, -1, 75, (110, 95), (250, 25)) - self.g3 = wx.Gauge(self, -1, 100, (110, 135), (25, 100), wx.GA_VERTICAL) - # self.g3.SetBezelFace(12) - # self.g3.SetShadowWidth(8) + self.g1 = wx.Gauge(self, -1, 50, (110, 50), (250, -1)) + self.g2 = wx.Gauge(self, -1, 75, (110, 95), (250, -1)) + + if 'wxMac' not in wx.PlaformInfo: + self.g3 = wx.Gauge(self, -1, 100, (110, 135), (-1, 100), wx.GA_VERTICAL) self.Bind(wx.EVT_TIMER, self.TimerHandler) self.timer = wx.Timer(self) @@ -33,7 +33,8 @@ class TestPanel(wx.Panel): self.g1.SetValue(self.count) self.g2.Pulse() - self.g3.Pulse() + if 'wxMac' not in wx.PlaformInfo: + self.g3.Pulse() #---------------------------------------------------------------------- diff --git a/demo/ItemsPicker.py b/demo/ItemsPicker.py index d800417f..2fd4bbd8 100644 --- a/demo/ItemsPicker.py +++ b/demo/ItemsPicker.py @@ -53,21 +53,30 @@ class ItemsPickerDialog(wx.Dialog): 'Stuff:', 'Selected stuff:',ipStyle = style) self.ip.Bind(EVT_IP_SELECTION_CHANGED, self.OnSelectionChange) self.ip._source.SetMinSize((-1,150)) - self.ip.bAdd.SetBitmap(_bp_btn2.GetBitmap(), dir=wx.RIGHT) - self.ip.bAdd.SetLabel('Add') - self.ip.bRemove.SetBitmap(_bp_btn1.GetBitmap(), dir=wx.LEFT) - self.ip.bRemove.SetLabel('Remove') + + # Customize the buttons for this example. + if 'wxMac' not in wx.PlatformInfo: + # NOTE: wx.Button on OSX does not modify the button size when adding a + # bitmap after the fact like this, and these bitmaps are a little too + # large and look funny in OSX, so we won't do this customization there. + self.ip.bAdd.SetBitmap(_bp_btn2.GetBitmap(), dir=wx.RIGHT) + self.ip.bAdd.SetLabel('Add') + self.ip.bRemove.SetBitmap(_bp_btn1.GetBitmap(), dir=wx.LEFT) + self.ip.bRemove.SetLabel('Remove') + sizer.Add(self.ip, 0, wx.ALL, 10) self.SetSizer(sizer) self.itemCount = 3 self.Fit() + def OnAdd(self,e): items = self.ip.GetItems() self.itemCount += 1 newItem = "item%d" % self.itemCount self.ip.SetItems(items + [newItem]) + def OnSelectionChange(self, e): self.log.write("EVT_IP_SELECTION_CHANGED %s\n" % \ ",".join(e.GetItems())) diff --git a/demo/data/widgetTest.htm b/demo/data/widgetTest.htm index ae27d62e..9527ed02 100644 --- a/demo/data/widgetTest.htm +++ b/demo/data/widgetTest.htm @@ -12,7 +12,7 @@

Mixing wxPython and wxHTML

The widgets on this page were created dynamically on the fly by a -custom wxTagHandler found in wxPython.lib.wxpTag. You can look at the +custom wxTagHandler found in wx.lib.wxpTag. You can look at the sources and doc-string in the wxPython library at wx/lib/wxpTag.py.

@@ -45,10 +45,10 @@ parameter value. Source code is here.

+ - diff --git a/demo/widgetTest.py b/demo/widgetTest.py index f81dd4a6..42522988 100644 --- a/demo/widgetTest.py +++ b/demo/widgetTest.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# This file is used for the wx.HtmlWindow demo. +# This file is used for the HtmlWindow demo. import sys From 217a3f19672b76ce5536082ccbce752f0868d18e Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Wed, 3 Aug 2016 16:40:41 -0700 Subject: [PATCH 17/33] Add setters for wxListEvent to fix the ListCtrl_edit sample --- demo/ListCtrl_edit.py | 3 +++ ext/wxWidgets | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/demo/ListCtrl_edit.py b/demo/ListCtrl_edit.py index 2a4c5ff8..ba752de6 100644 --- a/demo/ListCtrl_edit.py +++ b/demo/ListCtrl_edit.py @@ -40,6 +40,7 @@ class TestListCtrl(wx.ListCtrl, self.Populate() listmix.TextEditMixin.__init__(self) + def Populate(self): # for normal, simple columns, you can add them like this: self.InsertColumn(0, "Column 1") @@ -62,6 +63,7 @@ class TestListCtrl(wx.ListCtrl, self.currentItem = 0 + def SetStringItem(self, index, col, data): if col in range(3): wx.ListCtrl.SetItem(self, index, col, data) @@ -100,6 +102,7 @@ class TestListCtrlPanel(wx.Panel): style=wx.LC_REPORT | wx.BORDER_NONE | wx.LC_SORT_ASCENDING + | wx.LC_HRULES | wx.LC_VRULES ) sizer.Add(self.list, 1, wx.EXPAND) diff --git a/ext/wxWidgets b/ext/wxWidgets index 73fca4c3..8d543d6d 160000 --- a/ext/wxWidgets +++ b/ext/wxWidgets @@ -1 +1 @@ -Subproject commit 73fca4c37d1ee2e9e495aaa68442cdfcb4243b52 +Subproject commit 8d543d6d3cf1b7a3df25221b315221d3b23494c5 From 08f2a9af59f33936e28d2bd32a86e1f1583d177d Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Thu, 4 Aug 2016 10:53:24 -0700 Subject: [PATCH 18/33] wxMac doesn't like wx.INVERT anymore --- demo/Mask.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/demo/Mask.py b/demo/Mask.py index 806543fe..5f0c2cdd 100644 --- a/demo/Mask.py +++ b/demo/Mask.py @@ -23,11 +23,10 @@ logicList = [ ('wx.XOR', wx.XOR), ] -if 'mac-cg' in wx.PlatformInfo: +if 'wxMac' in wx.PlatformInfo: # that's all, folks! logicList = [ ('wx.COPY', wx.COPY), - ('wx.INVERT', wx.INVERT), ('wx.XOR', wx.XOR), ] From f271cd7a6f9ecd02022f61f777ca91b044988e98 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Thu, 4 Aug 2016 10:57:09 -0700 Subject: [PATCH 19/33] More minor demo updates for Py3, Phoenix, etc. --- demo/Joystick.py | 2 +- demo/MDIDemo.py | 1 - demo/MDISashDemo.py | 1 - demo/MDIWindows.py | 15 +-------------- 4 files changed, 2 insertions(+), 17 deletions(-) diff --git a/demo/Joystick.py b/demo/Joystick.py index e902a164..e5ff4b5a 100644 --- a/demo/Joystick.py +++ b/demo/Joystick.py @@ -888,7 +888,7 @@ class JoystickDemoPanel(wx.Panel): # Calibrate our controls wx.CallAfter(self.Calibrate) wx.CallAfter(self.OnJoystick) - except NotImplementedError, v: + except NotImplementedError as v: wx.MessageBox(str(v), "Exception Message") self.stick = None diff --git a/demo/MDIDemo.py b/demo/MDIDemo.py index 0179053a..4d148753 100644 --- a/demo/MDIDemo.py +++ b/demo/MDIDemo.py @@ -76,7 +76,6 @@ class MyParentFrame(wx.MDIParentFrame): if __name__ == '__main__': class MyApp(wx.App): def OnInit(self): - wx.InitAllImageHandlers() frame = MyParentFrame() frame.Show(True) self.SetTopWindow(frame) diff --git a/demo/MDISashDemo.py b/demo/MDISashDemo.py index b9a78e5b..e5d03d49 100644 --- a/demo/MDISashDemo.py +++ b/demo/MDISashDemo.py @@ -140,7 +140,6 @@ class MyParentFrame(wx.MDIParentFrame): if __name__ == '__main__': class MyApp(wx.App): def OnInit(self): - wx.InitAllImageHandlers() frame = MyParentFrame() frame.Show(True) self.SetTopWindow(frame) diff --git a/demo/MDIWindows.py b/demo/MDIWindows.py index 1764a3f6..0cd57096 100644 --- a/demo/MDIWindows.py +++ b/demo/MDIWindows.py @@ -36,21 +36,8 @@ class TestPanel(wx.Panel): exe, spawn = self.GetPyExecutable() spawn(os.P_NOWAIT, exe, exe, "MDISashDemo.py") - # TODO: This hack can be removed once we fix the way the Python - # app bundles are generated so that they are not bundling and - # pointing to an otherwise unused and non-GUI-friendly version of - # Python on OS X. def GetPyExecutable(self): - if 'wxMac' in wx.PlatformInfo: - # sys.executable will be wrong if running the demo from - # an app bundle. But the bundle is always using a system - # framework so just hardcode the path to it. - if sys.version[:3] == "2.4": - return '/usr/local/bin/pythonw', os.spawnl - else: - return '/usr/bin/pythonw', os.spawnl - else: - return sys.executable, os.spawnl + return sys.executable, os.spawnl #---------------------------------------------------------------------- From 090931f415c4627a0f2e44004eda45ac07c2e8ac Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Thu, 4 Aug 2016 10:58:22 -0700 Subject: [PATCH 20/33] Updates for the TODO list --- TODO.rst | 6 +++++- demo/demodata.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/TODO.rst b/TODO.rst index da750f9c..06275308 100644 --- a/TODO.rst +++ b/TODO.rst @@ -209,5 +209,9 @@ Other Dev Stuff self-contained instead of some relying on files generated by others. This won't work if we want to run tests in parallel. - * Port the old DynamicSashWindow from wxCode to a pure-python implementation? + * Port these modules from wxCode to a pure-python wx.lib implementation? + * DynamicSashWindow + * LEDNumberCtrl + + * The Masked controls modules and demos need some help with Py3 compatibility. diff --git a/demo/demodata.py b/demo/demodata.py index 5853f102..6f4b091a 100644 --- a/demo/demodata.py +++ b/demo/demodata.py @@ -151,7 +151,7 @@ _treeList = [ 'GenericButtons', 'GenericDirCtrl', 'ItemsPicker', - 'LEDNumberCtrl', + #'LEDNumberCtrl', # TODO 'MultiSash', 'PlateButton', 'PopupControl', From b00620f2865caa8ada6f3fa6a23cf14f014a0a68 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Thu, 4 Aug 2016 12:02:04 -0700 Subject: [PATCH 21/33] Reorder overloads when adding a new one, in case the primary FunctionDef has been ignored. --- etgtools/extractors.py | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/etgtools/extractors.py b/etgtools/extractors.py index 66b795ef..ce5a1b36 100644 --- a/etgtools/extractors.py +++ b/etgtools/extractors.py @@ -54,6 +54,8 @@ class BaseDef(object): def __iter__(self): return iter(self.items) + def __repr__(self): + return "{}: '{}', '{}'".format(self.__class__.__name__, self.name, self.pyName) def extract(self, element): # Pull info from the ElementTree element that is pertinent to this @@ -391,11 +393,19 @@ class FunctionDef(BaseDef, FixWxPrefix): return item - def ignore(self, val=True): - # If the item being ignored has overloads then try to reorder the - # items so the primary item is not an ignored one. + def ignore(self, val=True): + # In addition to ignoring this item, reorder any overloads to ensure + # the primary overload is not ignored, if possible. super(FunctionDef, self).ignore(val) if val and self.overloads: + self.reorderOverloads() + return self + + + def reorderOverloads(self): + # Reorder a set of overloaded functions such that the primary + # FunctionDef is one that is not ignored. + if self.overloads and self.ignored: all = [self] + self.overloads all.sort(key=lambda item: item.ignored) first = all[0] @@ -408,8 +418,7 @@ class FunctionDef(BaseDef, FixWxPrefix): first.overloads = all[1:] idx = parent.items.index(self) parent.items[idx] = first - return self - + def _findItems(self): items = list(self.items) @@ -919,10 +928,13 @@ class ClassDef(BaseDef): def _addMethod(self, md, overloadOkay=True): md.klass = self if overloadOkay and self.findItem(md.name): - self.findItem(md.name).overloads.append(md) + item = self.findItem(md.name) + item.overloads.append(md) + item.reorderOverloads() else: self.items.append(md) - + + def addCppMethod(self, type, name, argsString, body, doc=None, isConst=False, cppSignature=None, overloadOkay=True, **kw): """ @@ -1180,7 +1192,7 @@ class CppMethodDef(MethodDef): so it can be used to write the code for a new wrapper function. TODO: It might be better to just refactor the code in the generator - so it can be shared more easily intstead of using a hack like this... + so it can be shared more easily instead of using a hack like this... """ m = CppMethodDef('', '', '', '') m.__dict__.update(method.__dict__) From 9c5073244f890036d07e2bffc14a7b497d232c17 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Thu, 4 Aug 2016 12:18:33 -0700 Subject: [PATCH 22/33] Fixes for the mimetype wrappers and demo --- demo/MimeTypesManager.py | 14 ++++++-------- etg/mimetype.py | 24 +++++++++++++++++++++--- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/demo/MimeTypesManager.py b/demo/MimeTypesManager.py index ffb48477..e0054cea 100644 --- a/demo/MimeTypesManager.py +++ b/demo/MimeTypesManager.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - #---------------------------------------------------------------------- # Name: wxMimeTypesManager # Purpose: Demonstrate use of wx.MimeTypesManager, wx.FileType @@ -299,21 +297,21 @@ class MimeTypesDemoPanel(wx.Panel): mime = ft.GetMimeType() or "" #------- OPEN command - cmd = ft.GetOpenCommand(filename, mime) + params = wx.FileType.MessageParameters(filename, mime) + cmd = ft.GetOpenCommand(params) self.opencommand.SetValue(convert(cmd)) #------- PRINT command - cmd = ft.GetPrintCommand(filename, mime) + cmd = ft.GetPrintCommand(params) self.printcommand.SetValue(convert(cmd)) #------- All commands - all = ft.GetAllCommands(filename, mime) + verbs, commands = ft.GetAllCommands(params) - if all is None: + if not verbs and not commands: self.allcommands.SetValue("") else: - verbs, commands = all - text = pprint.pformat(map(None, verbs, commands)) + text = pprint.pformat(list(zip(verbs, commands))) self.allcommands.SetValue(text) diff --git a/etg/mimetype.py b/etg/mimetype.py index db3c5af7..4befa712 100644 --- a/etg/mimetype.py +++ b/etg/mimetype.py @@ -115,7 +115,8 @@ def run(): """) - c.find('GetOpenCommand').findOverload('command').ignore() + for m in c.find('GetOpenCommand').all(): + m.ignore() c.addCppMethod('wxString', 'GetOpenCommand', '(const wxFileType::MessageParameters& params)', doc="""\ Returns the command which must be executed (see wx.Execute()) in order @@ -126,6 +127,16 @@ def run(): self->GetOpenCommand(&rv, *params); return new wxString(rv); """) + c.addCppMethod('wxString', 'GetOpenCommand', '(const wxString& filename)', + doc="""\ + Returns the command which should be used to open the given + filename. An empty string is returned to indicate that an error + occurred (typically meaning that there is no standard way to open + this kind of files).""", + body="""\ + return new wxString( self->GetOpenCommand(*filename) ); + """) + c.find('GetPrintCommand').ignore() c.addCppMethod('wxString', 'GetPrintCommand', '(const wxFileType::MessageParameters& params)', @@ -139,8 +150,15 @@ def run(): return new wxString(rv); """) - c.find('GetAllCommands.verbs').out = True - c.find('GetAllCommands.commands').out = True + m = c.find('GetAllCommands') + m.find('verbs').out = True + m.find('commands').out = True + m.type = 'void' + m.briefDoc = \ + "Returns a tuple containing the `verbs` and `commands` arrays, " \ + "corresponding for the registered information for this mime type." + + c.addCppMethod('PyObject*', 'GetIconInfo', '()', doc="""\ From 33e3e8d32c8499ae37273806b373f5535a2f4fc2 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Thu, 4 Aug 2016 15:11:53 -0700 Subject: [PATCH 23/33] Get rid of mvctree --- demo/MVCTree.py | 79 ---- demo/demodata.py | 1 - wx/lib/mvctree.py | 1148 --------------------------------------------- 3 files changed, 1228 deletions(-) delete mode 100644 demo/MVCTree.py delete mode 100644 wx/lib/mvctree.py diff --git a/demo/MVCTree.py b/demo/MVCTree.py deleted file mode 100644 index 0642a257..00000000 --- a/demo/MVCTree.py +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env python - -import os -import sys - -import wx -import wx.lib.mvctree as tree - -logger = None -def selchanging(evt): - logger.write("SelChanging!\n") - -def selchanged(evt): - logger.write("SelChange!\n") - logger.write(str(evt.node)) -def expanded(evt): - logger.write("Expanded\n") -def closed(evt): - logger.write("Closed!\n") -def key(evt): - logger.write("Key\n") -def add(evt): - logger.write("Add\n") -def delitem(evt): - logger.write("Delete\n") - -def runTest(frame, nb, log): - #f = wx.Frame(frame, -1, "MVCTree", (0,0), (200,500)) - global logger - logger = log - p = tree.MVCTree(nb, -1) - #f = wx.Frame(frame, -1, "MVCTree") - #p = tree.MVCTree(f, -1) - p.SetAssumeChildren(True) - p.SetModel(tree.LateFSTreeModel(os.path.normpath(os.getcwd() + os.sep +'..'))) - - #Uncomment this to enable live filename editing! -# p.AddEditor(FileEditor(p)) - - p.SetMultiSelect(True) - p.Bind(tree.EVT_MVCTREE_SEL_CHANGING, selchanging) - p.Bind(tree.EVT_MVCTREE_SEL_CHANGED, selchanged) - p.Bind(tree.EVT_MVCTREE_ITEM_EXPANDED, expanded) - p.Bind(tree.EVT_MVCTREE_ITEM_COLLAPSED, closed) - p.Bind(tree.EVT_MVCTREE_ADD_ITEM, add) - p.Bind(tree.EVT_MVCTREE_DELETE_ITEM, delitem) - p.Bind(tree.EVT_MVCTREE_KEY_DOWN, key) - - return p - #frame.otherWin = f - #f.Show(True) - #return None - - -overview = """\ - -MVCTree is a control which handles hierarchical data. It is -constructed in model-view-controller architecture, so the display of -that data, and the content of the data can be changed greatly without -affecting the other parts. - -Multiple selections are possible by holding down the Ctrl key. - -This demo shows the wxPython directory structure. The interesting part -is that the tree model is late-bound to the filesystem, so the -filenames are not retrieved until the directory is expanded. In -mvctree.py are models for generic data, and both the early and -late-bound filesystem models. - -There is also support for editing, though it's not enabled in this -demo, to avoid accidentally renaming files! - -""" - - -if __name__ == '__main__': - import sys,os - import run - run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:]) diff --git a/demo/demodata.py b/demo/demodata.py index 6f4b091a..80d6d03d 100644 --- a/demo/demodata.py +++ b/demo/demodata.py @@ -183,7 +183,6 @@ _treeList = [ 'HTML2_WebView', 'InfoBar', 'IntCtrl', - 'MVCTree', 'MaskedEditControls', 'MaskedNumCtrl', 'MediaCtrl', diff --git a/wx/lib/mvctree.py b/wx/lib/mvctree.py deleted file mode 100644 index 8fd034e2..00000000 --- a/wx/lib/mvctree.py +++ /dev/null @@ -1,1148 +0,0 @@ -# 12/09/2003 - Jeff Grimmett (grimmtooth@softhome.net) -# -# o 2.5 compatability update. -# o I'm a little nervous about some of it though. -# -# 12/20/2003 - Jeff Grimmett (grimmtooth@softhome.net) -# -# o wxTreeModel -> TreeModel -# o wxMVCTree -> MVCTree -# o wxMVCTreeEvent -> MVCTreeEvent -# o wxMVCTreeNotifyEvent -> MVCTreeNotifyEvent -# - -""" -MVCTree is a control which handles hierarchical data. It is constructed -in model-view-controller architecture, so the display of that data, and -the content of the data can be changed greatly without affecting the other parts. - -MVCTree actually is even more configurable than MVC normally implies, because -almost every aspect of it is pluggable: - -* MVCTree - Overall controller, and the window that actually gets placed in the GUI. - - * Painter - Paints the control. The 'view' part of MVC. - - * NodePainter - Paints just the nodes - * LinePainter - Paints just the lines between the nodes - * TextConverter - Figures out what text to print for each node - - * Editor - Edits the contents of a node, if the model is editable. - * LayoutEngine - Determines initial placement of nodes - * Transform - Adjusts positions of nodes for movement or special effects. - * TreeModel - Contains the data which the rest of the control acts on. The 'model' part of MVC. - - -Author/Maintainer - Bryn Keller - - -.. note:: - - This module is *not* supported in any way. Use it however you - wish, but be warned that dealing with any consequences is - entirly up to you. - --Robin -""" - -#------------------------------------------------------------------------ -import os -import sys -import traceback -import warnings - -import wx -#------------------------------------------------------------------------ - -warningmsg = r"""\ - -################################################\ -# This module is not supported in any way! | -# | -# See cource code for wx.lib.mvctree for more | -# information. | -################################################/ - -""" - -warnings.warn(warningmsg, DeprecationWarning, stacklevel=2) -#------------------------------------------------------------------------ - -class MVCTreeNode: - """ - Used internally by MVCTree to manage its data. Contains information about - screen placement, the actual data associated with it, and more. These are - the nodes passed to all the other helper parts to do their work with. - """ - def __init__(self, data=None, parent = None, kids = None, x = 0, y = 0): - self.x = 0 - self.y = 0 - self.projx = 0 - self.projy = 0 - self.parent = parent - self.kids = kids - if self.kids is None: - self.kids = [] - self.data = data - self.expanded = False - self.selected = False - self.built = False - self.scale = 0 - - def GetChildren(self): - return self.kids - - def GetParent(self): - return self.parent - - def Remove(self, node): - try: - self.kids.remove(node) - except: - pass - def Add(self, node): - self.kids.append(node) - node.SetParent(self) - - def SetParent(self, parent): - if self.parent and not (self.parent is parent): - self.parent.Remove(self) - self.parent = parent - def __str__(self): - return "Node: " + str(self.data) + " (" + str(self.x) + ", " + str(self.y) + ")" - def __repr__(self): - return str(self.data) - def GetTreeString(self, tabs=0): - s = tabs * '\t' + str(self) + '\n' - for kid in self.kids: - s = s + kid.GetTreeString(tabs + 1) - return s - - -class Editor: - def __init__(self, tree): - self.tree = tree - def Edit(self, node): - raise NotImplementedError - def EndEdit(self, node, commit): - raise NotImplementedError - def CanEdit(self, node): - raise NotImplementedError - -class LayoutEngine: - """ - Interface for layout engines. - """ - def __init__(self, tree): - self.tree = tree - def Layout(self, node): - raise NotImplementedError - def GetNodeList(self): - raise NotImplementedError - -class Transform: - """ - Transform interface. - """ - def __init__(self, tree): - self.tree = tree - def Transform(self, node, offset, rotation): - """ - This method should only change the projx and projy attributes of - the node. These represent the position of the node as it should - be drawn on screen. Adjusting the x and y attributes can and - should cause havoc. - """ - raise NotImplementedError - - def GetSize(self): - """ - Returns the size of the entire tree as laid out and transformed - as a tuple - """ - raise NotImplementedError - -class Painter: - """ - This is the interface that MVCTree expects from painters. All painters should - be Painter subclasses. - """ - def __init__(self, tree): - self.tree = tree - self.textcolor = wx.BLACK - self.bgcolor = wx.WHITE - self.fgcolor = wx.BLUE - self.linecolor = wx.Colour("GREY") - self.font = wx.Font(9, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) - self.bmp = None - - def GetFont(self): - return self.font - - def SetFont(self, font): - self.font = font - self.tree.Refresh() - def GetBuffer(self): - return self.bmp - def ClearBuffer(self): - self.bmp = None - def Paint(self, dc, node, doubleBuffered=1, paintBackground=1): - raise NotImplementedError - def GetTextColour(self): - return self.textcolor - def SetTextColour(self, color): - self.textcolor = color - self.textbrush = wx.Brush(color) - self.textpen = wx.Pen(color, 1, wx.PENSTYLE_SOLID) - def GetBackgroundColour(self): - return self.bgcolor - def SetBackgroundColour(self, color): - self.bgcolor = color - self.bgbrush = wx.Brush(color) - self.bgpen = wx.Pen(color, 1, wx.PENSTYLE_SOLID) - def GetForegroundColour(self): - return self.fgcolor - def SetForegroundColour(self, color): - self.fgcolor = color - self.fgbrush = wx.Brush(color) - self.fgpen = wx.Pen(color, 1, wx.PENSTYLE_SOLID) - def GetLineColour(self): - return self.linecolor - def SetLineColour(self, color): - self.linecolor = color - self.linebrush = wx.Brush(color) - self.linepen = wx.Pen( color, 1, wx.PENSTYLE_SOLID) - def GetForegroundPen(self): - return self.fgpen - def GetBackgroundPen(self): - return self.bgpen - def GetTextPen(self): - return self.textpen - def GetForegroundBrush(self): - return self.fgbrush - def GetBackgroundBrush(self): - return self.bgbrush - def GetTextBrush(self): - return self.textbrush - def GetLinePen(self): - return self.linepen - def GetLineBrush(self): - return self.linebrush - def OnMouse(self, evt): - if evt.LeftDClick(): - x, y = self.tree.CalcUnscrolledPosition(evt.GetX(), evt.GetY()) - for item in self.rectangles: - if item[1].Contains((x,y)): - self.tree.Edit(item[0].data) - self.tree.OnNodeClick(item[0], evt) - return - elif evt.ButtonDown(): - x, y = self.tree.CalcUnscrolledPosition(evt.GetX(), evt.GetY()) - for item in self.rectangles: - if item[1].Contains((x, y)): - self.tree.OnNodeClick(item[0], evt) - return - for item in self.knobs: - if item[1].Contains((x, y)): - self.tree.OnKnobClick(item[0]) - return - evt.Skip() - - -class TreeModel: - """ - Interface for tree models - """ - def GetRoot(self): - raise NotImplementedError - def SetRoot(self, root): - raise NotImplementedError - def GetChildCount(self, node): - raise NotImplementedError - def GetChildAt(self, node, index): - raise NotImplementedError - def GetParent(self, node): - raise NotImplementedError - def AddChild(self, parent, child): - if hasattr(self, 'tree') and self.tree: - self.tree.NodeAdded(parent, child) - def RemoveNode(self, child): - if hasattr(self, 'tree') and self.tree: - self.tree.NodeRemoved(child) - def InsertChild(self, parent, child, index): - if hasattr(self, 'tree') and self.tree: - self.tree.NodeInserted(parent, child, index) - def IsLeaf(self, node): - raise NotImplementedError - - def IsEditable(self, node): - return False - - def SetEditable(self, node): - return False - -class NodePainter: - """ - This is the interface expected of a nodepainter. - """ - def __init__(self, painter): - self.painter = painter - def Paint(self, node, dc, location = None): - """ - location should be provided only to draw in an unusual position - (not the node's normal position), otherwise the node's projected x and y - coordinates will be used. - """ - raise NotImplementedError - -class LinePainter: - """ - The linepainter interface. - """ - def __init__(self, painter): - self.painter = painter - def Paint(self, parent, child, dc): - raise NotImplementedError - -class TextConverter: - """ - TextConverter interface. - """ - def __init__(self, painter): - self.painter = painter - def Convert(node): - """ - Should return a string. The node argument will be an - MVCTreeNode. - """ - raise NotImplementedError - - -class BasicTreeModel(TreeModel): - """ - A very simple treemodel implementation, but flexible enough for many needs. - """ - def __init__(self): - self.children = {} - self.parents = {} - self.root = None - def GetRoot(self): - return self.root - def SetRoot(self, root): - self.root = root - def GetChildCount(self, node): - if node in self.children: - return len(self.children[node]) - else: - return 0 - def GetChildAt(self, node, index): - return self.children[node][index] - - def GetParent(self, node): - return self.parents[node] - - def AddChild(self, parent, child): - self.parents[child]=parent - if not parent in self.children: - self.children[parent]=[] - self.children[parent].append(child) - TreeModel.AddChild(self, parent, child) - return child - - def RemoveNode(self, node): - parent = self.parents[node] - del self.parents[node] - self.children[parent].remove(node) - TreeModel.RemoveNode(self, node) - - def InsertChild(self, parent, child, index): - self.parents[child]=parent - if not parent in self.children: - self.children[parent]=[] - self.children[parent].insert(child, index) - TreeModel.InsertChild(self, parent, child, index) - return child - - def IsLeaf(self, node): - return not node in self.children - - def IsEditable(self, node): - return False - - def SetEditable(self, node, bool): - return False - - -class FileEditor(Editor): - def Edit(self, node): - treenode = self.tree.nodemap[node] - self.editcomp = wxTextCtrl(self.tree, -1) - for rect in self.tree.painter.rectangles: - if rect[0] == treenode: - self.editcomp.SetPosition((rect[1][0], rect[1][1])) - break - self.editcomp.SetValue(node.fileName) - self.editcomp.SetSelection(0, len(node.fileName)) - self.editcomp.SetFocus() - self.treenode = treenode -# self.editcomp.Bind(wx.EVT_KEY_DOWN, self._key) - self.editcomp.Bind(wx.EVT_KEY_UP, self._key) - self.editcomp.Bind(wx.EVT_LEFT_DOWN, self._mdown) - self.editcomp.CaptureMouse() - - def CanEdit(self, node): - return isinstance(node, FileWrapper) - - def EndEdit(self, commit): - if not self.tree._EditEnding(self.treenode.data): - return - if commit: - node = self.treenode.data - try: - os.rename(node.path + os.sep + node.fileName, node.path + os.sep + self.editcomp.GetValue()) - node.fileName = self.editcomp.GetValue() - except: - traceback.print_exc() - self.editcomp.ReleaseMouse() - self.editcomp.Destroy() - del self.editcomp - self.tree.Refresh() - - - def _key(self, evt): - if evt.GetKeyCode() == wx.WXK_RETURN: - self.EndEdit(True) - elif evt.GetKeyCode() == wx.WXK_ESCAPE: - self.EndEdit(False) - else: - evt.Skip() - - def _mdown(self, evt): - if evt.IsButton(): - x, y = evt.GetPosition() - w, h = self.editcomp.GetSize() - if x < 0 or y < 0 or x > w or y > h: - self.EndEdit(False) - - -class FileWrapper: - """ - Node class for FSTreeModel. - """ - def __init__(self, path, fileName): - self.path = path - self.fileName = fileName - - def __str__(self): - return self.fileName - -class FSTreeModel(BasicTreeModel): - """ - This treemodel models the filesystem starting from a given path. - """ - def __init__(self, path): - BasicTreeModel.__init__(self) - fw = FileWrapper(path, path.split(os.sep)[-1]) - self._Build(path, fw) - self.SetRoot(fw) - self._editable = True - def _Build(self, path, fileWrapper): - for name in os.listdir(path): - fw = FileWrapper(path, name) - self.AddChild(fileWrapper, fw) - childName = path + os.sep + name - if os.path.isdir(childName): - self._Build(childName, fw) - - def IsEditable(self, node): - return self._editable - - def SetEditable(self, node, bool): - self._editable = bool - -class LateFSTreeModel(FSTreeModel): - """ - This treemodel models the filesystem starting from a given path. - It retrieves the directory list as requested. - """ - def __init__(self, path): - BasicTreeModel.__init__(self) - name = path.split(os.sep)[-1] - pathpart = path[:-len(name)] - fw = FileWrapper(pathpart, name) - self._Build(path, fw) - self.SetRoot(fw) - self._editable = True - self.children = {} - self.parents = {} - def _Build(self, path, parent): - ppath = parent.path + os.sep + parent.fileName - if not os.path.isdir(ppath): - return - for name in os.listdir(ppath): - fw = FileWrapper(ppath, name) - self.AddChild(parent, fw) - def GetChildCount(self, node): - if node in self.children: - return FSTreeModel.GetChildCount(self, node) - else: - self._Build(node.path, node) - return FSTreeModel.GetChildCount(self, node) - - def IsLeaf(self, node): - return not os.path.isdir(node.path + os.sep + node.fileName) - -class StrTextConverter(TextConverter): - def Convert(self, node): - return str(node.data) - -class NullTransform(Transform): - def GetSize(self): - return tuple(self.size) - - def Transform(self, node, offset, rotation): - self.size = [0,0] - list = self.tree.GetLayoutEngine().GetNodeList() - for node in list: - node.projx = node.x + offset[0] - node.projy = node.y + offset[1] - if node.projx > self.size[0]: - self.size[0] = node.projx - if node.projy > self.size[1]: - self.size[1] = node.projy - -class Rect(object): - def __init__(self, x, y, width, height): - self.x = x - self.y = y - self.width = width - self.height = height - def __getitem__(self, index): - return (self.x, self.y, self.width, self.height)[index] - - def __setitem__(self, index, value): - name = ['x', 'y', 'width', 'height'][index] - setattr(self, name, value) - - def Contains(self, other): - if type(other) == type(()): - other = Rect(other[0], other[1], 0, 0) - if other.x >= self.x: - if other.y >= self.y: - if other.width + other.x <= self.width + self.x: - if other.height + other.y <= self.height + self.y: - return True - return False - - def __str__(self): - return "Rect: " + str([self.x, self.y, self.width, self.height]) - -class TreeLayout(LayoutEngine): - def SetHeight(self, num): - self.NODE_HEIGHT = num - - def __init__(self, tree): - LayoutEngine.__init__(self, tree) - self.NODE_STEP = 20 - self.NODE_HEIGHT = 20 - self.nodelist = [] - - def Layout(self, node): - self.nodelist = [] - self.NODE_HEIGHT = self.tree.GetFont().GetPointSize() * 2 - self.layoutwalk(node) - - def GetNodeList(self): - return self.nodelist - - def layoutwalk(self, node): - if node == self.tree.currentRoot: - node.level = 1 - self.lastY = (-self.NODE_HEIGHT) - node.x = self.NODE_STEP * node.level - node.y = self.lastY + self.NODE_HEIGHT - self.lastY = node.y - self.nodelist.append(node) - if node.expanded: - for kid in node.kids: - kid.level = node.level + 1 - self.layoutwalk(kid) - -class TreePainter(Painter): - """ - The default painter class. Uses double-buffering, delegates the painting of nodes and - lines to helper classes deriving from NodePainter and LinePainter. - """ - def __init__(self, tree, nodePainter = None, linePainter = None, textConverter = None): - Painter.__init__(self, tree) - if not nodePainter: - nodePainter = TreeNodePainter(self) - self.nodePainter = nodePainter - if not linePainter: - linePainter = TreeLinePainter(self) - self.linePainter = linePainter - if not textConverter: - textConverter = StrTextConverter(self) - self.textConverter = textConverter - self.charWidths = [] - - def Paint(self, dc, node, doubleBuffered=1, paintBackground=1): - if not self.charWidths: - self.charWidths = [] - for i in range(25): - self.charWidths.append(dc.GetTextExtent("D")[0] * i) - self.charHeight = dc.GetTextExtent("D")[1] - self.textpen = wx.Pen(self.GetTextColour(), 1, wx.PENSTYLE_SOLID) - self.fgpen = wx.Pen(self.GetForegroundColour(), 1, wx.PENSTYLE_SOLID) - self.bgpen = wx.Pen(self.GetBackgroundColour(), 1, wx.PENSTYLE_SOLID) - self.linepen = wx.Pen(self.GetLineColour(), 1, wx.PENSTYLE_SOLID) - self.dashpen = wx.Pen(self.GetLineColour(), 1, wx.PENSTYLE_DOT) - self.textbrush = wx.Brush(self.GetTextColour(), wx.BRUSHSTYLE_SOLID) - self.fgbrush = wx.Brush(self.GetForegroundColour(), wx.BRUSHSTYLE_SOLID) - self.bgbrush = wx.Brush(self.GetBackgroundColour(), wx.BRUSHSTYLE_SOLID) - self.linebrush = wx.Pen(self.GetLineColour(), 1, wx.PENSTYLE_SOLID) - treesize = self.tree.GetSize() - size = self.tree.transform.GetSize() - size = (max(treesize.width, size[0]+50), max(treesize.height, size[1]+50)) - if doubleBuffered: - mem_dc = wx.MemoryDC() - if not self.GetBuffer(): - self.knobs = [] - self.rectangles = [] - self.bmp = wx.Bitmap(size[0], size[1]) - mem_dc.SelectObject(self.GetBuffer()) - mem_dc.SetPen(self.GetBackgroundPen()) - mem_dc.SetBrush(self.GetBackgroundBrush()) - mem_dc.DrawRectangle(0, 0, size[0], size[1]) - mem_dc.SetFont(self.tree.GetFont()) - self.paintWalk(node, mem_dc) - else: - mem_dc.SelectObject(self.GetBuffer()) - xstart, ystart = self.tree.CalcUnscrolledPosition(0,0) - size = self.tree.GetClientSize() - dc.Blit(xstart, ystart, size[0], size[1], mem_dc, xstart, ystart) - else: - if node == self.tree.currentRoot: - self.knobs = [] - self.rectangles = [] - dc.SetPen(self.GetBackgroundPen()) - dc.SetBrush(self.GetBackgroundBrush()) - dc.SetFont(self.tree.GetFont()) - if paintBackground: - dc.DrawRectangle(0, 0, size[0], size[1]) - if node: - #Call with not paintBackground because if we are told not to paint the - #whole background, we have to paint in parts to undo selection coloring. - pb = paintBackground - self.paintWalk(node, dc, not pb) - - def GetDashPen(self): - return self.dashpen - - def SetLinePen(self, pen): - Painter.SetLinePen(self, pen) - self.dashpen = wx.Pen(pen.GetColour(), 1, wx.PENSTYLE_DOT) - - def paintWalk(self, node, dc, paintRects=0): - self.linePainter.Paint(node.parent, node, dc) - self.nodePainter.Paint(node, dc, drawRects = paintRects) - if node.expanded: - for kid in node.kids: - if not self.paintWalk(kid, dc, paintRects): - return False - for kid in node.kids: - px = (kid.projx - self.tree.layout.NODE_STEP) + 5 - py = kid.projy + kid.height/2 - if (not self.tree.model.IsLeaf(kid.data)) or ((kid.expanded or self.tree._assumeChildren) and len(kid.kids)): - dc.SetPen(self.linepen) - dc.SetBrush(self.bgbrush) - dc.DrawRectangle(px -4, py-4, 9, 9) - self.knobs.append( (kid, Rect(px -4, py -4, 9, 9)) ) - dc.SetPen(self.textpen) - if not kid.expanded: - dc.DrawLine(px, py -2, px, py + 3) - dc.DrawLine(px -2, py, px + 3, py) - if node == self.tree.currentRoot: - px = (node.projx - self.tree.layout.NODE_STEP) + 5 - py = node.projy + node.height/2 - dc.SetPen(self.linepen) - dc.SetBrush(self.bgbrush) - dc.DrawRectangle(px -4, py-4, 9, 9) - self.knobs.append( (node, Rect(px -4, py -4, 9, 9)) ) - dc.SetPen(self.textpen) - if not node.expanded: - dc.DrawLine(px, py -2, px, py + 3) - dc.DrawLine(px -2, py, px + 3, py) - return True - - def OnMouse(self, evt): - Painter.OnMouse(self, evt) - -class TreeNodePainter(NodePainter): - def Paint(self, node, dc, location = None, drawRects = 0): - text = self.painter.textConverter.Convert(node) - extent = dc.GetTextExtent(text) - node.width = extent[0] - node.height = extent[1] - if node.selected: - dc.SetPen(self.painter.GetLinePen()) - dc.SetBrush(self.painter.GetForegroundBrush()) - dc.SetTextForeground(wx.WHITE) - dc.DrawRectangle(node.projx -1, node.projy -1, node.width + 3, node.height + 3) - else: - if drawRects: - dc.SetBrush(self.painter.GetBackgroundBrush()) - dc.SetPen(self.painter.GetBackgroundPen()) - dc.DrawRectangle(node.projx -1, node.projy -1, node.width + 3, node.height + 3) - dc.SetTextForeground(self.painter.GetTextColour()) - dc.DrawText(text, node.projx, node.projy) - self.painter.rectangles.append((node, Rect(node.projx, node.projy, node.width, node.height))) - -class TreeLinePainter(LinePainter): - def Paint(self, parent, child, dc): - dc.SetPen(self.painter.GetDashPen()) - px = py = cx = cy = 0 - if parent is None or child == self.painter.tree.currentRoot: - px = (child.projx - self.painter.tree.layout.NODE_STEP) + 5 - py = child.projy + self.painter.tree.layout.NODE_HEIGHT/2 -2 - cx = child.projx - cy = py - dc.DrawLine(px, py, cx, cy) - else: - px = parent.projx + 5 - py = parent.projy + parent.height - cx = child.projx -5 - cy = child.projy + self.painter.tree.layout.NODE_HEIGHT/2 -3 - dc.DrawLine(px, py, px, cy) - dc.DrawLine(px, cy, cx, cy) - -#>> Event defs -wxEVT_MVCTREE_BEGIN_EDIT = wx.NewEventType() #Start editing. Vetoable. -wxEVT_MVCTREE_END_EDIT = wx.NewEventType() #Stop editing. Vetoable. -wxEVT_MVCTREE_DELETE_ITEM = wx.NewEventType() #Item removed from model. -wxEVT_MVCTREE_ITEM_EXPANDED = wx.NewEventType() -wxEVT_MVCTREE_ITEM_EXPANDING = wx.NewEventType() -wxEVT_MVCTREE_ITEM_COLLAPSED = wx.NewEventType() -wxEVT_MVCTREE_ITEM_COLLAPSING = wx.NewEventType() -wxEVT_MVCTREE_SEL_CHANGED = wx.NewEventType() -wxEVT_MVCTREE_SEL_CHANGING = wx.NewEventType() #Vetoable. -wxEVT_MVCTREE_KEY_DOWN = wx.NewEventType() -wxEVT_MVCTREE_ADD_ITEM = wx.NewEventType() #Item added to model. - -EVT_MVCTREE_SEL_CHANGED = wx.PyEventBinder(wxEVT_MVCTREE_SEL_CHANGED, 1) -EVT_MVCTREE_SEL_CHANGING = wx.PyEventBinder(wxEVT_MVCTREE_SEL_CHANGING, 1) -EVT_MVCTREE_ITEM_EXPANDED = wx.PyEventBinder(wxEVT_MVCTREE_ITEM_EXPANDED, 1) -EVT_MVCTREE_ITEM_EXPANDING = wx.PyEventBinder(wxEVT_MVCTREE_ITEM_EXPANDING, 1) -EVT_MVCTREE_ITEM_COLLAPSED = wx.PyEventBinder(wxEVT_MVCTREE_ITEM_COLLAPSED, 1) -EVT_MVCTREE_ITEM_COLLAPSING = wx.PyEventBinder(wxEVT_MVCTREE_ITEM_COLLAPSING, 1) -EVT_MVCTREE_ADD_ITEM = wx.PyEventBinder(wxEVT_MVCTREE_ADD_ITEM, 1) -EVT_MVCTREE_DELETE_ITEM = wx.PyEventBinder(wxEVT_MVCTREE_DELETE_ITEM, 1) -EVT_MVCTREE_KEY_DOWN = wx.PyEventBinder(wxEVT_MVCTREE_KEY_DOWN, 1) - -class MVCTreeEvent(wx.PyCommandEvent): - def __init__(self, type, id, node = None, nodes = None, keyEvent = None, **kwargs): - wx.PyCommandEvent.__init__(self, type, id, **kwargs) - self.node = node - self.nodes = nodes - self.keyEvent = keyEvent - def GetNode(self): - return self.node - def GetNodes(self): - return self.nodes - def getKeyEvent(self): - return self.keyEvent - -class MVCTreeNotifyEvent(MVCTreeEvent): - def __init__(self, type, id, node = None, nodes = None, **kwargs): - MVCTreeEvent.__init__(self, type, id, node, nodes, **kwargs) - self.notify = wx.NotifyEvent(type, id) - def getNotifyEvent(self): - return self.notify - -class MVCTree(wx.ScrolledWindow): - """ - The main mvc tree class. - """ - def __init__(self, parent, id, model = None, layout = None, transform = None, - painter = None, *args, **kwargs): - wx.ScrolledWindow.__init__(self, parent, id, **kwargs) - self.nodemap = {} - self._multiselect = False - self._selections = [] - self._assumeChildren = False - self._scrollx = False - self._scrolly = False - self.doubleBuffered = False - self._lastPhysicalSize = self.GetSize() - self._editors = [] - if not model: - model = BasicTreeModel() - model.SetRoot("Root") - self.SetModel(model) - if not layout: - layout = TreeLayout(self) - self.layout = layout - if not transform: - transform = NullTransform(self) - self.transform = transform - if not painter: - painter = TreePainter(self) - self.painter = painter - self.SetFont(wx.Font(9, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)) - self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse) - self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) - self.doubleBuffered = True - self.Bind(wx.EVT_SIZE, self.OnSize) - self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) - self.Bind(wx.EVT_PAINT, self.OnPaint) - - - def Refresh(self): - if self.doubleBuffered: - self.painter.ClearBuffer() - wx.ScrolledWindow.Refresh(self, False) - - def GetPainter(self): - return self.painter - - def GetLayoutEngine(self): - return self.layout - - def GetTransform(self): - return self.transform - - def __repr__(self): - return "" % str(hex(id(self))) - - def __str__(self): - return self.__repr__() - - def NodeAdded(self, parent, child): - e = MVCTreeEvent(wxEVT_MVCTREE_ADD_ITEM, self.GetId(), node = child, nodes = [parent, child]) - self.GetEventHandler().ProcessEvent(e) - self.painter.ClearBuffer() - - def NodeInserted(self, parent, child, index): - e = MVCTreeEvent(wxEVT_MVCTREE_ADD_ITEM, self.GetId(), node = child, nodes = [parent, child]) - self.GetEventHandler().ProcessEvent(e) - self.painter.ClearBuffer() - - def NodeRemoved(self, node): - e = MVCTreeEvent(wxEVT_MVCTREE_DELETE_ITEM, self.GetId(), node = child, nodes = [parent, child]) - self.GetEventHandler().ProcessEvent(e) - self.painter.ClearBuffer() - - def OnKeyDown(self, evt): - e = MVCTreeEvent(wxEVT_MVCTREE_KEY_DOWN, self.GetId(), keyEvent = evt) - self.GetEventHandler().ProcessEvent(e) - - def SetFont(self, font): - self.painter.SetFont(font) - dc = wx.ClientDC(self) - dc.SetFont(font) - self.layout.SetHeight(dc.GetTextExtent("")[1] + 18) - self.painter.ClearBuffer() - - def GetFont(self): - return self.painter.GetFont() - - def AddEditor(self, editor): - self._editors.append(editor) - - def RemoveEditor(self, editor): - self._editors.remove(editor) - - def OnMouse(self, evt): - self.painter.OnMouse(evt) - - def OnNodeClick(self, node, mouseEvent): - if node.selected and (self.IsMultiSelect() and mouseEvent.ControlDown()): - self.RemoveFromSelection(node.data) - else: - self.AddToSelection(node.data, mouseEvent.ControlDown(), mouseEvent.ShiftDown()) - - def OnKnobClick(self, node): - self.SetExpanded(node.data, not node.expanded) - - def GetDisplayText(self, node): - treenode = self.nodemap[node] - return self.painter.textConverter.Convert(treenode) - - def IsDoubleBuffered(self): - return self.doubleBuffered - - def SetDoubleBuffered(self, bool): - """ - By default MVCTree is double-buffered. - """ - self.doubleBuffered = bool - - def GetModel(self): - return self.model - - def SetModel(self, model): - """ - Completely change the data to be displayed. - """ - self.model = model - model.tree = self - self.laidOut = 0 - self.transformed = 0 - self._selections = [] - self.layoutRoot = MVCTreeNode() - self.layoutRoot.data = self.model.GetRoot() - self.layoutRoot.expanded = True - self.LoadChildren(self.layoutRoot) - self.currentRoot = self.layoutRoot - self.offset = [0,0] - self.rotation = 0 - self._scrollset = None - self.Refresh() - - def GetCurrentRoot(self): - return self.currentRoot - - def LoadChildren(self, layoutNode): - if layoutNode.built: - return - else: - self.nodemap[layoutNode.data]=layoutNode - for i in range(self.GetModel().GetChildCount(layoutNode.data)): - p = MVCTreeNode("RAW", layoutNode, []) - layoutNode.Add(p) - p.data = self.GetModel().GetChildAt(layoutNode.data, i) - self.nodemap[p.data]=p - layoutNode.built = True - if not self._assumeChildren: - for kid in layoutNode.kids: - self.LoadChildren(kid) - - def OnEraseBackground(self, evt): - pass - - def OnSize(self, evt): - size = self.GetSize() - self.center = (size.width/2, size.height/2) - if self._lastPhysicalSize.width < size.width or self._lastPhysicalSize.height < size.height: - self.painter.ClearBuffer() - self._lastPhysicalSize = size - - def GetSelection(self): - "Returns a tuple of selected nodes." - return tuple(self._selections) - - def SetSelection(self, nodeTuple): - if type(nodeTuple) != type(()): - nodeTuple = (nodeTuple,) - e = MVCTreeNotifyEvent(wxEVT_MVCTREE_SEL_CHANGING, self.GetId(), nodeTuple[0], nodes = nodeTuple) - self.GetEventHandler().ProcessEvent(e) - if not e.notify.IsAllowed(): - return - for node in nodeTuple: - treenode = self.nodemap[node] - treenode.selected = True - for node in self._selections: - treenode = self.nodemap[node] - node.selected = False - self._selections = list(nodeTuple) - e = MVCTreeEvent(wxEVT_MVCTREE_SEL_CHANGED, self.GetId(), nodeTuple[0], nodes = nodeTuple) - self.GetEventHandler().ProcessEvent(e) - - def IsMultiSelect(self): - return self._multiselect - - def SetMultiSelect(self, bool): - self._multiselect = bool - - def IsSelected(self, node): - return self.nodemap[node].selected - - def Edit(self, node): - if not self.model.IsEditable(node): - return - for ed in self._editors: - if ed.CanEdit(node): - e = MVCTreeNotifyEvent(wxEVT_MVCTREE_BEGIN_EDIT, self.GetId(), node) - self.GetEventHandler().ProcessEvent(e) - if not e.notify.IsAllowed(): - return - ed.Edit(node) - self._currentEditor = ed - break - - def EndEdit(self): - if self._currentEditor: - self._currentEditor.EndEdit - self._currentEditor = None - - def _EditEnding(self, node): - e = MVCTreeNotifyEvent(wxEVT_MVCTREE_END_EDIT, self.GetId(), node) - self.GetEventHandler().ProcessEvent(e) - if not e.notify.IsAllowed(): - return False - self._currentEditor = None - return True - - - def SetExpanded(self, node, bool): - treenode = self.nodemap[node] - if bool: - e = MVCTreeNotifyEvent(wxEVT_MVCTREE_ITEM_EXPANDING, self.GetId(), node) - self.GetEventHandler().ProcessEvent(e) - if not e.notify.IsAllowed(): - return - if not treenode.built: - self.LoadChildren(treenode) - else: - e = MVCTreeNotifyEvent(wxEVT_MVCTREE_ITEM_COLLAPSING, self.GetId(), node) - self.GetEventHandler().ProcessEvent(e) - if not e.notify.IsAllowed(): - return - treenode.expanded = bool - e = None - if treenode.expanded: - e = MVCTreeEvent(wxEVT_MVCTREE_ITEM_EXPANDED, self.GetId(), node) - else: - e = MVCTreeEvent(wxEVT_MVCTREE_ITEM_COLLAPSED, self.GetId(), node) - self.GetEventHandler().ProcessEvent(e) - self.layout.Layout(self.currentRoot) - self.transform.Transform(self.currentRoot, self.offset, self.rotation) - self.Refresh() - - def IsExpanded(self, node): - return self.nodemap[node].expanded - - def AddToSelection(self, nodeOrTuple, enableMulti = True, shiftMulti = False): - nodeTuple = nodeOrTuple - if type(nodeOrTuple)!= type(()): - nodeTuple = (nodeOrTuple,) - e = MVCTreeNotifyEvent(wxEVT_MVCTREE_SEL_CHANGING, self.GetId(), nodeTuple[0], nodes = nodeTuple) - self.GetEventHandler().ProcessEvent(e) - if not e.notify.IsAllowed(): - return - changeparents = [] - if not (self.IsMultiSelect() and (enableMulti or shiftMulti)): - for node in self._selections: - treenode = self.nodemap[node] - treenode.selected = False - changeparents.append(treenode) - node = nodeTuple[0] - self._selections = [node] - treenode = self.nodemap[node] - changeparents.append(treenode) - treenode.selected = True - else: - if shiftMulti: - for node in nodeTuple: - treenode = self.nodemap[node] - oldtreenode = self.nodemap[self._selections[0]] - if treenode.parent == oldtreenode.parent: - found = 0 - for kid in oldtreenode.parent.kids: - if kid == treenode or kid == oldtreenode: - found = not found - kid.selected = True - self._selections.append(kid.data) - changeparents.append(kid) - elif found: - kid.selected = True - self._selections.append(kid.data) - changeparents.append(kid) - else: - for node in nodeTuple: - try: - self._selections.index(node) - except ValueError: - self._selections.append(node) - treenode = self.nodemap[node] - treenode.selected = True - changeparents.append(treenode) - e = MVCTreeEvent(wxEVT_MVCTREE_SEL_CHANGED, self.GetId(), nodeTuple[0], nodes = nodeTuple) - self.GetEventHandler().ProcessEvent(e) - dc = wx.ClientDC(self) - self.PrepareDC(dc) - for node in changeparents: - if node: - self.painter.Paint(dc, node, doubleBuffered = 0, paintBackground = 0) - self.painter.ClearBuffer() - - def RemoveFromSelection(self, nodeTuple): - if type(nodeTuple) != type(()): - nodeTuple = (nodeTuple,) - changeparents = [] - for node in nodeTuple: - self._selections.remove(node) - treenode = self.nodemap[node] - changeparents.append(treenode) - treenode.selected = False - e = MVCTreeEvent(wxEVT_MVCTREE_SEL_CHANGED, self.GetId(), node, nodes = nodeTuple) - self.GetEventHandler().ProcessEvent(e) - dc = wx.ClientDC(self) - self.PrepareDC(dc) - for node in changeparents: - if node: - self.painter.Paint(dc, node, doubleBuffered = 0, paintBackground = 0) - self.painter.ClearBuffer() - - - def GetBackgroundColour(self): - if hasattr(self, 'painter') and self.painter: - return self.painter.GetBackgroundColour() - else: - return wx.Window.GetBackgroundColour(self) - def SetBackgroundColour(self, color): - if hasattr(self, 'painter') and self.painter: - self.painter.SetBackgroundColour(color) - else: - wx.Window.SetBackgroundColour(self, color) - def GetForegroundColour(self): - if hasattr(self, 'painter') and self.painter: - return self.painter.GetForegroundColour() - else: - return wx.Window.GetBackgroundColour(self) - def SetForegroundColour(self, color): - if hasattr(self, 'painter') and self.painter: - self.painter.SetForegroundColour(color) - else: - wx.Window.SetBackgroundColour(self, color) - - def SetAssumeChildren(self, bool): - self._assumeChildren = bool - - def GetAssumeChildren(self): - return self._assumeChildren - - def OnPaint(self, evt): - """ - Ensures that the tree has been laid out and transformed, then calls the painter - to paint the control. - """ - try: - self.EnableScrolling(False, False) - if not self.laidOut: - self.layout.Layout(self.currentRoot) - self.laidOut = True - self.transformed = False - if not self.transformed: - self.transform.Transform(self.currentRoot, self.offset, self.rotation) - self.transformed = True - tsize = None - tsize = list(self.transform.GetSize()) - tsize[0] = tsize[0] + 50 - tsize[1] = tsize[1] + 50 - w, h = self.GetSize() - if tsize[0] > w or tsize[1] > h: - if not hasattr(self, '_oldsize') or (tsize[0] > self._oldsize[0] or tsize[1] > self._oldsize[1]): - self._oldsize = tsize - oldstart = self.GetViewStart() - self._lastPhysicalSize = self.GetSize() - self.SetScrollbars(10, 10, tsize[0]/10, tsize[1]/10) - self.Scroll(oldstart[0], oldstart[1]) - dc = wx.PaintDC(self) - self.PrepareDC(dc) - dc.SetFont(self.GetFont()) - self.painter.Paint(dc, self.currentRoot, self.doubleBuffered) - except: - traceback.print_exc() - - - From 0e79905aa05c617b7bd3ff472e3785d7bbd81323 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Thu, 4 Aug 2016 15:14:43 -0700 Subject: [PATCH 24/33] Don't set the hourglass cursor every time the diagram is redrawn. It can get real annoying. --- wx/lib/ogl/diagram.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/wx/lib/ogl/diagram.py b/wx/lib/ogl/diagram.py index c79007f5..64f0c9bb 100644 --- a/wx/lib/ogl/diagram.py +++ b/wx/lib/ogl/diagram.py @@ -38,12 +38,8 @@ class Diagram(object): def Redraw(self, dc): """Redraw the shapes in the diagram on the specified device context.""" if self._shapeList: - if self.GetCanvas(): - self.GetCanvas().SetCursor(wx.HOURGLASS_CURSOR) for object in self._shapeList: object.Draw(dc) - if self.GetCanvas(): - self.GetCanvas().SetCursor(wx.STANDARD_CURSOR) def Clear(self, dc): """Clear the specified device context.""" From 8b8536ccfb6c774d2b04d2763bf6593710cc8a9a Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Thu, 4 Aug 2016 15:19:43 -0700 Subject: [PATCH 25/33] More minor demo fixes and updates for Py3, Phoenix, etc. --- demo/MultiSash.py | 10 +++++----- demo/NotificationMessage.py | 2 +- demo/Overlay.py | 38 ++++++++++++++++++++++++++----------- demo/PopupWindow.py | 2 +- 4 files changed, 34 insertions(+), 18 deletions(-) diff --git a/demo/MultiSash.py b/demo/MultiSash.py index 818c38fd..1903150f 100644 --- a/demo/MultiSash.py +++ b/demo/MultiSash.py @@ -47,11 +47,11 @@ class TestWindow(stc.StyledTextCtrl): wx.Font(fSize, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL) ) - ## if self.doc: - ## self.SetDocPointer(self.doc) - ## else: - self.SetText(sampleText) - TestWindow.doc = self.GetDocPointer() + if self.doc: + self.SetDocPointer(self.doc) + else: + self.SetText(sampleText) + TestWindow.doc = self.GetDocPointer() def ShutDownDemo(self): diff --git a/demo/NotificationMessage.py b/demo/NotificationMessage.py index a78d1ccb..c7dfe990 100644 --- a/demo/NotificationMessage.py +++ b/demo/NotificationMessage.py @@ -1 +1 @@ -#!/usr/bin/env python import wx import wx.adv class TestPanel(wx.Panel): def __init__(self, parent, log): self.log = log wx.Panel.__init__(self, parent, -1) self.btn = wx.Button(self, -1, "Notify me of something...!", pos=(50,50)) self.btn.Bind(wx.EVT_BUTTON, self.OnButton) def OnButton(self, event): notify = wx.adv.NotificationMessage(title="Notify!", message="...you of something...\numm...Updates are Available.", parent=None, flags=wx.ICON_INFORMATION) notify.SetFlags( # wx.ICON_INFORMATION wx.ICON_WARNING # wx.ICON_ERROR ) # notify.SetTitle("Wooot") # notify.SetMessage("It's a message!") # notify.SetParent(self) notify.Show(timeout=1)#1 for short timeout, 100 for long timeout # notify.Close()# Hides the notification. def runTest(frame, nb, log): win = TestPanel(nb, log) return win #--------------------------------------------------------------------------- overview = """\ This class allows to show the user a message non intrusively. Currently it is implemented natively for Windows and GTK and uses (non-modal) dialogs for the display of the notifications under the other platforms. """ if __name__ == '__main__': import sys,os import run run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:]) \ No newline at end of file +#!/usr/bin/env python import wx import wx.adv class TestPanel(wx.Panel): def __init__(self, parent, log): self.log = log wx.Panel.__init__(self, parent, -1) self.btn = wx.Button(self, -1, "Notify me of something...!", pos=(50,50)) self.btn.Bind(wx.EVT_BUTTON, self.OnButton) def OnButton(self, event): notify = wx.adv.NotificationMessage( title="This is a Notification!", message="wxPython is awesome. Phoenix is awesomer! Python is awesomest!!\n\n" "The quick brown fox jumped over the lazy dog.", parent=None, flags=wx.ICON_INFORMATION) # Various options can be set after the message is created if desired. # notify.SetFlags(# wx.ICON_INFORMATION # wx.ICON_WARNING # # wx.ICON_ERROR # ) # notify.SetTitle("Wooot") # notify.SetMessage("It's a message!") # notify.SetParent(self) notify.Show(timeout=5) # 1 for short timeout, 100 for long timeout # notify.Close() # Hides the notification. def runTest(frame, nb, log): win = TestPanel(nb, log) return win #--------------------------------------------------------------------------- overview = """\ This class allows to show the user a message non intrusively. Currently it is implemented natively for Windows and GTK and uses (non-modal) dialogs for the display of the notifications under the other platforms. """ if __name__ == '__main__': import sys,os import run run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:]) \ No newline at end of file diff --git a/demo/Overlay.py b/demo/Overlay.py index 53e6df7e..65164cd8 100644 --- a/demo/Overlay.py +++ b/demo/Overlay.py @@ -26,7 +26,8 @@ class TestPanel(wx.Panel): self.endPos = None self.overlay = wx.Overlay() - self.cropbitmap = wx.Bitmap('bitmaps/cropshot24x20.png', wx.BITMAP_TYPE_PNG) + self.cropbitmap = wx.Bitmap('bitmaps/cropshot24x20.png') + self.honeyBitmap = wx.Bitmap('bitmaps/honeycomb300.png') self.wxPenStylesDict = OrderedDict([ ('Solid' , wx.PENSTYLE_SOLID), @@ -36,39 +37,47 @@ class TestPanel(wx.Panel): ('Dot Dash' , wx.PENSTYLE_DOT_DASH), ('User Dash' , wx.PENSTYLE_USER_DASH), ('Transparent' , wx.PENSTYLE_TRANSPARENT), - ('Stipple' , wx.PENSTYLE_STIPPLE), + #('Stipple' , wx.PENSTYLE_STIPPLE), ('BDiagonal Hatch' , wx.PENSTYLE_BDIAGONAL_HATCH), ('CrossDiag Hatch' , wx.PENSTYLE_CROSSDIAG_HATCH), ('FDiagonal Hatch' , wx.PENSTYLE_FDIAGONAL_HATCH), ('Cross Hatch' , wx.PENSTYLE_CROSS_HATCH), ('Horizontal Hatch' , wx.PENSTYLE_HORIZONTAL_HATCH), ('Vertical Hatch' , wx.PENSTYLE_VERTICAL_HATCH), - ('First Hatch' , wx.PENSTYLE_FIRST_HATCH), - ('Last Hatch' , wx.PENSTYLE_LAST_HATCH), ]) list = [] for key, value in self.wxPenStylesDict.items(): list.append(key) self.penstylesCombo = wx.ComboBox(self, -1, choices=list, - pos=(10, 5), size=(100, -1), + size=(150, -1), style=wx.CB_READONLY) self.penstylesCombo.SetSelection(0) self.penstylesCombo.SetToolTip('Pen Style') self.overlayPenWidth = wx.SpinCtrl(self, -1, value='', - pos=(120, 5), - size=(100, -1), + size=(75, -1), style=wx.SP_ARROW_KEYS, min=1, max=24, initial=1) self.overlayPenWidth.SetToolTip('Pen Width') - self.overlayPenColor = wx.ColourPickerCtrl(self, -1, colour=wx.BLUE, - pos=(230, 5), size=(100, -1)) + from wx.lib.colourselect import ColourSelect + self.overlayPenColor = ColourSelect(self, -1, colour=wx.BLUE) self.overlayPenColor.SetToolTip('Pen Color') + sizer = wx.BoxSizer(wx.HORIZONTAL) + sizer.Add(self.penstylesCombo, 0, wx.ALL, 5) + sizer.Add(self.overlayPenWidth, 0, wx.ALL, 5) + sizer.Add(self.overlayPenColor, 0, wx.ALL, 5) + box = wx.BoxSizer(wx.VERTICAL) + box.Add(sizer, 0) + box.Add((1,1), 1) + + self.SetSizer(box) + self.OnSize() + def OnLeftDown(self, event): # Capture the mouse and save the starting posiiton for the rubber-band self.CaptureMouse() @@ -77,6 +86,7 @@ class TestPanel(wx.Panel): self.SetFocus() ## print('OnLeftDown') + def OnMouseMove(self, event): if event.Dragging() and event.LeftIsDown(): evtPos = event.GetPosition() @@ -128,6 +138,7 @@ class TestPanel(wx.Panel): del odc # Make sure the odc is destroyed before the dc is. ## print('OnMouseMove') + def OnLeftUp(self, event): if self.HasCapture(): self.ReleaseMouse() @@ -146,7 +157,11 @@ class TestPanel(wx.Panel): self.overlay.Reset() ## print('OnLeftUp') + def OnSize(self, event=None): + if event: + event.Skip() + x, y = self.GetSize() if x <= 0 or y <= 0: return @@ -158,8 +173,9 @@ class TestPanel(wx.Panel): dc.SetBackground(self.background) dc.Clear() - dc.DrawBitmap(wx.Bitmap('bitmaps/snakey_render.png'), 10, 35) - dc.DrawBitmap(wx.Bitmap('bitmaps/honeycomb300.png'), 100, 210) + dc.DrawBitmap(self.honeyBitmap, 40, 40) + dc.SetFont(wx.Font(wx.FontInfo(18))) + dc.DrawText('Drag the mouse on this window.', 325, 100) del dc self.Refresh() diff --git a/demo/PopupWindow.py b/demo/PopupWindow.py index 0c964d1f..2428fbe8 100644 --- a/demo/PopupWindow.py +++ b/demo/PopupWindow.py @@ -69,7 +69,7 @@ class TestPopup(wx.PopupWindow): def OnRightUp(self, evt): self.Show(False) - self.Destroy() + wx.CallAfter(self.Destroy) From 92d15292ec5adcceed9ae9c179e4882f1096659a Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Thu, 4 Aug 2016 19:43:49 -0700 Subject: [PATCH 26/33] Enforce passing a bytes object to wx.OutputStream.write, don't try to coerce it. Fix use of the thread blocker in streams --- demo/Process.py | 3 ++- etg/stream.py | 44 ++++++++++++++++++++------------------------ 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/demo/Process.py b/demo/Process.py index 16f49eb9..c4f0fa5e 100644 --- a/demo/Process.py +++ b/demo/Process.py @@ -87,7 +87,8 @@ class TestPanel(wx.Panel): text = self.inp.GetValue() self.inp.SetValue('') self.log.write('OnSendText: "%s"\n' % text) - self.process.GetOutputStream().write(text + '\n') + text += '\n' + self.process.GetOutputStream().write(text.encode('utf-8')) self.inp.SetFocus() diff --git a/etg/stream.py b/etg/stream.py index cc7852c8..bec32460 100644 --- a/etg/stream.py +++ b/etg/stream.py @@ -179,17 +179,16 @@ def run(): static PyObject* _readlinesHelper(wxInputStream* self, bool useSizeHint=false, ulong sizehint=0) { PyObject* pylist; - + // init list - wxPyBlock_t blocked = wxPyBeginBlockThreads(); - pylist = PyList_New(0); - wxPyEndBlockThreads(blocked); - - if (!pylist) { - wxPyBlock_t blocked = wxPyBeginBlockThreads(); - PyErr_NoMemory(); - wxPyEndBlockThreads(blocked); - return NULL; + { + wxPyThreadBlocker blocker; + pylist = PyList_New(0); + + if (!pylist) { + PyErr_NoMemory(); + return NULL; + } } // read sizehint bytes or until EOF @@ -197,24 +196,21 @@ def run(): for (i=0; (self->CanRead()) && (useSizeHint || (i < sizehint));) { PyObject* s = _wxInputStream_readline(self); if (s == NULL) { - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; Py_DECREF(pylist); - wxPyEndBlockThreads(blocked); return NULL; } - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; PyList_Append(pylist, s); i += PyBytes_Size(s); - wxPyEndBlockThreads(blocked); } // error check wxStreamError err = self->GetLastError(); if (err != wxSTREAM_NO_ERROR && err != wxSTREAM_EOF) { - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + wxPyThreadBlocker blocker; Py_DECREF(pylist); PyErr_SetString(PyExc_IOError,"IOError in wxInputStream"); - wxPyEndBlockThreads(blocked); return NULL; } return pylist; @@ -271,15 +267,15 @@ def run(): return false; //self->Eof(); """) - c.addCppMethod('void', 'write', '(PyObject* data)', """\ - // We use only strings for the streams, not unicode - PyObject* str = PyObject_Bytes(data); - if (! str) { - PyErr_SetString(PyExc_TypeError, "Unable to convert to string"); - return; + c.addCppMethod('PyObject*', 'write', '(PyObject* data)', """\ + // We use only bytes objects (strings in 2.7) for the streams, never unicode + wxPyThreadBlocker blocker; + if (!PyBytes_Check(data)) { + PyErr_SetString(PyExc_TypeError, "Bytes object expected"); + return NULL; } - self->Write(PyBytes_AS_STRING(str), PyBytes_GET_SIZE(str)); - Py_DECREF(str); + self->Write(PyBytes_AS_STRING(data), PyBytes_GET_SIZE(data)); + RETURN_NONE(); """) # TODO: Add a writelines(sequence) method From 23b4a1b4d3206b83df0336cffbca1620eec8a17c Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Thu, 4 Aug 2016 19:44:43 -0700 Subject: [PATCH 27/33] Don't use apply() --- demo/ScrolledWindow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/ScrolledWindow.py b/demo/ScrolledWindow.py index c57d9d5b..4f3f8a68 100644 --- a/demo/ScrolledWindow.py +++ b/demo/ScrolledWindow.py @@ -149,7 +149,7 @@ class MyCanvas(wx.ScrolledWindow): dc.SetPen(wx.Pen('MEDIUM FOREST GREEN', 4)) for line in self.lines: for coords in line: - apply(dc.DrawLine, coords) + dc.DrawLine(*coords) def SetXY(self, event): From 7dc652896ce1856134343798c8076b97e7e7361a Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Thu, 4 Aug 2016 19:46:47 -0700 Subject: [PATCH 28/33] Add comment explaining the magic '2' --- demo/PrintFramework.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/demo/PrintFramework.py b/demo/PrintFramework.py index 8fae51c9..e7a29a07 100644 --- a/demo/PrintFramework.py +++ b/demo/PrintFramework.py @@ -34,7 +34,7 @@ class MyPrintout(wx.Printout): def HasPage(self, page): self.log.WriteText("MyPrintout.HasPage: %d\n" % page) - if page <= 2: + if page <= 2: # we only have 2 pages in this document return True else: return False @@ -80,7 +80,6 @@ class MyPrintout(wx.Printout): dc.SetDeviceOrigin(int(posX), int(posY)) #------------------------------------------- - self.canvas.DoDrawing(dc, True) dc.DrawText("Page: %d" % page, marginX/2, maxY-marginY) From 048f8dfe062d29af80743778a22d4fcb49b20d8c Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Fri, 5 Aug 2016 21:17:15 -0700 Subject: [PATCH 29/33] PyFunctionDef should also have a hasOverloads() method for quacking like a duck. --- etgtools/extractors.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/etgtools/extractors.py b/etgtools/extractors.py index ce5a1b36..3920c77f 100644 --- a/etgtools/extractors.py +++ b/etgtools/extractors.py @@ -1260,6 +1260,15 @@ class PyFunctionDef(BaseDef): self.overloads = [] self.__dict__.update(kw) + + def hasOverloads(self): + """ + Returns True if there are any overloads that are not ignored. + """ + return bool([x for x in self.overloads if not x.ignored]) + + + #--------------------------------------------------------------------------- class PyClassDef(BaseDef): From 9fe03f7570f66f820b2977a8929ef29cfca8c9dd Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Wed, 10 Aug 2016 11:00:23 -0700 Subject: [PATCH 30/33] Remove some additional unused/outdated demo files --- TODO.rst | 4 +- demo/RightTextCtrl.py | 64 ------------ demo/RowColSizer.py | 93 ----------------- demo/demodata.py | 7 +- demo/pyTree.py | 231 ------------------------------------------ demo/quotes.xml | 13 --- wx/lib/rcsizer.py | 229 ----------------------------------------- 7 files changed, 5 insertions(+), 636 deletions(-) delete mode 100644 demo/RightTextCtrl.py delete mode 100644 demo/RowColSizer.py delete mode 100644 demo/pyTree.py delete mode 100644 demo/quotes.xml delete mode 100644 wx/lib/rcsizer.py diff --git a/TODO.rst b/TODO.rst index 757fca05..299fe3d6 100644 --- a/TODO.rst +++ b/TODO.rst @@ -203,9 +203,11 @@ Other Dev Stuff self-contained instead of some relying on files generated by others. This won't work if we want to run tests in parallel. - * Port these modules from wxCode to a pure-python wx.lib implementation? + * Port these modules from gizmos in wxCode to a pure-python wx.lib implementation? * DynamicSashWindow * LEDNumberCtrl + * SplitTree ?? + * TreeListCtrl ?? (We have a treelist ctrl in dataview now) * The Masked controls modules and demos need some help with Py3 compatibility. diff --git a/demo/RightTextCtrl.py b/demo/RightTextCtrl.py deleted file mode 100644 index c21c27c4..00000000 --- a/demo/RightTextCtrl.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python - -#####################################################################\ -# Note: This control is deprecated because wx.TextCtrl now supports | -# the wx.TE_RIGHT style flag, which makes this control completely | -# superfluous. | -#####################################################################/ - -import wx -import wx.lib.rightalign as right - -#---------------------------------------------------------------------- - -class TestPanel(wx.Panel): - def __init__(self, parent): - wx.Panel.__init__(self, parent, -1) - - fgs = wx.FlexGridSizer(cols=2, vgap=5, hgap=5) - - txt = wx.StaticText( - self, -1, - "These text controls will align their contents to\n" - "the right (on wxMSW) when they don't have focus.", - style=wx.ALIGN_RIGHT - ) - - fgs.Add(txt) - fgs.Add(right.RightTextCtrl(self, -1, "", size=(75, -1))) - - fgs.Add((10,10)) - fgs.Add(right.RightTextCtrl(self, -1, "123.45", size=(75, -1))) - - fgs.Add((10,10)) - fgs.Add(right.RightTextCtrl(self, -1, "234.56", size=(75, -1))) - - fgs.Add((10,10)) - fgs.Add(right.RightTextCtrl(self, -1, "345.67", size=(75, -1))) - - fgs.Add((10,10)) - fgs.Add(right.RightTextCtrl(self, -1, "456.78", size=(75, -1))) - - sizer = wx.BoxSizer(wx.VERTICAL) - sizer.Add(fgs, 0, wx.ALL, 25) - - self.SetSizer(sizer) - self.SetAutoLayout(True) - - - -#---------------------------------------------------------------------- - -def runTest(frame, nb, log): - win = TestPanel(nb) - return win - -#---------------------------------------------------------------------- - -overview = right.__doc__ - - -if __name__ == '__main__': - import sys,os - import run - run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:]) diff --git a/demo/RowColSizer.py b/demo/RowColSizer.py deleted file mode 100644 index 1770b74f..00000000 --- a/demo/RowColSizer.py +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/env python - -import wx -import wx.lib.rcsizer as rcs - -#---------------------------------------------------------------------- - -class TestPanel(wx.Panel): - def __init__(self, parent): - wx.Panel.__init__(self, parent, -1) - - sizer = rcs.RowColSizer() - - text = "This sizer lays out it's items by row and column "\ - "that are specified explicitly when the item is \n"\ - "added to the sizer. Grid cells with nothing in "\ - "them are supported and column- or row-spanning is \n"\ - "handled as well. Growable rows and columns are "\ - "specified just like the wxFlexGridSizer." - - sizer.Add(wx.StaticText(self, -1, text), row=1, col=1, colspan=5) - - sizer.Add(wx.TextCtrl(self, -1, "(3,1)"), flag=wx.EXPAND, row=3, col=1) - sizer.Add(wx.TextCtrl(self, -1, "(3,2)"), row=3, col=2) - sizer.Add(wx.TextCtrl(self, -1, "(3,3)"), row=3, col=3) - sizer.Add(wx.TextCtrl(self, -1, "(3,4)"), row=3, col=4) - sizer.Add( - wx.TextCtrl(self, -1, "(4,2) span:(2,2)"), - flag=wx.EXPAND, row=4, col=2, rowspan=2, colspan=2 - ) - - sizer.Add(wx.TextCtrl(self, -1, "(6,4)"), row=6, col=4) - sizer.Add(wx.TextCtrl(self, -1, "(7,2)"), row=7, col=2) - sizer.Add(wx.TextCtrl(self, -1, "(8,3)"), row=8, col=3) - sizer.Add( - wx.TextCtrl(self, -1, "(10,1) colspan: 4"), - flag=wx.EXPAND, pos=(10,1), colspan=4 - ) - - sizer.Add( - wx.TextCtrl(self, -1, "(3,5) rowspan: 8, growable col", style=wx.TE_MULTILINE), - flag=wx.EXPAND, pos=(3,5), size=(8,1) - ) - - box = wx.BoxSizer(wx.VERTICAL) - box.Add(wx.Button(self, -1, "A vertical box"), flag=wx.EXPAND) - box.Add(wx.Button(self, -1, "sizer put in the"), flag=wx.EXPAND) - box.Add(wx.Button(self, -1, "RowColSizer at (12,1)"), flag=wx.EXPAND) - sizer.Add(box, pos=(12,1)) - - sizer.Add( - wx.TextCtrl(self, -1, "(12,2) align bottom"), - flag=wx.ALIGN_BOTTOM, pos=(12,2) - ) - - sizer.Add( - wx.TextCtrl(self, -1, "(12,3) align center"), - flag=wx.ALIGN_CENTER_VERTICAL, pos=(12,3) - ) - - sizer.Add(wx.TextCtrl(self, -1, "(12,4)"),pos=(12,4)) - sizer.Add( - wx.TextCtrl(self, -1, "(12,5) full border"), - flag=wx.EXPAND|wx.ALL, border=15, pos=(12,5) - ) - - sizer.AddGrowableCol(5) - sizer.AddGrowableRow(9) - - sizer.AddSpacer(10,10, pos=(1,6)) - sizer.AddSpacer(10,10, pos=(13,1)) - - self.SetSizer(sizer) - self.SetAutoLayout(True) - - -#---------------------------------------------------------------------- - -def runTest(frame, nb, log): - win = TestPanel(nb) - return win - - -#---------------------------------------------------------------------- - - -overview = rcs.__doc__ - -if __name__ == '__main__': - import sys,os - import run - run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:]) - diff --git a/demo/demodata.py b/demo/demodata.py index 80d6d03d..b036e351 100644 --- a/demo/demodata.py +++ b/demo/demodata.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - # demodata.py """ @@ -156,7 +154,7 @@ _treeList = [ 'PlateButton', 'PopupControl', 'PyColourChooser', - 'TreeListCtrl', + #'TreeListCtrl', # TODO or toss it? ]), # controls coming from other libraries @@ -196,7 +194,7 @@ _treeList = [ 'ResizeWidget', 'RichTextCtrl', 'ScrolledPanel', - 'SplitTree', + #'SplitTree', # TODO or toss it? 'StyledTextCtrl_1', 'StyledTextCtrl_2', 'TablePrint', @@ -214,7 +212,6 @@ _treeList = [ 'LayoutAnchors', 'LayoutConstraints', 'Layoutf', - 'RowColSizer', 'ScrolledPanel', 'SizedControls', 'Sizers', diff --git a/demo/pyTree.py b/demo/pyTree.py deleted file mode 100644 index 9518bc83..00000000 --- a/demo/pyTree.py +++ /dev/null @@ -1,231 +0,0 @@ -#!/usr/bin/env python - -""" -Hello, and welcome to this test of the wxTreeItemData -class. - -The wxTreeItemData class can be used to associate a python -object with a wxTreeCtrl item. In this sample, its use is -demonstrated via a tree control that shows the contents of a -python namespace according to the standard dir() -command. Every item in the tree has its label taken from the -dir() output, and 'behind it' a reference to the python -object is stored in a wxTreeItemData object. - -As you may have guessed by now, this sample automatically -displays '__doc__' strings if the selected python object -happens to have one. Please expand the pyTree object to -learn more about the implementation. - -Version 1.0, April 4 1999. -Harm van der Heijden (H.v.d.Heijden@phys.tue.nl) - -P.S. Check out the string module. It's imported in this -sample not because it's used, but because it's so -beautifully documented... -""" - -import string # Used for demo purposes, nothing more. :-) -import sys - -import wx - -#---------------------------------------------------------------------- - -def _getindent(line): - """Returns the indentation level of the given line.""" - indent = 0 - for c in line: - if c == ' ': indent = indent + 1 - elif c == '\t': indent = indent + 8 - else: break - return indent - -def _sourcefinder(func): - """Given a func_code object, this function tries to find and return - the python source code of the function.""" - try: - f = open(func.co_filename,"r") - except: - return "(could not open file %s)" % (func.co_filename,) - - for i in range(func.co_firstlineno): - line = f.readline() - - ind = _getindent(line) - msg = "" - - while line: - msg = msg + line - line = f.readline() - # the following should be <= ind, but then we get - # confused by multiline docstrings. Using == works most of - # the time... but not always! - if _getindent(line) == ind: break - - return msg - -#---------------------------------------------------------------------- - -class pyTree(wx.TreeCtrl): - """ - This wx.TreeCtrl derivative displays a tree view of a Python namespace. - Anything from which the dir() command returns a non-empty list is a branch - in this tree. - """ - - def __init__(self, parent, id, root): - """ - Initialize function; because we insert branches into the tree - as needed, we use the ITEM_EXPANDING event handler. The - ITEM_COLLAPSED handler removes the stuff afterwards. The - SEL_CHANGED handler attempts to display interesting - information about the selected object. - """ - wx.TreeCtrl.__init__(self, parent, id) - self.root = self.AddRoot(str(root), -1, -1, wx.TreeItemData(root)) - - if dir(root): - self.SetItemHasChildren(self.root, True) - - self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.OnItemExpanding, id=self.GetId()) - self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed, id=self.GetId()) - self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=self.GetId()) - - self.output = None - self.Expand(self.root) - - - def SetOutput(self, output): - """ - Set output function (accepts single string). Used to display string - representation of the selected object by OnSelChanged. - """ - self.output = output - - - def OnItemExpanding(self,event): - """ - The real workhorse of this class. First we retrieve the object - (parent) belonging to the branch that is to be expanded. This - is done by calling GetPyData(parent), which is a short-cut for - GetPyItemData(parent).Get(). - - Then we get the dir() list of that object. For each item in - this list, a tree item is created with associated - wxTreeItemData referencing the child object. We get this - object using child = getattr(parent, item). - - Finally, we check wether the child returns a non-empty dir() - list. If so, it is labeled as 'having children', so that it - may be expanded. When it actually is expanded, this function - will again figure out what the offspring is. - """ - item = event.GetItem() - - if self.IsExpanded(item): # This event can happen twice in the self.Expand call - return - - obj = self.GetPyData( item ) - lst = dir(obj) - - for key in lst: - new_obj = getattr(obj,key) - new_item = self.AppendItem( item, key, -1, -1, - wx.TreeItemData(new_obj) ) - - if dir(new_obj): - self.SetItemHasChildren(new_item, True) - - def OnItemCollapsed(self, event): - """ - We need to remove all children here, otherwise we'll see all - that old rubbish again after the next expansion. - """ - item = event.GetItem() - self.DeleteChildren(item) - - def OnSelChanged(self, event): - """ - If an output function is defined, we try to print some - informative, interesting and thought-provoking stuff to it. - If it has a __doc__ string, we print it. If it's a function or - unbound class method, we attempt to find the python source. - """ - if not self.output: - return - - obj = self.GetPyData( event.GetItem() ) - msg = str(obj) - - if hasattr(obj, '__doc__'): - msg = msg+"\n\nDocumentation string:\n\n%s" % ( getattr(obj, '__doc__'),) - - # Is it a function? - func = None - - if hasattr(obj, "func_code"): # normal function - func = getattr(obj, "func_code") - - elif hasattr(obj, "im_func"): # unbound class method - func = getattr(getattr(obj, "im_func"), "func_code") - - if func: # if we found one, let's try to print the source - msg = msg+"\n\nFunction source:\n\n" + _sourcefinder(func) - - apply(self.output, (msg,)) - -#---------------------------------------------------------------------- - -overview = __doc__ - -def runTest(frame, nb, log): - """ - This method is used by the wxPython Demo Framework for integrating - this demo with the rest. - """ - thisModule = sys.modules[__name__] - win = wx.Frame(frame, -1, "PyTreeItemData Test") - split = wx.SplitterWindow(win, -1) - tree = pyTree(split, -1, thisModule) - text = wx.TextCtrl(split, -1, "", style=wx.TE_MULTILINE) - split.SplitVertically(tree, text, 200) - tree.SetOutput(text.SetValue) - tree.SelectItem(tree.root) - win.SetSize((800,500)) - frame.otherWin = win - win.Show(1) - - - -#---------------------------------------------------------------------- -if __name__ == '__main__': - - class MyFrame(wx.Frame): - """Very standard Frame class. Nothing special here!""" - - def __init__(self): - """Make a splitter window; left a tree, right a textctrl. Wow.""" - import __main__ - wx.Frame.__init__(self, None, -1, "PyTreeItemData Test", size=(800,500)) - split = wx.SplitterWindow(self, -1) - tree = pyTree(split, -1, __main__) - text = wx.TextCtrl(split, -1, "", style=wx.TE_MULTILINE) - split.SplitVertically(tree, text, 200) - tree.SetOutput(text.SetValue) - tree.SelectItem(tree.root) - - class MyApp(wx.App): - """This class is even less interesting than MyFrame.""" - - def OnInit(self): - """OnInit. Boring, boring, boring!""" - frame = MyFrame() - frame.Show(True) - self.SetTopWindow(frame) - return True - - app = MyApp(False) - app.MainLoop() - - diff --git a/demo/quotes.xml b/demo/quotes.xml deleted file mode 100644 index b8f56e34..00000000 --- a/demo/quotes.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - -]> -We will perhaps eventually be writing only small modules which are identified by name as they are used to build larger ones, so that devices like indentation, rather than delimiters, might become feasible for expressing local structure in the source language. Donald E. Knuth, "Structured Programming with goto Statements", Computing Surveys, Vol 6 No 4, Dec. 1974 The infinities aren't contagious except in that they often appear that way due to to their large size. Tim Peters on the IEEE 754 floating point standard, 27 Apr 1998 diff --git a/wx/lib/rcsizer.py b/wx/lib/rcsizer.py deleted file mode 100644 index 1ca9d390..00000000 --- a/wx/lib/rcsizer.py +++ /dev/null @@ -1,229 +0,0 @@ -#---------------------------------------------------------------------- -# Name: wx.lib.rcsizer -# Purpose: RowColSizer: -# -# Author: Robin Dunn, adapted from code by Niki Spahiev -# -# Created: 26-Feb-2002 -# Copyright: (c) 2002 by Total Control Software -# Licence: wxWindows license -#---------------------------------------------------------------------- -# 12/10/2003 - Jeff Grimmett (grimmtooth@softhome.net) -# -# o 2.5 compatability update. -# o There appears to be a prob with the wx.Sizer.GetSize() method. -# -# 12/23/2003 - Jeff Grimmett (grimmtooth@softhome.net) -# -# o wx.Sizer.GetSize() method working right now. -# - -""" -A pure-Python Sizer that lays out items in a grid similar to -wx.FlexGridSizer but item position is not implicit but explicitly -specified by row and col, and row/col spanning is supported. - -Adapted from code by Niki Spahiev. - -NOTE: There is now a C++ version of this class that has been wrapped -as wx.GridBagSizer. It is quicker and more capable so you are -encouraged to switch. -""" - -import operator -import wx - -import six -if six.PY3: - from functools import reduce as reduce - -# After the lib and demo no longer uses this sizer enable this warning... - -## import warnings -## warningmsg = r"""\ - -## #####################################################\ -## # THIS MODULE IS NOW DEPRECATED | -## # | -## # The core wx library now contains a similar class | -## # wrapped as wx.GridBagSizer. | -## #####################################################/ - -## """ - -## warnings.warn(warningmsg, DeprecationWarning, stacklevel=2) - -#---------------------------------------------------------------------- - -class RowColSizer(wx.Sizer): - - # default sizes for cells with no item - col_w = 10 - row_h = 22 - - def __init__(self): - wx.Sizer.__init__(self) - self.growableRows = [] - self.growableCols = [] - - - def AddGrowableRow(self, idx): - self.growableRows.append(idx) - - def AddGrowableCol(self, idx): - self.growableCols.append(idx) - - - #-------------------------------------------------- - def Add(self, item, option=0, flag=0, border=0, - # row, col and spanning can be specified individually... - row=-1, col=-1, - rowspan=1, colspan=1, - # or as tuples (row,col) and (rowspan,colspan) - pos=None, size=None, - ): - - if pos is not None: - row, col = pos - if size is not None: - rowspan, colspan = size - - assert row != -1, "Row must be specified" - assert col != -1, "Column must be specified" - - # Do I really want to do this? Probably not... - #if rowspan > 1 or colspan > 1: - # flag = flag | wx.EXPAND - - return wx.Sizer.Add(self, item, option, flag, border, - userData=(row, col, row+rowspan, col+colspan)) - - #AddWindow = Add - #AddSizer = Add - - def AddSpacer(self, width, height, option=0, flag=0, border=0, - row=-1, col=-1, - rowspan=1, colspan=1, - pos=None, size=None, - ): - if pos is not None: - row, col = pos - if size is not None: - rowspan, colspan = size - - assert row != -1, "Row must be specified" - assert col != -1, "Column must be specified" - - return wx.Sizer.Add(self, (width, height), option, flag, border, - userData=(row, col, row+rowspan, col+colspan)) - - #-------------------------------------------------- - def _add( self, size, dim ): - r, c, r2, c2 = dim # unpack coords and spanning - - # are the widths and heights lists long enough? - if r2 > len(self.rowHeights): - x = [self.row_h] * (r2-len(self.rowHeights)) - self.rowHeights.extend( x ) - if c2 > len(self.colWidths): - x = [self.col_w] * (c2-len(self.colWidths)) - self.colWidths.extend( x ) - - # set the widths and heights lists for this item - scale = (r2 - r) - for i in range(r, r2): - self.rowHeights[i] = max( self.rowHeights[i], size.height / scale ) - scale = (c2 - c) - for i in range(c, c2): - self.colWidths[i] = max( self.colWidths[i], size.width / scale ) - - - #-------------------------------------------------- - def CalcMin( self ): - self.rowHeights = [] - self.colWidths = [] - - items = self.GetChildren() - if not items: - return wx.Size(10, 10) - - for item in items: - self._add( item.CalcMin(), item.GetUserData() ) - - size = wx.Size( reduce( operator.add, self.colWidths), - reduce( operator.add, self.rowHeights) ) - return size - - - #-------------------------------------------------- - def RecalcSizes( self ): - # save current dimensions, etc. - curWidth, curHeight = self.GetSize() - px, py = self.GetPosition() - minWidth, minHeight = self.CalcMin() - - # Check for growables - if self.growableRows and curHeight > minHeight: - delta = (curHeight - minHeight) / len(self.growableRows) - extra = (curHeight - minHeight) % len(self.growableRows) - for idx in self.growableRows: - self.rowHeights[idx] += delta - self.rowHeights[self.growableRows[0]] += extra - - if self.growableCols and curWidth > minWidth: - delta = (curWidth - minWidth) / len(self.growableCols) - extra = (curWidth - minWidth) % len(self.growableCols) - for idx in self.growableCols: - self.colWidths[idx] += delta - self.colWidths[self.growableCols[0]] += extra - - rpos = [0] * len(self.rowHeights) - cpos = [0] * len(self.colWidths) - - for i in range(len(self.rowHeights)): - height = self.rowHeights[i] - rpos[i] = py - py += height - - for i in range(len(self.colWidths)): - width = self.colWidths[i] - cpos[i] = px - px += width - - # iterate children and set dimensions... - for item in self.GetChildren(): - r, c, r2, c2 = item.GetUserData() - width = reduce( operator.add, self.colWidths[c:c2] ) - height = reduce( operator.add, self.rowHeights[r:r2] ) - self.SetItemBounds( item, cpos[c], rpos[r], width, height ) - - - #-------------------------------------------------- - def SetItemBounds(self, item, x, y, w, h): - # calculate the item's actual size and position within - # its grid cell - ipt = wx.Point(x, y) - isz = item.CalcMin() - flag = item.GetFlag() - - if flag & wx.EXPAND or flag & wx.SHAPED: - isz = wx.Size(w, h) - else: - if flag & wx.ALIGN_CENTER_HORIZONTAL: - ipt.x = x + (w - isz.width) / 2 - elif flag & wx.ALIGN_RIGHT: - ipt.x = x + (w - isz.width) - - if flag & wx.ALIGN_CENTER_VERTICAL: - ipt.y = y + (h - isz.height) / 2 - elif flag & wx.ALIGN_BOTTOM: - ipt.y = y + (h - isz.height) - - item.SetDimension(ipt, isz) - - -#---------------------------------------------------------------------- -#---------------------------------------------------------------------- - - - From 9d5e743e50833cca3e6aa9dfb8b2b60b898de1ef Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Wed, 10 Aug 2016 15:56:04 -0700 Subject: [PATCH 31/33] More minor demo fixes and updates for Py3, Phoenix, etc. --- demo/Sizers.py | 16 ++++++------- demo/Slider.py | 17 +++++++------ demo/Sound.py | 6 ++--- demo/StaticBitmap.py | 1 - demo/StyledTextCtrl_1.py | 37 +++------------------------- demo/TablePrint.py | 6 ++--- demo/Threads.py | 14 +++++------ demo/TreeMixin.py | 52 ++++++++++++++++++++-------------------- wx/lib/printout.py | 13 ++++++---- 9 files changed, 67 insertions(+), 95 deletions(-) diff --git a/demo/Sizers.py b/demo/Sizers.py index c13f4d06..6d06b3ca 100644 --- a/demo/Sizers.py +++ b/demo/Sizers.py @@ -551,37 +551,35 @@ class TestSelectionPanel(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) - self.list = wx.ListBox(self, -1, - (-1, -1), (-1, -1), - []) - self.list.Fit() + self.list = wx.ListBox(self, size=(300,150)) self.Bind(wx.EVT_LISTBOX, self.OnSelect, self.list) self.Bind(wx.EVT_LISTBOX_DCLICK, self.OnDClick, self.list) btn = wx.Button(self, -1, "Try it!", (120, 10)) btn.Bind(wx.EVT_BUTTON, self.OnDClick) - self.text = wx.TextCtrl(self, -1, "", - (10, 115), - (200, 50), - wx.TE_MULTILINE | wx.TE_READONLY) + self.text = wx.TextCtrl(self, style = wx.TE_MULTILINE | wx.TE_READONLY) for item in theTests: self.list.Append(item[0]) + self.list.SetSelection(0) + wx.CallAfter(self.list.EnsureVisible, 0) + hsizer = wx.BoxSizer(wx.HORIZONTAL) hsizer.Add(self.list, 1, wx.ALL, 8) hsizer.Add(btn, 0, wx.ALL, 8) vsizer = wx.BoxSizer(wx.VERTICAL) vsizer.Add(hsizer) vsizer.Add(self.text, 1, wx.EXPAND | wx.ALL, 8) - self.SetSizerAndFit(vsizer) + self.SetSizer(vsizer) def OnSelect(self, event): pos = self.list.GetSelection() self.text.SetValue(theTests[pos][2]) + def OnDClick(self, event): pos = self.list.GetSelection() title = theTests[pos][0] diff --git a/demo/Slider.py b/demo/Slider.py index df4c6db6..503d19e5 100644 --- a/demo/Slider.py +++ b/demo/Slider.py @@ -10,15 +10,22 @@ class TestPanel(wx.Panel): self.log = log self.count = 0 - wx.StaticText(self, -1, "This is a wx.Slider.", (45, 15)) + label = wx.StaticText(self, -1, "This is a wx.Slider.") slider = wx.Slider( - self, 100, 25, 1, 100, (30, 60), (250, -1), - wx.SL_HORIZONTAL | wx.SL_AUTOTICKS | wx.SL_LABELS + self, 100, 25, 1, 100, size=(250, -1), + style=wx.SL_HORIZONTAL | wx.SL_AUTOTICKS | wx.SL_LABELS ) slider.SetTickFreq(5) + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add((1,20)) + sizer.Add(label, 0, wx.LEFT, 20) + sizer.Add((1,10)) + sizer.Add(slider, 0, wx.LEFT, 20) + self.SetSizer(sizer) + #---------------------------------------------------------------------- @@ -35,10 +42,6 @@ def runTest(frame, nb, log): overview = """\ A slider is a control with a handle which can be pulled back and forth to change the value. - -In Windows versions below Windows 95, a scrollbar is used to simulate the slider. -In Windows 95, the track bar control is used. - """ diff --git a/demo/Sound.py b/demo/Sound.py index c50543f9..96afa984 100644 --- a/demo/Sound.py +++ b/demo/Sound.py @@ -27,7 +27,7 @@ class TestPanel(wx.Panel): self.log.write("before Play...\n") sound.Play(wx.adv.SOUND_SYNC) self.log.write("...after Play\n") - except NotImplementedError, v: + except NotImplementedError as v: wx.MessageBox(str(v), "Exception Message") @@ -45,7 +45,7 @@ class TestPanel(wx.Panel): self.sound = sound # save a reference (This shoudln't be needed, but there seems to be a bug...) # wx.YieldIfNeeded() self.log.write("...after Play\n") - except NotImplementedError, v: + except NotImplementedError as v: wx.MessageBox(str(v), "Exception Message") @@ -62,7 +62,7 @@ class TestPanel(wx.Panel): # another way to do it. wx.adv.Sound.PlaySound(dlg.GetPath(), wx.adv.SOUND_SYNC) - except NotImplementedError, v: + except NotImplementedError as v: wx.MessageBox(str(v), "Exception Message") dlg.Destroy() diff --git a/demo/StaticBitmap.py b/demo/StaticBitmap.py index 75dad2b4..329b12d2 100644 --- a/demo/StaticBitmap.py +++ b/demo/StaticBitmap.py @@ -32,7 +32,6 @@ class TestPanel(wx.Panel): bmp = images.Robin.GetBitmap() StaticBitmap(self, -1, bmp, (80, 150)) - StaticText(self, -1, "Hey, if Ousterhout can do it, so can I.", (200, 175)) #---------------------------------------------------------------------- diff --git a/demo/StyledTextCtrl_1.py b/demo/StyledTextCtrl_1.py index 37f0ecdf..759ad02f 100644 --- a/demo/StyledTextCtrl_1.py +++ b/demo/StyledTextCtrl_1.py @@ -1,15 +1,7 @@ -#!/usr/bin/env python - -# 11/21/2003 - Jeff Grimmett (grimmtooth@softhome.net) -# -# o wx.TheClipboard.Flush() generates a warning on program exit. - import wx import wx.stc as stc -import images - #---------------------------------------------------------------------- debug = 1 @@ -171,43 +163,20 @@ def runTest(frame, nb, log): p.SetAutoLayout(True) - #ed.SetBufferedDraw(False) - #ed.StyleClearAll() - #ed.SetScrollWidth(800) - #ed.SetWrapMode(True) - #ed.SetUseAntiAliasing(False) - #ed.SetViewEOL(True) - - #ed.CmdKeyClear(stc.STC_KEY_BACK, - # stc.STC_SCMOD_CTRL) - #ed.CmdKeyAssign(stc.STC_KEY_BACK, - # stc.STC_SCMOD_CTRL, - # stc.STC_CMD_DELWORDLEFT) - ed.SetText(demoText) - # if wx.USE_UNICODE: import codecs decode = codecs.lookup("utf-8")[1] ed.GotoPos(ed.GetLength()) ed.AddText("\n\nwx.StyledTextCtrl can also do Unicode:\n") uniline = ed.GetCurrentLine() - unitext, l = decode('\xd0\x9f\xd0\xb8\xd1\x82\xd0\xbe\xd0\xbd - ' - '\xd0\xbb\xd1\x83\xd1\x87\xd1\x88\xd0\xb8\xd0\xb9 ' - '\xd1\x8f\xd0\xb7\xd1\x8b\xd0\xba \xd0\xbf\xd1\x80\xd0\xbe\xd0\xb3\xd1\x80\xd0\xb0\xd0\xbc\xd0\xbc\xd0\xb8\xd1\x80\xd0\xbe\xd0\xb2\xd0\xb0\xd0\xbd\xd0\xb8\xd1\x8f!\n\n') + unitext, l = decode(b'\xd0\x9f\xd0\xb8\xd1\x82\xd0\xbe\xd0\xbd - ' + + b'\xd0\xbb\xd1\x83\xd1\x87\xd1\x88\xd0\xb8\xd0\xb9 ' + + b'\xd1\x8f\xd0\xb7\xd1\x8b\xd0\xba \xd0\xbf\xd1\x80\xd0\xbe\xd0\xb3\xd1\x80\xd0\xb0\xd0\xbc\xd0\xbc\xd0\xb8\xd1\x80\xd0\xbe\xd0\xb2\xd0\xb0\xd0\xbd\xd0\xb8\xd1\x8f!\n\n') ed.AddText('\tRussian: ') ed.AddText(unitext) ed.GotoPos(0) - #else: - # #ed.StyleSetFontEncoding(stc.STC_STYLE_DEFAULT, wx.FONTENCODING_KOI8) - # #text = u'\u041f\u0438\u0442\u043e\u043d - \u043b\u0443\u0447\u0448\u0438\u0439 \u044f\u0437\u044b\u043a \n\u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f!' - # #text = text.encode('koi8-r') - # #ed.StyleSetFontEncoding(stc.STC_STYLE_DEFAULT, wx.FONTENCODING_BIG5) - # #text = u'Python \u662f\u6700\u597d\u7684\u7de8\u7a0b\u8a9e\u8a00\uff01' - # #text = text.encode('big5') - # ed.GotoPos(ed.GetLength()) - # ed.AddText('\n\n' + text) ed.EmptyUndoBuffer() diff --git a/demo/TablePrint.py b/demo/TablePrint.py index 2be3db08..f058962b 100644 --- a/demo/TablePrint.py +++ b/demo/TablePrint.py @@ -24,7 +24,7 @@ class TablePanel(wx.Panel): box = wx.BoxSizer(wx.VERTICAL) box.Add((20, 30)) - keys = buttonDefs.keys() + keys = list(buttonDefs.keys()) keys.sort() for k in keys: @@ -38,8 +38,8 @@ class TablePanel(wx.Panel): def OnButton(self, evt): funct = buttonDefs[evt.GetId()][0] - code = 'self.' + funct + '()' - eval(code) + funct = getattr(self, funct) + funct() def ReadData(self): test_file = "./data/testtable.txt" diff --git a/demo/Threads.py b/demo/Threads.py index 1cfe59b1..79fb6cb9 100644 --- a/demo/Threads.py +++ b/demo/Threads.py @@ -1,11 +1,9 @@ -#!/usr/bin/env python +import random +import time +from six.moves import _thread -import random -import time -import thread - -import wx -import wx.lib.newevent +import wx +import wx.lib.newevent #---------------------------------------------------------------------- @@ -23,7 +21,7 @@ class CalcBarThread: def Start(self): self.keepGoing = self.running = True - thread.start_new_thread(self.Run, ()) + _thread.start_new_thread(self.Run, ()) def Stop(self): self.keepGoing = False diff --git a/demo/TreeMixin.py b/demo/TreeMixin.py index b838ac1a..782d0ec0 100644 --- a/demo/TreeMixin.py +++ b/demo/TreeMixin.py @@ -126,31 +126,31 @@ class VirtualTreeCtrl(DemoTreeMixin, wx.TreeCtrl): pass -class VirtualTreeListCtrl(DemoTreeMixin, wx.adv.TreeListCtrl): - def __init__(self, *args, **kwargs): - kwargs['style'] = wx.TR_DEFAULT_STYLE | wx.TR_FULL_ROW_HIGHLIGHT - super(VirtualTreeListCtrl, self).__init__(*args, **kwargs) - self.AppendColumn('Column 0') - self.AppendColumn('Column 1') - for art in wx.ART_TIP, wx.ART_WARNING: - self.imageList.Add(wx.ArtProvider.GetBitmap(art, wx.ART_OTHER, - (16, 16))) - - def OnGetItemText(self, indices, column=0): - # Return a different label depending on column. - return '%s, column %d'%\ - (super(VirtualTreeListCtrl, self).OnGetItemText(indices), column) - - def OnGetItemImage(self, indices, which, column=0): - # Also change the image of the other columns when the item has - # children. - if column == 0: - return super(VirtualTreeListCtrl, self).OnGetItemImage(indices, - which) - elif self.OnGetChildrenCount(indices): - return 4 - else: - return 3 +# class VirtualTreeListCtrl(DemoTreeMixin, wx.adv.TreeListCtrl): +# def __init__(self, *args, **kwargs): +# kwargs['style'] = wx.TR_DEFAULT_STYLE | wx.TR_FULL_ROW_HIGHLIGHT +# super(VirtualTreeListCtrl, self).__init__(*args, **kwargs) +# self.AppendColumn('Column 0') +# self.AppendColumn('Column 1') +# for art in wx.ART_TIP, wx.ART_WARNING: +# self.imageList.Add(wx.ArtProvider.GetBitmap(art, wx.ART_OTHER, +# (16, 16))) +# +# def OnGetItemText(self, indices, column=0): +# # Return a different label depending on column. +# return '%s, column %d'%\ +# (super(VirtualTreeListCtrl, self).OnGetItemText(indices), column) +# +# def OnGetItemImage(self, indices, which, column=0): +# # Also change the image of the other columns when the item has +# # children. +# if column == 0: +# return super(VirtualTreeListCtrl, self).OnGetItemImage(indices, +# which) +# elif self.OnGetChildrenCount(indices): +# return 4 +# else: +# return 3 class VirtualCustomTreeCtrl(DemoTreeMixin, @@ -192,7 +192,7 @@ class TreeNotebook(wx.Notebook): super(TreeNotebook, self).__init__(*args, **kwargs) self.trees = [] for class_, title in [(VirtualTreeCtrl, 'TreeCtrl'), - (VirtualTreeListCtrl, 'TreeListCtrl'), + #(VirtualTreeListCtrl, 'TreeListCtrl'), (VirtualCustomTreeCtrl, 'CustomTreeCtrl')]: tree = class_(self, treemodel=treemodel, log=log) self.trees.append(tree) diff --git a/wx/lib/printout.py b/wx/lib/printout.py index 5b7ad7c6..52afc6f6 100644 --- a/wx/lib/printout.py +++ b/wx/lib/printout.py @@ -27,6 +27,8 @@ import copy import types import wx +import six + class PrintBase(object): def SetPrintFont(self, font): # set the DC font parameters fattr = font["Attr"] @@ -271,7 +273,7 @@ class PrintTableDraw(wx.ScrolledWindow, PrintBase): self.column.append(pos_x) #module logic expects two dimensional data -- fix input if needed - if isinstance(self.data,types.StringTypes): + if isinstance(self.data, six.string_types): self.data = [[copy.copy(self.data)]] # a string becomes a single cell try: rows = len(self.data) @@ -280,7 +282,7 @@ class PrintTableDraw(wx.ScrolledWindow, PrintBase): rows = 1 first_value = self.data[0] - if isinstance(first_value, types.StringTypes): # a sequence of strings + if isinstance(first_value, six.string_types): # a sequence of strings if self.label == [] and self.set_column == []: data = [] for x in self.data: #becomes one column @@ -560,7 +562,7 @@ class PrintTableDraw(wx.ScrolledWindow, PrintBase): self.col = 0 max_y = 0 for vtxt in row_val: - if not isinstance(vtxt,types.StringTypes): + if not isinstance(vtxt, six.string_types): vtxt = str(vtxt) self.region = self.column[self.col+1] - self.column[self.col] self.indent = self.column[self.col] @@ -681,7 +683,10 @@ class PrintTable(object): self.row_line_colour = {} self.parentFrame = parentFrame - self.SetPreviewSize() + if parentFrame: + self.SetPreviewSize(parentFrame.GetPosition(), parentFrame.GetSize()) + else: + self.SetPreviewSize() self.printData = wx.PrintData() self.scale = 1.0 From 2f1f0ee1e96862e0cd93706e2ea3d4d8c2785bcd Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Wed, 10 Aug 2016 15:57:47 -0700 Subject: [PATCH 32/33] XRC and MemoryFSHandler fixes --- demo/XmlResource.py | 17 +++++++++------- demo/XmlResourceHandler.py | 18 +++++++---------- demo/XmlResourceSubclass.py | 10 ++++------ etg/_xrc.py | 5 +++-- etg/filesys.py | 39 ++++++++++++++++++++++++++++--------- etgtools/extractors.py | 2 +- ext/wxWidgets | 2 +- 7 files changed, 56 insertions(+), 37 deletions(-) diff --git a/demo/XmlResource.py b/demo/XmlResource.py index 8b7060e6..268b2a89 100644 --- a/demo/XmlResource.py +++ b/demo/XmlResource.py @@ -18,8 +18,10 @@ class TestPanel(wx.Panel): label = wx.StaticText(self, -1, "The lower panel was built from this XML:") label.SetFont(wx.Font(12, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)) - resourceText = open(RESFILE).read() - text = wx.TextCtrl(self, -1, resourceText, + with open(RESFILE, 'rb') as f: + resourceBytes = f.read() + + text = wx.TextCtrl(self, -1, resourceBytes, style=wx.TE_READONLY|wx.TE_MULTILINE) text.SetInsertionPoint(0) @@ -33,13 +35,14 @@ class TestPanel(wx.Panel): elif 1: # or from a Virtual FileSystem: wx.FileSystem.AddHandler(wx.MemoryFSHandler()) - wx.MemoryFSHandler().AddFile("XRC_Resources/data_file", resourceText) - res = xrc.XmlResource("memory:XRC_Resources/data_file") + wx.MemoryFSHandler.AddFile("my_XRC_data", resourceBytes) + # notice the matching filename + res = xrc.XmlResource("memory:my_XRC_data") else: - # or from a string, like this: - res = xrc.EmptyXmlResource() - res.LoadFromString(resourceText) + # or from a buffer compatible object, like this: + res = xrc.XmlResource() + res.LoadFromBuffer(resourceBytes) # Now create a panel from the resource data diff --git a/demo/XmlResourceHandler.py b/demo/XmlResourceHandler.py index cf10c439..33339195 100644 --- a/demo/XmlResourceHandler.py +++ b/demo/XmlResourceHandler.py @@ -5,7 +5,7 @@ import wx.xrc as xrc #---------------------------------------------------------------------- -resourceText = r''' +resourceText = br''' @@ -48,8 +48,7 @@ class MyCustomPanel(wx.Panel): # the different requirements are. class PreMyCustomPanel(wx.Panel): def __init__(self): - p = wx.PrePanel() - self.PostCreate(p) + wx.Panel.__init__(self) def Create(self, parent, id, pos, size, style, name): wx.Panel.Create(self, parent, id, pos, size, style, name) @@ -84,12 +83,10 @@ class MyCustomPanelXmlHandler(xrc.XmlResourceHandler): def DoCreateResource(self): # NOTE: wxWindows can be created in either a single-phase or # in a two-phase way. Single phase is what you normally do, - # and two-phase creates the instnace first, and then later + # and two-phase creates the instance first, and then later # creates the actual window when the Create method is called. - # (In wxPython the first phase is done using the wxPre* - # function, for example, wxPreFrame, wxPrePanel, etc.) # - # wxXmlResource supports either method, a premade instance can + # wxXmlResource supports either method, a pre-made instance can # be created and populated by xrc using the appropriate # LoadOn* method (such as LoadOnPanel) or xrc can create the # instance too, using the Load* method. However this makes @@ -98,7 +95,7 @@ class MyCustomPanelXmlHandler(xrc.XmlResourceHandler): # instance, then you can make the handle much simpler. I'll # show both methods below. - if 1: + if 0: # The simple method assumes that there is no existing # instance. Be sure of that with an assert. assert self.GetInstance() is None @@ -156,9 +153,9 @@ class TestPanel(wx.Panel): line = wx.StaticLine(self, -1) # Load the resource - res = xrc.EmptyXmlResource() + res = xrc.XmlResource() res.InsertHandler(MyCustomPanelXmlHandler()) - res.LoadFromString(resourceText) + res.LoadFromBuffer(resourceText) # Now create a panel from the resource data panel = res.LoadObject(self, "MyPanel", "MyCustomPanel") @@ -171,7 +168,6 @@ class TestPanel(wx.Panel): sizer.Add(panel, 1, wx.EXPAND|wx.ALL, 5) self.SetSizer(sizer) - self.SetAutoLayout(True) #---------------------------------------------------------------------- diff --git a/demo/XmlResourceSubclass.py b/demo/XmlResourceSubclass.py index 07b44b8a..3ffb043f 100644 --- a/demo/XmlResourceSubclass.py +++ b/demo/XmlResourceSubclass.py @@ -5,7 +5,7 @@ import wx.xrc as xrc #---------------------------------------------------------------------- -resourceText = r''' +resourceText = br'''