From 431f4c161d25318d8d612dc90df1d063a54f1784 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Thu, 6 Sep 2001 21:19:48 +0000 Subject: [PATCH] Added pySketch sample git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@11570 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- wxPython/CHANGES.txt | 1 + wxPython/MANIFEST.in | 13 +- wxPython/distrib/make_installer.py | 7 +- wxPython/samples/pySketch/.cvsignore | 1 + .../samples/pySketch/images/duplicate.bmp | Bin 0 -> 246 bytes .../samples/pySketch/images/ellipseIcon.bmp | Bin 0 -> 630 bytes .../pySketch/images/ellipseIconSel.bmp | Bin 0 -> 630 bytes .../samples/pySketch/images/fillOptIcon.bmp | Bin 0 -> 246 bytes wxPython/samples/pySketch/images/lineIcon.bmp | Bin 0 -> 630 bytes .../samples/pySketch/images/lineIconSel.bmp | Bin 0 -> 630 bytes .../samples/pySketch/images/lineOptIcon.bmp | Bin 0 -> 246 bytes wxPython/samples/pySketch/images/logo.bmp | Bin 0 -> 3526 bytes wxPython/samples/pySketch/images/moveBack.bmp | Bin 0 -> 246 bytes .../samples/pySketch/images/moveForward.bmp | Bin 0 -> 246 bytes wxPython/samples/pySketch/images/new.bmp | Bin 0 -> 238 bytes wxPython/samples/pySketch/images/open.bmp | Bin 0 -> 238 bytes .../samples/pySketch/images/penOptIcon.bmp | Bin 0 -> 246 bytes wxPython/samples/pySketch/images/rectIcon.bmp | Bin 0 -> 630 bytes .../samples/pySketch/images/rectIconSel.bmp | Bin 0 -> 630 bytes wxPython/samples/pySketch/images/save.bmp | Bin 0 -> 822 bytes .../samples/pySketch/images/selectIcon.bmp | Bin 0 -> 630 bytes .../samples/pySketch/images/selectIconSel.bmp | Bin 0 -> 630 bytes wxPython/samples/pySketch/images/textIcon.bmp | Bin 0 -> 630 bytes .../samples/pySketch/images/textIconSel.bmp | Bin 0 -> 630 bytes wxPython/samples/pySketch/images/undo.bmp | Bin 0 -> 246 bytes wxPython/samples/pySketch/pySketch.py | 2638 +++++++++++++++++ 26 files changed, 2653 insertions(+), 7 deletions(-) create mode 100644 wxPython/samples/pySketch/.cvsignore create mode 100644 wxPython/samples/pySketch/images/duplicate.bmp create mode 100644 wxPython/samples/pySketch/images/ellipseIcon.bmp create mode 100644 wxPython/samples/pySketch/images/ellipseIconSel.bmp create mode 100644 wxPython/samples/pySketch/images/fillOptIcon.bmp create mode 100644 wxPython/samples/pySketch/images/lineIcon.bmp create mode 100644 wxPython/samples/pySketch/images/lineIconSel.bmp create mode 100644 wxPython/samples/pySketch/images/lineOptIcon.bmp create mode 100644 wxPython/samples/pySketch/images/logo.bmp create mode 100644 wxPython/samples/pySketch/images/moveBack.bmp create mode 100644 wxPython/samples/pySketch/images/moveForward.bmp create mode 100644 wxPython/samples/pySketch/images/new.bmp create mode 100644 wxPython/samples/pySketch/images/open.bmp create mode 100644 wxPython/samples/pySketch/images/penOptIcon.bmp create mode 100644 wxPython/samples/pySketch/images/rectIcon.bmp create mode 100644 wxPython/samples/pySketch/images/rectIconSel.bmp create mode 100644 wxPython/samples/pySketch/images/save.bmp create mode 100644 wxPython/samples/pySketch/images/selectIcon.bmp create mode 100644 wxPython/samples/pySketch/images/selectIconSel.bmp create mode 100644 wxPython/samples/pySketch/images/textIcon.bmp create mode 100644 wxPython/samples/pySketch/images/textIconSel.bmp create mode 100644 wxPython/samples/pySketch/images/undo.bmp create mode 100644 wxPython/samples/pySketch/pySketch.py diff --git a/wxPython/CHANGES.txt b/wxPython/CHANGES.txt index 43ef79bba6..3169d4befb 100644 --- a/wxPython/CHANGES.txt +++ b/wxPython/CHANGES.txt @@ -40,6 +40,7 @@ Added wxGenBitmapTextButton, TablePrint, etc. contribs from Lorne White. Added wxNativeFontInfo and wxFontMapper. +Added pySketch to the samples. diff --git a/wxPython/MANIFEST.in b/wxPython/MANIFEST.in index 3cf83906b3..3b5fb84398 100644 --- a/wxPython/MANIFEST.in +++ b/wxPython/MANIFEST.in @@ -47,6 +47,9 @@ include samples/wxProject/*.py include samples/StyleEditor/*.py include samples/StyleEditor/*.txt include samples/StyleEditor/*.cfg +include samples/pySketch/*.py +include samples/pySketch/images/*.bmp + include wxPython/lib/*.py include wxPython/lib/*.txt @@ -77,11 +80,11 @@ include src/gtk/*.h include src/gtk/*.py include tools/*.py -tools\XRCed\CHANGES -tools\XRCed\TODO -tools\XRCed\README -tools\XRCed\*.py -tools\XRCed\*.xrc +tools/XRCed/CHANGES +tools/XRCed/TODO +tools/XRCed/README +tools/XRCed/*.py +tools/XRCed/*.xrc include contrib/glcanvas/*.i diff --git a/wxPython/distrib/make_installer.py b/wxPython/distrib/make_installer.py index 4be598300a..50435888ed 100644 --- a/wxPython/distrib/make_installer.py +++ b/wxPython/distrib/make_installer.py @@ -136,10 +136,13 @@ Source: "samples\stxview\*.stx"; DestDir: "{app}\wxPython\sample Source: "samples\stxview\*.txt"; DestDir: "{app}\wxPython\samples\stxview"; Components: samples Source: "samples\stxview\StructuredText\*.py"; DestDir: "{app}\wxPython\samples\stxview\StructuredText"; Components: samples -Source: "samples\StyleEditor\*.txt"; DestDir: "{app}\wxPython\samples\StyleEditor"; Components: samples -Source: "samples\StyleEditor\*.py"; DestDir: "{app}\wxPython\samples\StyleEditor"; Components: samples +Source: "samples\StyleEditor\*.txt"; DestDir: "{app}\wxPython\samples\StyleEditor"; Components: samples +Source: "samples\StyleEditor\*.py"; DestDir: "{app}\wxPython\samples\StyleEditor"; Components: samples Source: "samples\StyleEditor\*.cfg"; DestDir: "{app}\wxPython\samples\StyleEditor"; Components: samples +Source: "samples\pySketch\*.py"; DestDir: "{app}\wxPython\samples\pySketch"; Components: samples +Source: "samples\pySketch\images\*.bmp"; DestDir: "{app}\wxPython\samples\pySketch\images"; Components: samples + ;;------------------------------------------------------------ diff --git a/wxPython/samples/pySketch/.cvsignore b/wxPython/samples/pySketch/.cvsignore new file mode 100644 index 0000000000..96a9f2a176 --- /dev/null +++ b/wxPython/samples/pySketch/.cvsignore @@ -0,0 +1 @@ +errors.txt diff --git a/wxPython/samples/pySketch/images/duplicate.bmp b/wxPython/samples/pySketch/images/duplicate.bmp new file mode 100644 index 0000000000000000000000000000000000000000..be7c02f61bad3bab27cdbc3c92043edd1daab470 GIT binary patch literal 246 zcmZ9EF%AMT3%K2Q*Zw7#fF29nS9+^2Jx!NvD*a+Bfz~hzAWw z4@!xpW2)0pM>_nLZquC$nBQCfI)6HFx^KJiT#tA4x_9rP`bDaLma1nV|Iqm41#vnX0_t&$BVuqG(wu^dR^cq8czG_=ETC8~n**2tnu6`uG4* zpmI`oYVj!QG~zX_4ST$8cNQ>zG*@G634QF{|GBXD+Y0(TKF;gId(?TUb^ezgz+W4r H;dk2#r`q}V literal 0 HcmV?d00001 diff --git a/wxPython/samples/pySketch/images/fillOptIcon.bmp b/wxPython/samples/pySketch/images/fillOptIcon.bmp new file mode 100644 index 0000000000000000000000000000000000000000..6291dc8af0fe451e77081c95a4197e988ce01049 GIT binary patch literal 246 zcmZWiu?>JQ3=0wxAXS1hxPzTvI>DN;BP-wZfZW4coTLaO+}KWQrCHXC92;x~r^<#} z3)s{}vlmQ+B4#%zT97H{jG$OiDcXTJ`lM3P?S?oegF2F=KAgwb$?q)g;paXVL~_C$ Ri}dRF4nM~cOM&?8y#bf1MW+A& literal 0 HcmV?d00001 diff --git a/wxPython/samples/pySketch/images/lineIcon.bmp b/wxPython/samples/pySketch/images/lineIcon.bmp new file mode 100644 index 0000000000000000000000000000000000000000..27ba6e574365de98296a6cdc6269a53e6c041a25 GIT binary patch literal 630 zcmcJMJr08~3`R}Gz}6n2Bj>1)n0lLvt{!`=+${b46USle(2vCN%R?Z@<9S?2bD*x6 zcb^l@=n(hP-lRmB2!ohyK}AcY6s#hu4ysi;OkA=h<&@O?NgO>^j(Wc}Lw9~>9C8Yuj+?$(6YnDc literal 0 HcmV?d00001 diff --git a/wxPython/samples/pySketch/images/lineIconSel.bmp b/wxPython/samples/pySketch/images/lineIconSel.bmp new file mode 100644 index 0000000000000000000000000000000000000000..713f632ffef29b7573a32e49d10eb9061865ada8 GIT binary patch literal 630 zcmb`EF%H5o3`GrM=*STmIR`>w;x?#RGjgmPi5rCfZO3uD!f)dEHfO$JHI!nyWcHeJQ3^NiEV>yF6*!iUstQmV&uVO8B+6W2U*iPcA-H#g`Cv79w!3nbm=GsNs y3jqccu8fvW!nipDANAhST8CWhyPQ{UvDJXxBJp6kv_TiDo%uk7|*M$e~mOu*t literal 0 HcmV?d00001 diff --git a/wxPython/samples/pySketch/images/logo.bmp b/wxPython/samples/pySketch/images/logo.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2723334d1a746e91c14e92d34710a4a41b904061 GIT binary patch literal 3526 zcmeH}J#^Yu6vuB8BjHBk!xH8h@l?q`069g*2nE@0jpe3JsFM<E8S9eMi{uu6Xd8xdBkX(LO#Ee4aqq zTmN&@>y|%HzKrkg?qEEegH>*TWm(W_wO~4(!fZB!a=8pDDZ-#T0mp7bI2?xIa0s`z zw-AfPz_w63TEMV3h4FX{i^T$NZf>C6Zo}2p6^uqB==b|@d3g!*`5Xu#FqurC*Xtoq zU@#a!r_+Hx_T6q5ip3&aUtfc5+u%43#86Lgtvz2rx!Z#<&O7}9sOY0T96=2CoWT$p zs4ccT&|cg@5%-+w3;^|j8iFxFZvbWN%cvoZ1^_ZDvKTOcjq}kM0Qc-m?41@^$f2{K zqP8)aLTfgMGLAdAKe)!TLK|INAECWAY8z`?0Gk>`RK;nQqFk{BT+Gb_aX_UE;w8|VoQ zsf;8g)*=?ArIRj(p^2_s!sdF;DjRfNo+L_2kUXVFP&t!9zh$0MhM@u^J{xuiqLO17 zhA2B3IhAwgk>n?e29kn6sUQoCv?7m0lxT+HUNR?9MV3{oe;1+nl8zTP>logcV>zlDp!yZ4q+IkW3|3?cyV z^U2$iX6Uc&`Vj)`dW}7bLgee_F0xElwGfaM*z!JHOn;2Q~T^hK?}Z#5Kl_l z8w9M{VOk+H=?{e8XqC4{ql&;TCc+=c7PXRYXx3rv+{>I_s>RU0R{6=$j+a1yL~IvK zea|qC&uaA$F9ZNWQB$uP#@;e>2>AJ=aORH{!_ZId*9Ux%+8~^6x~axQWW7{BO4Zr| z0o}AT!>rd{d9~r2Y9XqdRRl0|Cz!b)wQ;O{ZyH+l@P{L;4gbvD7n7U0BRL3=OINk3 zX&9APH7*b!hKpK7H;mFtJsqqzykgS!%Ln;<-mN~rODZC;_wu`ZJ|}SbATdNj(ZVju zX*$Ji;DzwN)uHGMSI&o1YL?I-)dy5a@1{vSp2?CRh<&?+Yg0)J*DFW1$ z$K&$1JmY&sU0`Rak8g1)-_k=1Omuq*Y(dg52cCZ{7xIATo4%KA84nwcJ&nh=K=8Yp dOL-*1hBv=?&hZaZ|NiN-jnL|U0)OUS@fXa=G)Mpd literal 0 HcmV?d00001 diff --git a/wxPython/samples/pySketch/images/moveBack.bmp b/wxPython/samples/pySketch/images/moveBack.bmp new file mode 100644 index 0000000000000000000000000000000000000000..4d596e6321e10761103035006acc22cdb5790ba3 GIT binary patch literal 246 zcmYjKxe>!K3}fzGq*KPo96q&PCp8Nd)CSZx8Y<_O0ZI8BhyeIW+K-bGAJ`jwEk0;@Adh&c_46l9unVqg(u<{60NEr|Rz$GV=+-@*|jhIL|)zR4t`!B2{6Vs2fCkt#q1KC P@SFI__rX8&Q51;>ne0`U literal 0 HcmV?d00001 diff --git a/wxPython/samples/pySketch/images/new.bmp b/wxPython/samples/pySketch/images/new.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d66feb2384ad4c626a078c995c1a3e49af78ec3c GIT binary patch literal 238 zcmbu2yA6Oa5JQcGL>b0l4tjP;1(ew=nJ8j|<6WY3bU0`GK9aK^RUId`!4(ELX-1{H u>6c`QQqJ8_T9s0iQsQ8(Wab^@`3kuPA_p->w)Xt4Pkz>06 literal 0 HcmV?d00001 diff --git a/wxPython/samples/pySketch/images/open.bmp b/wxPython/samples/pySketch/images/open.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2ddb142162bc649f7673a4ff75102a7b8e19b563 GIT binary patch literal 238 zcmYLBu?+$-3_Qe3U&0uaNWTF+yST!iE?c1AA{!(VA^H^IY)9f^-`Qu!kLL%07v;vh zGB3;%7Ba9q2ife83^0&$2$>~XYg8Ky4~$_Ilg})YP6&Ollk-NbD#i4!GQyA=hV<^6 dQi;O-PJF4g_JMP*NH_oh literal 0 HcmV?d00001 diff --git a/wxPython/samples/pySketch/images/penOptIcon.bmp b/wxPython/samples/pySketch/images/penOptIcon.bmp new file mode 100644 index 0000000000000000000000000000000000000000..93497a9a1763d558515d22c1d475fbdfa0923355 GIT binary patch literal 246 zcmY*Su?>VU3^dY9lrYB6LC-F!pmk}a43J0+lQKo-p$%sT1YCS~j!%iF`Jjdc_6Ns{ z9sMj|(+M|@@q!$J7XOrjpWT3^fV=meAf#?5pdAJzyL%H4Gjf#?5pdAJzyL%H4Gj4go4?00U%|5Csq^AW3!g zaJzsu<1!!ODom1E>fvsHGazpP literal 0 HcmV?d00001 diff --git a/wxPython/samples/pySketch/images/selectIcon.bmp b/wxPython/samples/pySketch/images/selectIcon.bmp new file mode 100644 index 0000000000000000000000000000000000000000..73c00a1e30c210095b9042c39b90eeae8c3399f9 GIT binary patch literal 630 zcmZ?rEn{K;gEAng0mKSW%*en3WB~zG9>f#?5pdAJzyL%H4Gjf#?5pdAJzyL%H4GjV%EwC-~N literal 0 HcmV?d00001 diff --git a/wxPython/samples/pySketch/images/textIcon.bmp b/wxPython/samples/pySketch/images/textIcon.bmp new file mode 100644 index 0000000000000000000000000000000000000000..06b0c89e73a49dbd8c5e2b91973fcf3dae9de0ed GIT binary patch literal 630 zcmd5(yA6ae3^j;?4o0A44rGanZ6eaTWUOo!o*m3V+S5t*;@`8S*ol{IZ^^L3&d}-7 zhB`#RRo8G-b3=$ABvKqKLP{yYYayFZ&Kf~+G^M6Of4m?=eBnntFt66f?`JM;orhA6 jf5}6PI-ibc^MMp`&W2CD;*THp_j&4jjwL@J)w<~f*AeT$ literal 0 HcmV?d00001 diff --git a/wxPython/samples/pySketch/images/textIconSel.bmp b/wxPython/samples/pySketch/images/textIconSel.bmp new file mode 100644 index 0000000000000000000000000000000000000000..5055c32f1f483e913fb49a34c62bcceaedbb4db1 GIT binary patch literal 630 zcmd5(F%Ezr46Me1(I+_i4nyMPZ-}gnK8q*$1H4i|VP^1XORtwSEo{d*QpAb4!k6R& zO~`)1E 0 + onlyOne = len(self.selection) == 1 + isText = onlyOne and (self.selection[0].getType() == obj_TEXT) + front = onlyOne and (self.selection[0] == self.contents[0]) + back = onlyOne and (self.selection[0] == self.contents[-1]) + + # Enable/disable our menu items. + + self.fileMenu.Enable(wxID_SAVE, canSave) + self.fileMenu.Enable(wxID_REVERT, canRevert) + + self.editMenu.Enable(menu_UNDO, canUndo) + self.editMenu.Enable(menu_DUPLICATE, selection) + self.editMenu.Enable(menu_EDIT_TEXT, isText) + self.editMenu.Enable(menu_DELETE, selection) + + self.toolsMenu.Check(menu_SELECT, self.curTool == self.selectIcon) + self.toolsMenu.Check(menu_LINE, self.curTool == self.lineIcon) + self.toolsMenu.Check(menu_RECT, self.curTool == self.rectIcon) + self.toolsMenu.Check(menu_ELLIPSE, self.curTool == self.ellipseIcon) + self.toolsMenu.Check(menu_TEXT, self.curTool == self.textIcon) + + self.objectMenu.Enable(menu_MOVE_FORWARD, onlyOne and not front) + self.objectMenu.Enable(menu_MOVE_TO_FRONT, onlyOne and not front) + self.objectMenu.Enable(menu_MOVE_BACKWARD, onlyOne and not back) + self.objectMenu.Enable(menu_MOVE_TO_BACK, onlyOne and not back) + + # Enable/disable our toolbar icons. + + self.toolbar.EnableTool(wxID_NEW, true) + self.toolbar.EnableTool(wxID_OPEN, true) + self.toolbar.EnableTool(wxID_SAVE, canSave) + self.toolbar.EnableTool(menu_UNDO, canUndo) + self.toolbar.EnableTool(menu_DUPLICATE, selection) + self.toolbar.EnableTool(menu_MOVE_FORWARD, onlyOne and not front) + self.toolbar.EnableTool(menu_MOVE_BACKWARD, onlyOne and not back) + + + def _setCurrentTool(self, newToolIcon): + """ Set the currently selected tool. + """ + if self.curTool == newToolIcon: return # Nothing to do. + + if self.curTool != None: + self.curTool.deselect() + + newToolIcon.select() + self.curTool = newToolIcon + + + def _setPenColour(self, colour): + """ Set the default or selected object's pen colour. + """ + if len(self.selection) > 0: + self._saveUndoInfo() + for obj in self.selection: + obj.setPenColour(colour) + self.drawPanel.Refresh() + else: + self.penColour = colour + self.optionIndicator.setPenColour(colour) + + + def _setFillColour(self, colour): + """ Set the default or selected object's fill colour. + """ + if len(self.selection) > 0: + self._saveUndoInfo() + for obj in self.selection: + obj.setFillColour(colour) + self.drawPanel.Refresh() + else: + self.fillColour = colour + self.optionIndicator.setFillColour(colour) + + + def _setLineSize(self, size): + """ Set the default or selected object's line size. + """ + if len(self.selection) > 0: + self._saveUndoInfo() + for obj in self.selection: + obj.setLineSize(size) + self.drawPanel.Refresh() + else: + self.lineSize = size + self.optionIndicator.setLineSize(size) + + + def _saveUndoInfo(self): + """ Remember the current state of the document, to allow for undo. + + We make a copy of the document's contents, so that we can return to + the previous contents if the user does something and then wants to + undo the operation. + """ + savedContents = [] + for obj in self.contents: + savedContents.append([obj.getType(), obj.getData()]) + + savedSelection = [] + for i in range(len(self.contents)): + if self.contents[i] in self.selection: + savedSelection.append(i) + + self.undoInfo = {"contents" : savedContents, + "selection" : savedSelection} + + + def _resizeObject(self, obj, anchorPt, oldPt, newPt): + """ Resize the given object. + + 'anchorPt' is the unchanging corner of the object, while the + opposite corner has been resized. 'oldPt' are the current + coordinates for this corner, while 'newPt' are the new coordinates. + The object should fit within the given dimensions, though if the + new point is less than the anchor point the object will need to be + moved as well as resized, to avoid giving it a negative size. + """ + if obj.getType() == obj_TEXT: + # Not allowed to resize text objects -- they're sized to fit text. + wxBell() + return + + self._saveUndoInfo() + + topLeft = wxPoint(min(anchorPt.x, newPt.x), + min(anchorPt.y, newPt.y)) + botRight = wxPoint(max(anchorPt.x, newPt.x), + max(anchorPt.y, newPt.y)) + + newWidth = botRight.x - topLeft.x + newHeight = botRight.y - topLeft.y + + if obj.getType() == obj_LINE: + # Adjust the line so that its start and end points match the new + # overall object size. + + startPt = obj.getStartPt() + endPt = obj.getEndPt() + + slopesDown = ((startPt.x < endPt.x) and (startPt.y < endPt.y)) or \ + ((startPt.x > endPt.x) and (startPt.y > endPt.y)) + + # Handle the user flipping the line. + + hFlip = ((anchorPt.x < oldPt.x) and (anchorPt.x > newPt.x)) or \ + ((anchorPt.x > oldPt.x) and (anchorPt.x < newPt.x)) + vFlip = ((anchorPt.y < oldPt.y) and (anchorPt.y > newPt.y)) or \ + ((anchorPt.y > oldPt.y) and (anchorPt.y < newPt.y)) + + if (hFlip and not vFlip) or (vFlip and not hFlip): + slopesDown = not slopesDown # Line flipped. + + if slopesDown: + obj.setStartPt(wxPoint(0, 0)) + obj.setEndPt(wxPoint(newWidth, newHeight)) + else: + obj.setStartPt(wxPoint(0, newHeight)) + obj.setEndPt(wxPoint(newWidth, 0)) + + # Finally, adjust the bounds of the object to match the new dimensions. + + obj.setPosition(topLeft) + obj.setSize(wxSize(botRight.x - topLeft.x, botRight.y - topLeft.y)) + + self.drawPanel.Refresh() + + + def _moveObject(self, offsetX, offsetY): + """ Move the currently selected object(s) by the given offset. + """ + self._saveUndoInfo() + + for obj in self.selection: + pos = obj.getPosition() + pos.x = pos.x + offsetX + pos.y = pos.y + offsetY + obj.setPosition(pos) + + self.drawPanel.Refresh() + + + def _buildLineSizePopup(self, lineSize): + """ Build the pop-up menu used to set the line size. + + 'lineSize' is the current line size value. The corresponding item + is checked in the pop-up menu. + """ + menu = wxMenu() + menu.Append(id_LINESIZE_0, "no line", checkable=true) + menu.Append(id_LINESIZE_1, "1-pixel line", checkable=true) + menu.Append(id_LINESIZE_2, "2-pixel line", checkable=true) + menu.Append(id_LINESIZE_3, "3-pixel line", checkable=true) + menu.Append(id_LINESIZE_4, "4-pixel line", checkable=true) + menu.Append(id_LINESIZE_5, "5-pixel line", checkable=true) + + if lineSize == 0: menu.Check(id_LINESIZE_0, true) + elif lineSize == 1: menu.Check(id_LINESIZE_1, true) + elif lineSize == 2: menu.Check(id_LINESIZE_2, true) + elif lineSize == 3: menu.Check(id_LINESIZE_3, true) + elif lineSize == 4: menu.Check(id_LINESIZE_4, true) + elif lineSize == 5: menu.Check(id_LINESIZE_5, true) + + EVT_MENU(self, id_LINESIZE_0, self._lineSizePopupSelected) + EVT_MENU(self, id_LINESIZE_1, self._lineSizePopupSelected) + EVT_MENU(self, id_LINESIZE_2, self._lineSizePopupSelected) + EVT_MENU(self, id_LINESIZE_3, self._lineSizePopupSelected) + EVT_MENU(self, id_LINESIZE_4, self._lineSizePopupSelected) + EVT_MENU(self, id_LINESIZE_5, self._lineSizePopupSelected) + + return menu + + + def _lineSizePopupSelected(self, event): + """ Respond to the user selecting an item from the line size popup menu + """ + id = event.GetId() + if id == id_LINESIZE_0: self._setLineSize(0) + elif id == id_LINESIZE_1: self._setLineSize(1) + elif id == id_LINESIZE_2: self._setLineSize(2) + elif id == id_LINESIZE_3: self._setLineSize(3) + elif id == id_LINESIZE_4: self._setLineSize(4) + elif id == id_LINESIZE_5: self._setLineSize(5) + else: + wxBell() + return + + self.optionIndicator.setLineSize(self.lineSize) + + + def _getEventCoordinates(self, event): + """ Return the coordinates associated with the given mouse event. + + The coordinates have to be adjusted to allow for the current scroll + position. + """ + originX, originY = self.drawPanel.GetViewStart() + unitX, unitY = self.drawPanel.GetScrollPixelsPerUnit() + return wxPoint(event.GetX() + (originX * unitX), + event.GetY() + (originY * unitY)) + + + def _getObjectAndSelectionHandleAt(self, pt): + """ Return the object and selection handle at the given point. + + We draw selection handles (small rectangles) around the currently + selected object(s). If the given point is within one of the + selection handle rectangles, we return the associated object and a + code indicating which selection handle the point is in. If the + point isn't within any selection handle at all, we return the tuple + (None, handle_NONE). + """ + for obj in self.selection: + handle = obj.getSelectionHandleContainingPoint(pt.x, pt.y) + if handle != handle_NONE: + return obj, handle + + return None, handle_NONE + + + def _getObjectAt(self, pt): + """ Return the first object found which is at the given point. + """ + for obj in self.contents: + if obj.objectContainsPoint(pt.x, pt.y): + return obj + return None + + + def _drawObjectOutline(self, offsetX, offsetY): + """ Draw an outline of the currently selected object. + + The selected object's outline is drawn at the object's position + plus the given offset. + + Note that the outline is drawn by *inverting* the window's + contents, so calling _drawObjectOutline twice in succession will + restore the window's contents back to what they were previously. + """ + if len(self.selection) != 1: return + + position = self.selection[0].getPosition() + size = self.selection[0].getSize() + + dc = wxClientDC(self.drawPanel) + self.drawPanel.PrepareDC(dc) + dc.BeginDrawing() + dc.SetPen(wxBLACK_DASHED_PEN) + dc.SetBrush(wxTRANSPARENT_BRUSH) + dc.SetLogicalFunction(wxINVERT) + + dc.DrawRectangle(position.x + offsetX, position.y + offsetY, + size.width, size.height) + + dc.EndDrawing() + + + def _drawVisualFeedback(self, startPt, endPt, type, dashedLine): + """ Draw visual feedback for a drawing operation. + + The visual feedback consists of a line, ellipse, or rectangle based + around the two given points. 'type' should be one of the following + predefined feedback type constants: + + feedback_RECT -> draw rectangular feedback. + feedback_LINE -> draw line feedback. + feedback_ELLIPSE -> draw elliptical feedback. + + if 'dashedLine' is true, the feedback is drawn as a dashed rather + than a solid line. + + Note that the feedback is drawn by *inverting* the window's + contents, so calling _drawVisualFeedback twice in succession will + restore the window's contents back to what they were previously. + """ + dc = wxClientDC(self.drawPanel) + self.drawPanel.PrepareDC(dc) + dc.BeginDrawing() + if dashedLine: + dc.SetPen(wxBLACK_DASHED_PEN) + else: + dc.SetPen(wxBLACK_PEN) + dc.SetBrush(wxTRANSPARENT_BRUSH) + dc.SetLogicalFunction(wxINVERT) + + if type == feedback_RECT: + dc.DrawRectangle(startPt.x, startPt.y, + endPt.x - startPt.x, + endPt.y - startPt.y) + elif type == feedback_LINE: + dc.DrawLine(startPt.x, startPt.y, endPt.x, endPt.y) + elif type == feedback_ELLIPSE: + dc.DrawEllipse(startPt.x, startPt.y, + endPt.x - startPt.x, + endPt.y - startPt.y) + + dc.EndDrawing() + +#---------------------------------------------------------------------------- + +class DrawingObject: + """ An object within the drawing panel. + + A pySketch document consists of a front-to-back ordered list of + DrawingObjects. Each DrawingObject has the following properties: + + 'type' What type of object this is (text, line, etc). + 'position' The position of the object within the document. + 'size' The size of the object within the document. + 'penColour' The colour to use for drawing the object's outline. + 'fillColour' Colour to use for drawing object's interior. + 'lineSize' Line width (in pixels) to use for object's outline. + 'startPt' The point, relative to the object's position, where + an obj_LINE object's line should start. + 'endPt' The point, relative to the object's position, where + an obj_LINE object's line should end. + 'text' The object's text (obj_TEXT objects only). + 'textFont' The text object's font name. + 'textSize' The text object's point size. + 'textBoldface' If true, this text object will be drawn in + boldface. + 'textItalic' If true, this text object will be drawn in italic. + 'textUnderline' If true, this text object will be drawn underlined. + """ + + # ================== + # == Constructors == + # ================== + + def __init__(self, type, position=wxPoint(0, 0), size=wxSize(0, 0), + penColour=wxBLACK, fillColour=wxWHITE, lineSize=1, + text=None, startPt=wxPoint(0, 0), endPt=wxPoint(0,0)): + """ Standard constructor. + + 'type' is the type of object being created. This should be one of + the following constants: + + obj_LINE + obj_RECT + obj_ELLIPSE + obj_TEXT + + The remaining parameters let you set various options for the newly + created DrawingObject. + """ + self.type = type + self.position = position + self.size = size + self.penColour = penColour + self.fillColour = fillColour + self.lineSize = lineSize + self.startPt = startPt + self.endPt = endPt + self.text = text + self.textFont = wxSystemSettings_GetSystemFont( + wxSYS_DEFAULT_GUI_FONT).GetFaceName() + self.textSize = 12 + self.textBoldface = false + self.textItalic = false + self.textUnderline = false + + # ============================= + # == Object Property Methods == + # ============================= + + def getData(self): + """ Return a copy of the object's internal data. + + This is used to save this DrawingObject to disk. + """ + return [self.type, self.position.x, self.position.y, + self.size.width, self.size.height, + self.penColour.Red(), + self.penColour.Green(), + self.penColour.Blue(), + self.fillColour.Red(), + self.fillColour.Green(), + self.fillColour.Blue(), + self.lineSize, + self.startPt.x, self.startPt.y, + self.endPt.x, self.endPt.y, + self.text, + self.textFont, + self.textSize, + self.textBoldface, + self.textItalic, + self.textUnderline] + + + def setData(self, data): + """ Set the object's internal data. + + 'data' is a copy of the object's saved data, as returned by + getData() above. This is used to restore a previously saved + DrawingObject. + """ + #data = copy.deepcopy(data) # Needed? + + self.type = data[0] + self.position = wxPoint(data[1], data[2]) + self.size = wxSize(data[3], data[4]) + self.penColour = wxColour(red=data[5], + green=data[6], + blue=data[7]) + self.fillColour = wxColour(red=data[8], + green=data[9], + blue=data[10]) + self.lineSize = data[11] + self.startPt = wxPoint(data[12], data[13]) + self.endPt = wxPoint(data[14], data[15]) + self.text = data[16] + self.textFont = data[17] + self.textSize = data[18] + self.textBoldface = data[19] + self.textItalic = data[20] + self.textUnderline = data[21] + + + def getType(self): + """ Return this DrawingObject's type. + """ + return self.type + + + def setPosition(self, position): + """ Set the origin (top-left corner) for this DrawingObject. + """ + self.position = position + + + def getPosition(self): + """ Return this DrawingObject's position. + """ + return self.position + + + def setSize(self, size): + """ Set the size for this DrawingObject. + """ + self.size = size + + + def getSize(self): + """ Return this DrawingObject's size. + """ + return self.size + + + def setPenColour(self, colour): + """ Set the pen colour used for this DrawingObject. + """ + self.penColour = colour + + + def getPenColour(self): + """ Return this DrawingObject's pen colour. + """ + return self.penColour + + + def setFillColour(self, colour): + """ Set the fill colour used for this DrawingObject. + """ + self.fillColour = colour + + + def getFillColour(self): + """ Return this DrawingObject's fill colour. + """ + return self.fillColour + + + def setLineSize(self, lineSize): + """ Set the linesize used for this DrawingObject. + """ + self.lineSize = lineSize + + + def getLineSize(self): + """ Return this DrawingObject's line size. + """ + return self.lineSize + + + def setStartPt(self, startPt): + """ Set the starting point for this line DrawingObject. + """ + self.startPt = startPt + + + def getStartPt(self): + """ Return the starting point for this line DrawingObject. + """ + return self.startPt + + + def setEndPt(self, endPt): + """ Set the ending point for this line DrawingObject. + """ + self.endPt = endPt + + + def getEndPt(self): + """ Return the ending point for this line DrawingObject. + """ + return self.endPt + + + def setText(self, text): + """ Set the text for this DrawingObject. + """ + self.text = text + + + def getText(self): + """ Return this DrawingObject's text. + """ + return self.text + + + def setTextFont(self, font): + """ Set the typeface for this text DrawingObject. + """ + self.textFont = font + + + def getTextFont(self): + """ Return this text DrawingObject's typeface. + """ + return self.textFont + + + def setTextSize(self, size): + """ Set the point size for this text DrawingObject. + """ + self.textSize = size + + + def getTextSize(self): + """ Return this text DrawingObject's text size. + """ + return self.textSize + + + def setTextBoldface(self, boldface): + """ Set the boldface flag for this text DrawingObject. + """ + self.textBoldface = boldface + + + def getTextBoldface(self): + """ Return this text DrawingObject's boldface flag. + """ + return self.textBoldface + + + def setTextItalic(self, italic): + """ Set the italic flag for this text DrawingObject. + """ + self.textItalic = italic + + + def getTextItalic(self): + """ Return this text DrawingObject's italic flag. + """ + return self.textItalic + + + def setTextUnderline(self, underline): + """ Set the underling flag for this text DrawingObject. + """ + self.textUnderline = underline + + + def getTextUnderline(self): + """ Return this text DrawingObject's underline flag. + """ + return self.textUnderline + + # ============================ + # == Object Drawing Methods == + # ============================ + + def draw(self, dc, selected): + """ Draw this DrawingObject into our window. + + 'dc' is the device context to use for drawing. If 'selected' is + true, the object is currently selected and should be drawn as such. + """ + if self.type != obj_TEXT: + if self.lineSize == 0: + dc.SetPen(wxPen(self.penColour, self.lineSize, wxTRANSPARENT)) + else: + dc.SetPen(wxPen(self.penColour, self.lineSize, wxSOLID)) + dc.SetBrush(wxBrush(self.fillColour, wxSOLID)) + else: + dc.SetTextForeground(self.penColour) + dc.SetTextBackground(self.fillColour) + + self._privateDraw(dc, self.position, selected) + + # ======================= + # == Selection Methods == + # ======================= + + def objectContainsPoint(self, x, y): + """ Returns true iff this object contains the given point. + + This is used to determine if the user clicked on the object. + """ + # Firstly, ignore any points outside of the object's bounds. + + if x < self.position.x: return false + if x > self.position.x + self.size.x: return false + if y < self.position.y: return false + if y > self.position.y + self.size.y: return false + + if self.type in [obj_RECT, obj_TEXT]: + # Rectangles and text are easy -- they're always selected if the + # point is within their bounds. + return true + + # Now things get tricky. There's no straightforward way of knowing + # whether the point is within the object's bounds...to get around this, + # we draw the object into a memory-based bitmap and see if the given + # point was drawn. This could no doubt be done more efficiently by + # some tricky maths, but this approach works and is simple enough. + + bitmap = wxEmptyBitmap(self.size.x + 10, self.size.y + 10) + dc = wxMemoryDC() + dc.SelectObject(bitmap) + dc.BeginDrawing() + dc.SetBackground(wxWHITE_BRUSH) + dc.Clear() + dc.SetPen(wxPen(wxBLACK, self.lineSize + 5, wxSOLID)) + dc.SetBrush(wxBLACK_BRUSH) + self._privateDraw(dc, wxPoint(5, 5), true) + dc.EndDrawing() + pixel = dc.GetPixel(x - self.position.x + 5, y - self.position.y + 5) + if (pixel.Red() == 0) and (pixel.Green() == 0) and (pixel.Blue() == 0): + return true + else: + return false + + + def getSelectionHandleContainingPoint(self, x, y): + """ Return the selection handle containing the given point, if any. + + We return one of the predefined selection handle ID codes. + """ + if self.type == obj_LINE: + # We have selection handles at the start and end points. + if self._pointInSelRect(x, y, self.position.x + self.startPt.x, + self.position.y + self.startPt.y): + return handle_START_POINT + elif self._pointInSelRect(x, y, self.position.x + self.endPt.x, + self.position.y + self.endPt.y): + return handle_END_POINT + else: + return handle_NONE + else: + # We have selection handles at all four corners. + if self._pointInSelRect(x, y, self.position.x, self.position.y): + return handle_TOP_LEFT + elif self._pointInSelRect(x, y, self.position.x + self.size.width, + self.position.y): + return handle_TOP_RIGHT + elif self._pointInSelRect(x, y, self.position.x, + self.position.y + self.size.height): + return handle_BOTTOM_LEFT + elif self._pointInSelRect(x, y, self.position.x + self.size.width, + self.position.y + self.size.height): + return handle_BOTTOM_RIGHT + else: + return handle_NONE + + + def objectWithinRect(self, x, y, width, height): + """ Return true iff this object falls completely within the given rect. + """ + if x > self.position.x: return false + if x + width < self.position.x + self.size.width: return false + if y > self.position.y: return false + if y + height < self.position.y + self.size.height: return false + return true + + # ===================== + # == Utility Methods == + # ===================== + + def fitToText(self): + """ Resize a text DrawingObject so that it fits it's text exactly. + """ + if self.type != obj_TEXT: return + + if self.textBoldface: weight = wxBOLD + else: weight = wxNORMAL + if self.textItalic: style = wxITALIC + else: style = wxNORMAL + font = wxFont(self.textSize, wxDEFAULT, style, weight, + self.textUnderline, self.textFont) + + dummyWindow = wxFrame(None, -1, "") + dummyWindow.SetFont(font) + width, height = dummyWindow.GetTextExtent(self.text) + dummyWindow.Destroy() + + self.size = wxSize(width, height) + + # ===================== + # == Private Methods == + # ===================== + + def _privateDraw(self, dc, position, selected): + """ Private routine to draw this DrawingObject. + + 'dc' is the device context to use for drawing, while 'position' is + the position in which to draw the object. If 'selected' is true, + the object is drawn with selection handles. This private drawing + routine assumes that the pen and brush have already been set by the + caller. + """ + if self.type == obj_LINE: + dc.DrawLine(position.x + self.startPt.x, + position.y + self.startPt.y, + position.x + self.endPt.x, + position.y + self.endPt.y) + elif self.type == obj_RECT: + dc.DrawRectangle(position.x, position.y, + self.size.width, self.size.height) + elif self.type == obj_ELLIPSE: + dc.DrawEllipse(position.x, position.y, + self.size.width, self.size.height) + elif self.type == obj_TEXT: + if self.textBoldface: weight = wxBOLD + else: weight = wxNORMAL + if self.textItalic: style = wxITALIC + else: style = wxNORMAL + font = wxFont(self.textSize, wxDEFAULT, style, weight, + self.textUnderline, self.textFont) + dc.SetFont(font) + dc.DrawText(self.text, position.x, position.y) + + if selected: + dc.SetPen(wxTRANSPARENT_PEN) + dc.SetBrush(wxBLACK_BRUSH) + + if self.type == obj_LINE: + # Draw selection handles at the start and end points. + self._drawSelHandle(dc, position.x + self.startPt.x, + position.y + self.startPt.y) + self._drawSelHandle(dc, position.x + self.endPt.x, + position.y + self.endPt.y) + else: + # Draw selection handles at all four corners. + self._drawSelHandle(dc, position.x, position.y) + self._drawSelHandle(dc, position.x + self.size.width, + position.y) + self._drawSelHandle(dc, position.x, + position.y + self.size.height) + self._drawSelHandle(dc, position.x + self.size.width, + position.y + self.size.height) + + + def _drawSelHandle(self, dc, x, y): + """ Draw a selection handle around this DrawingObject. + + 'dc' is the device context to draw the selection handle within, + while 'x' and 'y' are the coordinates to use for the centre of the + selection handle. + """ + dc.DrawRectangle(x - 3, y - 3, 6, 6) + + + def _pointInSelRect(self, x, y, rX, rY): + """ Return true iff (x, y) is within the selection handle at (rX, ry). + """ + if x < rX - 3: return false + elif x > rX + 3: return false + elif y < rY - 3: return false + elif y > rY + 3: return false + else: return true + +#---------------------------------------------------------------------------- + +class ToolPaletteIcon(wxStaticBitmap): + """ An icon appearing in the tool palette area of our sketching window. + + Note that this is actually implemented as a wxStaticBitmap rather + than as a wxIcon. wxIcon has a very specific meaning, and isn't + appropriate for this more general use. + """ + + def __init__(self, parent, iconID, iconName, toolTip): + """ Standard constructor. + + 'parent' is the parent window this icon will be part of. + 'iconID' is the internal ID used for this icon. + 'iconName' is the name used for this icon. + 'toolTip' is the tool tip text to show for this icon. + + The icon name is used to get the appropriate bitmap for this icon. + """ + bmp = wxBitmap("images/" + iconName + "Icon.bmp", wxBITMAP_TYPE_BMP) + wxStaticBitmap.__init__(self, parent, iconID, bmp, wxDefaultPosition, + wxSize(bmp.GetWidth(), bmp.GetHeight())) + self.SetToolTip(wxToolTip(toolTip)) + + self.iconID = iconID + self.iconName = iconName + self.isSelected = false + + + def select(self): + """ Select the icon. + + The icon's visual representation is updated appropriately. + """ + if self.isSelected: return # Nothing to do! + + bmp = wxBitmap("images/" + self.iconName + "IconSel.bmp", + wxBITMAP_TYPE_BMP) + self.SetBitmap(bmp) + self.isSelected = true + + + def deselect(self): + """ Deselect the icon. + + The icon's visual representation is updated appropriately. + """ + if not self.isSelected: return # Nothing to do! + + bmp = wxBitmap("images/" + self.iconName + "Icon.bmp", + wxBITMAP_TYPE_BMP) + self.SetBitmap(bmp) + self.isSelected = false + +#---------------------------------------------------------------------------- + +class ToolOptionIndicator(wxWindow): + """ A visual indicator which shows the current tool options. + """ + def __init__(self, parent): + """ Standard constructor. + """ + wxWindow.__init__(self, parent, -1, wxDefaultPosition, wxSize(52, 32)) + + self.penColour = wxBLACK + self.fillColour = wxWHITE + self.lineSize = 1 + + EVT_PAINT(self, self.OnPaint) + + + def setPenColour(self, penColour): + """ Set the indicator's current pen colour. + """ + self.penColour = penColour + self.Refresh() + + + def setFillColour(self, fillColour): + """ Set the indicator's current fill colour. + """ + self.fillColour = fillColour + self.Refresh() + + + def setLineSize(self, lineSize): + """ Set the indicator's current pen colour. + """ + self.lineSize = lineSize + self.Refresh() + + + def OnPaint(self, event): + """ Paint our tool option indicator. + """ + dc = wxPaintDC(self) + dc.BeginDrawing() + + if self.lineSize == 0: + dc.SetPen(wxPen(self.penColour, self.lineSize, wxTRANSPARENT)) + else: + dc.SetPen(wxPen(self.penColour, self.lineSize, wxSOLID)) + dc.SetBrush(wxBrush(self.fillColour, wxSOLID)) + + dc.DrawRectangle(5, 5, self.GetSize().width - 10, + self.GetSize().height - 10) + + dc.EndDrawing() + +#---------------------------------------------------------------------------- + +class EditTextObjectDialog(wxDialog): + """ Dialog box used to edit the properties of a text object. + + The user can edit the object's text, font, size, and text style. + """ + + def __init__(self, parent, title): + """ Standard constructor. + """ + wxDialog.__init__(self, parent, -1, title) + + self.textCtrl = wxTextCtrl(self, 1001, "", style=wxTE_PROCESS_ENTER, + validator=TextObjectValidator()) + extent = self.textCtrl.GetFullTextExtent("Hy") + lineHeight = extent[1] + extent[3] + self.textCtrl.SetSize(wxSize(-1, lineHeight * 4)) + + EVT_TEXT_ENTER(self, 1001, self._doEnter) + + fonts = wxFontEnumerator() + fonts.EnumerateFacenames() + self.fontList = fonts.GetFacenames() + self.fontList.sort() + + fontLabel = wxStaticText(self, -1, "Font:") + self._setFontOptions(fontLabel, weight=wxBOLD) + + self.fontCombo = wxComboBox(self, -1, "", wxDefaultPosition, + wxDefaultSize, self.fontList, + style = wxCB_READONLY) + self.fontCombo.SetSelection(0) # Default to first available font. + + self.sizeList = ["8", "9", "10", "12", "14", "16", + "18", "20", "24", "32", "48", "72"] + + sizeLabel = wxStaticText(self, -1, "Size:") + self._setFontOptions(sizeLabel, weight=wxBOLD) + + self.sizeCombo = wxComboBox(self, -1, "", wxDefaultPosition, + wxDefaultSize, self.sizeList, + style=wxCB_READONLY) + self.sizeCombo.SetSelection(3) # Default to 12 point text. + + gap = wxLEFT | wxTOP | wxRIGHT + + comboSizer = wxBoxSizer(wxHORIZONTAL) + comboSizer.Add(fontLabel, 0, gap | wxALIGN_CENTRE_VERTICAL, 5) + comboSizer.Add(self.fontCombo, 0, gap, 5) + comboSizer.Add(5, 5) # Spacer. + comboSizer.Add(sizeLabel, 0, gap | wxALIGN_CENTRE_VERTICAL, 5) + comboSizer.Add(self.sizeCombo, 0, gap, 5) + + self.boldCheckbox = wxCheckBox(self, -1, "Bold") + self.italicCheckbox = wxCheckBox(self, -1, "Italic") + self.underlineCheckbox = wxCheckBox(self, -1, "Underline") + + self._setFontOptions(self.boldCheckbox, weight=wxBOLD) + self._setFontOptions(self.italicCheckbox, style=wxITALIC) + self._setFontOptions(self.underlineCheckbox, underline=true) + + styleSizer = wxBoxSizer(wxHORIZONTAL) + styleSizer.Add(self.boldCheckbox, 0, gap, 5) + styleSizer.Add(self.italicCheckbox, 0, gap, 5) + styleSizer.Add(self.underlineCheckbox, 0, gap, 5) + + self.okButton = wxButton(self, wxID_OK, "OK") + self.cancelButton = wxButton(self, wxID_CANCEL, "Cancel") + + btnSizer = wxBoxSizer(wxHORIZONTAL) + btnSizer.Add(self.okButton, 0, gap, 5) + btnSizer.Add(self.cancelButton, 0, gap, 5) + + sizer = wxBoxSizer(wxVERTICAL) + sizer.Add(self.textCtrl, 1, gap | wxEXPAND, 5) + sizer.Add(10, 10) # Spacer. + sizer.Add(comboSizer, 0, gap | wxALIGN_CENTRE, 5) + sizer.Add(styleSizer, 0, gap | wxALIGN_CENTRE, 5) + sizer.Add(10, 10) # Spacer. + sizer.Add(btnSizer, 0, gap | wxALIGN_CENTRE, 5) + + self.SetAutoLayout(true) + self.SetSizer(sizer) + sizer.Fit(self) + + self.textCtrl.SetFocus() + + + def objectToDialog(self, obj): + """ Copy the properties of the given text object into the dialog box. + """ + self.textCtrl.SetValue(obj.getText()) + self.textCtrl.SetSelection(0, len(obj.getText())) + + for i in range(len(self.fontList)): + if self.fontList[i] == obj.getTextFont(): + self.fontCombo.SetSelection(i) + break + + for i in range(len(self.sizeList)): + if self.sizeList[i] == str(obj.getTextSize()): + self.sizeCombo.SetSelection(i) + break + + self.boldCheckbox.SetValue(obj.getTextBoldface()) + self.italicCheckbox.SetValue(obj.getTextItalic()) + self.underlineCheckbox.SetValue(obj.getTextUnderline()) + + + def dialogToObject(self, obj): + """ Copy the properties from the dialog box into the given text object. + """ + obj.setText(self.textCtrl.GetValue()) + obj.setTextFont(self.fontCombo.GetValue()) + obj.setTextSize(string.atoi(self.sizeCombo.GetValue())) + obj.setTextBoldface(self.boldCheckbox.GetValue()) + obj.setTextItalic(self.italicCheckbox.GetValue()) + obj.setTextUnderline(self.underlineCheckbox.GetValue()) + obj.fitToText() + + # ====================== + # == Private Routines == + # ====================== + + def _setFontOptions(self, ctrl, family=None, pointSize=-1, + style=wxNORMAL, weight=wxNORMAL, + underline=false): + """ Change the font settings for the given control. + + The meaning of the 'family', 'pointSize', 'style', 'weight' and + 'underline' parameters are the same as for the wxFont constructor. + If the family and/or pointSize isn't specified, the current default + value is used. + """ + if family == None: family = ctrl.GetFont().GetFamily() + if pointSize == -1: pointSize = ctrl.GetFont().GetPointSize() + + ctrl.SetFont(wxFont(pointSize, family, style, weight, underline)) + ctrl.SetSize(ctrl.GetBestSize()) # Adjust size to reflect font change. + + + def _doEnter(self, event): + """ Respond to the user hitting the ENTER key. + + We simulate clicking on the "OK" button. + """ + if self.Validate(): self.Show(false) + +#---------------------------------------------------------------------------- + +class TextObjectValidator(wxPyValidator): + """ This validator is used to ensure that the user has entered something + into the text object editor dialog's text field. + """ + def __init__(self): + """ Standard constructor. + """ + wxPyValidator.__init__(self) + + + def Clone(self): + """ Standard cloner. + + Note that every validator must implement the Clone() method. + """ + return TextObjectValidator() + + + def Validate(self, win): + """ Validate the contents of the given text control. + """ + textCtrl = wxPyTypeCast(self.GetWindow(), "wxTextCtrl") + text = textCtrl.GetValue() + + if len(text) == 0: + wxMessageBox("A text object must contain some text!", "Error") + return false + else: + return true + + + def TransferToWindow(self): + """ Transfer data from validator to window. + + The default implementation returns false, indicating that an error + occurred. We simply return true, as we don't do any data transfer. + """ + return true # Prevent wxDialog from complaining. + + + def TransferFromWindow(self): + """ Transfer data from window to validator. + + The default implementation returns false, indicating that an error + occurred. We simply return true, as we don't do any data transfer. + """ + return true # Prevent wxDialog from complaining. + +#---------------------------------------------------------------------------- + +class ExceptionHandler: + """ A simple error-handling class to write exceptions to a text file. + + Under MS Windows, the standard DOS console window doesn't scroll and + closes as soon as the application exits, making it hard to find and + view Python exceptions. This utility class allows you to handle Python + exceptions in a more friendly manner. + """ + + def __init__(self): + """ Standard constructor. + """ + self._buff = "" + if os.path.exists("errors.txt"): + os.remove("errors.txt") # Delete previous error log, if any. + + + def write(self, s): + """ Write the given error message to a text file. + + Note that if the error message doesn't end in a carriage return, we + have to buffer up the inputs until a carriage return is received. + """ + if (s[-1] != "\n") and (s[-1] != "\r"): + self._buff = self._buff + s + return + + try: + s = self._buff + s + self._buff = "" + + if s[:9] == "Traceback": + # Tell the user than an exception occurred. + wxMessageBox("An internal error has occurred.\nPlease " + \ + "refer to the 'errors.txt' file for details.", + "Error", wxOK | wxCENTRE | wxICON_EXCLAMATION) + + f = open("errors.txt", "a") + f.write(s) + f.close() + except: + pass # Don't recursively crash on errors. + +#---------------------------------------------------------------------------- + +class SketchApp(wxApp): + """ The main pySketch application object. + """ + def OnInit(self): + """ Initialise the application. + """ + wxInitAllImageHandlers() + + global _docList + _docList = [] + + if len(sys.argv) == 1: + # No file name was specified on the command line -> start with a + # blank document. + frame = DrawingFrame(None, -1, "Untitled") + frame.Centre() + frame.Show(TRUE) + _docList.append(frame) + else: + # Load the file(s) specified on the command line. + for arg in sys.argv[1:]: + fileName = os.path.join(os.getcwd(), arg) + if os.path.isfile(fileName): + frame = DrawingFrame(None, -1, + os.path.basename(fileName), + fileName=fileName) + frame.Show(TRUE) + _docList.append(frame) + + return TRUE + +#---------------------------------------------------------------------------- + +def main(): + """ Start up the pySketch application. + """ + global _app + + # Redirect python exceptions to a log file. + + sys.stderr = ExceptionHandler() + + # Create and start the pySketch application. + + _app = SketchApp(0) + _app.MainLoop() + + +if __name__ == "__main__": + main() + -- 2.45.2