📄 pysketch.py
字号:
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(wx.ID_NEW, True)
self.toolbar.EnableTool(wx.ID_OPEN, True)
self.toolbar.EnableTool(wx.ID_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.
wx.Bell(); print "4"
return
self._saveUndoInfo()
topLeft = wx.Point(min(anchorPt.x, newPt.x),
min(anchorPt.y, newPt.y))
botRight = wx.Point(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(wx.Point(0, 0))
obj.setEndPt(wx.Point(newWidth, newHeight))
else:
obj.setStartPt(wx.Point(0, newHeight))
obj.setEndPt(wx.Point(newWidth, 0))
# Finally, adjust the bounds of the object to match the new dimensions.
obj.setPosition(topLeft)
obj.setSize(wx.Size(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 = wx.Menu()
menu.Append(id_LINESIZE_0, "no line", kind=wx.ITEM_CHECK)
menu.Append(id_LINESIZE_1, "1-pixel line", kind=wx.ITEM_CHECK)
menu.Append(id_LINESIZE_2, "2-pixel line", kind=wx.ITEM_CHECK)
menu.Append(id_LINESIZE_3, "3-pixel line", kind=wx.ITEM_CHECK)
menu.Append(id_LINESIZE_4, "4-pixel line", kind=wx.ITEM_CHECK)
menu.Append(id_LINESIZE_5, "5-pixel line", kind=wx.ITEM_CHECK)
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)
self.Bind(wx.EVT_MENU, self._lineSizePopupSelected, id=id_LINESIZE_0, id2=id_LINESIZE_5)
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:
wx.Bell(); print "5"
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 wx.Point(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 = wx.ClientDC(self.drawPanel)
self.drawPanel.PrepareDC(dc)
dc.BeginDrawing()
dc.SetPen(wx.BLACK_DASHED_PEN)
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.SetLogicalFunction(wx.INVERT)
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 = wx.ClientDC(self.drawPanel)
self.drawPanel.PrepareDC(dc)
dc.BeginDrawing()
if dashedLine:
dc.SetPen(wx.BLACK_DASHED_PEN)
else:
dc.SetPen(wx.BLACK_PEN)
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.SetLogicalFunction(wx.INVERT)
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=wx.Point(0, 0), size=wx.Size(0, 0),
penColour=wx.BLACK, fillColour=wx.WHITE, lineSize=1,
text=None, startPt=wx.Point(0, 0), endPt=wx.Point(0,0)):
""" Standard constructor.
'
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -