Repository: psychopy/psychopy

Issue:
print code from coder (hardcopy)
seems like this would be useful for some people, on occasion, and be unobtrusive for others



Discussion:

Resolving patch:
diff --git a/psychopy/app/coder/coder.py b/psychopy/app/coder/coder.py
--- a/psychopy/app/coder/coder.py
+++ b/psychopy/app/coder/coder.py
@@ -10,6 +10,7 @@
 import introspect, py_compile
 from psychopy.app import stdOutRich, dialogs
 from psychopy import logging
+from wx.html import HtmlEasyPrinting
 
 #advanced prefs (not set in prefs files)
 prefTestSubset = ""
@@ -42,6 +43,34 @@ def fromPickle(filename):
     f.close()
     return contents
 
+class Printer(HtmlEasyPrinting):
+    """bare-bones printing, no control over anything
+
+    from http://wiki.wxpython.org/Printing
+    """
+    def __init__(self):
+        HtmlEasyPrinting.__init__(self)
+
+    def GetHtmlText(self, text):
+        "Simple conversion of text."
+
+        text = text.replace('&', '&amp;')
+        text = text.replace('<P>', '&#60;P&#62;')
+        text = text.replace('<BR>', '&#60;BR&#62;')
+        text = text.replace('<HR>', '&#60;HR&#62;')
+        text = text.replace('<p>', '&#60;p&#62;')
+        text = text.replace('<br>', '&#60;br&#62;')
+        text = text.replace('<hr>', '&#60;hr&#62;')
+        text = text.replace('\n\n', '<P>')
+        text = text.replace('\t', '    ')  # tabs -> 4 spaces
+        text = text.replace(' ', '&nbsp;')  # preserve indentation
+        html_text = text.replace('\n', '<BR>')
+        return html_text
+
+    def Print(self, text, doc_name):
+        self.SetHeader(doc_name)
+        self.PrintText('<HR>' + self.GetHtmlText(text), doc_name)
+
 class ScriptThread(threading.Thread):
     """A subclass of threading.Thread, with a kill()
     method."""
@@ -1242,12 +1271,14 @@ def makeMenus(self):
         self.fileMenu.AppendSubMenu(self.recentFilesMenu,"Open &Recent")
         self.fileMenu.Append(wx.ID_SAVE,    "&Save\t%s" %self.app.keys['save'])
         self.fileMenu.Append(wx.ID_SAVEAS,  "Save &as...\t%s" %self.app.keys['saveAs'])
+        self.fileMenu.Append(self.IDs.filePrint,  "Print\t%s" %self.app.keys['print'])
         self.fileMenu.Append(wx.ID_CLOSE,   "&Close file\t%s" %self.app.keys['close'])
         wx.EVT_MENU(self, wx.ID_NEW,  self.fileNew)
         wx.EVT_MENU(self, wx.ID_OPEN,  self.fileOpen)
         wx.EVT_MENU(self, wx.ID_SAVE,  self.fileSave)
         wx.EVT_MENU(self, wx.ID_SAVEAS,  self.fileSaveAs)
         wx.EVT_MENU(self, wx.ID_CLOSE,  self.fileClose)
+        wx.EVT_MENU(self, self.IDs.filePrint,  self.filePrint)
         item = self.fileMenu.Append(wx.ID_PREFERENCES, text = "&Preferences")
         self.Bind(wx.EVT_MENU, self.app.showPrefs, item)
         #-------------quit
@@ -1644,6 +1675,12 @@ def closeFrame(self, event=None, checkSave=True):
         self.Destroy()
         self.app.coder=None
 
+    def filePrint(self, event=None):
+        pr = Printer()
+        docName = self.currentDoc.filename
+        text = open(docName, 'r').read()
+        pr.Print(text, docName)
+
     def fileNew(self, event=None, filepath=""):
         self.setCurrentDoc(filepath)
     def fileReload(self, event, filename=None, checkSave=False):
diff --git a/psychopy/app/wxIDs.py b/psychopy/app/wxIDs.py
--- a/psychopy/app/wxIDs.py
+++ b/psychopy/app/wxIDs.py
@@ -46,6 +46,7 @@
 openUpdater=wx.NewId()
 unitTests=wx.NewId()
 benchmarkWizard=wx.NewId()
+filePrint=wx.NewId()
 
 #help menu
 #these should be assigned to the relevant buttons/menu items in the app
diff --git a/psychopy/visual.py b/psychopy/visual.py
--- a/psychopy/visual.py
+++ b/psychopy/visual.py
@@ -1456,6 +1456,17 @@ def contains(self, x, y=None):
             y = x0 * sinOri + y0 * cosOri + self._posRendered[1]
         
         return pointInPolygon(x, y, self)
+
+    def _getPolyAsRendered(self):
+        """return a list of vertices as rendered; used by overlaps(), centroid()
+        """
+        oriRadians = numpy.radians(self.ori)
+        sinOri = numpy.sin(-oriRadians)
+        cosOri = numpy.cos(-oriRadians)
+        x = self._verticesRendered[:,0] * cosOri - self._verticesRendered[:,1] * sinOri
+        y = self._verticesRendered[:,0] * sinOri + self._verticesRendered[:,1] * cosOri
+        return numpy.column_stack((x,y)) + self._posRendered
+
     def overlaps(self, polygon):
         """Determines if this stimulus intersects another one. If `polygon` is
         another stimulus instance, then the vertices and location of that stimulus
@@ -1464,7 +1475,7 @@ def overlaps(self, polygon):
 
         Note that, if your stimulus uses a mask (such as a Gaussian blob) then
         this is not accounted for by the `overlaps` method; the extent of the
-        stmulus is determined purely by the size, pos and orientation settings
+        stimulus is determined purely by the size, pos, and orientation settings
         (and by the vertices for shape stimuli).
 
         See coder demo, shapeContains.py
@@ -1472,12 +1483,8 @@ def overlaps(self, polygon):
         if self.needVertexUpdate:
             self._calcVerticesRendered()
         if self.ori:
-            oriRadians = numpy.radians(self.ori)
-            sinOri = numpy.sin(-oriRadians)
-            cosOri = numpy.cos(-oriRadians)
-            x = self._verticesRendered[:,0] * cosOri - self._verticesRendered[:,1] * sinOri
-            y = self._verticesRendered[:,0] * sinOri + self._verticesRendered[:,1] * cosOri
-            return polygonsOverlap(numpy.column_stack((x,y)) + self._posRendered, polygon)
+            polyRendered = self._getPolyAsRendered()
+            return polygonsOverlap(polyRendered, polygon)
         else:
             return polygonsOverlap(self, polygon)
 
@@ -7291,6 +7298,7 @@ def polygonsOverlap(poly1, poly2):
             return True
     return False
 
+
 def _setTexIfNoShaders(obj):
     """Useful decorator for classes that need to update Texture after other properties
     """


Test patch: