Repository: psychopy/psychopy

Issue:
StaticComponent stopped time is always None
I created an experiment in Builder with a single routine, containing a StaticComponent, with all default values. After running the experiment, the `.stopped` value for the StaticComponent is `None`. I expect the value of `.stopped` to be the value of `.started` plus 0.5 (the default duration of a StaticComponent). This is a contrived minimal example of a problem that I have in an experiment with multiple routines.

I've attached the experiment file and output. Incidentally, some of the standard columns of the data file, such as `date`, `expName`, `psychopyVersion`, `frameRate` are not written.

[ISI.txt](https://github.com/psychopy/psychopy/files/4861253/ISI.txt)
[999_ISI_2020_Jul_01_1616.txt](https://github.com/psychopy/psychopy/files/4861235/999_ISI_2020_Jul_01_1616.txt)
Copying and pasting components doesn't increment name more than once
I've noticed if you copy a component in the Builder (for instance, "example_text") and paste the same component multiple times, it won't increment past the 1st paste (all copies will be called "example_text_2" by default), which lets the user paste multiple components with the same name.

It would be useful if there were a smarter default title; in a project some components' names aren't important, and it can be handy to just copy and paste them a few times without caring about duplicate names.


Discussion:

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
@@ -1905,6 +1905,7 @@ def pasteCompon(self, event=None, component=None):
             if 'name' in dir(newCompon):
                 newCompon.name = newName
             self.routine.addComponent(newCompon)
+            self.frame.exp.namespace.user.append(newName)
             # could do redrawRoutines but would be slower?
             self.redrawRoutine()
             self.frame.addToUndoStack("PASTE Component `%s`" % newName)
diff --git a/psychopy/app/themes/_themes.py b/psychopy/app/themes/_themes.py
--- a/psychopy/app/themes/_themes.py
+++ b/psychopy/app/themes/_themes.py
@@ -63,7 +63,7 @@ class ThemeMixin:
     }
     # these are populated and modified by PsychoPyApp.theme.setter
     spec = None
-    codetheme = 'PsychopyDark'
+    codetheme = 'PsychopyLight'
     mode = 'light'
     icons = 'light'
     codeColors = {}
@@ -159,6 +159,14 @@ def applyToNotebook(target):
         def applyToCodeEditor(target):
             spec = ThemeMixin.codeColors
             base = spec['base']
+            # Override base font with user spec if present
+            prefkey = 'outputFont' if isinstance(target, wx.py.shell.Shell) else 'codeFont'
+            if prefs.coder[prefkey].lower() != "From Theme...".lower():
+                for key in spec:
+                    if 'font' in spec[key]:
+                        spec[key]['font'] = prefs.coder[prefkey] if spec[key]['font'] == base['font'] \
+                            else base['font']
+                base['font'] = prefs.coder[prefkey]
 
             # Check that key is in tag list
             invalid = []
@@ -180,11 +188,6 @@ def applyToCodeEditor(target):
             else:
                 lang = {}
 
-            # Override base font with user spec if present
-            key = 'outputFont' if isinstance(target, wx.py.shell.Shell) else 'codeFont'
-            if prefs.coder[key].lower() != "From Theme...".lower():
-                base['font'] = [prefs.coder[key]]
-
             # Set style for undefined lexers
             for key in [getattr(wx._stc, item) for item in dir(wx._stc) if item.startswith("STC_LEX")]:
                 target.StyleSetBackground(key, base['bg'])
@@ -227,7 +230,12 @@ def applyToRichText(target):
             # Then construct default styles
             bold = wx.FONTWEIGHT_BOLD if "bold" in base['font'] else wx.FONTWEIGHT_NORMAL
             italic = wx.FONTSTYLE_ITALIC if "italic" in base['font'] else wx.FONTSTYLE_NORMAL
-            fontName = base['font'].replace("bold", "").replace("italic", "").replace(",", "")
+            # Override base font with user spec if present
+            if prefs.coder['outputFont'].lower() == "From Theme...".lower():
+                fontName = base['font'].replace("bold", "").replace("italic", "").replace(",", "")
+            else:
+                fontName = prefs.coder['outputFont']
+
             _font = wx.Font(
                 int(prefs.coder['outputFontSize']),
                 wx.FONTFAMILY_TELETYPE, italic,
@@ -502,7 +510,6 @@ def shiftColour(self, col, offset=15):
 
     def extractFont(self, fontList, base=[]):
         """Extract specified font from theme spec"""
-        global fm
         # Convert to list if not already
         if isinstance(base, str):
             base = base.split(",")
diff --git a/psychopy/experiment/components/static/__init__.py b/psychopy/experiment/components/static/__init__.py
--- a/psychopy/experiment/components/static/__init__.py
+++ b/psychopy/experiment/components/static/__init__.py
@@ -131,6 +131,21 @@ def writeStopTestCode(self, buff):
         self.writeParamUpdates(buff)
         code = "%(name)s.complete()  # finish the static period\n"
         buff.writeIndented(code % self.params)
+        # Calculate stop time
+        if self.params['stopType'].val == 'time (s)':
+            code = "%(name)s.tStop = %(stopVal)s  # record stop time\n"
+        elif self.params['stopType'].val == 'duration (s)':
+            code = "%(name)s.tStop = %(name)s.tStart + %(stopVal)s  # record stop time\n"
+        elif self.params['stopType'].val == 'duration (frames)':
+            code = "%(name)s.tStop = %(name)s.tStart + %(stopVal)s*frameDur  # record stop time\n"
+        elif self.params['stopType'].val == 'frame N':
+            code = "%(name)s.tStop = %(stopVal)s*frameDur  # record stop time\n"
+        else:
+            msg = ("Couldn't deduce end point for startType=%(startType)s, "
+                   "stopType=%(stopType)s")
+            raise Exception(msg % self.params)
+        # Store stop time
+        buff.writeIndented(code % self.params)
         # to get out of the if statement
         buff.setIndentLevel(-1, relative=True)
 


Test patch: