Skip to content

Commit 958d89f

Browse files
authored
Merge pull request #109 from dihm/EO_widgets
Enum Widget Support
2 parents 651b981 + d6b042a commit 958d89f

3 files changed

Lines changed: 365 additions & 30 deletions

File tree

blacs/device_base_class.py

Lines changed: 137 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from blacs import BLACS_DIR
2727
from blacs.tab_base_classes import Tab, Worker, define_state
2828
from blacs.tab_base_classes import MODE_MANUAL, MODE_TRANSITION_TO_BUFFERED, MODE_TRANSITION_TO_MANUAL, MODE_BUFFERED
29-
from blacs.output_classes import AO, DO, DDS, Image
29+
from blacs.output_classes import AO, DO, DDS, Image, EO
3030
from labscript_utils.qtwidgets.toolpalette import ToolPaletteGroup
3131
from labscript_utils.shared_drive import path_to_agnostic
3232

@@ -41,6 +41,7 @@ def __init__(self,notebook,settings,restart=False):
4141
self._DO = {}
4242
self._DDS = {}
4343
self._image = {}
44+
self._EO = {}
4445

4546
self._final_values = {}
4647
self._last_programmed_values = {}
@@ -151,6 +152,13 @@ def supports_remote_value_check(self,support):
151152
# },
152153
# }
153154
#
155+
#
156+
# eo_properties = {'hardware_channel_reference': {'options':['option 1', 'option 2']},
157+
# 'eo1': {'options': {'option 1': {'index': 0, 'tooltip': 'description 1'},
158+
# 'option 2': {'index': 3, 'tooltip': 'description 2'},
159+
# 'option 3': 1},
160+
# 'return_index': True}}
161+
154162
def create_digital_outputs(self,digital_properties):
155163
for hardware_name,properties in digital_properties.items():
156164
# Save the DO object
@@ -222,7 +230,16 @@ def create_dds_outputs(self,dds_properties):
222230
sub_chnls['gate'] = self._create_DO_object(connection_name,hardware_name+'_gate','gate',properties)
223231

224232
self._DDS[hardware_name] = DDS(hardware_name,connection_name,sub_chnls)
225-
233+
234+
def create_enum_outputs(self, eo_properties):
235+
for output_name, properties in eo_properties.items():
236+
self._EO[output_name] = self._create_EO_object(self.device_name, output_name, properties)
237+
238+
def _create_EO_object(self, parent_device, device_property, properties):
239+
properties.setdefault('return_index', False)
240+
return EO(device_property, parent_device, self.device_name, self.program_device, self.settings,
241+
properties['options'], properties['return_index'])
242+
226243
def get_child_from_connection_table(self, parent_device_name, port):
227244
return self.connection_table.find_child(parent_device_name, port)
228245

@@ -271,6 +288,23 @@ def create_dds_widgets(self,channel_properties):
271288

272289
return widgets
273290

291+
def create_enum_widgets(self,device_properties):
292+
293+
widgets = {}
294+
for output_name, properties in device_properties.items():
295+
properties.setdefault('display_name',output_name)
296+
properties.setdefault('horizontal_alignment',False)
297+
properties.setdefault('parent',None)
298+
widgets[output_name] = self._EO[output_name].create_widget(properties['display_name'], properties['horizontal_alignment'], properties['parent'])
299+
300+
return widgets
301+
302+
def auto_create_enum_widgets(self):
303+
eo_properties = {}
304+
for channel,_ in self._EO.items():
305+
eo_properties[channel] = {}
306+
return self.create_enum_widgets(eo_properties)
307+
274308
def auto_create_widgets(self):
275309
dds_properties = {}
276310
for channel,output in self._DDS.items():
@@ -361,7 +395,7 @@ def update_from_settings(self,settings):
361395
self.restore_save_data(settings['saved_data'])
362396

363397
self.settings = settings
364-
for output in [self._AO, self._DO, self._image]:
398+
for output in [self._AO, self._DO, self._image, self._EO]:
365399
for name,channel in output.items():
366400
if not channel._locked:
367401
channel._update_from_settings(settings)
@@ -374,8 +408,8 @@ def update_from_settings(self,settings):
374408
subchnl._update_from_settings(settings)
375409

376410
def get_front_panel_values(self):
377-
return {channel:item.value for output in [self._AO,self._DO,self._image,self._DDS] for channel,item in output.items()}
378-
411+
return {channel:item.value for output in [self._AO,self._DO,self._image,self._DDS,self._EO] for channel,item in output.items()}
412+
379413
def get_channel(self,channel):
380414
if channel in self._AO:
381415
return self._AO[channel]
@@ -385,6 +419,8 @@ def get_channel(self,channel):
385419
return self._image[channel]
386420
elif channel in self._DDS:
387421
return self._DDS[channel]
422+
elif channel in self._EO:
423+
return self._EO[channel]
388424
else:
389425
return None
390426

@@ -519,6 +555,16 @@ def check_remote_values(self):
519555
ui.channel_label.setText(self._AO[channel].name)
520556
ui.front_value.setText(front_value)
521557
ui.remote_value.setText(remote_value)
558+
elif channel in self._EO:
559+
# very easy case, values are strings
560+
front_value = str(self._last_programmed_values[channel])
561+
remote_value = str(remote_value)
562+
if front_value != remote_value:
563+
changed = True
564+
ui = UiLoader().load(os.path.join(BLACS_DIR, 'tab_value_changed.ui'))
565+
ui.channel_label.setText(self._EO[channel].name)
566+
ui.front_value.setText(front_value)
567+
ui.remote_value.setText(remote_value)
522568
else:
523569
raise RuntimeError('device_base_class.py is not programmed to handle channel types other than DDS, AO and DO in check_remote_values')
524570

@@ -666,7 +712,8 @@ def transition_to_manual(self,notify_queue,program=False):
666712
self._image[channel].set_value(value,program=False)
667713
elif channel in self._DDS:
668714
self._DDS[channel].set_value(value,program=False)
669-
715+
elif channel in self._EO:
716+
self._EO[channel].set_value(value,program=False)
670717

671718

672719
if success:
@@ -707,18 +754,20 @@ def shutdown(self):
707754

708755
def program_manual(self,front_panel_values):
709756
for channel,value in front_panel_values.items():
710-
if type(value) != type(True):
757+
if isinstance(value, float):
711758
front_panel_values[channel] += 0.001
712759
self.fpv = front_panel_values
713760
return front_panel_values
714761

715762
def check_remote_values(self):
716763
front_panel_values = {}
717764
for channel,value in self.fpv.items():
718-
if type(value) != type(True):
765+
if isinstance(value, float):
719766
front_panel_values[channel] = value + 1.1
720-
else:
767+
elif isinstance(value, bool):
721768
front_panel_values[channel] = not value
769+
else:
770+
front_panel_values[channel] = value
722771

723772
if not front_panel_values:
724773
front_panel_values['ao0'] = 0
@@ -728,7 +777,7 @@ def check_remote_values(self):
728777
def transition_to_buffered(self,device_name,h5file,front_panel_values,refresh):
729778
time.sleep(3)
730779
for channel,value in front_panel_values.items():
731-
if type(value) != type(True):
780+
if isinstance(value, float):
732781
front_panel_values[channel] += 0.003
733782
return front_panel_values
734783

@@ -766,10 +815,10 @@ def transition_to_manual(self):
766815
if __name__ == '__main__':
767816
# Test case!
768817

769-
from connections import ConnectionTable
818+
from labscript_utils.connections import ConnectionTable
770819
from labscript_utils.qtwidgets.dragdroptab import DragDropTabWidget
771820

772-
class MyTab(DeviceTab):
821+
class MyDAQTab(DeviceTab):
773822

774823
def initialise_GUI(self):
775824
# Create Digital Output Objects
@@ -821,8 +870,79 @@ def sort(channel):
821870
button2 = QPushButton("Transition to Manual")
822871
button2.clicked.connect(lambda: self.transition_to_manual(Queue()))
823872
self.get_tab_layout().addWidget(button2)
873+
874+
class MyDummyTab(DeviceTab):
875+
876+
def initialise_GUI(self):
877+
# Create Digital Output Objects
878+
do_prop = {}
879+
for i in range(1):
880+
do_prop['port0/line%d'] = {}
881+
self.create_digital_outputs(do_prop)
882+
883+
# Create Analog Output objects
884+
ao_prop = {}
885+
for i in range(1):
886+
ao_prop['ao%d'%i] = {'base_unit':'V',
887+
'min':-10.0,
888+
'max':10.0,
889+
'step':0.01,
890+
'decimals':3
891+
}
892+
self.create_analog_outputs(ao_prop)
893+
894+
eo_prop = {
895+
'Enum1':{
896+
'options':['option 1', 'option 2'],
897+
'return_index':True,
898+
},
899+
'Enum2':{
900+
'options':{
901+
'option 1':{'index':2, 'tooltip':'description 1'},
902+
'option 2':4,
903+
}
904+
}
905+
}
906+
self.create_enum_outputs(eo_prop)
907+
908+
# Create widgets for output objects
909+
dds_widgets,ao_widgets,do_widgets = self.auto_create_widgets()
910+
eo_widgets = self.auto_create_enum_widgets()
911+
912+
# This function allows you do sort the order of widgets by hardware name.
913+
# it is pass to the Python 'sorted' function as key=sort when passed in as
914+
# the 3rd item of a tuple p(the tuple being an argument of self.auto_place_widgets()
915+
#
916+
# This function takes the channel name (hardware name) and returns a string (or whatever)
917+
# that when sorted alphabetically, returns the correct order
918+
def sort(channel):
919+
port,line = channel.replace('port','').replace('line','').split('/')
920+
port,line = int(port),int(line)
921+
return '%02d/%02d'%(port,line)
922+
923+
# and auto place them in the UI
924+
self.auto_place_widgets(("DDS Outputs",dds_widgets),
925+
("Analog Outputs",ao_widgets),
926+
("Digital Outputs - Port 0",do_widgets),
927+
('Enums', eo_widgets))
928+
929+
# Set the primary worker
930+
self.create_worker("my_worker_name",DeviceWorker,{})
931+
self.primary_worker = "my_worker_name"
932+
self.create_worker("my_secondary_worker_name",DeviceWorker,{})
933+
self.add_secondary_worker("my_secondary_worker_name")
934+
935+
self.supports_remote_value_check(True)
936+
937+
# Create buttons to test things!
938+
button1 = QPushButton("Transition to Buffered")
939+
button1.clicked.connect(lambda: self.transition_to_buffered('',Queue()))
940+
self.get_tab_layout().addWidget(button1)
941+
button2 = QPushButton("Transition to Manual")
942+
button2.clicked.connect(lambda: self.transition_to_manual(Queue()))
943+
self.get_tab_layout().addWidget(button2)
824944

825-
connection_table = ConnectionTable(r'example_connection_table.h5')
945+
connection_table = ConnectionTable('../tests/device_base_classes_connection_table.h5')
826946

827947
class MyWindow(QWidget):
828948

@@ -833,14 +953,11 @@ def __init__(self,*args,**kwargs):
833953
def closeEvent(self,event):
834954
if not self.are_we_closed:
835955
event.ignore()
836-
self.my_tab.shutdown()
956+
self.my_tab.close_tab()
837957
self.are_we_closed = True
838958
QTimer.singleShot(1000,self.close)
839959
else:
840-
if not self.my_tab.shutdown_complete:
841-
QTimer.singleShot(1000,self.close)
842-
else:
843-
event.accept()
960+
event.accept()
844961

845962
def add_my_tab(self,tab):
846963
self.my_tab = tab
@@ -851,7 +968,8 @@ def add_my_tab(self,tab):
851968
notebook = DragDropTabWidget()
852969
layout.addWidget(notebook)
853970

854-
tab1 = MyTab(notebook,settings = {'device_name': 'ni_pcie_6363_0', 'connection_table':connection_table})
971+
tab1 = MyDAQTab(notebook,settings = {'device_name': 'ni_pcie_6363_0', 'connection_table':connection_table})
972+
tab2 = MyDummyTab(notebook,settings = {'device_name': 'intermediate_device', 'connection_table':connection_table})
855973
window.add_my_tab(tab1)
856974
window.show()
857975
def run():

0 commit comments

Comments
 (0)