PageRenderTime 56ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/client/scripts/Teleplace/Scribbler/Scribbler.py

http://openqwaq.googlecode.com/
Python | 346 lines | 269 code | 27 blank | 50 comment | 19 complexity | ffca79473201e21d4cdd0719d0470bc8 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, LGPL-2.0, BSD-3-Clause
  1. # Project OpenQwaq
  2. #
  3. # Copyright (c) 2005-20011, Teleplace, Inc., All Rights Reserved
  4. #
  5. # Redistributions in source code form must reproduce the above
  6. # copyright and this condition.
  7. #
  8. # The contents of this file are subject to the GNU General Public
  9. # License, Version 2 (the "License"); you may not use this file
  10. # except in compliance with the License. A copy of the License is
  11. # available at http://www.opensource.org/licenses/gpl-2.0.php.
  12. # Scribbler.py: The main file for a Python Panel App
  13. # Import the Panel API
  14. from Qwaq.PanelAPI import *
  15. from Button import *
  16. import StringIO # needed for load/save
  17. class Scribbler(QPanelApp):
  18. """A simple panel application """
  19. # --------------------------------------------------------------- #
  20. # Initialization
  21. # --------------------------------------------------------------- #
  22. def onCreate(self):
  23. """Initializes the paint application"""
  24. # Debug covers various amounts of logging info. The use of println()
  25. # is pretty slow so some of the more extensive logs are only shown
  26. # if debug is enabled.
  27. self.debug = True
  28. if(self.debug):
  29. self.println("[Scribbler]: Local User ID is " + str(self.userID))
  30. self.println("[Scribbler]: Base User ID is " + str(self.panel.getBaseUserID()))
  31. self.println("[Scribbler]: I am controlling:" + str(self.userID))
  32. # Print some status info in the Qwaq Forums transcript
  33. self.println("[Scribbler]: Creating new app")
  34. self.println("[Scribbler]: API version is " + str(self.apiVersion))
  35. self.println("[Scribbler]: Document name is " + str(self.panel.getDocumentName()))
  36. # Some extra info: Print all the user names currently logged in
  37. userNames = ", ".join([self.panel.getUserName(uid) for uid in self.panel.getUsers()])
  38. self.println("[Scribbler]: Users in this space: " + userNames)
  39. # userFilter can be set to constrain the app to only respond to a single user.
  40. # its default is None as here.
  41. self.userFilter = None
  42. # Set the defaults for our application
  43. self.lastPos = (0, 0)
  44. self.activeRadius = 5
  45. self.activeColor = QColor.Red.rgba
  46. self.strokes = self.panel["strokes"]
  47. self.activePoints = ()
  48. self.activeStroke = None
  49. self.drawing = False
  50. self.dlStrokes = ()
  51. # Set up the buttons
  52. self.buttons = []
  53. self.setExtent(800, 600)
  54. self.setupColorButtons()
  55. self.setupToolButtons()
  56. # If strokes exists, then the document has already been loaded.
  57. if(self.strokes is None):
  58. if(self.panel.getDocumentName() is None):
  59. # No document name given - initialize empty document
  60. self.strokes = ()
  61. # Save the strokes
  62. self.panel["strokes"] = self.strokes
  63. self.ready = True
  64. else:
  65. # The document hasn't been loaded yet.
  66. # We will receive an event when it is.
  67. self.ready = False
  68. else:
  69. # The document has been loaded already
  70. for stroke in self.strokes:
  71. self.addDLStroke(stroke)
  72. self.ready = True
  73. # Construct a display list for the application frame
  74. self.makeAppFrame()
  75. # --------------------------------------------------------------- #
  76. def onDestroy(self):
  77. """Cleans up after the paint application"""
  78. self.dlAppFrame.destroy()
  79. for dlStroke in self.dlStrokes:
  80. dlStroke.destroy()
  81. if(self.debug): self.println("[Scribbler]: Going away, bye, bye")
  82. # --------------------------------------------------------------- #
  83. def onNewUser(self, userID):
  84. """A complete redraw when a new user arrives"""
  85. if(self.debug): self.println("[Scribbler]: Add new user")
  86. self.setExtent(800, 600)
  87. self.draw()
  88. # --------------------------------------------------------------- #
  89. def onLoadSuccessEvent(self, success):
  90. if success:
  91. if(self.debug): self.println("[Scribbler]: onLoadSuccessEvent data is loaded")
  92. self.strokes = self.panel["strokes"]
  93. for stroke in self.strokes:
  94. self.addDLStroke(stroke)
  95. else:
  96. # No document name given - initialize empty document
  97. if(self.debug): self.println("[Scribbler]: onLoadSuccessEvent failure")
  98. self.strokes = ()
  99. # Save the strokes
  100. self.panel["strokes"] = self.strokes
  101. self.ready = True
  102. self.draw() # redraw strokes
  103. # --------------------------------------------------------------- #
  104. def setupColorButtons(self):
  105. """Initializes all the color buttons"""
  106. allColors = (QColor.White, QColor.Black, QColor.LightGray, QColor.Red, QColor.Yellow,
  107. QColor.Green, QColor.Cyan, QColor.Blue, QColor.Magenta)
  108. px = self.extent[0] - 36
  109. py = (self.extent[1] - len(allColors) * 32) // 2
  110. for color in allColors:
  111. box = QRect(px, py, 32, 32)
  112. button = Button(box, None, color, False)
  113. button.action = self.activateColor
  114. button.argument = color.rgba
  115. self.buttons.append(button)
  116. py = py + 31
  117. # --------------------------------------------------------------- #
  118. def setupToolButtons(self):
  119. """Initializes all the tool buttons"""
  120. toolSpec = (
  121. ("ClearButton.png", self.clearStrokes, None, QColor.White),
  122. (),
  123. # ("SelectTool.png", self.activateSelect, None, QColor.White),
  124. # ("TextTool.png", self.activateText, None, QColor.White),
  125. ("SmallBrush.png", self.activateBrush, 2, QColor.White),
  126. ("MidBrush.png", self.activateBrush, 5, QColor.White),
  127. ("LargeBrush.png", self.activateBrush, 10, QColor.White),
  128. (),
  129. ("DebugButton.png", self.toggleDebug, None, QColor.White),
  130. )
  131. px = 4
  132. py = (self.extent[1] - len(toolSpec) * 32) // 2
  133. for spec in toolSpec:
  134. if(len(spec) > 0):
  135. pic = spec[0]
  136. image = self.canvas.loadImage(pic)
  137. if(image is None): self.println("[Scribbler]: Failed to load " + pic)
  138. box = QRect(px, py, 32, 32)
  139. button = Button(box, image, spec[3], False)
  140. button.action = spec[1]
  141. button.argument = spec[2]
  142. self.buttons.append(button)
  143. py = py + 31
  144. # --------------------------------------------------------------- #
  145. def activateBrush(self, button, radius):
  146. """Activates a particular brush size for drawing"""
  147. self.activeRadius = radius
  148. # --------------------------------------------------------------- #
  149. def activateColor(self, button, rgba):
  150. """Activates a particular drawing color"""
  151. self.activeColor = rgba
  152. # --------------------------------------------------------------- #
  153. def clearStrokes(self, button, ignored):
  154. """Toggle the current drawing tool"""
  155. self.strokes = ()
  156. for dls in self.dlStrokes:
  157. dls.destroy()
  158. self.dlStrokes = ()
  159. self.panel["strokes"] = ()
  160. self.activePoints = ()
  161. self.draw()
  162. # --------------------------------------------------------------- #
  163. def toggleDebug(self, button, ignored):
  164. """Toggles the amount of debug output"""
  165. # The debug property is purely local and has no effect on the replicated
  166. # environment. If we wanted to use it replicated we'd need to use the
  167. # same steps as for color or brush sizes.
  168. self.debug = not(self.debug)
  169. if(self.debug): self.println("[Scribbler]: Debug is now ON")
  170. else: self.println("[Scribbler]: Debug is now OFF")
  171. # --------------------------------------------------------------- #
  172. def onKeyStroke(self, event):
  173. self.println("onKeyStroke.event.value, buttons: " + str((event.type,event.value, event.buttons)))
  174. # --------------------------------------------------------------- #
  175. def onMouseDown(self, event):
  176. """Handles the mouseDown event"""
  177. if(self.debug): self.println("[Scribbler]: onMouseDown")
  178. if(not(self.ready)): return # we aren't ready yet
  179. if(event.buttons & 4):
  180. # See if we pressed inside a button
  181. for button in self.buttons:
  182. if(button.bounds.containsPoint(event.position)):
  183. return button.action(button, button.argument)
  184. # Otherwise start a new stroke
  185. self.startStroke(event)
  186. # --------------------------------------------------------------- #
  187. def onMouseMove(self, event):
  188. """Handles the mouseMove event"""
  189. if(self.drawing):
  190. self.followStroke(event)
  191. # --------------------------------------------------------------- #
  192. def onMouseUp(self, event):
  193. """Handles the mouseUp event"""
  194. if(self.drawing):
  195. self.followStroke(event)
  196. self.endStroke(event)
  197. # --------------------------------------------------------------- #
  198. def startStroke(self, event):
  199. self.activePoints = (event.position,)
  200. # And draw immediately
  201. self.lastPos = event.position
  202. self.drawTo(event.position)
  203. self.drawing = True
  204. # --------------------------------------------------------------- #
  205. def followStroke(self, event):
  206. """Takes a mouseMove event and continues to draw the current stroke"""
  207. if(self.lastPos == event.position): return
  208. self.activePoints = self.activePoints+(event.position,)
  209. self.drawTo(event.position)
  210. # --------------------------------------------------------------- #
  211. def endStroke(self, event):
  212. """Takes a mouseUp event and finalizes the current stroke"""
  213. stroke = (self.activeColor,
  214. self.activeRadius,
  215. self.activePoints)
  216. self.strokes = self.strokes + (stroke,) # Append local (cached)
  217. self.panel.append("strokes", stroke) # Append replicated
  218. self.drawing = False
  219. self.addDLStroke(stroke).drawOffsetScaled(0,0,1,1)
  220. # --------------------------------------------------------------- #
  221. def addDLStroke(self, stroke):
  222. newStroke = QDisplayList(self.panel)
  223. newStroke.begin()
  224. self.drawStroke(stroke[0], stroke[1], stroke[2])
  225. newStroke.end()
  226. self.dlStrokes = self.dlStrokes + (newStroke,)
  227. return newStroke
  228. # --------------------------------------------------------------- #
  229. def drawTo(self, newPos):
  230. """Draws a line from to the next position. Updates lastPos."""
  231. self.canvas.drawLine(self.lastPos[0], self.lastPos[1],
  232. newPos[0], newPos[1],
  233. self.activeRadius*2,
  234. QColor.fromRGBA(self.activeColor))
  235. rect = QRect.encompassing((self.lastPos, newPos))
  236. rect = rect.expandBy((self.activeRadius, self.activeRadius))
  237. self.canvas.flush()
  238. self.lastPos = newPos
  239. # --------------------------------------------------------------- #
  240. def drawStroke(self, rgba, radius, pts):
  241. """Draws a stroke on the given canvas"""
  242. lastPt = None
  243. if(len(pts)==1):lastPt = pts[0]
  244. for nextPt in pts:
  245. if(lastPt is not None):
  246. self.canvas.drawLine(lastPt[0], lastPt[1],
  247. nextPt[0], nextPt[1],
  248. radius*2, QColor.fromRGBA(rgba))
  249. lastPt = nextPt
  250. # --------------------------------------------------------------- #
  251. def makeAppFrame(self):
  252. """Draws the application's frame and title"""
  253. # Obviously, this is just an example for how to draw text since
  254. # we have the application title already set initially.
  255. title = "Scribbler"
  256. font = self.canvas.selectFont("Default", 24, 0)
  257. if(self.debug): self.println("[Scribbler]: Selected font is " + str(font))
  258. sz = self.canvas.measureString(title, 0, len(title), font)
  259. px = (self.extent[0] - sz[0]) // 2
  260. py = sz[1] // 2
  261. self.dlAppFrame = QDisplayList(self.panel)
  262. self.dlAppFrame.begin()
  263. self.canvas.frameRect(20, 10 + py, self.extent[0] - 40, self.extent[1] - py - 20,
  264. 1, QColor.Black)
  265. self.canvas.fillRect(px-10, py, sz[0]+20,sz[1], QColor.White)
  266. self.canvas.drawString(title, px, 10, font, QColor.Black)
  267. self.dlAppFrame.end()
  268. # --------------------------------------------------------------- #
  269. def draw(self):
  270. """Draws the application on the given canvas"""
  271. if(self.debug): self.println("draw")
  272. # Clear its background to white
  273. self.canvas.clearColor(QColor.White)
  274. # Draw the existing strokes
  275. if(self.ready):
  276. for dls in self.dlStrokes:
  277. dls.draw()
  278. # And the currently active stroke using the old method...
  279. self.drawStroke(self.activeColor,
  280. self.activeRadius,
  281. self.activePoints)
  282. # Draw the app frame (on top of the strokes)
  283. self.dlAppFrame.draw()
  284. # Draw the buttons
  285. for button in self.buttons:
  286. button.draw(self.canvas)
  287. # And force an update
  288. self.canvas.flush()