Skip to content

Commit 6bc24c7

Browse files
committed
- Fixed bug which prevented experiment from running when task or hardware test were in a subdirectory.
- Fixed bug which prevented task file from being copied to data folder if it was in a subdirectory. - Added more detailed docstring to TaskSelectMenu class (renamed from menuSelect). - Moved example tasks into 'example' subfolder.
1 parent c8d046c commit 6bc24c7

12 files changed

+618
-607
lines changed

com/data_logger.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def copy_task_file(self, data_dir, tasks_dir, dir_name='task_files'):
4747
if not os.path.exists(exp_tasks_dir):
4848
os.mkdir(exp_tasks_dir)
4949
task_file_path = os.path.join(tasks_dir, self.sm_info['name']+'.py')
50-
task_save_name = self.sm_info['name']+'_{}.py'.format(self.sm_info['task_hash'])
50+
task_save_name = os.path.split(self.sm_info['name'])[1] +'_{}.py'.format(self.sm_info['task_hash'])
5151
if not task_save_name in os.listdir(exp_tasks_dir):
5252
copyfile(task_file_path, os.path.join(exp_tasks_dir, task_save_name))
5353

gui/GUI_main.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,13 @@ def view_github(self):
132132
QtGui.QDesktopServices.openUrl(QtCore.QUrl("https://github.com/pyControl/pyControl"))
133133

134134
def get_task_file_list(self):
135-
matches = []
136-
for root, dirnames, filenames in os.walk(dirs['tasks']):
137-
for filename in filenames:
138-
if filename.endswith(('.py')):
139-
matches.append(filename[:-3])
140-
return matches
135+
'''Return list of .py files in tasks folder and subfolders in format:
136+
subdir_1/subdir_2/task_file_name.py'''
137+
task_files = []
138+
for (dirpath, dirnames, filenames) in os.walk(dirs['tasks']):
139+
task_files += [os.path.join(dirpath, file).split(dirs['tasks'])[1][1:-3]
140+
for file in filenames if file.endswith('.py')]
141+
return task_files
141142

142143
def refresh(self):
143144
'''Called regularly when framework not running.'''

gui/configure_experiment_tab.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from config.paths import dirs
77
from gui.dialogs import invalid_run_experiment_dialog, invalid_save_experiment_dialog,unrun_subjects_dialog
8-
from gui.utility import TableCheckbox, cbox_update_options, cbox_set_item, null_resize, variable_constants, init_keyboard_shortcuts,menuSelect
8+
from gui.utility import TableCheckbox, cbox_update_options, cbox_set_item, null_resize, variable_constants, init_keyboard_shortcuts,TaskSelectMenu
99

1010
# --------------------------------------------------------------------------------
1111
# Experiments_tab
@@ -46,9 +46,9 @@ def __init__(self, parent=None):
4646
self.name_label = QtGui.QLabel('Experiment name:')
4747
self.name_text = QtGui.QLineEdit()
4848
self.task_label = QtGui.QLabel('Task:')
49-
self.task_select = menuSelect(dirs['tasks'],'select task')
49+
self.task_select = TaskSelectMenu(dirs['tasks'],'select task')
5050
self.hardware_test_label = QtGui.QLabel('Hardware test:')
51-
self.hardware_test_select = menuSelect(dirs['tasks'],'no hardware test',add_default=True)
51+
self.hardware_test_select = TaskSelectMenu(dirs['tasks'],'no hardware test',add_default=True)
5252
self.data_dir_label = QtGui.QLabel('Data dir:')
5353
self.data_dir_text = QtGui.QLineEdit(dirs['data'])
5454
self.data_dir_button = QtGui.QPushButton('')
@@ -153,7 +153,7 @@ def refresh(self):
153153
if (str(self.name_text.text()) == '') and not self.custom_dir:
154154
self.data_dir_text.setText(dirs['data'])
155155

156-
def experiment_dict(self,filtered = False):
156+
def experiment_dict(self, filtered=False):
157157
'''Return the current state of the experiments tab as a dictionary.'''
158158
return {'name': self.name_text.text(),
159159
'task': str(self.task_select.text()),
@@ -256,7 +256,6 @@ def run_experiment(self):
256256
'''Check that the experiment is valid. Prompt user to save experiment if
257257
it is new or has been edited. Then run experiment.'''
258258
experiment = self.experiment_dict(filtered=True)
259-
260259
if not experiment['name']:
261260
invalid_run_experiment_dialog(self, 'Experiment must have a name.')
262261
return

gui/run_task_tab.py

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
from gui.dialogs import Variables_dialog
1414
from gui.plotting import Task_plot
15-
from gui.utility import init_keyboard_shortcuts,menuSelect
15+
from gui.utility import init_keyboard_shortcuts,TaskSelectMenu
1616

1717
# Run_task_gui ------------------------------------------------------------------------
1818

@@ -105,7 +105,7 @@ def __init__(self, parent=None):
105105
self.task_groupbox = QtGui.QGroupBox('Task')
106106

107107
self.task_label = QtGui.QLabel('Task:')
108-
self.task_select = menuSelect(dirs['tasks'],'select task')
108+
self.task_select = TaskSelectMenu(dirs['tasks'],'select task')
109109
self.task_select.set_callback(self.task_changed)
110110
self.upload_button = QtGui.QPushButton('Upload')
111111
self.upload_button.setIcon(QtGui.QIcon("gui/icons/circle-arrow-up.svg"))
@@ -293,35 +293,36 @@ def task_changed(self,task=None):
293293
self.start_button.setEnabled(False)
294294

295295
def setup_task(self):
296+
task = self.task_select.text()
297+
if task == 'select task':
298+
return
296299
try:
297-
task = self.task_select.text()
298-
if task != 'select task':
299-
if self.uploaded:
300-
self.status_text.setText('Resetting task..')
301-
else:
302-
self.status_text.setText('Uploading..')
303-
self.task_hash = _djb2_file(os.path.join(dirs['tasks'], task + '.py'))
304-
self.start_button.setEnabled(False)
305-
self.variables_button.setEnabled(False)
306-
self.repaint()
307-
self.board.setup_state_machine(task, uploaded=self.uploaded)
308-
if self.variables_dialog:
309-
self.variables_button.clicked.disconnect()
310-
self.variables_dialog.deleteLater()
311-
self.variables_dialog = Variables_dialog(self, self.board)
312-
self.variables_button.clicked.connect(self.variables_dialog.exec_)
313-
self.variables_button.setEnabled(True)
314-
self.task_plot.set_state_machine(self.board.sm_info)
315-
self.file_groupbox.setEnabled(True)
316-
self.session_groupbox.setEnabled(True)
317-
self.start_button.setEnabled(True)
318-
self.stop_button.setEnabled(False)
319-
self.status_text.setText('Uploaded : ' + task)
320-
self.task = task
321-
self.fresh_task = True
322-
self.uploaded = True
323-
self.upload_button.setText('Reset')
324-
self.upload_button.setIcon(QtGui.QIcon("gui/icons/refresh.svg"))
300+
if self.uploaded:
301+
self.status_text.setText('Resetting task..')
302+
else:
303+
self.status_text.setText('Uploading..')
304+
self.task_hash = _djb2_file(os.path.join(dirs['tasks'], task + '.py'))
305+
self.start_button.setEnabled(False)
306+
self.variables_button.setEnabled(False)
307+
self.repaint()
308+
self.board.setup_state_machine(task, uploaded=self.uploaded)
309+
if self.variables_dialog:
310+
self.variables_button.clicked.disconnect()
311+
self.variables_dialog.deleteLater()
312+
self.variables_dialog = Variables_dialog(self, self.board)
313+
self.variables_button.clicked.connect(self.variables_dialog.exec_)
314+
self.variables_button.setEnabled(True)
315+
self.task_plot.set_state_machine(self.board.sm_info)
316+
self.file_groupbox.setEnabled(True)
317+
self.session_groupbox.setEnabled(True)
318+
self.start_button.setEnabled(True)
319+
self.stop_button.setEnabled(False)
320+
self.status_text.setText('Uploaded : ' + task)
321+
self.task = task
322+
self.fresh_task = True
323+
self.uploaded = True
324+
self.upload_button.setText('Reset')
325+
self.upload_button.setIcon(QtGui.QIcon("gui/icons/refresh.svg"))
325326
except PyboardError:
326327
self.status_text.setText('Error setting up state machine.')
327328

gui/utility.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,9 +293,19 @@ def dropEvent(self, event):
293293
self.dragDropedPos = event.pos()
294294
QtWidgets.QTabBar.dropEvent(self, event)
295295

296-
class menuSelect(QtGui.QPushButton):
297-
# https://stackoverflow.com/questions/35924235/how-to-create-combobox-with-combobox-inside-using-pyqt
298-
def __init__(self,root_folder,initial_text,add_default = False):
296+
# ----------------------------------------------------------------------------------
297+
# TaskSelectMenu
298+
# ----------------------------------------------------------------------------------
299+
300+
class TaskSelectMenu(QtGui.QPushButton):
301+
'''Nested menu used to select tasks. The menu items are the names of
302+
any .py files in root_folder and it's sub-directories. Items are
303+
nested in the menu according to the sub-directory structure.
304+
initial_text is shown before anything is selected, and if add_default
305+
is True, initial_text is included as a menu option.
306+
Adapted from: https://stackoverflow.com/questions/35924235
307+
'''
308+
def __init__(self, root_folder, initial_text, add_default=False):
299309
self.callback = lambda task: None
300310
self.menu = QtGui.QMenu()
301311
self.menu_root = root_folder
Lines changed: 83 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,84 @@
1-
# Example of how the all_states function can be used to make things happen in parallel
2-
# with the state set of a task. The states cycle the red, green and yellow LEDs on
3-
# when the usr pushbutton on the pyboard is pressed. In parallel the blue LED is
4-
# flashed on and off using the all_states function and timer events.
5-
6-
# Does not require any hardware except a micropython board.
7-
8-
from pyControl.utility import *
9-
from devices import *
10-
11-
# Define hardware (normally done in seperate hardware definition file).
12-
13-
pyboard_button = Digital_input('X17', falling_event='button_press', pull='up') # USR button on pyboard.
14-
15-
blue_LED = Digital_output('B4')
16-
red_LED = Digital_output('A13')
17-
green_LED = Digital_output('A14')
18-
yellow_LED = Digital_output('A15')
19-
20-
# States and events.
21-
22-
states= ['red_on',
23-
'green_on',
24-
'yellow_on']
25-
26-
events = ['button_press',
27-
'blue_on',
28-
'blue_off']
29-
30-
initial_state = 'red_on'
31-
32-
# Run start behaviour.
33-
34-
def run_start():
35-
# Turn on blue LED and set timer to turn it off in 1 second.
36-
blue_LED.on()
37-
set_timer('blue_off', 1*second, output_event=True)
38-
39-
# State behaviour functions.
40-
41-
def red_on(event):
42-
# Red LED on, button press transitions to green_on state.
43-
if event == 'entry':
44-
red_LED.on()
45-
elif event == 'exit':
46-
red_LED.off()
47-
elif event == 'button_press':
48-
goto_state('green_on')
49-
50-
def green_on(event):
51-
# Green LED on, button press transitions to yellow_on state.
52-
if event == 'entry':
53-
green_LED.on()
54-
elif event == 'exit':
55-
green_LED.off()
56-
elif event == 'button_press':
57-
goto_state('yellow_on')
58-
59-
def yellow_on(event):
60-
# Yellow LED on, button press transitions to red_on state.
61-
if event == 'entry':
62-
yellow_LED.on()
63-
elif event == 'exit':
64-
yellow_LED.off()
65-
elif event == 'button_press':
66-
goto_state('red_on')
67-
68-
# State independent behaviour.
69-
70-
def all_states(event):
71-
# Turn blue LED on and off when the corrsponding timer trigger, set timer for next blue on/off.
72-
if event == 'blue_on':
73-
blue_LED.on()
74-
set_timer('blue_off', 1*second, output_event=True)
75-
elif event == 'blue_off':
76-
blue_LED.off()
77-
set_timer('blue_on' , 1*second, output_event=True)
78-
79-
# Run end behaviour.
80-
81-
def run_end():
82-
# Turn off LEDs at end of run.
83-
for LED in [blue_LED, red_LED, green_LED, yellow_LED]:
1+
# Example of how the all_states function can be used to make things happen in parallel
2+
# with the state set of a task. The states cycle the red, green and yellow LEDs on
3+
# when the usr pushbutton on the pyboard is pressed. In parallel the blue LED is
4+
# flashed on and off using the all_states function and timer events.
5+
6+
# Does not require any hardware except a micropython board.
7+
8+
from pyControl.utility import *
9+
from devices import *
10+
11+
# Define hardware (normally done in seperate hardware definition file).
12+
13+
pyboard_button = Digital_input('X17', falling_event='button_press', pull='up') # USR button on pyboard.
14+
15+
blue_LED = Digital_output('B4')
16+
red_LED = Digital_output('A13')
17+
green_LED = Digital_output('A14')
18+
yellow_LED = Digital_output('A15')
19+
20+
# States and events.
21+
22+
states= ['red_on',
23+
'green_on',
24+
'yellow_on']
25+
26+
events = ['button_press',
27+
'blue_on',
28+
'blue_off']
29+
30+
initial_state = 'red_on'
31+
32+
# Run start behaviour.
33+
34+
def run_start():
35+
# Turn on blue LED and set timer to turn it off in 1 second.
36+
blue_LED.on()
37+
set_timer('blue_off', 1*second, output_event=True)
38+
39+
# State behaviour functions.
40+
41+
def red_on(event):
42+
# Red LED on, button press transitions to green_on state.
43+
if event == 'entry':
44+
red_LED.on()
45+
elif event == 'exit':
46+
red_LED.off()
47+
elif event == 'button_press':
48+
goto_state('green_on')
49+
50+
def green_on(event):
51+
# Green LED on, button press transitions to yellow_on state.
52+
if event == 'entry':
53+
green_LED.on()
54+
elif event == 'exit':
55+
green_LED.off()
56+
elif event == 'button_press':
57+
goto_state('yellow_on')
58+
59+
def yellow_on(event):
60+
# Yellow LED on, button press transitions to red_on state.
61+
if event == 'entry':
62+
yellow_LED.on()
63+
elif event == 'exit':
64+
yellow_LED.off()
65+
elif event == 'button_press':
66+
goto_state('red_on')
67+
68+
# State independent behaviour.
69+
70+
def all_states(event):
71+
# Turn blue LED on and off when the corrsponding timer trigger, set timer for next blue on/off.
72+
if event == 'blue_on':
73+
blue_LED.on()
74+
set_timer('blue_off', 1*second, output_event=True)
75+
elif event == 'blue_off':
76+
blue_LED.off()
77+
set_timer('blue_on' , 1*second, output_event=True)
78+
79+
# Run end behaviour.
80+
81+
def run_end():
82+
# Turn off LEDs at end of run.
83+
for LED in [blue_LED, red_LED, green_LED, yellow_LED]:
8484
LED.off()

0 commit comments

Comments
 (0)