Repository: psychopy/psychopy

Issue:
ISI disappears in Builder when using variable for duration
I wanted to generate a dynamic ISI and therefore used a variable for "duration". Unfortunately, this makes the ISI disappear. Well, also for other objects the visualization of the timing disappears when you do this, but for the other objects, you at least still have the icon to access the object. For the ISI, I wasn't able to access the ISI object at all after that. The object is still there though. I can change the duration variable in my code section and it has an effect, but, for now, I am just not able to reopen the component. As a workaround, you can set the "expected duration". That helps. But it might be a good idea that at least an icon of the ISI sticks around so that one can use the Builder still.

Thanks and best,

Axel



Discussion:
I agree that at least some sort of icon should be accessible in this case, perhaps on the left like the other components?


Resolving patch:
diff --git a/psychopy/app/builder/builder.py b/psychopy/app/builder/builder.py
--- a/psychopy/app/builder/builder.py
+++ b/psychopy/app/builder/builder.py
@@ -1421,7 +1421,7 @@ def setFontSize(self, size, dc):
         font.SetPointSize(size)
         dc.SetFont(font)
     def drawStatic(self, dc, component, yPosTop, yPosBottom):
-        """draw a static component box"""
+        """draw a static (ISI) component box"""
         #set an id for the region of this component (so it can act as a button)
         ##see if we created this already
         id=None
@@ -1434,31 +1434,37 @@ def drawStatic(self, dc, component, yPosTop, yPosBottom):
         dc.SetId(id)
         #deduce start and stop times if possible
         startTime, duration, nonSlipSafe = component.getStartAndDuration()
-        #draw entries on timeline (if they have some time definition)
-        if startTime!=None and duration!=None:#then we can draw a sensible time bar!
-            #calculate rectangle for component
-            xScale = self.getSecsPerPixel()
-            dc.SetPen(wx.Pen(wx.Colour(200, 100, 100, 0), style=wx.TRANSPARENT))
-            dc.SetBrush(wx.Brush(staticTimeColor))
-            xSt = self.timeXposStart + startTime / xScale
-            w = duration / xScale + 1  # +1 to compensate for border alpha=0 in dc.SetPen
-            if w > 10000:
-                w = 10000  #limit width to 10000 pixels!
-            if w < 2:
-                w = 2  #make sure at least one pixel shows
-            h = yPosBottom-yPosTop
-            # name label, position:
-            name = component.params['name'].val  # "ISI"
-            nameW, nameH = self.GetFullTextExtent(name)[0:2]
-            x = xSt+w/2
-            staticLabelTop = (0, 50, 60)[self.drawSize]
-            y = staticLabelTop - nameH * 3
-            fullRect = wx.Rect(x-20,y,nameW, nameH)
-            #draw the rectangle, draw text on top:
-            dc.DrawRectangle(xSt, yPosTop-nameH*4, w, h+nameH*5)
-            dc.DrawText(name, x-nameW/2, y)
-            fullRect.Union(wx.Rect(xSt, yPosTop, w, h))#update bounds to include time bar
-            dc.SetIdBounds(id,fullRect)
+        # ensure static comps are clickable (even if $code start or duration)
+        unknownTiming = False
+        if startTime is None:
+            startTime = 0
+            unknownTiming = True
+        if duration is None:
+            duration = 0  # minimal extent ensured below
+            unknownTiming = True
+        #calculate rectangle for component
+        xScale = self.getSecsPerPixel()
+        dc.SetPen(wx.Pen(wx.Colour(200, 100, 100, 0), style=wx.TRANSPARENT))
+        dc.SetBrush(wx.Brush(staticTimeColor))
+        xSt = self.timeXposStart + startTime / xScale
+        w = duration / xScale + 1  # +1 to compensate for border alpha=0 in dc.SetPen
+        w = max(min(w, 10000), 2)  # ensure 2..10000 pixels
+        h = yPosBottom-yPosTop
+        # name label, position:
+        name = component.params['name'].val  # "ISI"
+        if unknownTiming:
+            # flag it as not literally represented in time, e.g., $code duration
+            name += ' ???'
+        nameW, nameH = self.GetFullTextExtent(name)[0:2]
+        x = xSt+w/2
+        staticLabelTop = (0, 50, 60)[self.drawSize]
+        y = staticLabelTop - nameH * 3
+        fullRect = wx.Rect(x-20,y,nameW, nameH)
+        #draw the rectangle, draw text on top:
+        dc.DrawRectangle(xSt, yPosTop-nameH*4, w, h+nameH*5)
+        dc.DrawText(name, x-nameW/2, y)
+        fullRect.Union(wx.Rect(xSt, yPosTop, w, h))#update bounds to include time bar
+        dc.SetIdBounds(id,fullRect)
     def drawComponent(self, dc, component, yPos):
         """Draw the timing of one component on the timeline"""
         #set an id for the region of this component (so it can act as a button)


Test patch: