def __init__(self, parent, global_store, *args, **kwargs):
		wx.Panel.__init__(self, parent, *args, **kwargs)

		self.global_store = global_store
		self._measurement_resource_name = None

		# Defaults.
		self.plot_settings = PlotSettings()
		self.unit_conversion = 0

		self.enabled = False
		self.capturing_data = False
		self.restart_live_view = False
		self.resource_backup = None

		# This lock blocks the acquisition thread from acquiring.
		self.running_lock = Lock()

		# Initialize recorded values.
		self.init_values()

		# Plot and toolbar.
		display_box = wx.BoxSizer(wx.VERTICAL)

		## Plot.
		if plot_available:
			self.plot = TwoDimensionalPlot(self, color='blue')
			display_box.Add(self.plot.control, proportion=1, flag=wx.EXPAND)

			self.plot.x_label = 'Time (s)'
		else:
			display_box.Add((500, -1), proportion=1, flag=wx.EXPAND)

		## Controls.
		if plot_available:
			controls_box = wx.BoxSizer(wx.HORIZONTAL)
			display_box.Add(controls_box, flag=wx.CENTER|wx.ALL, border=5)

			### Numeric display.
			numeric_display_static_box = wx.StaticBox(self, label='Reading')
			numeric_display_box = wx.StaticBoxSizer(numeric_display_static_box, wx.HORIZONTAL)
			controls_box.Add(numeric_display_box, flag=wx.CENTER)

			self.numeric_display = wx.TextCtrl(self, size=(100, -1), style=wx.TE_READONLY)
			self.numeric_display.BackgroundColour = wx.LIGHT_GREY
			numeric_display_box.Add(self.numeric_display)

			### Capture.
			capture_static_box = wx.StaticBox(self, label='Control')
			capture_box = wx.StaticBoxSizer(capture_static_box)
			controls_box.Add(capture_box, flag=wx.CENTER|wx.LEFT, border=10)

			self.run_button = wx.Button(self, label='Run')
			self.Bind(wx.EVT_BUTTON, self.OnRun, self.run_button)
			capture_box.Add(self.run_button, flag=wx.CENTER)

			self.pause_button = wx.Button(self, label='Pause')
			self.Bind(wx.EVT_BUTTON, self.OnPause, self.pause_button)
			capture_box.Add(self.pause_button, flag=wx.CENTER)

			self.reset_button = wx.Button(self, label='Reset')
			self.Bind(wx.EVT_BUTTON, self.OnReset, self.reset_button)
			capture_box.Add(self.reset_button, flag=wx.CENTER|wx.LEFT, border=10)

			### Settings.
			settings_static_box = wx.StaticBox(self, label='Settings')
			settings_box = wx.StaticBoxSizer(settings_static_box, wx.HORIZONTAL)
			controls_box.Add(settings_box, flag=wx.CENTER|wx.LEFT, border=10)

			self.plot_settings_button = wx.Button(self, label='Plot...')
			self.Bind(wx.EVT_BUTTON, self.OnPlotSettings, self.plot_settings_button)
			settings_box.Add(self.plot_settings_button, flag=wx.CENTER)

		self.SetSizer(display_box)

		# Acquisition thread.
		callback = functools.partial(wx.CallAfter, self.add_value)
		self.acq_thread = AcquisitionThread(self.plot_settings.delay, callback,
				running_lock=self.running_lock)
		self.acq_thread.daemon = True
		self.acq_thread.start()

		# Wait for a resource to begin capturing.
		self.OnPause()
		self.run_button.Disable()

		# Subscriptions.
		pub.subscribe(self.msg_resource, 'resource.added')
		pub.subscribe(self.msg_resource, 'resource.removed')
		pub.subscribe(self.msg_data_capture_start, 'data_capture.start')
		pub.subscribe(self.msg_data_capture_data, 'data_capture.data')
		pub.subscribe(self.msg_data_capture_stop, 'data_capture.stop')
Пример #2
0
	def __init__(self, parent, global_store, *args, **kwargs):
		wx.Panel.__init__(self, parent, *args, **kwargs)

		self.global_store = global_store
		self._measurement_resource_name = None

		# Defaults.
		self.plot_settings = PlotSettings()
		self.unit_conversion = 0

		self.enabled = False
		self.capturing_data = False
		self.restart_live_view = False
		self.resource_backup = None

		# This lock blocks the acquisition thread from acquiring.
		self.running_lock = Lock()

		# Initialize recorded values.
		self.init_values()

		# Plot and toolbar.
		display_box = wx.BoxSizer(wx.VERTICAL)

		## Plot.
		if plot_available:
			self.plot = TwoDimensionalPlot(self, color='blue')
			display_box.Add(self.plot.control, proportion=1, flag=wx.EXPAND)

			self.plot.x_label = 'Time (s)'
		else:
			display_box.Add((500, -1), proportion=1, flag=wx.EXPAND)

		## Controls.
		if plot_available:
			controls_box = wx.BoxSizer(wx.HORIZONTAL)
			display_box.Add(controls_box, flag=wx.CENTER|wx.ALL, border=5)

			### Numeric display.
			numeric_display_static_box = wx.StaticBox(self, label='Reading')
			numeric_display_box = wx.StaticBoxSizer(numeric_display_static_box, wx.HORIZONTAL)
			controls_box.Add(numeric_display_box, flag=wx.CENTER)

			self.numeric_display = wx.TextCtrl(self, size=(100, -1), style=wx.TE_READONLY)
			self.numeric_display.BackgroundColour = wx.LIGHT_GREY
			numeric_display_box.Add(self.numeric_display)

			### Capture.
			capture_static_box = wx.StaticBox(self, label='Control')
			capture_box = wx.StaticBoxSizer(capture_static_box)
			controls_box.Add(capture_box, flag=wx.CENTER|wx.LEFT, border=10)

			self.run_button = wx.Button(self, label='Run')
			self.Bind(wx.EVT_BUTTON, self.OnRun, self.run_button)
			capture_box.Add(self.run_button, flag=wx.CENTER)

			self.pause_button = wx.Button(self, label='Pause')
			self.Bind(wx.EVT_BUTTON, self.OnPause, self.pause_button)
			capture_box.Add(self.pause_button, flag=wx.CENTER)

			self.reset_button = wx.Button(self, label='Reset')
			self.Bind(wx.EVT_BUTTON, self.OnReset, self.reset_button)
			capture_box.Add(self.reset_button, flag=wx.CENTER|wx.LEFT, border=10)

			### Settings.
			settings_static_box = wx.StaticBox(self, label='Settings')
			settings_box = wx.StaticBoxSizer(settings_static_box, wx.HORIZONTAL)
			controls_box.Add(settings_box, flag=wx.CENTER|wx.LEFT, border=10)

			self.plot_settings_button = wx.Button(self, label='Plot...')
			self.Bind(wx.EVT_BUTTON, self.OnPlotSettings, self.plot_settings_button)
			settings_box.Add(self.plot_settings_button, flag=wx.CENTER)

		self.SetSizer(display_box)

		# Acquisition thread.
		callback = functools.partial(wx.CallAfter, self.add_value)
		self.acq_thread = AcquisitionThread(self.plot_settings.delay, callback,
				running_lock=self.running_lock)
		self.acq_thread.daemon = True
		self.acq_thread.start()

		# Wait for a resource to begin capturing.
		self.OnPause()
		self.run_button.Disable()

		# Subscriptions.
		pub.subscribe(self.msg_resource, 'resource.added')
		pub.subscribe(self.msg_resource, 'resource.removed')
		pub.subscribe(self.msg_data_capture_start, 'data_capture.start')
		pub.subscribe(self.msg_data_capture_data, 'data_capture.data')
		pub.subscribe(self.msg_data_capture_stop, 'data_capture.stop')
class ScalarLiveViewPanel(wx.Panel):
	"""
	A panel to display a live view plot of a scalar resource.
	"""

	def __init__(self, parent, global_store, *args, **kwargs):
		wx.Panel.__init__(self, parent, *args, **kwargs)

		self.global_store = global_store
		self._measurement_resource_name = None

		# Defaults.
		self.plot_settings = PlotSettings()
		self.unit_conversion = 0

		self.enabled = False
		self.capturing_data = False
		self.restart_live_view = False
		self.resource_backup = None

		# This lock blocks the acquisition thread from acquiring.
		self.running_lock = Lock()

		# Initialize recorded values.
		self.init_values()

		# Plot and toolbar.
		display_box = wx.BoxSizer(wx.VERTICAL)

		## Plot.
		if plot_available:
			self.plot = TwoDimensionalPlot(self, color='blue')
			display_box.Add(self.plot.control, proportion=1, flag=wx.EXPAND)

			self.plot.x_label = 'Time (s)'
		else:
			display_box.Add((500, -1), proportion=1, flag=wx.EXPAND)

		## Controls.
		if plot_available:
			controls_box = wx.BoxSizer(wx.HORIZONTAL)
			display_box.Add(controls_box, flag=wx.CENTER|wx.ALL, border=5)

			### Numeric display.
			numeric_display_static_box = wx.StaticBox(self, label='Reading')
			numeric_display_box = wx.StaticBoxSizer(numeric_display_static_box, wx.HORIZONTAL)
			controls_box.Add(numeric_display_box, flag=wx.CENTER)

			self.numeric_display = wx.TextCtrl(self, size=(100, -1), style=wx.TE_READONLY)
			self.numeric_display.BackgroundColour = wx.LIGHT_GREY
			numeric_display_box.Add(self.numeric_display)

			### Capture.
			capture_static_box = wx.StaticBox(self, label='Control')
			capture_box = wx.StaticBoxSizer(capture_static_box)
			controls_box.Add(capture_box, flag=wx.CENTER|wx.LEFT, border=10)

			self.run_button = wx.Button(self, label='Run')
			self.Bind(wx.EVT_BUTTON, self.OnRun, self.run_button)
			capture_box.Add(self.run_button, flag=wx.CENTER)

			self.pause_button = wx.Button(self, label='Pause')
			self.Bind(wx.EVT_BUTTON, self.OnPause, self.pause_button)
			capture_box.Add(self.pause_button, flag=wx.CENTER)

			self.reset_button = wx.Button(self, label='Reset')
			self.Bind(wx.EVT_BUTTON, self.OnReset, self.reset_button)
			capture_box.Add(self.reset_button, flag=wx.CENTER|wx.LEFT, border=10)

			### Settings.
			settings_static_box = wx.StaticBox(self, label='Settings')
			settings_box = wx.StaticBoxSizer(settings_static_box, wx.HORIZONTAL)
			controls_box.Add(settings_box, flag=wx.CENTER|wx.LEFT, border=10)

			self.plot_settings_button = wx.Button(self, label='Plot...')
			self.Bind(wx.EVT_BUTTON, self.OnPlotSettings, self.plot_settings_button)
			settings_box.Add(self.plot_settings_button, flag=wx.CENTER)

		self.SetSizer(display_box)

		# Acquisition thread.
		callback = functools.partial(wx.CallAfter, self.add_value)
		self.acq_thread = AcquisitionThread(self.plot_settings.delay, callback,
				running_lock=self.running_lock)
		self.acq_thread.daemon = True
		self.acq_thread.start()

		# Wait for a resource to begin capturing.
		self.OnPause()
		self.run_button.Disable()

		# Subscriptions.
		pub.subscribe(self.msg_resource, 'resource.added')
		pub.subscribe(self.msg_resource, 'resource.removed')
		pub.subscribe(self.msg_data_capture_start, 'data_capture.start')
		pub.subscribe(self.msg_data_capture_data, 'data_capture.data')
		pub.subscribe(self.msg_data_capture_stop, 'data_capture.stop')

	@property
	def running(self):
		return self.pause_button.Enabled

	@property
	def resource(self):
		return self.acq_thread.resource

	@resource.setter
	def resource(self, value):
		# Ignore unreadable resources.
		if value is not None and not value.readable:
			value = None

		if self.running:
			# Currently running.
			running = True
			self.OnPause()
		else:
			running = False

		self.acq_thread.resource = value

		self.run_button.Enable(value is not None)

		# Resume if applicable.
		if running:
			self.OnRun()

	@property
	def measurement_resource_name(self):
		if self._measurement_resource_name is None:
			return ''
		else:
			return self._measurement_resource_name

	@measurement_resource_name.setter
	def measurement_resource_name(self, value):
		if value:
			self._measurement_resource_name = value
			try:
				self.resource = self.global_store.resources[self._measurement_resource_name]
			except KeyError:
				self.resource = None
		else:
			self._measurement_resource_name = None
			self.resource = None

	def init_values(self):
		"""
		Clear captured values.
		"""

		self._points = numpy.array([])
		self._times = numpy.array([])
		self._values = numpy.array([])

		self.current_value = None

		self.start_time = None

	def update_plot(self):
		"""
		Redraw the plot.
		"""

		if not len(self._points) > 0:
			display_time = [0]
			display_values = [0]
		else:
			if self.plot_settings.time_value == 0: # Time.
				display_time = self._times

				if self.plot_settings.time_mode == 0: # Relative.
					# Calculate the number of seconds passed since each point.
					max_time = self._times[-1]
					display_time = [x - max_time for x in display_time]
				elif self.plot_settings.time_mode == 1: # Absolute.
					display_time = [x - self.start_time for x in display_time]
			elif self.plot_settings.time_value == 1: # Points.
				display_time = self._points

				if self.plot_settings.time_mode == 0: # Relative.
					# Calculate the number of seconds passed since each point.
					max_point = self._points[-1]
					display_time = [x - max_point for x in display_time]

			display_values = [x * 10 ** (self.plot_settings.y_scale + self.unit_conversion) for
					x in self._values]

		if self.plot_settings.update_x:
			self.plot.x_autoscale()
		if self.plot_settings.update_y:
			self.plot.y_autoscale()

		self.plot.x_data, self.plot.y_data = display_time, display_values

	def add_value(self, value):
		"""
		Update the plot with a new value.
		"""

		if not self.plot_settings.enabled:
			return

		# Extract the value of a Quantity.
		try:
			# Label with the base dimensions.
			if self.unit_conversion == 0:
				self.plot.y_label = '({0})'.format(value.original_units)
			value = value.original_value
		except AttributeError:
			pass

		# Update values.
		try:
			self._points = numpy.append(self._points, self._points[-1] + 1)
		except IndexError:
			self._points = numpy.append(self._points, 0)
		cur_time = time.time()
		self._times = numpy.append(self._times, cur_time)
		self._values = numpy.append(self._values, value)

		if self.start_time is None:
			self.start_time = cur_time

		cut_idx = len(self._points) - int(self.plot_settings.num_points)
		if cut_idx > 0:
			self._points = self._points[cut_idx:]
			self._times = self._times[cut_idx:]
			self._values = self._values[cut_idx:]

		# Set number display.
		self.current_value = value * 10 ** (self.plot_settings.y_scale + self.unit_conversion)
		self.numeric_display.Value = '{0:.6g}'.format(self.current_value)

		# Plot.
		self.update_plot()

	def close(self):
		"""
		Perform cleanup.
		"""

		# Unsubscriptions.
		pub.unsubscribe(self.msg_resource, 'resource.added')
		pub.unsubscribe(self.msg_resource, 'resource.removed')
		pub.unsubscribe(self.msg_data_capture_start, 'data_capture.start')
		pub.unsubscribe(self.msg_data_capture_data, 'data_capture.data')
		pub.unsubscribe(self.msg_data_capture_stop, 'data_capture.stop')

		# Ensure the thread exits.
		self.acq_thread.resource = None
		self.acq_thread.done = True
		if not self.running:
			self.running_lock.release()
		self.acq_thread.join()
		del self.acq_thread

	def OnRun(self, evt=None):
		"""
		Let the acquisition thread run.
		"""

		self.run_button.Disable()

		if self.acq_thread.resource is None:
			return

		self.running_lock.release()

		self.pause_button.Enable()

	def OnPause(self, evt=None):
		"""
		Block the acquisition thread.
		"""

		if not self.running:
			return

		self.running_lock.acquire()

		if self.acq_thread.resource is not None:
			self.run_button.Enable()
		self.pause_button.Disable()

	def OnReset(self, evt=None):
		self.init_values()
		self.update_plot()

	def OnPlotSettings(self, evt=None):
		"""
		Open the plot settings dialog.
		"""

		def ok_callback(dlg):
			self.plot_settings = dlg.GetValue()

			if self.plot_settings.units_from and self.plot_settings.units_to:
				try:
					quantity_from = Quantity(1, self.plot_settings.units_from)
					quantity_to = Quantity(1, self.plot_settings.units_to)
				except ValueError as e:
					self.unit_conversion = 0
					MessageDialog(self, str(e), 'Invalid unit').Show()
				else:
					# We don't actually care about the units; just the prefix values.
					self.unit_conversion = math.log(quantity_from.value, 10) - math.log(quantity_to.value, 10)
			else:
				self.unit_conversion = 0

			self.acq_thread.delay = self.plot_settings.delay

			if self.plot_settings.time_value == 0:
				self.plot.x_label = 'Time (s)'
			elif self.plot_settings.time_value == 1:
				self.plot.x_label = 'Points'

			if self.plot_settings.y_scale != 0:
				self.plot.y_label = '/ 10 ^ {0}'.format(self.plot_settings.y_scale)
			else:
				self.plot.y_label = ''

			if self.plot_settings.units_to:
				self.plot.y_label += ' ({0})'.format(self.plot_settings.units_to)

			self.update_plot()

		dlg = PlotSettingsDialog(self, ok_callback)
		dlg.SetValue(self.plot_settings)
		dlg.Show()

	def msg_resource(self, name, value=None):
		if self.measurement_resource_name is not None and name == self.measurement_resource_name:
			self.resource = value

	def msg_data_capture_start(self, name):
		if name == self.measurement_resource_name:
			if self.enabled:
				self.capturing_data = True

				# Keep track of whether to restart the capture afterwards.
				self.restart_live_view = self.running

				# Disable live view.
				self.resource_backup = self.resource
				self.resource = None

	def msg_data_capture_data(self, name, value):
		if name == self.measurement_resource_name:
			if self.capturing_data:
				self.add_value(value)

	def msg_data_capture_stop(self, name):
		if name == self.measurement_resource_name:
			if self.capturing_data:
				self.capturing_data = False

				# Re-enable live view.
				self.resource = self.resource_backup
				self.resource_backup = None

				if self.restart_live_view:
					self.OnRun()
Пример #4
0
    def __init__(self, parent, global_store, subdevice, *args, **kwargs):
        wx.Panel.__init__(self, parent, *args, **kwargs)

        self.global_store = global_store
        self.delay = Quantity(1.0, 's')

        # This lock blocks the acquisition thread from acquiring.
        self.running_lock = Lock()
        self.channel_subdevice = subdevice

        self.displays = {}
        self.control_state_displays = {}
        self.readout_displays = {}
        self.control_state_list = [
            'persistent_switch_heater', 'virt_sync_currents'
        ]
        self.readout_list = [
            'magnet_current', 'power_supply_current',
            'persistent_switch_heater', 'high_limit', 'low_limit', 'sweep',
            'rate_0', 'rate_1', 'rate_2', 'rate_3', 'rate_4',
            'virt_sync_currents'
        ]
        self.measurement_resources = []
        for name in self.readout_list:
            self.displays[name] = []
            self.measurement_resources.append(
                (name, self.channel_subdevice.resources[name]))
        # A list to save acquired data to before outputting to GUI.
        self.measurements = [None] * len(self.measurement_resources)

        # Main Box.

        main_box = wx.BoxSizer(wx.VERTICAL)

        # Channel Header Box.

        channel_header_box = wx.BoxSizer(wx.HORIZONTAL)
        main_box.Add(channel_header_box, flag=wx.EXPAND)

        self.channel_button = wx.ToggleButton(
            self,
            label='Channel {0} Toggle'.format(self.channel_subdevice.channel))
        self.Bind(wx.EVT_TOGGLEBUTTON, self.OnChannelToggle,
                  self.channel_button)
        self.channel_button.SetValue(False)
        channel_header_box.Add(self.channel_button)

        ## Control states.

        control_state_grid = wx.FlexGridSizer(rows=2, cols=2, hgap=1)
        channel_header_box.Add((0, 0), 1, wx.EXPAND)
        channel_header_box.Add(control_state_grid,
                               flag=wx.ALIGN_RIGHT,
                               border=20)

        for control_state_name in self.control_state_list:
            control_state_display = wx.TextCtrl(self,
                                                size=(100, -1),
                                                style=wx.TE_READONLY)
            control_state_display.BackgroundColour = wx.LIGHT_GREY
            control_state_grid.Add(control_state_display, flag=wx.ALIGN_RIGHT)
            self.displays[control_state_name].append(control_state_display)
            self.control_state_displays[
                control_state_name] = control_state_display

        # reverse our dictionary for key retrieval by item.
        self.inv_control_state_displays = dict(
            (v, k) for k, v in self.control_state_displays.iteritems())

        # Readouts.

        readout_static_box = wx.StaticBox(self, label='Readouts')
        readout_box = wx.StaticBoxSizer(readout_static_box, wx.VERTICAL)
        main_box.Add(readout_box, flag=wx.EXPAND, proportion=1)

        #		readout_grid = wx.FlexGridSizer(rows=len(self.readout_list), cols=2, hgap=1)
        readout_grid = wx.FlexGridSizer(
            rows=len(self.readout_list), cols=3, hgap=1
        )  #TODO: for debugging model4g GUI...replace when no longer needed.
        readout_box.Add(readout_grid, flag=wx.ALIGN_RIGHT)

        self.checkboxes = {}

        ## Setup individual labels + displays

        for resource_name in self.readout_list:

            ### Checkbox. #TODO: for debugging model4g GUI...remove when no longer needed.
            checkbox = wx.CheckBox(self)
            readout_grid.Add(checkbox, flag=wx.ALIGN_LEFT)
            self.checkboxes[resource_name] = checkbox

            ### Label.
            label = resource_name.replace('_', ' ').title()
            readout_grid.Add(wx.StaticText(self, label=label + ':'),
                             flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)

            ### Display.
            display = wx.TextCtrl(self, size=(100, -1), style=wx.TE_READONLY)
            display.BackgroundColour = wx.LIGHT_GREY
            self.displays[resource_name].append(display)
            self.readout_displays[resource_name] = display

            ### Connect display to GUI.
            readout_grid.Add(self.displays[resource_name][-1],
                             flag=wx.ALIGN_RIGHT)

        # reverse our dictionary for key retrieval by item.
        self.inv_readout_displays = dict(
            (v, k) for k, v in self.readout_displays.iteritems())

        # Controls.

        self.control_static_box = wx.StaticBox(self, label='Controls')
        self.control_box = wx.StaticBoxSizer(self.control_static_box,
                                             wx.VERTICAL)
        main_box.Add(self.control_box, flag=wx.EXPAND)

        ## Persistent Heater Switch.

        heater_box = wx.BoxSizer(wx.HORIZONTAL)
        self.control_box.Add(heater_box, flag=wx.ALIGN_RIGHT)

        heater_box.Add(wx.StaticText(self, label='Persistent Switch Heater:'),
                       flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)

        self.heater_toggle = wx.ToggleButton(self,
                                             label='on/off',
                                             size=(100, -1))
        initial_state = self.channel_subdevice.persistent_switch_heater
        self.heater_toggle.SetValue(True if initial_state == 1 else 0)
        self.Bind(wx.EVT_TOGGLEBUTTON, self.OnHeaterToggle, self.heater_toggle)
        heater_box.Add(self.heater_toggle, flag=wx.ALIGN_RIGHT)

        ## Sweeper Control Box.

        sweeper_static_box = wx.StaticBox(self, label='Sweep')
        sweeper_box = wx.StaticBoxSizer(sweeper_static_box, wx.VERTICAL)
        self.control_box.Add(sweeper_box, flag=wx.EXPAND)

        sweep_buttons_box = wx.BoxSizer(wx.HORIZONTAL)
        sweeper_box.Add(sweep_buttons_box, flag=wx.CENTER | wx.ALL)

        ### Sweep buttons.

        sweep_buttons_grid = wx.FlexGridSizer(rows=2, cols=2, hgap=1)
        sweep_buttons_box.Add(sweep_buttons_grid, flag=wx.CENTER | wx.ALL)

        sweepup_button = wx.Button(self, label='up')
        sweepzero_button = wx.Button(self, label='zero')
        sweepdown_button = wx.Button(self, label='down')
        sweeppause_button = wx.Button(self, label='pause')
        self.Bind(wx.EVT_BUTTON, self.OnSweepUp, sweepup_button)
        self.Bind(wx.EVT_BUTTON, self.OnSweepZero, sweepzero_button)
        self.Bind(wx.EVT_BUTTON, self.OnSweepDown, sweepdown_button)
        self.Bind(wx.EVT_BUTTON, self.OnSweepPause, sweeppause_button)
        sweep_buttons_grid.Add(sweepup_button)
        sweep_buttons_grid.Add(sweepzero_button)
        sweep_buttons_grid.Add(sweepdown_button)
        sweep_buttons_grid.Add(sweeppause_button)

        ### Current syncing.

        ####some space

        sync_button = wx.Button(self, label='sync currents')
        self.Bind(wx.EVT_BUTTON, self.OnSyncCurrents, sync_button)
        sweep_buttons_box.Add(sync_button, flag=wx.LEFT | wx.CENTER, border=20)

        ## Limits.

        limit_static_box = wx.StaticBox(self, label='Limits')
        limit_box = wx.StaticBoxSizer(limit_static_box, wx.VERTICAL)
        self.control_box.Add(limit_box, flag=wx.EXPAND)

        limits_grid = wx.FlexGridSizer(rows=2, cols=3, hgap=1)
        limits_grid.AddGrowableCol(1, 1)

        limit_box.Add(limits_grid, flag=wx.ALIGN_RIGHT)

        ### High Limit

        limits_grid.Add(wx.StaticText(self, label='High Limit:'),
                        flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)

        set_hilim_button = wx.Button(self, label='Set', style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.OnSetHighLimit, set_hilim_button)
        limits_grid.Add(set_hilim_button, flag=wx.ALIGN_RIGHT)

        self.hilim_input = wx.TextCtrl(self, size=(100, -1))
        limits_grid.Add(self.hilim_input, flag=wx.EXPAND)

        ### Low Limit

        limits_grid.Add(wx.StaticText(self, label='Low Limit:'),
                        flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)

        set_lolim_button = wx.Button(self, label='Set', style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.OnSetLowLimit, set_lolim_button)
        limits_grid.Add(set_lolim_button, flag=wx.ALIGN_RIGHT)

        self.lolim_input = wx.TextCtrl(self, size=(100, -1))
        limits_grid.Add(self.lolim_input)

        ## Rates.

        rates_static_box = wx.StaticBox(self, label='Rates')
        rates_box = wx.StaticBoxSizer(rates_static_box, wx.VERTICAL)
        self.control_box.Add(rates_box, flag=wx.EXPAND)

        ## used to have content of rates box all right aligned.
        rates_inner_box = wx.BoxSizer(wx.HORIZONTAL)
        rates_box.Add(rates_inner_box, flag=wx.ALIGN_RIGHT)

        menu_items = []
        for resource in self.readout_list:
            if resource.startswith('rate_'):
                menu_items.append(resource)

        self.rates_menu = wx.ComboBox(self,
                                      choices=menu_items,
                                      style=wx.CB_READONLY)
        self.rates_menu.SetStringSelection(menu_items[0])
        rates_inner_box.Add(self.rates_menu, flag=wx.ALIGN_RIGHT)

        set_rate_button = wx.Button(self, label='Set', style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.OnSetRate, set_rate_button)
        rates_inner_box.Add(set_rate_button, flag=wx.ALIGN_RIGHT)

        self.rate_input = wx.TextCtrl(self, size=(100, -1))
        rates_inner_box.Add(self.rate_input, flag=wx.ALIGN_RIGHT)

        # Finish GUI building.

        self.SetSizerAndFit(main_box)

        # Default behaviour.

        ## start with...
        ### ...the threads locked out of acquisition.
        self.running_lock.acquire()
        ### ...controls disabled.
        self.RecursiveEnableSizer(self.control_box, False)

        # Threading.

        self.acqthreads = []
        #TODO: implement with a normal thread instead, to avoid the use of a dummy call to a resource.
        guicallback = partial(wx.CallAfter, self.Update)
        self.guiupdatethread = AcquisitionThread(
            self.delay,
            guicallback,
            resource=self.channel_subdevice.
            resources['persistent_switch_heater'],
            running_lock=self.running_lock)
        self.acqthreads.append(self.guiupdatethread)
        self.guiupdatethread.daemon = True
        self.guiupdatethread.start()
Пример #5
0
class ScalarLiveViewPanel(wx.Panel):
	"""
	A panel to display a live view plot of a scalar resource.
	"""

	def __init__(self, parent, global_store, *args, **kwargs):
		wx.Panel.__init__(self, parent, *args, **kwargs)

		self.global_store = global_store
		self._measurement_resource_name = None

		# Defaults.
		self.plot_settings = PlotSettings()
		self.unit_conversion = 0

		self.enabled = False
		self.capturing_data = False
		self.restart_live_view = False
		self.resource_backup = None

		# This lock blocks the acquisition thread from acquiring.
		self.running_lock = Lock()

		# Initialize recorded values.
		self.init_values()

		# Plot and toolbar.
		display_box = wx.BoxSizer(wx.VERTICAL)

		## Plot.
		if plot_available:
			self.plot = TwoDimensionalPlot(self, color='blue')
			display_box.Add(self.plot.control, proportion=1, flag=wx.EXPAND)

			self.plot.x_label = 'Time (s)'
		else:
			display_box.Add((500, -1), proportion=1, flag=wx.EXPAND)

		## Controls.
		if plot_available:
			controls_box = wx.BoxSizer(wx.HORIZONTAL)
			display_box.Add(controls_box, flag=wx.CENTER|wx.ALL, border=5)

			### Numeric display.
			numeric_display_static_box = wx.StaticBox(self, label='Reading')
			numeric_display_box = wx.StaticBoxSizer(numeric_display_static_box, wx.HORIZONTAL)
			controls_box.Add(numeric_display_box, flag=wx.CENTER)

			self.numeric_display = wx.TextCtrl(self, size=(100, -1), style=wx.TE_READONLY)
			self.numeric_display.BackgroundColour = wx.LIGHT_GREY
			numeric_display_box.Add(self.numeric_display)

			### Capture.
			capture_static_box = wx.StaticBox(self, label='Control')
			capture_box = wx.StaticBoxSizer(capture_static_box)
			controls_box.Add(capture_box, flag=wx.CENTER|wx.LEFT, border=10)

			self.run_button = wx.Button(self, label='Run')
			self.Bind(wx.EVT_BUTTON, self.OnRun, self.run_button)
			capture_box.Add(self.run_button, flag=wx.CENTER)

			self.pause_button = wx.Button(self, label='Pause')
			self.Bind(wx.EVT_BUTTON, self.OnPause, self.pause_button)
			capture_box.Add(self.pause_button, flag=wx.CENTER)

			self.reset_button = wx.Button(self, label='Reset')
			self.Bind(wx.EVT_BUTTON, self.OnReset, self.reset_button)
			capture_box.Add(self.reset_button, flag=wx.CENTER|wx.LEFT, border=10)

			### Settings.
			settings_static_box = wx.StaticBox(self, label='Settings')
			settings_box = wx.StaticBoxSizer(settings_static_box, wx.HORIZONTAL)
			controls_box.Add(settings_box, flag=wx.CENTER|wx.LEFT, border=10)

			self.plot_settings_button = wx.Button(self, label='Plot...')
			self.Bind(wx.EVT_BUTTON, self.OnPlotSettings, self.plot_settings_button)
			settings_box.Add(self.plot_settings_button, flag=wx.CENTER)

		self.SetSizer(display_box)

		# Acquisition thread.
		callback = functools.partial(wx.CallAfter, self.add_value)
		self.acq_thread = AcquisitionThread(self.plot_settings.delay, callback,
				running_lock=self.running_lock)
		self.acq_thread.daemon = True
		self.acq_thread.start()

		# Wait for a resource to begin capturing.
		self.OnPause()
		self.run_button.Disable()

		# Subscriptions.
		pub.subscribe(self.msg_resource, 'resource.added')
		pub.subscribe(self.msg_resource, 'resource.removed')
		pub.subscribe(self.msg_data_capture_start, 'data_capture.start')
		pub.subscribe(self.msg_data_capture_data, 'data_capture.data')
		pub.subscribe(self.msg_data_capture_stop, 'data_capture.stop')

	@property
	def running(self):
		return self.pause_button.Enabled

	@property
	def resource(self):
		return self.acq_thread.resource

	@resource.setter
	def resource(self, value):
		# Ignore unreadable resources.
		if value is not None and not value.readable:
			value = None

		if self.running:
			# Currently running.
			running = True
			self.OnPause()
		else:
			running = False

		self.acq_thread.resource = value

		self.run_button.Enable(value is not None)

		# Resume if applicable.
		if running:
			self.OnRun()

	@property
	def measurement_resource_name(self):
		if self._measurement_resource_name is None:
			return ''
		else:
			return self._measurement_resource_name

	@measurement_resource_name.setter
	def measurement_resource_name(self, value):
		if value:
			self._measurement_resource_name = value
			try:
				self.resource = self.global_store.resources[self._measurement_resource_name]
			except KeyError:
				self.resource = None
		else:
			self._measurement_resource_name = None
			self.resource = None

	def init_values(self):
		"""
		Clear captured values.
		"""

		self._points = numpy.array([])
		self._times = numpy.array([])
		self._values = numpy.array([])

		self.current_value = None

		self.start_time = None

	def update_plot(self):
		"""
		Redraw the plot.
		"""

		if not len(self._points) > 0:
			display_time = [0]
			display_values = [0]
		else:
			if self.plot_settings.time_value == 0: # Time.
				display_time = self._times

				if self.plot_settings.time_mode == 0: # Relative.
					# Calculate the number of seconds passed since each point.
					max_time = self._times[-1]
					display_time = [x - max_time for x in display_time]
				elif self.plot_settings.time_mode == 1: # Absolute.
					display_time = [x - self.start_time for x in display_time]
			elif self.plot_settings.time_value == 1: # Points.
				display_time = self._points

				if self.plot_settings.time_mode == 0: # Relative.
					# Calculate the number of seconds passed since each point.
					max_point = self._points[-1]
					display_time = [x - max_point for x in display_time]

			display_values = [x * 10 ** (self.plot_settings.y_scale + self.unit_conversion) for
					x in self._values]

		if self.plot_settings.update_x:
			self.plot.x_autoscale()
		if self.plot_settings.update_y:
			self.plot.y_autoscale()

		self.plot.x_data, self.plot.y_data = display_time, display_values

	def add_value(self, value):
		"""
		Update the plot with a new value.
		"""

		if not self.plot_settings.enabled:
			return

		# Extract the value of a Quantity.
		try:
			# Label with the base dimensions.
			if self.unit_conversion == 0:
				self.plot.y_label = '({0})'.format(value.original_units)
			value = value.original_value
		except AttributeError:
			pass

		# Update values.
		try:
			self._points = numpy.append(self._points, self._points[-1] + 1)
		except IndexError:
			self._points = numpy.append(self._points, 0)
		cur_time = time.time()
		self._times = numpy.append(self._times, cur_time)
		self._values = numpy.append(self._values, value)

		if self.start_time is None:
			self.start_time = cur_time

		cut_idx = len(self._points) - int(self.plot_settings.num_points)
		if cut_idx > 0:
			self._points = self._points[cut_idx:]
			self._times = self._times[cut_idx:]
			self._values = self._values[cut_idx:]

		# Set number display.
		self.current_value = value * 10 ** (self.plot_settings.y_scale + self.unit_conversion)
		self.numeric_display.Value = '{0:.6g}'.format(self.current_value)

		# Plot.
		self.update_plot()

	def close(self):
		"""
		Perform cleanup.
		"""

		# Unsubscriptions.
		pub.unsubscribe(self.msg_resource, 'resource.added')
		pub.unsubscribe(self.msg_resource, 'resource.removed')
		pub.unsubscribe(self.msg_data_capture_start, 'data_capture.start')
		pub.unsubscribe(self.msg_data_capture_data, 'data_capture.data')
		pub.unsubscribe(self.msg_data_capture_stop, 'data_capture.stop')

		# Ensure the thread exits.
		self.acq_thread.resource = None
		self.acq_thread.done = True
		if not self.running:
			self.running_lock.release()
		self.acq_thread.join()
		del self.acq_thread

	def OnRun(self, evt=None):
		"""
		Let the acquisition thread run.
		"""

		self.run_button.Disable()

		if self.acq_thread.resource is None:
			return

		self.running_lock.release()

		self.pause_button.Enable()

	def OnPause(self, evt=None):
		"""
		Block the acquisition thread.
		"""

		if not self.running:
			return

		self.running_lock.acquire()

		if self.acq_thread.resource is not None:
			self.run_button.Enable()
		self.pause_button.Disable()

	def OnReset(self, evt=None):
		self.init_values()
		self.update_plot()

	def OnPlotSettings(self, evt=None):
		"""
		Open the plot settings dialog.
		"""

		def ok_callback(dlg):
			self.plot_settings = dlg.GetValue()

			if self.plot_settings.units_from and self.plot_settings.units_to:
				try:
					quantity_from = Quantity(1, self.plot_settings.units_from)
					quantity_to = Quantity(1, self.plot_settings.units_to)
				except ValueError as e:
					self.unit_conversion = 0
					MessageDialog(self, str(e), 'Invalid unit').Show()
				else:
					# We don't actually care about the units; just the prefix values.
					self.unit_conversion = math.log(quantity_from.value, 10) - math.log(quantity_to.value, 10)
			else:
				self.unit_conversion = 0

			self.acq_thread.delay = self.plot_settings.delay

			if self.plot_settings.time_value == 0:
				self.plot.x_label = 'Time (s)'
			elif self.plot_settings.time_value == 1:
				self.plot.x_label = 'Points'

			if self.plot_settings.y_scale != 0:
				self.plot.y_label = '/ 10 ^ {0}'.format(self.plot_settings.y_scale)
			else:
				self.plot.y_label = ''

			if self.plot_settings.units_to:
				self.plot.y_label += ' ({0})'.format(self.plot_settings.units_to)

			self.update_plot()

		dlg = PlotSettingsDialog(self, ok_callback)
		dlg.SetValue(self.plot_settings)
		dlg.Show()

	def msg_resource(self, name, value=None):
		if self.measurement_resource_name is not None and name == self.measurement_resource_name:
			self.resource = value

	def msg_data_capture_start(self, name):
		if name == self.measurement_resource_name:
			if self.enabled:
				self.capturing_data = True

				# Keep track of whether to restart the capture afterwards.
				self.restart_live_view = self.running

				# Disable live view.
				self.resource_backup = self.resource
				self.resource = None

	def msg_data_capture_data(self, name, value):
		if name == self.measurement_resource_name:
			if self.capturing_data:
				self.add_value(value)

	def msg_data_capture_stop(self, name):
		if name == self.measurement_resource_name:
			if self.capturing_data:
				self.capturing_data = False

				# Re-enable live view.
				self.resource = self.resource_backup
				self.resource_backup = None

				if self.restart_live_view:
					self.OnRun()
Пример #6
0
class Model4GChannelPanel(wx.Panel):
    def __init__(self, parent, global_store, subdevice, *args, **kwargs):
        wx.Panel.__init__(self, parent, *args, **kwargs)

        self.global_store = global_store
        self.delay = Quantity(1.0, 's')

        # This lock blocks the acquisition thread from acquiring.
        self.running_lock = Lock()
        self.channel_subdevice = subdevice

        self.displays = {}
        self.control_state_displays = {}
        self.readout_displays = {}
        self.control_state_list = [
            'persistent_switch_heater', 'virt_sync_currents'
        ]
        self.readout_list = [
            'magnet_current', 'power_supply_current',
            'persistent_switch_heater', 'high_limit', 'low_limit', 'sweep',
            'rate_0', 'rate_1', 'rate_2', 'rate_3', 'rate_4',
            'virt_sync_currents'
        ]
        self.measurement_resources = []
        for name in self.readout_list:
            self.displays[name] = []
            self.measurement_resources.append(
                (name, self.channel_subdevice.resources[name]))
        # A list to save acquired data to before outputting to GUI.
        self.measurements = [None] * len(self.measurement_resources)

        # Main Box.

        main_box = wx.BoxSizer(wx.VERTICAL)

        # Channel Header Box.

        channel_header_box = wx.BoxSizer(wx.HORIZONTAL)
        main_box.Add(channel_header_box, flag=wx.EXPAND)

        self.channel_button = wx.ToggleButton(
            self,
            label='Channel {0} Toggle'.format(self.channel_subdevice.channel))
        self.Bind(wx.EVT_TOGGLEBUTTON, self.OnChannelToggle,
                  self.channel_button)
        self.channel_button.SetValue(False)
        channel_header_box.Add(self.channel_button)

        ## Control states.

        control_state_grid = wx.FlexGridSizer(rows=2, cols=2, hgap=1)
        channel_header_box.Add((0, 0), 1, wx.EXPAND)
        channel_header_box.Add(control_state_grid,
                               flag=wx.ALIGN_RIGHT,
                               border=20)

        for control_state_name in self.control_state_list:
            control_state_display = wx.TextCtrl(self,
                                                size=(100, -1),
                                                style=wx.TE_READONLY)
            control_state_display.BackgroundColour = wx.LIGHT_GREY
            control_state_grid.Add(control_state_display, flag=wx.ALIGN_RIGHT)
            self.displays[control_state_name].append(control_state_display)
            self.control_state_displays[
                control_state_name] = control_state_display

        # reverse our dictionary for key retrieval by item.
        self.inv_control_state_displays = dict(
            (v, k) for k, v in self.control_state_displays.iteritems())

        # Readouts.

        readout_static_box = wx.StaticBox(self, label='Readouts')
        readout_box = wx.StaticBoxSizer(readout_static_box, wx.VERTICAL)
        main_box.Add(readout_box, flag=wx.EXPAND, proportion=1)

        #		readout_grid = wx.FlexGridSizer(rows=len(self.readout_list), cols=2, hgap=1)
        readout_grid = wx.FlexGridSizer(
            rows=len(self.readout_list), cols=3, hgap=1
        )  #TODO: for debugging model4g GUI...replace when no longer needed.
        readout_box.Add(readout_grid, flag=wx.ALIGN_RIGHT)

        self.checkboxes = {}

        ## Setup individual labels + displays

        for resource_name in self.readout_list:

            ### Checkbox. #TODO: for debugging model4g GUI...remove when no longer needed.
            checkbox = wx.CheckBox(self)
            readout_grid.Add(checkbox, flag=wx.ALIGN_LEFT)
            self.checkboxes[resource_name] = checkbox

            ### Label.
            label = resource_name.replace('_', ' ').title()
            readout_grid.Add(wx.StaticText(self, label=label + ':'),
                             flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)

            ### Display.
            display = wx.TextCtrl(self, size=(100, -1), style=wx.TE_READONLY)
            display.BackgroundColour = wx.LIGHT_GREY
            self.displays[resource_name].append(display)
            self.readout_displays[resource_name] = display

            ### Connect display to GUI.
            readout_grid.Add(self.displays[resource_name][-1],
                             flag=wx.ALIGN_RIGHT)

        # reverse our dictionary for key retrieval by item.
        self.inv_readout_displays = dict(
            (v, k) for k, v in self.readout_displays.iteritems())

        # Controls.

        self.control_static_box = wx.StaticBox(self, label='Controls')
        self.control_box = wx.StaticBoxSizer(self.control_static_box,
                                             wx.VERTICAL)
        main_box.Add(self.control_box, flag=wx.EXPAND)

        ## Persistent Heater Switch.

        heater_box = wx.BoxSizer(wx.HORIZONTAL)
        self.control_box.Add(heater_box, flag=wx.ALIGN_RIGHT)

        heater_box.Add(wx.StaticText(self, label='Persistent Switch Heater:'),
                       flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)

        self.heater_toggle = wx.ToggleButton(self,
                                             label='on/off',
                                             size=(100, -1))
        initial_state = self.channel_subdevice.persistent_switch_heater
        self.heater_toggle.SetValue(True if initial_state == 1 else 0)
        self.Bind(wx.EVT_TOGGLEBUTTON, self.OnHeaterToggle, self.heater_toggle)
        heater_box.Add(self.heater_toggle, flag=wx.ALIGN_RIGHT)

        ## Sweeper Control Box.

        sweeper_static_box = wx.StaticBox(self, label='Sweep')
        sweeper_box = wx.StaticBoxSizer(sweeper_static_box, wx.VERTICAL)
        self.control_box.Add(sweeper_box, flag=wx.EXPAND)

        sweep_buttons_box = wx.BoxSizer(wx.HORIZONTAL)
        sweeper_box.Add(sweep_buttons_box, flag=wx.CENTER | wx.ALL)

        ### Sweep buttons.

        sweep_buttons_grid = wx.FlexGridSizer(rows=2, cols=2, hgap=1)
        sweep_buttons_box.Add(sweep_buttons_grid, flag=wx.CENTER | wx.ALL)

        sweepup_button = wx.Button(self, label='up')
        sweepzero_button = wx.Button(self, label='zero')
        sweepdown_button = wx.Button(self, label='down')
        sweeppause_button = wx.Button(self, label='pause')
        self.Bind(wx.EVT_BUTTON, self.OnSweepUp, sweepup_button)
        self.Bind(wx.EVT_BUTTON, self.OnSweepZero, sweepzero_button)
        self.Bind(wx.EVT_BUTTON, self.OnSweepDown, sweepdown_button)
        self.Bind(wx.EVT_BUTTON, self.OnSweepPause, sweeppause_button)
        sweep_buttons_grid.Add(sweepup_button)
        sweep_buttons_grid.Add(sweepzero_button)
        sweep_buttons_grid.Add(sweepdown_button)
        sweep_buttons_grid.Add(sweeppause_button)

        ### Current syncing.

        ####some space

        sync_button = wx.Button(self, label='sync currents')
        self.Bind(wx.EVT_BUTTON, self.OnSyncCurrents, sync_button)
        sweep_buttons_box.Add(sync_button, flag=wx.LEFT | wx.CENTER, border=20)

        ## Limits.

        limit_static_box = wx.StaticBox(self, label='Limits')
        limit_box = wx.StaticBoxSizer(limit_static_box, wx.VERTICAL)
        self.control_box.Add(limit_box, flag=wx.EXPAND)

        limits_grid = wx.FlexGridSizer(rows=2, cols=3, hgap=1)
        limits_grid.AddGrowableCol(1, 1)

        limit_box.Add(limits_grid, flag=wx.ALIGN_RIGHT)

        ### High Limit

        limits_grid.Add(wx.StaticText(self, label='High Limit:'),
                        flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)

        set_hilim_button = wx.Button(self, label='Set', style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.OnSetHighLimit, set_hilim_button)
        limits_grid.Add(set_hilim_button, flag=wx.ALIGN_RIGHT)

        self.hilim_input = wx.TextCtrl(self, size=(100, -1))
        limits_grid.Add(self.hilim_input, flag=wx.EXPAND)

        ### Low Limit

        limits_grid.Add(wx.StaticText(self, label='Low Limit:'),
                        flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)

        set_lolim_button = wx.Button(self, label='Set', style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.OnSetLowLimit, set_lolim_button)
        limits_grid.Add(set_lolim_button, flag=wx.ALIGN_RIGHT)

        self.lolim_input = wx.TextCtrl(self, size=(100, -1))
        limits_grid.Add(self.lolim_input)

        ## Rates.

        rates_static_box = wx.StaticBox(self, label='Rates')
        rates_box = wx.StaticBoxSizer(rates_static_box, wx.VERTICAL)
        self.control_box.Add(rates_box, flag=wx.EXPAND)

        ## used to have content of rates box all right aligned.
        rates_inner_box = wx.BoxSizer(wx.HORIZONTAL)
        rates_box.Add(rates_inner_box, flag=wx.ALIGN_RIGHT)

        menu_items = []
        for resource in self.readout_list:
            if resource.startswith('rate_'):
                menu_items.append(resource)

        self.rates_menu = wx.ComboBox(self,
                                      choices=menu_items,
                                      style=wx.CB_READONLY)
        self.rates_menu.SetStringSelection(menu_items[0])
        rates_inner_box.Add(self.rates_menu, flag=wx.ALIGN_RIGHT)

        set_rate_button = wx.Button(self, label='Set', style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.OnSetRate, set_rate_button)
        rates_inner_box.Add(set_rate_button, flag=wx.ALIGN_RIGHT)

        self.rate_input = wx.TextCtrl(self, size=(100, -1))
        rates_inner_box.Add(self.rate_input, flag=wx.ALIGN_RIGHT)

        # Finish GUI building.

        self.SetSizerAndFit(main_box)

        # Default behaviour.

        ## start with...
        ### ...the threads locked out of acquisition.
        self.running_lock.acquire()
        ### ...controls disabled.
        self.RecursiveEnableSizer(self.control_box, False)

        # Threading.

        self.acqthreads = []
        #TODO: implement with a normal thread instead, to avoid the use of a dummy call to a resource.
        guicallback = partial(wx.CallAfter, self.Update)
        self.guiupdatethread = AcquisitionThread(
            self.delay,
            guicallback,
            resource=self.channel_subdevice.
            resources['persistent_switch_heater'],
            running_lock=self.running_lock)
        self.acqthreads.append(self.guiupdatethread)
        self.guiupdatethread.daemon = True
        self.guiupdatethread.start()

    def __del__(self):
        try:

            #			if self.channel_button.GetValue() == False:
            #				self.running_lock.release()
            for thread in self.acqthreads:
                thread.resource = None
                thread.done = True
                thread.join()
                del thread


#			self.close()
        except Exception:
            pass

    def UpdateReadouts(self, resource_name, value):
        """
		Update appropriate readouts with a new resource value.
		Also update button permissions.
		"""
        if resource_name in self.readout_displays.keys():
            if self.checkboxes[
                    resource_name].Value == False:  #TODO: for debugging model4g GUI...remove when no longer needed.
                return

        for display in self.displays[resource_name]:

            #perform alterations to output based on where the resource is being readout in GUI.

            inv_cont_dict = self.inv_control_state_displays
            if display in inv_cont_dict.keys():
                if inv_cont_dict[display] == 'persistent_switch_heater':
                    if value == 'on':
                        display.BackgroundColour = OK_BACKGROUND_COLOR
                    elif value == 'off':
                        display.BackgroundColour = wx.LIGHT_GREY

                    value_readout = 'heater {0}'.format(value)

                elif inv_cont_dict[display] == 'virt_sync_currents':
                    if value == 'synced':
                        display.BackgroundColour = OK_BACKGROUND_COLOR
                    elif value == 'not synced':
                        display.BackgroundColour = wx.LIGHT_GREY
                    #display as-is
                    value_readout = value
            elif display in self.inv_readout_displays.keys():
                #display as-is
                value_readout = value

            display.SetValue(str(value_readout))

            # User Permissions

            # if currents don't match, heater toggle should be disabled.
            if display in inv_cont_dict.keys():
                if inv_cont_dict[display] == 'virt_sync_currents':
                    if value == 'synced':
                        self.heater_toggle.Enable()
                    else:
                        self.heater_toggle.Disable()

    def Update(self, dummyval):
        """
		Acquire data and then update the GUI with this data.
		
		Note: code taken from sweep controller.
		"""

        #this loop sets up separate threads for each resource.
        thrs = []
        for i, (name, resource) in enumerate(self.measurement_resources):
            if resource is not None:

                def save_callback(value, i=i):
                    self.measurements[i] = value

                callback = partial(
                    wx.CallAfter,
                    partial(self.read_resource, name, resource, save_callback))
                thr = Thread(target=callback)
                thrs.append(thr)
                thr.daemon = True
                thr.start()

        for thr in thrs:
            thr.join()

        #this code saves the values to the GUI readouts.
        for i, (name, _) in enumerate(self.measurement_resources):
            self.UpdateReadouts(name, self.measurements[i])

    def read_resource(self, name, resource, save_callback):
        """
		Read a value from a resource and handle exceptions.
		
		Note: code taken from sweep controller.
		"""

        if name in self.readout_displays.keys():
            if self.checkboxes[
                    name].Value == False:  #TODO: for debugging model4g GUI...remove when no longer needed.
                return

        try:
            value = resource.value
        except Exception as e:
            if self.resource_exception_handler is not None:
                self.resource_exception_handler(name, e, write=False)
            return

        save_callback(value)

    def OnChannelToggle(self, evt=None):
        toggle = self.channel_button.GetValue()
        if toggle == True:
            self.running_lock.release()
        elif toggle == False:
            self.running_lock.acquire()

        self.RecursiveEnableSizer(self.control_box, toggle)

        #permission defaults.
        self.heater_toggle.Disable()

    def OnSweepDown(self, evt=None):
        self.channel_subdevice.resources['sweep'].value = 'down'

    def OnSweepUp(self, evt=None):
        self.channel_subdevice.resources['sweep'].value = 'up'

    def OnSweepZero(self, evt=None):
        self.channel_subdevice.resources['sweep'].value = 'zero'

    def OnSweepPause(self, evt=None):
        self.channel_subdevice.resources['sweep'].value = 'pause'

    def OnSetRate(self, evt=None):
        try:
            Quantity(self.rate_input.GetValue())
        except ValueError as e:
            MessageDialog(self, str(e), 'Invalid value').Show()
            return False

        range_id = self.rates_menu.GetCurrentSelection()
        resource = self.channel_subdevice.resources['rate_{0}'.format(
            range_id)]
        new_value = self.rate_input.GetValue()
        try:
            resource.value = resource.convert(new_value)
        except IncompatibleDimensions:
            MessageDialog(
                self,
                ValueError('Expected dimensions to match "{0}"'.format(
                    resource.units))).Show()

    def OnHeaterToggle(self, evt=None):
        if self.heater_toggle.GetValue() == True:
            new_value = 'on'
        if self.heater_toggle.GetValue() == False:
            new_value = 'off'
        self.channel_subdevice.resources[
            'persistent_switch_heater'].value = new_value

    def OnSetHighLimit(self, evt=None):
        try:
            Quantity(self.hilim_input.GetValue())
        except ValueError as e:
            MessageDialog(self, str(e), 'Invalid value').Show()
            return False

        new_value = self.hilim_input.GetValue()
        resource = self.channel_subdevice.resources['high_limit']
        try:
            resource.value = resource.convert(new_value)
        except IncompatibleDimensions:
            MessageDialog(
                self,
                str(
                    ValueError('Expected dimensions to match "{0}"'.format(
                        resource.units)))).Show()

    def OnSetLowLimit(self, evt=None):
        try:
            Quantity(self.lolim_input.GetValue())
        except ValueError as e:
            MessageDialog(self, str(e), 'Invalid value').Show()
            return False

        new_value = self.lolim_input.GetValue()
        resource = self.channel_subdevice.resources['low_limit']
        try:
            resource.value = resource.convert(new_value)
        except IncompatibleDimensions:
            MessageDialog(
                self,
                ValueError('Expected dimensions to match "{0}"'.format(
                    resource.units))).Show()

    def OnSyncCurrents(self, evt=None):
        self.channel_subdevice.resources['virt_sync_currents'].value = 'start'

    def close(self):
        """
		Perform cleanup.
		"""
        # Ensure the threads exits.
        if self.channel_button.GetValue() == False:
            self.running_lock.release()
        for thread in self.acqthreads:
            thread.resource = None
            thread.done = True
            thread.join()
            del thread

    def RecursiveEnableSizer(self, wx_sizer, toggle):
        '''
		Helper function that accesses all subwindows of a wxPython 
		sizer, and enables or disables them based on toggle.
		'''
        children = wx_sizer.GetChildren()
        for item in children:

            window = item.GetWindow()
            sizer = item.GetSizer()

            if sizer:
                #recurse
                self.RecursiveEnableSizer(sizer, toggle)
            elif window:
                window.Enable(toggle)
Пример #7
0
    def __init__(self, parent, global_store, *args, **kwargs):
        wx.Panel.__init__(self, parent, *args, **kwargs)

        self.global_store = global_store
        self._measurement_resource_name = None

        # Defaults.
        self.plot_settings = PlotSettings()
        self.unit_conversion = 0

        self.enabled = True
        self.capturing_data = False
        self.restart_live_view = False
        self.resource_backup = None

        # csv save file information
        self.save_path = ''
        self.defaultName = ''

        # Initialize recorded values.
        self.init_values()

        # This lock blocks the acquisition thread from acquiring.
        self.running_lock = Lock()

        # Plot and toolbar.
        display_box = wx.BoxSizer(wx.VERTICAL)

        ## Plot.
        if plot_available:
            self.plot = TwoDimensionalPlot(self, color='blue')
            display_box.Add(self.plot.control, proportion=1, flag=wx.EXPAND)


#			self.plot.x_label = 'Waveform time (s)'
#			self.plot.y_label = 'History'
        else:
            display_box.Add((500, -1), proportion=1, flag=wx.EXPAND)

        ## Controls.
        if plot_available:
            controls_box = wx.BoxSizer(wx.HORIZONTAL)
            display_box.Add(controls_box, flag=wx.CENTER | wx.ALL, border=5)

            ### Manual data export
            saveData_static_box = wx.StaticBox(self, label='Last trace')
            saveData_box = wx.StaticBoxSizer(saveData_static_box,
                                             wx.HORIZONTAL)
            controls_box.Add(saveData_box, flag=wx.CENTER)

            self.csv_button = wx.Button(self, label='Save to .csv')
            self.Bind(wx.EVT_BUTTON, self.onSave, self.csv_button)
            saveData_box.Add(self.csv_button, flag=wx.CENTER)

            ### Capture.
            capture_static_box = wx.StaticBox(self, label='Control')
            capture_box = wx.StaticBoxSizer(capture_static_box)
            controls_box.Add(capture_box, flag=wx.CENTER)

            self.run_button = wx.Button(self, label='Run')
            self.Bind(wx.EVT_BUTTON, self.OnRun, self.run_button)
            capture_box.Add(self.run_button, flag=wx.CENTER)

            self.pause_button = wx.Button(self, label='Pause')
            self.Bind(wx.EVT_BUTTON, self.OnPause, self.pause_button)
            capture_box.Add(self.pause_button, flag=wx.CENTER)

            self.reset_button = wx.Button(self, label='Reset')
            self.Bind(wx.EVT_BUTTON, self.OnReset, self.reset_button)
            capture_box.Add(self.reset_button, flag=wx.CENTER)

            ### Settings.
            settings_static_box = wx.StaticBox(self, label='Settings')
            settings_box = wx.StaticBoxSizer(settings_static_box,
                                             wx.HORIZONTAL)
            controls_box.Add(settings_box, flag=wx.CENTER | wx.LEFT, border=10)

            self.plot_settings_button = wx.Button(self, label='Plot...')
            self.Bind(wx.EVT_BUTTON, self.OnPlotSettings,
                      self.plot_settings_button)
            settings_box.Add(self.plot_settings_button, flag=wx.CENTER)

        self.SetSizer(display_box)

        # Acquisition thread.
        callback = functools.partial(wx.CallAfter, self.add_values)
        self.acq_thread = AcquisitionThread(self.plot_settings.delay,
                                            callback,
                                            running_lock=self.running_lock)
        self.acq_thread.daemon = True
        self.acq_thread.start()

        # Wait for a resource to begin capturing.
        self.OnPause()
        self.run_button.Disable()

        # Subscriptions.
        pub.subscribe(self.msg_resource, 'resource.added')
        pub.subscribe(self.msg_resource, 'resource.removed')
        pub.subscribe(self.msg_data_capture_start, 'data_capture.start')
        pub.subscribe(self.msg_data_capture_data, 'data_capture.data')
        pub.subscribe(self.msg_data_capture_stop, 'data_capture.stop')
Пример #8
0
class ListLiveViewPanel(wx.Panel):
    """
	A panel to display a live view plot of a list resource.
	"""
    def __init__(self, parent, global_store, *args, **kwargs):
        wx.Panel.__init__(self, parent, *args, **kwargs)

        self.global_store = global_store
        self._measurement_resource_name = None

        # Defaults.
        self.plot_settings = PlotSettings()
        self.unit_conversion = 0

        self.enabled = True
        self.capturing_data = False
        self.restart_live_view = False
        self.resource_backup = None

        # csv save file information
        self.save_path = ''
        self.defaultName = ''

        # Initialize recorded values.
        self.init_values()

        # This lock blocks the acquisition thread from acquiring.
        self.running_lock = Lock()

        # Plot and toolbar.
        display_box = wx.BoxSizer(wx.VERTICAL)

        ## Plot.
        if plot_available:
            self.plot = TwoDimensionalPlot(self, color='blue')
            display_box.Add(self.plot.control, proportion=1, flag=wx.EXPAND)


#			self.plot.x_label = 'Waveform time (s)'
#			self.plot.y_label = 'History'
        else:
            display_box.Add((500, -1), proportion=1, flag=wx.EXPAND)

        ## Controls.
        if plot_available:
            controls_box = wx.BoxSizer(wx.HORIZONTAL)
            display_box.Add(controls_box, flag=wx.CENTER | wx.ALL, border=5)

            ### Manual data export
            saveData_static_box = wx.StaticBox(self, label='Last trace')
            saveData_box = wx.StaticBoxSizer(saveData_static_box,
                                             wx.HORIZONTAL)
            controls_box.Add(saveData_box, flag=wx.CENTER)

            self.csv_button = wx.Button(self, label='Save to .csv')
            self.Bind(wx.EVT_BUTTON, self.onSave, self.csv_button)
            saveData_box.Add(self.csv_button, flag=wx.CENTER)

            ### Capture.
            capture_static_box = wx.StaticBox(self, label='Control')
            capture_box = wx.StaticBoxSizer(capture_static_box)
            controls_box.Add(capture_box, flag=wx.CENTER)

            self.run_button = wx.Button(self, label='Run')
            self.Bind(wx.EVT_BUTTON, self.OnRun, self.run_button)
            capture_box.Add(self.run_button, flag=wx.CENTER)

            self.pause_button = wx.Button(self, label='Pause')
            self.Bind(wx.EVT_BUTTON, self.OnPause, self.pause_button)
            capture_box.Add(self.pause_button, flag=wx.CENTER)

            self.reset_button = wx.Button(self, label='Reset')
            self.Bind(wx.EVT_BUTTON, self.OnReset, self.reset_button)
            capture_box.Add(self.reset_button, flag=wx.CENTER)

            ### Settings.
            settings_static_box = wx.StaticBox(self, label='Settings')
            settings_box = wx.StaticBoxSizer(settings_static_box,
                                             wx.HORIZONTAL)
            controls_box.Add(settings_box, flag=wx.CENTER | wx.LEFT, border=10)

            self.plot_settings_button = wx.Button(self, label='Plot...')
            self.Bind(wx.EVT_BUTTON, self.OnPlotSettings,
                      self.plot_settings_button)
            settings_box.Add(self.plot_settings_button, flag=wx.CENTER)

        self.SetSizer(display_box)

        # Acquisition thread.
        callback = functools.partial(wx.CallAfter, self.add_values)
        self.acq_thread = AcquisitionThread(self.plot_settings.delay,
                                            callback,
                                            running_lock=self.running_lock)
        self.acq_thread.daemon = True
        self.acq_thread.start()

        # Wait for a resource to begin capturing.
        self.OnPause()
        self.run_button.Disable()

        # Subscriptions.
        pub.subscribe(self.msg_resource, 'resource.added')
        pub.subscribe(self.msg_resource, 'resource.removed')
        pub.subscribe(self.msg_data_capture_start, 'data_capture.start')
        pub.subscribe(self.msg_data_capture_data, 'data_capture.data')
        pub.subscribe(self.msg_data_capture_stop, 'data_capture.stop')

    @property
    def running(self):
        return self.pause_button.Enabled

    @property
    def resource(self):
        return self.acq_thread.resource

    @resource.setter
    def resource(self, value):
        # Ignore unreadable resources.
        if value is not None and not value.readable:
            value = None

        if self.running:
            # Currently running.
            running = True
            self.OnPause()
        else:
            running = False

        self.acq_thread.resource = value

        self.run_button.Enable(value is not None)

        # Resume if applicable.
        if running:
            self.OnRun()

    @property
    def measurement_resource_name(self):
        if self._measurement_resource_name is None:
            return ''
        else:
            return self._measurement_resource_name

    @measurement_resource_name.setter
    def measurement_resource_name(self, value):
        if value:
            self._measurement_resource_name = value
            try:
                self.resource = self.global_store.resources[
                    self._measurement_resource_name]
            except KeyError:
                self.resource = None
        else:
            self._measurement_resource_name = None
            self.resource = None

    def init_values(self):
        """
		Clear captured values.
		"""

        self._times = numpy.array([])
        self._values = numpy.array([])

    def update_plot(self):
        """
		Redraw the plot.
		"""

        # Wait for at least one line.
        if not len(self._times) > 0:
            display_time = [0]
            display_values = [0]
        else:
            display_time = self._times
            display_values = self._values

        if self.plot_settings.update_x:
            self.plot.x_autoscale()
        if self.plot_settings.update_y:
            self.plot.y_autoscale()

        self.plot.x_data, self.plot.y_data = display_time, display_values

    def add_values(self, values):
        """
		Update the plot with a new list of values.
		"""

        if not self.plot_settings.enabled:
            return

        # Extract the times and the data values.
        times, values = list(zip(*values))

        # Update values.
        self._values = numpy.append(numpy.array([]), values)
        self._times = numpy.append(numpy.array([]), times)

        # Plot.
        self.update_plot()

    def close(self):
        """
		Perform cleanup.
		"""

        # Unsubscriptions.
        pub.unsubscribe(self.msg_resource, 'resource.added')
        pub.unsubscribe(self.msg_resource, 'resource.removed')
        pub.unsubscribe(self.msg_data_capture_start, 'data_capture.start')
        pub.unsubscribe(self.msg_data_capture_data, 'data_capture.data')
        pub.unsubscribe(self.msg_data_capture_stop, 'data_capture.stop')

        # Ensure the thread exits.
        self.acq_thread.resource = None
        self.acq_thread.done = True
        if not self.running:
            self.running_lock.release()
        self.acq_thread.join()
        del self.acq_thread

    def onSave(self, evt=None):
        """
		save the latest trace to a manually named csv
		"""
        outArray = numpy.column_stack((self._times, self._values))

        self.defaultName = 'listTrace_{0:04}-{1:02}-{2:02}_{3:02}-{4:02}-{5:02}.csv'.format(
            *localtime())
        fdlg = wx.FileDialog(self, "Save last trace", "", self.defaultName,
                             "CSV files(*.csv)|*.*",
                             wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
        if fdlg.ShowModal() == wx.ID_OK:
            self.save_path = fdlg.GetPath()
            if not (self.save_path.endswith(".csv")):
                self.save_path = self.save_path + ".csv"
            numpy.savetxt(self.save_path, outArray, delimiter=',')

    def OnRun(self, evt=None):
        """
		Let the acquisition thread run.
		"""

        self.run_button.Disable()

        if self.acq_thread.resource is None:
            return

        self.running_lock.release()

        self.pause_button.Enable()

    def OnPause(self, evt=None):
        """
		Block the acquisition thread.
		"""

        if not self.running:
            return

        self.running_lock.acquire()

        if self.acq_thread.resource is not None:
            self.run_button.Enable()
        self.pause_button.Disable()

    def OnReset(self, evt=None):
        self.init_values()
        self.update_plot()

    def OnPlotSettings(self, evt=None):
        """
		Open the plot settings dialog.
		"""
        def ok_callback(dlg):
            self.plot_settings = dlg.GetValue()

        dlg = PlotSettingsDialog(self, ok_callback)
        dlg.SetValue(self.plot_settings)
        dlg.Show()

    def msg_resource(self, name, value=None):
        if self.measurement_resource_name is not None and name == self.measurement_resource_name:
            self.resource = value

    def msg_data_capture_start(self, name):
        if name == self.measurement_resource_name:
            if self.enabled:
                self.capturing_data = True

    def msg_data_capture_data(self, name, value):
        if name == self.measurement_resource_name:
            if self.capturing_data:
                self.add_values(value)

    def msg_data_capture_stop(self, name):
        if name == self.measurement_resource_name:
            if self.capturing_data:
                self.capturing_data = False
Пример #9
0
	def __init__(self, parent, global_store, subdevice, *args, **kwargs):
		wx.Panel.__init__(self, parent, *args, **kwargs)

		self.global_store = global_store
		self.delay = Quantity(1.0, 's')
		
		# This lock blocks the acquisition thread from acquiring.
		self.running_lock = Lock()
		self.channel_subdevice = subdevice
		
		self.displays = {}
		self.control_state_displays = {}
		self.readout_displays = {}
		self.control_state_list = ['persistent_switch_heater','virt_sync_currents']
		self.readout_list = [	'magnet_current','power_supply_current',
								'persistent_switch_heater', 'high_limit','low_limit','sweep',
								'rate_0','rate_1','rate_2','rate_3','rate_4', 'virt_sync_currents']
		self.measurement_resources = []
		for name in self.readout_list:
			self.displays[name] = []
			self.measurement_resources.append((name, self.channel_subdevice.resources[name]))
		# A list to save acquired data to before outputting to GUI.
		self.measurements = [None] * len(self.measurement_resources)

		# Main Box.
		
		main_box = wx.BoxSizer(wx.VERTICAL)
		
		# Channel Header Box.
		
		channel_header_box = wx.BoxSizer(wx.HORIZONTAL)
		main_box.Add(channel_header_box, flag=wx.EXPAND)
		
		self.channel_button = wx.ToggleButton(self, label='Channel {0} Toggle'.format(self.channel_subdevice.channel))
		self.Bind(wx.EVT_TOGGLEBUTTON, self.OnChannelToggle, self.channel_button)
		self.channel_button.SetValue(False)
		channel_header_box.Add(self.channel_button)
		
		## Control states.
		
		control_state_grid = wx.FlexGridSizer(rows=2, cols=2, hgap=1)
		channel_header_box.Add((0, 0), 1, wx.EXPAND)
		channel_header_box.Add(control_state_grid, flag=wx.ALIGN_RIGHT, border = 20)
		
		for control_state_name in self.control_state_list:
			control_state_display = wx.TextCtrl(self, size=(100, -1), style=wx.TE_READONLY)
			control_state_display.BackgroundColour = wx.LIGHT_GREY
			control_state_grid.Add(control_state_display, flag=wx.ALIGN_RIGHT)
			self.displays[control_state_name].append(control_state_display)
			self.control_state_displays[control_state_name] = control_state_display
		
		# reverse our dictionary for key retrieval by item.
		self.inv_control_state_displays = dict((v,k) for k, v in self.control_state_displays.iteritems())

		# Readouts.
		
		readout_static_box = wx.StaticBox(self, label = 'Readouts')
		readout_box = wx.StaticBoxSizer(readout_static_box, wx.VERTICAL)
		main_box.Add(readout_box, flag=wx.EXPAND, proportion=1)
		
#		readout_grid = wx.FlexGridSizer(rows=len(self.readout_list), cols=2, hgap=1)
		readout_grid = wx.FlexGridSizer(rows=len(self.readout_list), cols=3, hgap=1) #TODO: for debugging model4g GUI...replace when no longer needed.
		readout_box.Add(readout_grid, flag=wx.ALIGN_RIGHT)
		
		self.checkboxes = {}
				
		## Setup individual labels + displays
		
		for resource_name in self.readout_list:
			
			### Checkbox. #TODO: for debugging model4g GUI...remove when no longer needed.
			checkbox = wx.CheckBox(self)
			readout_grid.Add(checkbox, flag = wx.ALIGN_LEFT)
			self.checkboxes[resource_name] = checkbox
			
			### Label.
			label = resource_name.replace('_',' ').title()
			readout_grid.Add(wx.StaticText(self, label=label + ':'),
					flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)
			
			### Display.
			display = wx.TextCtrl(self, size=(100, -1), style=wx.TE_READONLY)
			display.BackgroundColour = wx.LIGHT_GREY
			self.displays[resource_name].append(display)
			self.readout_displays[resource_name] = display
									
			### Connect display to GUI.
			readout_grid.Add(self.displays[resource_name][-1], flag=wx.ALIGN_RIGHT)
			
		# reverse our dictionary for key retrieval by item.
		self.inv_readout_displays = dict((v,k) for k, v in self.readout_displays.iteritems())
		
		# Controls.
		
		self.control_static_box = wx.StaticBox(self, label='Controls')
		self.control_box=wx.StaticBoxSizer(self.control_static_box, wx.VERTICAL)
		main_box.Add(self.control_box, flag=wx.EXPAND)
		
		## Persistent Heater Switch.
		
		heater_box = wx.BoxSizer(wx.HORIZONTAL)
		self.control_box.Add(heater_box, flag=wx.ALIGN_RIGHT)
		
		heater_box.Add(wx.StaticText(self, label='Persistent Switch Heater:'),
				flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)
		
		self.heater_toggle = wx.ToggleButton(self, label='on/off', size=(100,-1))
		initial_state = self.channel_subdevice.persistent_switch_heater
		self.heater_toggle.SetValue(True if initial_state == 1 else 0)
		self.Bind(wx.EVT_TOGGLEBUTTON, self.OnHeaterToggle, self.heater_toggle)
		heater_box.Add(self.heater_toggle,flag=wx.ALIGN_RIGHT)
		
		## Sweeper Control Box.
		
		sweeper_static_box = wx.StaticBox(self, label = 'Sweep')
		sweeper_box = wx.StaticBoxSizer(sweeper_static_box, wx.VERTICAL)
		self.control_box.Add(sweeper_box, flag=wx.EXPAND)
		
		sweep_buttons_box = wx.BoxSizer(wx.HORIZONTAL)
		sweeper_box.Add(sweep_buttons_box, flag = wx.CENTER|wx.ALL)
		
		### Sweep buttons.
		
		sweep_buttons_grid = wx.FlexGridSizer(rows=2, cols=2, hgap=1)
		sweep_buttons_box.Add(sweep_buttons_grid, flag=wx.CENTER|wx.ALL)
		
		sweepup_button = wx.Button(self, label='up')
		sweepzero_button = wx.Button(self, label='zero')
		sweepdown_button = wx.Button(self, label='down')
		sweeppause_button = wx.Button(self, label='pause')
		self.Bind(wx.EVT_BUTTON, self.OnSweepUp, sweepup_button)
		self.Bind(wx.EVT_BUTTON, self.OnSweepZero, sweepzero_button)
		self.Bind(wx.EVT_BUTTON, self.OnSweepDown, sweepdown_button)
		self.Bind(wx.EVT_BUTTON, self.OnSweepPause, sweeppause_button)
		sweep_buttons_grid.Add(sweepup_button)
		sweep_buttons_grid.Add(sweepzero_button)
		sweep_buttons_grid.Add(sweepdown_button)
		sweep_buttons_grid.Add(sweeppause_button)
		
		### Current syncing.
		
		####some space
		
				
		sync_button = wx.Button(self, label='sync currents')
		self.Bind(wx.EVT_BUTTON, self.OnSyncCurrents, sync_button)
		sweep_buttons_box.Add(sync_button, flag=wx.LEFT|wx.CENTER, border = 20)

		## Limits.
		
		limit_static_box = wx.StaticBox(self, label = 'Limits')
		limit_box = wx.StaticBoxSizer(limit_static_box, wx.VERTICAL)
		self.control_box.Add(limit_box,flag=wx.EXPAND)
						
		limits_grid = wx.FlexGridSizer(rows=2, cols=3, hgap=1)
		limits_grid.AddGrowableCol(1,1)

		limit_box.Add(limits_grid, flag=wx.ALIGN_RIGHT)
		
		### High Limit
		
		limits_grid.Add(wx.StaticText(self, label='High Limit:'),
				flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)
		
		set_hilim_button = wx.Button(self, label='Set', style=wx.BU_EXACTFIT)
		self.Bind(wx.EVT_BUTTON, self.OnSetHighLimit, set_hilim_button)
		limits_grid.Add(set_hilim_button,flag=wx.ALIGN_RIGHT)
		
		self.hilim_input = wx.TextCtrl(self, size=(100, -1))
		limits_grid.Add(self.hilim_input, flag=wx.EXPAND)
		
		### Low Limit
		
		limits_grid.Add(wx.StaticText(self, label='Low Limit:'),
				flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)
		
		set_lolim_button = wx.Button(self, label='Set', style=wx.BU_EXACTFIT)
		self.Bind(wx.EVT_BUTTON, self.OnSetLowLimit, set_lolim_button)
		limits_grid.Add(set_lolim_button,flag=wx.ALIGN_RIGHT)
		
		self.lolim_input = wx.TextCtrl(self, size=(100, -1))
		limits_grid.Add(self.lolim_input)
		
		## Rates.
		
		rates_static_box = wx.StaticBox(self, label = 'Rates')
		rates_box = wx.StaticBoxSizer(rates_static_box, wx.VERTICAL)
		self.control_box.Add(rates_box,flag=wx.EXPAND)
		
		## used to have content of rates box all right aligned. 
		rates_inner_box = wx.BoxSizer(wx.HORIZONTAL)
		rates_box.Add(rates_inner_box, flag=wx.ALIGN_RIGHT)
		
		menu_items = []
		for resource in self.readout_list:
			if resource.startswith('rate_'):
				menu_items.append(resource) 
		
		self.rates_menu = wx.ComboBox(self,choices = menu_items, style=wx.CB_READONLY)
		self.rates_menu.SetStringSelection(menu_items[0])
		rates_inner_box.Add(self.rates_menu, flag=wx.ALIGN_RIGHT)
		
		set_rate_button = wx.Button(self, label='Set', style=wx.BU_EXACTFIT)
		self.Bind(wx.EVT_BUTTON, self.OnSetRate, set_rate_button)
		rates_inner_box.Add(set_rate_button,flag=wx.ALIGN_RIGHT)
		
		self.rate_input = wx.TextCtrl(self, size=(100, -1))
		rates_inner_box.Add(self.rate_input, flag=wx.ALIGN_RIGHT)
			
		# Finish GUI building.
		
		self.SetSizerAndFit(main_box)
		
		# Default behaviour.
		
		## start with... 
		### ...the threads locked out of acquisition.
		self.running_lock.acquire()
		### ...controls disabled.
		self.RecursiveEnableSizer(self.control_box,False)
		
		# Threading.
		
		self.acqthreads = []
		#TODO: implement with a normal thread instead, to avoid the use of a dummy call to a resource.
		guicallback = partial(wx.CallAfter, self.Update)
		self.guiupdatethread = AcquisitionThread(self.delay, guicallback, resource=self.channel_subdevice.resources['persistent_switch_heater'], running_lock=self.running_lock)
		self.acqthreads.append(self.guiupdatethread)
		self.guiupdatethread.daemon = True
		self.guiupdatethread.start()
Пример #10
0
class Model4GChannelPanel(wx.Panel):
	
	def __init__(self, parent, global_store, subdevice, *args, **kwargs):
		wx.Panel.__init__(self, parent, *args, **kwargs)

		self.global_store = global_store
		self.delay = Quantity(1.0, 's')
		
		# This lock blocks the acquisition thread from acquiring.
		self.running_lock = Lock()
		self.channel_subdevice = subdevice
		
		self.displays = {}
		self.control_state_displays = {}
		self.readout_displays = {}
		self.control_state_list = ['persistent_switch_heater','virt_sync_currents']
		self.readout_list = [	'magnet_current','power_supply_current',
								'persistent_switch_heater', 'high_limit','low_limit','sweep',
								'rate_0','rate_1','rate_2','rate_3','rate_4', 'virt_sync_currents']
		self.measurement_resources = []
		for name in self.readout_list:
			self.displays[name] = []
			self.measurement_resources.append((name, self.channel_subdevice.resources[name]))
		# A list to save acquired data to before outputting to GUI.
		self.measurements = [None] * len(self.measurement_resources)

		# Main Box.
		
		main_box = wx.BoxSizer(wx.VERTICAL)
		
		# Channel Header Box.
		
		channel_header_box = wx.BoxSizer(wx.HORIZONTAL)
		main_box.Add(channel_header_box, flag=wx.EXPAND)
		
		self.channel_button = wx.ToggleButton(self, label='Channel {0} Toggle'.format(self.channel_subdevice.channel))
		self.Bind(wx.EVT_TOGGLEBUTTON, self.OnChannelToggle, self.channel_button)
		self.channel_button.SetValue(False)
		channel_header_box.Add(self.channel_button)
		
		## Control states.
		
		control_state_grid = wx.FlexGridSizer(rows=2, cols=2, hgap=1)
		channel_header_box.Add((0, 0), 1, wx.EXPAND)
		channel_header_box.Add(control_state_grid, flag=wx.ALIGN_RIGHT, border = 20)
		
		for control_state_name in self.control_state_list:
			control_state_display = wx.TextCtrl(self, size=(100, -1), style=wx.TE_READONLY)
			control_state_display.BackgroundColour = wx.LIGHT_GREY
			control_state_grid.Add(control_state_display, flag=wx.ALIGN_RIGHT)
			self.displays[control_state_name].append(control_state_display)
			self.control_state_displays[control_state_name] = control_state_display
		
		# reverse our dictionary for key retrieval by item.
		self.inv_control_state_displays = dict((v,k) for k, v in self.control_state_displays.iteritems())

		# Readouts.
		
		readout_static_box = wx.StaticBox(self, label = 'Readouts')
		readout_box = wx.StaticBoxSizer(readout_static_box, wx.VERTICAL)
		main_box.Add(readout_box, flag=wx.EXPAND, proportion=1)
		
#		readout_grid = wx.FlexGridSizer(rows=len(self.readout_list), cols=2, hgap=1)
		readout_grid = wx.FlexGridSizer(rows=len(self.readout_list), cols=3, hgap=1) #TODO: for debugging model4g GUI...replace when no longer needed.
		readout_box.Add(readout_grid, flag=wx.ALIGN_RIGHT)
		
		self.checkboxes = {}
				
		## Setup individual labels + displays
		
		for resource_name in self.readout_list:
			
			### Checkbox. #TODO: for debugging model4g GUI...remove when no longer needed.
			checkbox = wx.CheckBox(self)
			readout_grid.Add(checkbox, flag = wx.ALIGN_LEFT)
			self.checkboxes[resource_name] = checkbox
			
			### Label.
			label = resource_name.replace('_',' ').title()
			readout_grid.Add(wx.StaticText(self, label=label + ':'),
					flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)
			
			### Display.
			display = wx.TextCtrl(self, size=(100, -1), style=wx.TE_READONLY)
			display.BackgroundColour = wx.LIGHT_GREY
			self.displays[resource_name].append(display)
			self.readout_displays[resource_name] = display
									
			### Connect display to GUI.
			readout_grid.Add(self.displays[resource_name][-1], flag=wx.ALIGN_RIGHT)
			
		# reverse our dictionary for key retrieval by item.
		self.inv_readout_displays = dict((v,k) for k, v in self.readout_displays.iteritems())
		
		# Controls.
		
		self.control_static_box = wx.StaticBox(self, label='Controls')
		self.control_box=wx.StaticBoxSizer(self.control_static_box, wx.VERTICAL)
		main_box.Add(self.control_box, flag=wx.EXPAND)
		
		## Persistent Heater Switch.
		
		heater_box = wx.BoxSizer(wx.HORIZONTAL)
		self.control_box.Add(heater_box, flag=wx.ALIGN_RIGHT)
		
		heater_box.Add(wx.StaticText(self, label='Persistent Switch Heater:'),
				flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)
		
		self.heater_toggle = wx.ToggleButton(self, label='on/off', size=(100,-1))
		initial_state = self.channel_subdevice.persistent_switch_heater
		self.heater_toggle.SetValue(True if initial_state == 1 else 0)
		self.Bind(wx.EVT_TOGGLEBUTTON, self.OnHeaterToggle, self.heater_toggle)
		heater_box.Add(self.heater_toggle,flag=wx.ALIGN_RIGHT)
		
		## Sweeper Control Box.
		
		sweeper_static_box = wx.StaticBox(self, label = 'Sweep')
		sweeper_box = wx.StaticBoxSizer(sweeper_static_box, wx.VERTICAL)
		self.control_box.Add(sweeper_box, flag=wx.EXPAND)
		
		sweep_buttons_box = wx.BoxSizer(wx.HORIZONTAL)
		sweeper_box.Add(sweep_buttons_box, flag = wx.CENTER|wx.ALL)
		
		### Sweep buttons.
		
		sweep_buttons_grid = wx.FlexGridSizer(rows=2, cols=2, hgap=1)
		sweep_buttons_box.Add(sweep_buttons_grid, flag=wx.CENTER|wx.ALL)
		
		sweepup_button = wx.Button(self, label='up')
		sweepzero_button = wx.Button(self, label='zero')
		sweepdown_button = wx.Button(self, label='down')
		sweeppause_button = wx.Button(self, label='pause')
		self.Bind(wx.EVT_BUTTON, self.OnSweepUp, sweepup_button)
		self.Bind(wx.EVT_BUTTON, self.OnSweepZero, sweepzero_button)
		self.Bind(wx.EVT_BUTTON, self.OnSweepDown, sweepdown_button)
		self.Bind(wx.EVT_BUTTON, self.OnSweepPause, sweeppause_button)
		sweep_buttons_grid.Add(sweepup_button)
		sweep_buttons_grid.Add(sweepzero_button)
		sweep_buttons_grid.Add(sweepdown_button)
		sweep_buttons_grid.Add(sweeppause_button)
		
		### Current syncing.
		
		####some space
		
				
		sync_button = wx.Button(self, label='sync currents')
		self.Bind(wx.EVT_BUTTON, self.OnSyncCurrents, sync_button)
		sweep_buttons_box.Add(sync_button, flag=wx.LEFT|wx.CENTER, border = 20)

		## Limits.
		
		limit_static_box = wx.StaticBox(self, label = 'Limits')
		limit_box = wx.StaticBoxSizer(limit_static_box, wx.VERTICAL)
		self.control_box.Add(limit_box,flag=wx.EXPAND)
						
		limits_grid = wx.FlexGridSizer(rows=2, cols=3, hgap=1)
		limits_grid.AddGrowableCol(1,1)

		limit_box.Add(limits_grid, flag=wx.ALIGN_RIGHT)
		
		### High Limit
		
		limits_grid.Add(wx.StaticText(self, label='High Limit:'),
				flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)
		
		set_hilim_button = wx.Button(self, label='Set', style=wx.BU_EXACTFIT)
		self.Bind(wx.EVT_BUTTON, self.OnSetHighLimit, set_hilim_button)
		limits_grid.Add(set_hilim_button,flag=wx.ALIGN_RIGHT)
		
		self.hilim_input = wx.TextCtrl(self, size=(100, -1))
		limits_grid.Add(self.hilim_input, flag=wx.EXPAND)
		
		### Low Limit
		
		limits_grid.Add(wx.StaticText(self, label='Low Limit:'),
				flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)
		
		set_lolim_button = wx.Button(self, label='Set', style=wx.BU_EXACTFIT)
		self.Bind(wx.EVT_BUTTON, self.OnSetLowLimit, set_lolim_button)
		limits_grid.Add(set_lolim_button,flag=wx.ALIGN_RIGHT)
		
		self.lolim_input = wx.TextCtrl(self, size=(100, -1))
		limits_grid.Add(self.lolim_input)
		
		## Rates.
		
		rates_static_box = wx.StaticBox(self, label = 'Rates')
		rates_box = wx.StaticBoxSizer(rates_static_box, wx.VERTICAL)
		self.control_box.Add(rates_box,flag=wx.EXPAND)
		
		## used to have content of rates box all right aligned. 
		rates_inner_box = wx.BoxSizer(wx.HORIZONTAL)
		rates_box.Add(rates_inner_box, flag=wx.ALIGN_RIGHT)
		
		menu_items = []
		for resource in self.readout_list:
			if resource.startswith('rate_'):
				menu_items.append(resource) 
		
		self.rates_menu = wx.ComboBox(self,choices = menu_items, style=wx.CB_READONLY)
		self.rates_menu.SetStringSelection(menu_items[0])
		rates_inner_box.Add(self.rates_menu, flag=wx.ALIGN_RIGHT)
		
		set_rate_button = wx.Button(self, label='Set', style=wx.BU_EXACTFIT)
		self.Bind(wx.EVT_BUTTON, self.OnSetRate, set_rate_button)
		rates_inner_box.Add(set_rate_button,flag=wx.ALIGN_RIGHT)
		
		self.rate_input = wx.TextCtrl(self, size=(100, -1))
		rates_inner_box.Add(self.rate_input, flag=wx.ALIGN_RIGHT)
			
		# Finish GUI building.
		
		self.SetSizerAndFit(main_box)
		
		# Default behaviour.
		
		## start with... 
		### ...the threads locked out of acquisition.
		self.running_lock.acquire()
		### ...controls disabled.
		self.RecursiveEnableSizer(self.control_box,False)
		
		# Threading.
		
		self.acqthreads = []
		#TODO: implement with a normal thread instead, to avoid the use of a dummy call to a resource.
		guicallback = partial(wx.CallAfter, self.Update)
		self.guiupdatethread = AcquisitionThread(self.delay, guicallback, resource=self.channel_subdevice.resources['persistent_switch_heater'], running_lock=self.running_lock)
		self.acqthreads.append(self.guiupdatethread)
		self.guiupdatethread.daemon = True
		self.guiupdatethread.start()
		
	def __del__(self):
		try:
			
#			if self.channel_button.GetValue() == False:
#				self.running_lock.release()
			for thread in self.acqthreads:
				thread.resource = None
				thread.done = True
				thread.join()
				del thread
			
#			self.close()
		except Exception:
			pass

	def UpdateReadouts(self, resource_name, value):
		"""
		Update appropriate readouts with a new resource value.
		Also update button permissions.
		"""
		if resource_name in self.readout_displays.keys():
			if self.checkboxes[resource_name].Value == False: #TODO: for debugging model4g GUI...remove when no longer needed.
				return
			
		for display in self.displays[resource_name]:
			
			#perform alterations to output based on where the resource is being readout in GUI.
			
			inv_cont_dict = self.inv_control_state_displays
			if display in inv_cont_dict.keys():
				if inv_cont_dict[display] == 'persistent_switch_heater':
					if value == 'on':
						display.BackgroundColour = OK_BACKGROUND_COLOR
					elif value == 'off':
						display.BackgroundColour = wx.LIGHT_GREY
					
					value_readout = 'heater {0}'.format(value)
					
				elif inv_cont_dict[display] == 'virt_sync_currents':
					if value == 'synced':
						display.BackgroundColour = OK_BACKGROUND_COLOR
					elif value == 'not synced':
						display.BackgroundColour = wx.LIGHT_GREY
					#display as-is
					value_readout = value
			elif display in self.inv_readout_displays.keys():
				#display as-is
				value_readout = value
			
			display.SetValue(str(value_readout))
			
			# User Permissions
			
			# if currents don't match, heater toggle should be disabled.
			if display in inv_cont_dict.keys():
				if inv_cont_dict[display] == 'virt_sync_currents':
					if value == 'synced':
						self.heater_toggle.Enable()
					else:
						self.heater_toggle.Disable()
			
			
	def Update(self, dummyval):
		"""
		Acquire data and then update the GUI with this data.
		
		Note: code taken from sweep controller.
		"""

		#this loop sets up separate threads for each resource.
		thrs = []
		for i, (name, resource) in enumerate(self.measurement_resources):
			if resource is not None:
				def save_callback(value, i=i):
					self.measurements[i] = value

				callback = partial(wx.CallAfter, partial(self.read_resource, name, resource, save_callback))
				thr = Thread(target=callback)
				thrs.append(thr)
				thr.daemon = True
				thr.start()

		for thr in thrs:
			thr.join()

		#this code saves the values to the GUI readouts.
		for i, (name,_) in enumerate(self.measurement_resources):
			self.UpdateReadouts(name, self.measurements[i])
		
			
	def read_resource(self, name, resource, save_callback):
		"""
		Read a value from a resource and handle exceptions.
		
		Note: code taken from sweep controller.
		"""
		
		if name in self.readout_displays.keys():
			if self.checkboxes[name].Value == False: #TODO: for debugging model4g GUI...remove when no longer needed.
				return

		try:
			value = resource.value
		except Exception as e:
			if self.resource_exception_handler is not None:
				self.resource_exception_handler(name, e, write=False)
			return

		save_callback(value)

	def OnChannelToggle(self, evt=None):
		toggle = self.channel_button.GetValue()
		if toggle == True:
			self.running_lock.release()
		elif toggle == False:
			self.running_lock.acquire()
			
		self.RecursiveEnableSizer(self.control_box, toggle)
		
		#permission defaults.
		self.heater_toggle.Disable()
		
		
	def OnSweepDown(self, evt=None):
		self.channel_subdevice.resources['sweep'].value = 'down'
		
	def OnSweepUp(self, evt=None):
		self.channel_subdevice.resources['sweep'].value = 'up'
		
	def OnSweepZero(self, evt=None):
		self.channel_subdevice.resources['sweep'].value = 'zero'
		
	def OnSweepPause(self, evt=None):
		self.channel_subdevice.resources['sweep'].value = 'pause'
		
	def OnSetRate(self, evt=None):
		try:
			Quantity(self.rate_input.GetValue())
		except ValueError as e:
			MessageDialog(self, str(e), 'Invalid value').Show()
			return False
		
		range_id = self.rates_menu.GetCurrentSelection()
		resource = self.channel_subdevice.resources['rate_{0}'.format(range_id)]
		new_value = self.rate_input.GetValue()
		try:
			resource.value = resource.convert(new_value)
		except IncompatibleDimensions:
			MessageDialog(self, ValueError('Expected dimensions to match "{0}"'.format(resource.units))).Show()
		
	def OnHeaterToggle(self, evt=None):
		if self.heater_toggle.GetValue() == True:
			new_value = 'on'
		if self.heater_toggle.GetValue() == False:
			new_value = 'off'
		self.channel_subdevice.resources['persistent_switch_heater'].value = new_value
		
	def OnSetHighLimit(self, evt=None):
		try:
			Quantity(self.hilim_input.GetValue())
		except ValueError as e:
			MessageDialog(self, str(e), 'Invalid value').Show()
			return False
		
		new_value = self.hilim_input.GetValue()
		resource = self.channel_subdevice.resources['high_limit']
		try:
			resource.value = resource.convert(new_value)
		except IncompatibleDimensions:
			MessageDialog(self, str(ValueError('Expected dimensions to match "{0}"'.format(resource.units)))).Show()
		
	def OnSetLowLimit(self, evt=None):
		try:
			Quantity(self.lolim_input.GetValue())
		except ValueError as e:
			MessageDialog(self, str(e), 'Invalid value').Show()
			return False
		
		new_value = self.lolim_input.GetValue()
		resource = self.channel_subdevice.resources['low_limit']
		try:
			resource.value = resource.convert(new_value)
		except IncompatibleDimensions:
			MessageDialog(self, ValueError('Expected dimensions to match "{0}"'.format(resource.units))).Show()
		
		
	def OnSyncCurrents(self, evt=None):
		self.channel_subdevice.resources['virt_sync_currents'].value = 'start'
		
	def close(self):
		"""
		Perform cleanup.
		"""		
		# Ensure the threads exits.
		if self.channel_button.GetValue() == False:
			self.running_lock.release()
		for thread in self.acqthreads:
			thread.resource = None
			thread.done = True
			thread.join()
			del thread
			
	def RecursiveEnableSizer(self,wx_sizer, toggle):
		'''
		Helper function that accesses all subwindows of a wxPython 
		sizer, and enables or disables them based on toggle.
		'''
		children = wx_sizer.GetChildren()
		for item in children:
			
			window = item.GetWindow()
			sizer = item.GetSizer()
			
			if sizer:
				#recurse
				self.RecursiveEnableSizer(sizer,toggle)
			elif window:
				window.Enable(toggle)