Skip to content

Commit b21a914

Browse files
committed
Merge remote-tracking branch 'origin/dev' into dev
2 parents aa94766 + 523bb2d commit b21a914

13 files changed

+349
-143
lines changed

com/pycboard.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
from serial import SerialException
66
from array import array
77
from .pyboard import Pyboard, PyboardError
8-
from config.paths import dirs
9-
from config.gui_settings import VERSION
8+
from config.settings import VERSION, dirs, get_setting
109

1110
# ----------------------------------------------------------------------------------------
1211
# Helper functions.
@@ -351,7 +350,7 @@ def setup_state_machine(self, sm_name, sm_dir=None, uploaded=False):
351350
to board. Instantiate state machine object as state_machine on pyboard.'''
352351
self.reset()
353352
if sm_dir is None:
354-
sm_dir = dirs['tasks']
353+
sm_dir = get_setting("folders","tasks")
355354
sm_path = os.path.join(sm_dir, sm_name + '.py')
356355
if uploaded:
357356
self.print('\nResetting task. ', end='')

config/gui_settings.py

Lines changed: 0 additions & 12 deletions
This file was deleted.

config/paths.py

Lines changed: 0 additions & 30 deletions
This file was deleted.

config/settings.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import os
2+
import json
3+
4+
VERSION = "1.8"
5+
6+
top_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Top level pyControl folder.
7+
dirs = {
8+
"config": os.path.join(top_dir, "config"),
9+
"framework": os.path.join(top_dir, "pyControl"),
10+
"devices": os.path.join(top_dir, "devices"),
11+
"gui": os.path.join(top_dir, "gui"),
12+
"experiments": os.path.join(top_dir, "experiments"),
13+
}
14+
15+
default_user_settings = {
16+
"folders": {
17+
"tasks": os.path.join(top_dir, "tasks"),
18+
"data": os.path.join(top_dir, "data"),
19+
},
20+
"plotting": {
21+
"update_interval": 10,
22+
"event_history_len": 200,
23+
"state_history_len": 100,
24+
"analog_history_dur": 12,
25+
},
26+
"GUI": {
27+
"ui_font_size": 11,
28+
"log_font_size": 9,
29+
},
30+
}
31+
32+
33+
def get_setting(setting_type, setting_name=None):
34+
"""
35+
gets a user setting or group of user settings
36+
from the user_settings.json or, if that doesn't exist,
37+
the default_user_settings dictionary
38+
"""
39+
json_path = os.path.join(dirs["config"], "user_settings.json")
40+
if os.path.exists(json_path):
41+
with open(json_path, "r", encoding='utf-8') as f:
42+
user_settings = json.loads(f.read())
43+
else:
44+
user_settings = default_user_settings
45+
if setting_name:
46+
return user_settings[setting_type][setting_name]
47+
return user_settings[setting_type]

config/user_variable_dialogs/blinker_gui_from_py.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# This is an example custom dialog for more advanced users.
22
# Complex custom dialogs can be directly coded using the PyQt framework
33

4-
from pyqtgraph.Qt import QtGui, QtCore
4+
from pyqtgraph.Qt import QtGui, QtCore, QtWidgets
55
from gui.custom_variables_dialog import Slider_var, Spin_var
66

77
# Custom Variable dialog

gui/GUI_main.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77
from serial.tools import list_ports
88
from pyqtgraph.Qt import QtGui, QtCore, QtWidgets
99

10-
from config.paths import dirs
11-
from config.gui_settings import VERSION, ui_font_size
10+
from config.settings import VERSION, dirs, get_setting
1211
from gui.run_task_tab import Run_task_tab
13-
from gui.dialogs import Board_config_dialog, Keyboard_shortcuts_dialog, Paths_dialog
12+
from gui.dialogs import Board_config_dialog, Keyboard_shortcuts_dialog, Settings_dialog
1413
from gui.configure_experiment_tab import Configure_experiment_tab
1514
from gui.run_experiment_tab import Run_experiment_tab
1615
from gui.setups_tab import Setups_tab
@@ -37,15 +36,15 @@ def __init__(self,app):
3736
self.available_tasks_changed = False
3837
self.available_experiments_changed = False
3938
self.available_ports_changed = False
39+
self.task_directory = get_setting("folders","tasks")
4040
self.data_dir_changed = False
4141
self.current_tab_ind = 0 # Which tab is currently selected.
4242
self.app = app
4343

4444
# Dialogs.
45-
4645
self.config_dialog = Board_config_dialog(parent=self)
4746
self.shortcuts_dialog = Keyboard_shortcuts_dialog(parent=self)
48-
self.paths_dialog = Paths_dialog(parent=self)
47+
self.settings_dialog = Settings_dialog(parent=self)
4948

5049
# Widgets.
5150
self.tab_widget = QtWidgets.QTabWidget(self)
@@ -68,7 +67,6 @@ def __init__(self,app):
6867
self.tab_widget.currentChanged.connect(self.tab_changed)
6968

7069
# Timers
71-
7270
self.refresh_timer = QtCore.QTimer() # Timer to regularly call refresh() when not running.
7371
self.refresh_timer.timeout.connect(self.refresh)
7472
self.refresh_timer.start(self.refresh_interval)
@@ -93,8 +91,8 @@ def __init__(self,app):
9391
## --------Settings menu--------
9492
settings_menu = main_menu.addMenu('Settings')
9593
# Folder paths
96-
paths_action = QtGui.QAction("&Folder paths", self)
97-
paths_action.triggered.connect(self.paths_dialog.exec)
94+
paths_action = QtGui.QAction("&Edit settings", self)
95+
paths_action.triggered.connect(self.settings_dialog.exec)
9896
settings_menu.addAction(paths_action)
9997
# ---------Help menu----------
10098
help_menu= main_menu.addMenu('Help')
@@ -122,10 +120,10 @@ def __init__(self,app):
122120
self.show()
123121

124122
def go_to_data(self):
125-
QtGui.QDesktopServices.openUrl(QtCore.QUrl.fromLocalFile(dirs['data']))
123+
QtGui.QDesktopServices.openUrl(QtCore.QUrl.fromLocalFile(get_setting("folders","data")))
126124

127125
def go_to_tasks(self):
128-
QtGui.QDesktopServices.openUrl(QtCore.QUrl.fromLocalFile(dirs['tasks']))
126+
QtGui.QDesktopServices.openUrl(QtCore.QUrl.fromLocalFile(get_setting("folders","tasks")))
129127

130128
def view_docs(self):
131129
QtGui.QDesktopServices.openUrl(QtCore.QUrl("https://pycontrol.readthedocs.io/en/latest/"))
@@ -140,8 +138,11 @@ def get_task_file_list(self):
140138
'''Return list of .py files in tasks folder and subfolders in format:
141139
subdir_1/subdir_2/task_file_name.py'''
142140
task_files = []
143-
for (dirpath, dirnames, filenames) in os.walk(dirs['tasks']):
144-
task_files += [os.path.join(dirpath, file).split(dirs['tasks'])[1][1:-3]
141+
# this function gets called every second. Normally we would use get_setting("folder","tasks")
142+
# but there no need to constantly be rereading the user_settings.json file that isn't changing
143+
# so we use this self.task_directory variable that is only updated when a new user settting is saved
144+
for (dirpath, dirnames, filenames) in os.walk(self.task_directory):
145+
task_files += [os.path.join(dirpath, file).split(self.task_directory)[1][1:-3]
145146
for file in filenames if file.endswith('.py')]
146147
return task_files
147148

@@ -150,12 +151,12 @@ def refresh(self):
150151
# Scan task folder.
151152
tasks = self.get_task_file_list()
152153
self.available_tasks_changed = tasks != self.available_tasks
153-
if self.available_tasks_changed:
154+
if self.available_tasks_changed:
154155
self.available_tasks = tasks
155156
# Scan experiments folder.
156157
experiments = [t.split('.')[0] for t in os.listdir(dirs['experiments']) if t[-4:] == '.pcx']
157158
self.available_experiments_changed = experiments != self.available_experiments
158-
if self.available_experiments_changed:
159+
if self.available_experiments_changed:
159160
self.available_experiments = experiments
160161
# Scan serial ports.
161162
ports = set([c[0] for c in list_ports.comports()
@@ -196,7 +197,7 @@ def launch_GUI():
196197
app.setStyle('Fusion')
197198
app.setWindowIcon(QtGui.QIcon("gui/icons/logo.svg"))
198199
font = QtGui.QFont()
199-
font.setPixelSize(ui_font_size)
200+
font.setPixelSize(get_setting("GUI",("ui_font_size")))
200201
app.setFont(font)
201202
gui_main = GUI_main(app)
202203
sys.excepthook = gui_main.excepthook

gui/configure_experiment_tab.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import json
44
from pyqtgraph.Qt import QtGui, QtCore, QtWidgets
55

6-
from config.paths import dirs
6+
from config.settings import dirs, get_setting
77
from gui.dialogs import invalid_run_experiment_dialog, invalid_save_experiment_dialog,unrun_subjects_dialog
88
from gui.utility import TableCheckbox, cbox_update_options, cbox_set_item, null_resize, variable_constants, init_keyboard_shortcuts,TaskSelectMenu
99

@@ -54,7 +54,7 @@ def __init__(self, parent=None):
5454
self.hardware_test_label = QtWidgets.QLabel('Hardware test:')
5555
self.hardware_test_select = TaskSelectMenu('no hardware test',add_default=True)
5656
self.data_dir_label = QtWidgets.QLabel('Data directory:')
57-
self.data_dir_text = QtWidgets.QLineEdit(dirs['data'])
57+
self.data_dir_text = QtWidgets.QLineEdit(get_setting("folders","data"))
5858
self.data_dir_button = QtWidgets.QPushButton('')
5959
self.data_dir_button.setIcon(QtGui.QIcon("gui/icons/folder.svg"))
6060
self.data_dir_button.setFixedWidth(30)
@@ -124,10 +124,10 @@ def __init__(self, parent=None):
124124

125125
def name_edited(self):
126126
if not self.custom_dir:
127-
self.data_dir_text.setText(os.path.join(dirs['data'], self.name_text.text()))
127+
self.data_dir_text.setText(os.path.join(get_setting("folders","data"), self.name_text.text()))
128128

129129
def select_data_dir(self):
130-
new_path = QtWidgets.QFileDialog.getExistingDirectory(self, 'Select data folder', dirs['data'])
130+
new_path = QtWidgets.QFileDialog.getExistingDirectory(self, 'Select data folder', get_setting("folders","data"))
131131
if new_path:
132132
self.data_dir_text.setText(new_path)
133133
self.custom_dir = True
@@ -142,8 +142,8 @@ def experiment_changed(self, experiment_name):
142142
def refresh(self):
143143
'''Called periodically when not running to update available task, ports, experiments.'''
144144
if self.GUI_main.available_tasks_changed:
145-
self.task_select.update_menu(dirs['tasks'])
146-
self.hardware_test_select.update_menu(dirs['tasks'])
145+
self.task_select.update_menu(get_setting("folders","tasks"))
146+
self.hardware_test_select.update_menu(get_setting("folders","tasks"))
147147
self.GUI_main.available_tasks_changed = False
148148
if self.GUI_main.available_experiments_changed:
149149
cbox_update_options(self.experiment_select, self.GUI_main.available_experiments)
@@ -157,7 +157,7 @@ def refresh(self):
157157
self.save_button.setEnabled(False)
158158
if self.GUI_main.data_dir_changed:
159159
if (str(self.name_text.text()) == '') and not self.custom_dir:
160-
self.data_dir_text.setText(dirs['data'])
160+
self.data_dir_text.setText(get_setting("folders","data"))
161161

162162
def experiment_dict(self, filtered=False):
163163
'''Return the current state of the experiments tab as a dictionary.'''
@@ -174,7 +174,7 @@ def new_experiment(self, dialog=True):
174174
if dialog:
175175
if not self.save_dialog(): return
176176
self.name_text.setText('')
177-
self.data_dir_text.setText(dirs['data'])
177+
self.data_dir_text.setText(get_setting("folders","data"))
178178
self.custom_dir = False
179179
self.subjects_table.reset()
180180
self.variables_table.reset()
@@ -605,7 +605,7 @@ def task_changed(self, task):
605605
'''Remove variables that are not defined in the new task.'''
606606
pattern = "[\n\r\.]v\.(?P<vname>\w+)\s*\="
607607
try:
608-
with open(os.path.join(dirs['tasks'], task+'.py'), "r") as file:
608+
with open(os.path.join(get_setting("folders","tasks"), task+'.py'), "r") as file:
609609
file_content = file.read()
610610
except FileNotFoundError:
611611
return

gui/custom_variables_dialog.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import json
33
import re
44
from pyqtgraph.Qt import QtGui, QtCore, QtWidgets
5-
from config.paths import dirs
5+
from config.settings import dirs, get_setting
66
from gui.utility import variable_constants, null_resize, cbox_set_item, cbox_update_options
77

88
# input widgets ---------------------------------------------------------------
@@ -571,7 +571,7 @@ def get_vars(self, task):
571571
"""Remove variables that are not defined in the new task."""
572572
pattern = "[\n\r]v\.(?P<vname>\w+)\s*\="
573573
try:
574-
with open(os.path.join(dirs["tasks"], task + ".py"), "r") as file:
574+
with open(os.path.join(get_setting("folders","tasks"), task + ".py"), "r") as file:
575575
file_content = file.read()
576576
except FileNotFoundError:
577577
return
@@ -603,9 +603,9 @@ def remove_tab(self):
603603
self,
604604
"Remove tab",
605605
f'Are you sure you want to remove "{self.tab_title_edit.text()}"?',
606-
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Cancel,
606+
QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.Cancel,
607607
)
608-
if reply == QtWidgets.QMessageBox.Yes:
608+
if reply == QtWidgets.QMessageBox.StandardButton.Yes:
609609
index = self.tabs.currentIndex()
610610
table_key = self.tabs.tabText(index)
611611
self.tabs.removeTab(index)

0 commit comments

Comments
 (0)