Repository: psychopy/psychopy

Issue:
Before experiment code in JS not having any effect?



Discussion:

Resolving patch:
diff --git a/psychopy/app/dialogs.py b/psychopy/app/dialogs.py
--- a/psychopy/app/dialogs.py
+++ b/psychopy/app/dialogs.py
@@ -536,12 +536,12 @@ def __init__(self, parent, value=None, order=None):
         """
         GlobSizer.__init__(self, hgap=2, vgap=2)
         self.parent = parent
-        self.value = value or [{}]
-        if type(value) != list or len(value) < 1:
+        self.value = value if value else [{"Field":"", "Default": ""}]
+        if type(value) != list:
             msg = 'The initial value for a ListWidget must be a list of dicts'
             raise AttributeError(msg)
         # sort fieldNames using order information where possible
-        allNames = list(value[0].keys())
+        allNames = list(self.value[0].keys())
         self.fieldNames = []
         if order is None:
             order = []
@@ -562,12 +562,13 @@ def __init__(self, parent, value=None, order=None):
 
     def createGrid(self):
         row = 0
-        for col, field in enumerate(self.fieldNames):
-            self.Add(wx.StaticText(self.parent, -1, label=_translate(field)),
-                     (row, col), flag=wx.ALL)
-        for entry in self.value:
-            row += 1
-            self.addEntryCtrls(row, entry)
+        if len(self.fieldNames) > 0:
+            for col, field in enumerate(self.fieldNames):
+                self.Add(wx.StaticText(self.parent, -1, label=_translate(field)),
+                         (row, col), flag=wx.ALL)
+            for entry in self.value:
+                row += 1
+                self.addEntryCtrls(row, entry)
         self.Layout()
 
     def addEntryCtrls(self, row, entry):
diff --git a/psychopy/experiment/_experiment.py b/psychopy/experiment/_experiment.py
--- a/psychopy/experiment/_experiment.py
+++ b/psychopy/experiment/_experiment.py
@@ -221,11 +221,11 @@ def writeScript(self, expPath=None, target="PsychoPy", modular=True):
             # routine init sections
             for entry in self_copy.flow:
                 # NB each entry is a routine or LoopInitiator/Terminator
+                if hasattr(entry, 'writePreCodeJS'):
+                    entry.writePreCodeJS(script)
                 self_copy._currentRoutine = entry
                 if hasattr(entry, 'writeInitCodeJS'):
                     entry.writeInitCodeJS(script)
-                if hasattr(entry, 'writePreCodeJS'):
-                    entry.writePreCodeJS(script)
 
             # create globalClock etc
             code = ("// Create some handy timers\n"
diff --git a/psychopy/experiment/routine.py b/psychopy/experiment/routine.py
--- a/psychopy/experiment/routine.py
+++ b/psychopy/experiment/routine.py
@@ -87,6 +87,14 @@ def writePreCode(self, buff):
             if hasattr(thisCompon, 'writePreCode'):
                 thisCompon.writePreCode(buff)
 
+    def writePreCodeJS(self, buff):
+        """This is start of the script (before window is created)
+        """
+        for thisCompon in self:
+            # check just in case; try to ensure backwards compatibility _base
+            if hasattr(thisCompon, 'writePreCodeJS'):
+                thisCompon.writePreCodeJS(buff)
+
     def writeStartCode(self, buff):
         """This is start of the *experiment* (after window is created)
         """


Test patch:
diff --git a/psychopy/tests/data/CodeComponent_eachtab.psyexp b/psychopy/tests/data/CodeComponent_eachtab.psyexp
new file mode 100644
--- /dev/null
+++ b/psychopy/tests/data/CodeComponent_eachtab.psyexp
@@ -0,0 +1,59 @@
+<?xml version="1.0" ?>
+<PsychoPy2experiment version="2020.2.4post1" encoding="utf-8">
+  <Settings>
+    <Param name="Audio latency priority" val="use prefs" valType="str" updates="None"/>
+    <Param name="Audio lib" val="use prefs" valType="str" updates="None"/>
+    <Param name="Completed URL" val="" valType="str" updates="None"/>
+    <Param name="Data file delimiter" val="auto" valType="str" updates="None"/>
+    <Param name="Data filename" val="u'data/%s_%s_%s' % (expInfo['participant'], expName, expInfo['date'])" valType="code" updates="None"/>
+    <Param name="Enable Escape" val="True" valType="bool" updates="None"/>
+    <Param name="Experiment info" val="{'participant':'', 'session':'001'}" valType="code" updates="None"/>
+    <Param name="Force stereo" val="True" valType="bool" updates="None"/>
+    <Param name="Full-screen window" val="True" valType="bool" updates="None"/>
+    <Param name="HTML path" val="" valType="str" updates="None"/>
+    <Param name="Incomplete URL" val="" valType="str" updates="None"/>
+    <Param name="Monitor" val="testMonitor" valType="str" updates="None"/>
+    <Param name="Resources" val="[]" valType="fileList" updates="None"/>
+    <Param name="Save csv file" val="False" valType="bool" updates="None"/>
+    <Param name="Save excel file" val="False" valType="bool" updates="None"/>
+    <Param name="Save log file" val="True" valType="bool" updates="None"/>
+    <Param name="Save psydat file" val="True" valType="bool" updates="None"/>
+    <Param name="Save wide csv file" val="True" valType="bool" updates="None"/>
+    <Param name="Screen" val="1" valType="num" updates="None"/>
+    <Param name="Show info dlg" val="True" valType="bool" updates="None"/>
+    <Param name="Show mouse" val="False" valType="bool" updates="None"/>
+    <Param name="Units" val="height" valType="str" updates="None"/>
+    <Param name="Use version" val="" valType="str" updates="None"/>
+    <Param name="Window size (pixels)" val="(1024, 768)" valType="code" updates="None"/>
+    <Param name="blendMode" val="avg" valType="str" updates="None"/>
+    <Param name="color" val="$[0,0,0]" valType="str" updates="None"/>
+    <Param name="colorSpace" val="rgb" valType="str" updates="None"/>
+    <Param name="expName" val="CodeComponent_eachtab" valType="str" updates="None"/>
+    <Param name="exportHTML" val="on Sync" valType="str" updates="None"/>
+    <Param name="logging level" val="exp" valType="code" updates="None"/>
+  </Settings>
+  <Routines>
+    <Routine name="trial">
+      <CodeComponent name="code">
+        <Param name="Before Experiment" val="___before_experiment___" valType="extendedCode" updates="constant"/>
+        <Param name="Before JS Experiment" val="___before_experiment___;&amp;#10;" valType="extendedCode" updates="constant"/>
+        <Param name="Begin Experiment" val="___begin_experiment___" valType="extendedCode" updates="constant"/>
+        <Param name="Begin JS Experiment" val="___begin_experiment___;&amp;#10;" valType="extendedCode" updates="constant"/>
+        <Param name="Begin JS Routine" val="___begin_routine___;&amp;#10;" valType="extendedCode" updates="constant"/>
+        <Param name="Begin Routine" val="___begin_routine___" valType="extendedCode" updates="constant"/>
+        <Param name="Code Type" val="Auto-&gt;JS" valType="str" updates="None"/>
+        <Param name="Each Frame" val="___each_frame___" valType="extendedCode" updates="constant"/>
+        <Param name="Each JS Frame" val="___each_frame___;&amp;#10;" valType="extendedCode" updates="constant"/>
+        <Param name="End Experiment" val="___end_experiment___" valType="extendedCode" updates="constant"/>
+        <Param name="End JS Experiment" val="___end_experiment___;&amp;#10;" valType="extendedCode" updates="constant"/>
+        <Param name="End JS Routine" val="___end_routine___;&amp;#10;" valType="extendedCode" updates="constant"/>
+        <Param name="End Routine" val="___end_routine___" valType="extendedCode" updates="constant"/>
+        <Param name="disabled" val="False" valType="bool" updates="None"/>
+        <Param name="name" val="code" valType="code" updates="None"/>
+      </CodeComponent>
+    </Routine>
+  </Routines>
+  <Flow>
+    <Routine name="trial"/>
+  </Flow>
+</PsychoPy2experiment>
diff --git a/psychopy/tests/test_scripts/test_psyexpCompile.py b/psychopy/tests/test_scripts/test_psyexpCompile.py
--- a/psychopy/tests/test_scripts/test_psyexpCompile.py
+++ b/psychopy/tests/test_scripts/test_psyexpCompile.py
@@ -32,3 +32,35 @@ def test_disabled_component_is_not_written_to_script(self):
         with io.open(outfile, mode='r', encoding='utf-8-sig') as f:
             script = f.read()
             assert 'visual.TextStim' not in script
+
+    def test_all_code_component_tabs(self):
+        psyexp_file = os.path.join(TESTS_DATA_PATH,
+                                   'CodeComponent_eachtab.psyexp')
+        # Check py code from each tab exists
+        outfile = os.path.join(self.temp_dir, 'outfile.py')
+        psyexpCompile.compileScript(infile=psyexp_file, outfile=outfile)
+        with io.open(outfile, mode='r', encoding='utf-8-sig') as f:
+            script = f.read()
+        assert '___before_experiment___' in script
+        assert '___begin_experiment___' in script
+        assert '___begin_routine___' in script
+        assert '___each_frame___' in script
+        assert '___end_routine___' in script
+        assert '___end_experiment___' in script
+        # Check py code is in the right order
+        assert script.find('___before_experiment___') < script.find('___begin_experiment___') < script.find('___begin_routine___') < script.find('___each_frame___') < script.find('___end_routine___') < script.find('___end_experiment___')
+        assert script.find('___before_experiment___') < script.find('visual.Window') < script.find('___begin_experiment___') < script.find('continueRoutine = True')
+        assert script.find('continueRoutine = True') < script.find('___begin_routine___') < script.find('while continueRoutine:') < script.find('___each_frame___')
+        assert script.find('thisComponent.setAutoDraw(False)') < script.find('___end_routine___') < script.find('routineTimer.reset()') < script.find('___end_experiment___')
+
+        # Check js code from each tab exists
+        outfile = os.path.join(self.temp_dir, 'outfile.js')
+        psyexpCompile.compileScript(infile=psyexp_file, outfile=outfile)
+        with io.open(outfile, mode='r', encoding='utf-8-sig') as f:
+            script = f.read()
+        assert '___before_experiment___;' in script
+        assert '___begin_experiment___;' in script
+        assert '___begin_routine___;' in script
+        assert '___each_frame___;' in script
+        assert '___end_routine___;' in script
+        assert '___end_experiment___;' in script
\ No newline at end of file