class ClrForm(object): def __init__(self, width=320, height=240, setup=None, teardown=None): """ Creates a .NET Windows Form instance. :param width: Width of the window :param height: Height of the window :param setup: Function callback to call when the form has been created :param tearmdown: Function delegate to assign to the Form's Closing event """ from System.Windows.Forms import Application, Form from System.Drawing import Size self.form = Form(Text="muFAT Test", Size=Size(width, height), MinimizeBox=False, MaximizeBox=False, TopMost=True) self.hwnd = self.form.Handle if setup is not None: setup.__call__() if teardown is not None: self.form.Closing += teardown else: self.form.Closing += self.__exit__ if hasattr(self, 'resized'): self.form.ResizeEnd += self.resized def __enter__(self): """ Override this function to specify what happens when the form is initialized """ return self def __exit__(self, *args): """ Override this function to specify what happens when the form is closed or closing. """ pass def show(self): self.form.ShowDialog() def close(self): self.form.Close()
class ConfirmationDialog(): ''' Dialog window for confirming changed stim settings. Used when clicking 'Save' on a stim dialog to confirm that the user wants to stop the currently-running stimulation. ''' def ShowDialog(self, controller, title, text): # set controller self.controller = controller # create confirmation boolean -- True means the user wants to save # the stimulus settings and stop the currently running stimulation. self.confirmation = False # create the form self.dialog_window = Form() self.dialog_window.AutoSize = True self.dialog_window.Width = 400 self.dialog_window.MaximumSize = Size(400, 225) self.dialog_window.StartPosition = FormStartPosition.CenterScreen self.dialog_window.Text = title self.dialog_window.FormBorderStyle = FormBorderStyle.FixedSingle # create the main panel self.panel = FlowLayoutPanel() self.panel.Parent = self.dialog_window self.panel.BackColor = DIALOG_COLOR self.panel.Dock = DockStyle.Top self.panel.Padding = Padding(10, 10, 0, 10) self.panel.FlowDirection = FlowDirection.TopDown self.panel.WrapContents = False self.panel.AutoSize = True self.panel.Font = BODY_FONT # add the dialog text dialog_label = Label() dialog_label.Parent = self.panel dialog_label.Text = text dialog_label.Width = self.panel.Width dialog_label.AutoSize = True dialog_label.Margin = Padding(0, 5, 0, 0) # add button panel self.add_button_panel() # show the dialog self.dialog_window.ShowDialog() # return the exp name return self.confirmation def add_button_panel(self): # create button panel self.button_panel = FlowLayoutPanel() self.button_panel.Parent = self.dialog_window self.button_panel.BackColor = BUTTON_PANEL_COLOR self.button_panel.Dock = DockStyle.Bottom self.button_panel.Padding = Padding(10, 0, 10, 10) self.button_panel.WrapContents = False self.button_panel.AutoSize = True self.button_panel.Font = BODY_FONT self.button_panel.FlowDirection = FlowDirection.LeftToRight # add yes button self.yes_button = Button() self.yes_button.Parent = self.button_panel self.yes_button.Text = "Yes, Stop the Stimulation" self.yes_button.Click += self.on_yes_button_click self.yes_button.BackColor = BUTTON_COLOR self.yes_button.AutoSize = True # add cancel button self.cancel_button = Button() self.cancel_button.Parent = self.button_panel self.cancel_button.Text = "Cancel" self.cancel_button.Click += self.on_cancel_button_click self.cancel_button.BackColor = BUTTON_COLOR self.cancel_button.Font = ERROR_FONT self.cancel_button.AutoSize = True # cancel button is activated when user presses Enter self.dialog_window.AcceptButton = self.cancel_button def on_yes_button_click(self, sender, event): self.confirmation = True # close the window self.dialog_window.Close() def on_cancel_button_click(self, sender, event): self.confirmation = False # close the window self.dialog_window.Close()
class StimDialog(): ''' Dialog window for setting stim parameters. Used when creating or editing a stimulus. ''' def ShowDialog(self, controller, stim_index): # set controller self.controller = controller # initialize success bool self.success = False # initialize invalid params label self.invalid_params_label = None # create confirmation dialog self.confirmation_dialog = ConfirmationDialog() # create a dictionary to save stim parameters for different stim types # as the user changes parameters & types of stimuli in this dialog. # If the user switches from looming dot to moving dot and back, # the previous settings for the looming dot can be restored. self.saved_stim_parameters = {} if stim_index != None: # we are editing an existing stim; get the stim params self.i = stim_index self.stim_name = self.controller.config_params['stim_list'][self.i] self.stim_type = self.controller.config_params['types_list'][self.i] self.stim_duration = self.controller.config_params['durations_list'][self.i] self.stim_parameters = self.controller.config_params['parameters_list'][self.i] else: # we are creating a new stim; make new stim params self.i = len(self.controller.config_params['stim_list']) self.stim_type = 'Looming Dot' self.stim_name = self.stim_type self.stim_duration = self.controller.default_stim_duration() self.stim_parameters = self.controller.default_stim_params(self.stim_type) # add the stim to the config params self.controller.add_stim(self.stim_name, self.stim_type, self.stim_duration, self.stim_parameters) # save a copy of the initial stim params so we can restore them later self.saved_stim_parameters[self.stim_type] = self.controller.config_params['parameters_list'][self.i] # create the form self.dialog_window = Form() self.dialog_window.FormBorderStyle = FormBorderStyle.FixedSingle self.dialog_window.Text = "{} Parameters".format(self.stim_name) self.dialog_window.StartPosition = FormStartPosition.CenterScreen self.dialog_window.Width = 400 # add & populate stim param panel self.add_stim_param_panel() self.populate_stim_param_panel() # add & populate stim choice panel self.add_stim_choice_panel() self.populate_stim_choice_panel() # add save button panel self.add_save_button_panel() # auto-size the window self.dialog_window.AutoSize = True # show the dialog self.dialog_window.ShowDialog() # return success boolean return self.success def add_stim_choice_panel(self): # create stim choice panel self.stim_choice_panel = FlowLayoutPanel() self.stim_choice_panel.Parent = self.dialog_window self.stim_choice_panel.BackColor = CHOICE_PANEL_COLOR self.stim_choice_panel.Dock = DockStyle.Top self.stim_choice_panel.Padding = Padding(10) self.stim_choice_panel.FlowDirection = FlowDirection.TopDown self.stim_choice_panel.WrapContents = False self.stim_choice_panel.AutoSize = True self.stim_choice_panel.Font = BODY_FONT def populate_stim_choice_panel(self): # empty stim choice panel list_of_controls = self.stim_choice_panel.Controls for control in list_of_controls: control.Dispose() self.stim_choice_panel.Controls.Clear() # add stim choice label stim_choice_label = Label() stim_choice_label.Parent = self.stim_choice_panel stim_choice_label.Text = "Stim:" stim_choice_label.AutoSize = True # add stim chooser self.stim_chooser = ComboBox() self.stim_chooser.DropDownStyle = ComboBoxStyle.DropDownList self.stim_chooser.Parent = self.stim_choice_panel self.stim_chooser.Items.AddRange(("Looming Dot", "Moving Dot", "Combined Dots", "Optomotor Grating", "Grating", "Broadband Grating", "Delay", "Black Flash", "White Flash")) ##!! need to add option for OKR here self.stim_chooser.SelectionChangeCommitted += self.on_stim_choice self.stim_chooser.Text = self.stim_type self.stim_chooser.Width = self.dialog_window.Width - 40 self.stim_chooser.AutoSize = True self.stim_chooser.Font = BODY_FONT def add_stim_param_panel(self): # create stim param panel self.stim_param_panel = FlowLayoutPanel() self.stim_param_panel.Parent = self.dialog_window self.stim_param_panel.BackColor = PARAM_PANEL_COLOR self.stim_param_panel.Dock = DockStyle.Top self.stim_param_panel.Padding = Padding(10) self.stim_param_panel.FlowDirection = FlowDirection.TopDown self.stim_param_panel.WrapContents = False self.stim_param_panel.AutoScroll = True self.stim_param_panel.Font = BODY_FONT self.stim_param_panel.AutoSize = True self.stim_param_panel.MaximumSize = Size(0, 500) def populate_stim_param_panel(self): # empty stim param panel list_of_controls = self.stim_param_panel.Controls for control in list_of_controls: control.Dispose() self.stim_param_panel.Controls.Clear() # initialize stim param text controls dict self.stim_param_textboxes = {} self.stim_param_checkboxes = {} # add name label & textbox add_param_label('Name:', self.stim_param_panel) self.name_textbox = TextBox() self.name_textbox.Parent = self.stim_param_panel self.name_textbox.Text = str(self.stim_name) self.name_textbox.AutoSize = True self.name_textbox.Width = 300 self.name_textbox.BackColor = BUTTON_PANEL_COLOR self.name_textbox.Font = BODY_FONT # add duration label & textbox add_param_label('Duration (s):', self.stim_param_panel) self.duration_textbox = TextBox() self.duration_textbox.Parent = self.stim_param_panel self.duration_textbox.Text = str(self.stim_duration) self.duration_textbox.AutoSize = True self.duration_textbox.Width = 300 self.duration_textbox.BackColor = BUTTON_PANEL_COLOR self.duration_textbox.Font = BODY_FONT # add parameters heading label if self.stim_type not in ("Delay", "Black Flash", "White Flash"): add_heading_label("{} Parameters".format(self.stim_type), self.stim_param_panel) # add param labels & textboxes if self.stim_type == "Looming Dot": self.add_stim_param_to_window('looming_dot_init_x_pos', 'Initial x position (deg)') self.add_stim_param_to_window('looming_dot_init_y_pos', 'Initial y position (deg)') self.add_stim_param_to_window('l_v', 'l/v (ms)') self.add_stim_param_to_window('looming_dot_brightness', 'Dot brightness (0 - 1)') #Will need to change to brightness ie moving dot self.add_stim_param_to_window('background_brightness', 'Background brightness (0 - 1)') #here is the background_brightness problem self.add_stim_param_checkbox_to_window('checkered', 'Checkerboard pattern?') #here is the background_brightness problem self.add_stim_param_to_window('num_squares', 'Number of squares in the checkerboard pattern') #here is the background_brightness problem self.add_stim_param_checkbox_to_window('expand_checkered_pattern', 'Expanding checkerboard pattern?') #here is the background_brightness problem ## self.add_stim_param_to_window('background', ' Background (0-1)')##Will add option to change background elif self.stim_type == "Moving Dot": self.add_stim_param_to_window('radius', 'Radius (px)') self.add_stim_param_to_window('moving_dot_init_x_pos', 'Initial x position (deg)') self.add_stim_param_to_window('moving_dot_init_y_pos', 'Initial y position (deg)') self.add_stim_param_to_window('v_x', 'Horiz. velocity (deg/s)') self.add_stim_param_to_window('v_y', 'Vertical velocity (deg/s)') self.add_stim_param_to_window('moving_dot_brightness', 'Dot brightness (0 - 1)') self.add_stim_param_to_window('background_brightness', 'Background brightness (0 - 1)') elif self.stim_type == "Combined Dots": ## ##Looming won't allow all of this, needs to fit window add_heading_label("Looming Dot Parameters", self.stim_param_panel) self.add_stim_param_to_window('looming_dot_init_x_pos', 'Initial x position (deg)') self.add_stim_param_to_window('looming_dot_init_y_pos', 'Initial y position (deg)') self.add_stim_param_to_window('l_v', 'l/v (ms)') self.add_stim_param_to_window('looming_dot_brightness', 'Contrast (0 - 1)') #changed brightness from contrast ##moving add_heading_label("Moving Dot Parameters", self.stim_param_panel) self.add_stim_param_to_window('radius', 'Radius (px)') self.add_stim_param_to_window('moving_dot_init_x_pos', 'Initial x position (deg)') self.add_stim_param_to_window('moving_dot_init_y_pos', 'Initial y position (deg)') self.add_stim_param_to_window('v_x', 'Horiz. velocity (deg/s)') self.add_stim_param_to_window('v_y', 'Vertical velocity (deg/s)') self.add_stim_param_to_window('moving_dot_brightness', 'Contrast (0 - 1)') self.add_stim_param_to_window('background_brightness', 'Background brightness (0-1)') elif self.stim_type == "Optomotor Grating": self.add_stim_param_to_window('frequency', 'Spatial frequency (1/deg)') self.add_stim_param_to_window('merging_pos', 'Converging position (x)')#will need to do something to get x self.add_stim_param_to_window('init_phase', 'Initial phase (deg)') self.add_stim_param_to_window('velocity', 'Velocity (deg/s)') self.add_stim_param_to_window('contrast', 'Contrast (0 - 1)') self.add_stim_param_to_window('brightness', 'Brightness (0 - 1)') self.add_stim_param_to_window('angle', 'Angle') elif self.stim_type in ["Grating", "Broadband Grating"]: self.add_stim_param_to_window('frequency', 'Spatial frequency (1/deg)') self.add_stim_param_to_window('init_phase', 'Initial phase (deg)') self.add_stim_param_to_window('velocity', 'Velocity (deg/s)') self.add_stim_param_to_window('contrast', 'Contrast (0 - 1)') self.add_stim_param_to_window('brightness', 'Brightness (0 - 1)') self.add_stim_param_to_window('angle', 'Angle') elif self.stim_type == "White Flash": self.add_stim_param_to_window('brightness', 'Brightness (0 - 1)') elif self.stim_type in ("Delay", "Black Flash"): pass def add_stim_param_to_window(self, name, label_text): # add param label add_param_label(label_text + ':', self.stim_param_panel) # add param textbox self.stim_param_textboxes[name] = TextBox() self.stim_param_textboxes[name].Parent = self.stim_param_panel self.stim_param_textboxes[name].Text = str(self.stim_parameters[name]) self.stim_param_textboxes[name].AutoSize = True self.stim_param_textboxes[name].Width = 360 self.stim_param_textboxes[name].BackColor = TEXTBOX_COLOR self.stim_param_textboxes[name].Font = BODY_FONT def add_stim_param_checkbox_to_window(self, name, label_text): # add param label add_param_label(label_text + ':', self.stim_param_panel) # add param textbox self.stim_param_checkboxes[name] = CheckBox() self.stim_param_checkboxes[name].Parent = self.stim_param_panel self.stim_param_checkboxes[name].Text = "" self.stim_param_checkboxes[name].Checked = self.stim_parameters[name] self.stim_param_checkboxes[name].AutoSize = True self.stim_param_checkboxes[name].Width = 360 self.stim_param_checkboxes[name].BackColor = TEXTBOX_COLOR self.stim_param_checkboxes[name].Font = BODY_FONT def on_stim_choice(self, sender, event): # stop the dialog window from refreshing self.dialog_window.SuspendLayout() # save a copy of the current stim params for the currently selected stim type self.stim_param_textbox_values = {key: value.Text for (key, value) in self.stim_param_textboxes.items()} self.stim_param_checkbox_values = {key: value.Checked for (key, value) in self.stim_param_checkboxes.items()} self.saved_stim_parameters[self.stim_type] = dict({key: float(value) for (key, value) in self.stim_param_textbox_values.items()}.items() + {key: bool(value) for (key, value) in self.stim_param_checkbox_values.items()}.items()) # get selected stim type new_stim_type = self.stim_chooser.SelectedItem.ToString() if new_stim_type != self.stim_type: # update stim type self.stim_type = new_stim_type self.stim_name = new_stim_type if new_stim_type in self.saved_stim_parameters: # we have previously set parameters for this stim type; restore these self.stim_parameters = self.saved_stim_parameters[self.stim_type] else: # create new default stim parameters self.stim_parameters = self.controller.default_stim_params(self.stim_type) # refresh panel self.stim_choice_panel.Refresh() self.stim_choice_panel.BackColor = stim_color(self.stim_type) # populate stim param panel self.populate_stim_param_panel() self.name_textbox.Text = self.stim_name # allow the dialog window to refresh self.dialog_window.ResumeLayout() def add_save_button_panel(self): # create save button panel self.save_button_panel = FlowLayoutPanel() self.save_button_panel.Parent = self.dialog_window self.save_button_panel.BackColor = BUTTON_PANEL_COLOR self.save_button_panel.Dock = DockStyle.Bottom self.save_button_panel.Padding = Padding(10) self.save_button_panel.WrapContents = False self.save_button_panel.AutoSize = True self.save_button_panel.Font = BODY_FONT # add save button self.save_button = Button() self.save_button.Parent = self.save_button_panel self.save_button.Text = "Save" self.save_button.Click += self.on_save_button_click self.save_button.BackColor = BUTTON_COLOR self.save_button.AutoSize = True # save button is activated when user presses Enter self.dialog_window.AcceptButton = self.save_button # add close button self.close_button = Button() self.close_button.Parent = self.save_button_panel self.close_button.Text = "Cancel" self.close_button.DialogResult = DialogResult.Cancel self.close_button.BackColor = BUTTON_COLOR self.close_button.AutoSize = True def on_save_button_click(self, sender, event): if self.controller.running_stim: confirmation = self.confirmation_dialog.ShowDialog(self.controller, "Stop Current Stimulation?", "Saving these settings will stop the currently-running stimulation. Continue?") else: confirmation = True if confirmation: # save stim params self.success = self.save_stim_params(sender, event) if self.success: # saving was successful; close the window self.dialog_window.Close() def save_stim_params(self, sender, event): # stop any running stim self.controller.stop_stim() # get contents of param textboxes self.stim_param_textbox_values = {key: value.Text for (key, value) in self.stim_param_textboxes.items()} self.stim_param_checkbox_values = {key: value.Checked for (key, value) in self.stim_param_checkboxes.items()} name = self.name_textbox.Text duration = self.duration_textbox.Text type = self.stim_chooser.SelectedItem.ToString() if self.are_valid_params(type, self.stim_param_textbox_values) and is_nonnegative_number(duration): # the params are valid # remove any invalid params text self.remove_invalid_params_text() # create new parameters dicts new_stim_params = dict({key: float(value) for (key, value) in self.stim_param_textbox_values.items()}.items() + {key: bool(value) for (key, value) in self.stim_param_checkbox_values.items()}.items()) # update config params self.controller.config_params['stim_list'][self.i] = name self.controller.config_params['durations_list'][self.i] = float(duration) self.controller.config_params['types_list'][self.i] = type self.controller.config_params['parameters_list'][self.i] = new_stim_params # save config params self.controller.save_config_params() # update stim window's params if self.controller.stim_window: self.controller.stim_window.update_params() return True else: # the params are invalid; add invalid params text self.add_invalid_params_text() return False def are_valid_params(self, stim_type, stim_params): # check that all of the params are valid if stim_type == "Looming Dot": stim_params_are_valid = (is_number(stim_params['looming_dot_init_x_pos']) and is_number(stim_params['looming_dot_init_y_pos']) and is_positive_number(stim_params['l_v']) and is_number_between_0_and_1(stim_params['looming_dot_brightness']) and is_number_between_0_and_1(stim_params['background_brightness'])) elif stim_type == "Moving Dot": stim_params_are_valid = (is_nonnegative_number(stim_params['radius']) and is_number(stim_params['moving_dot_init_x_pos']) and is_number(stim_params['moving_dot_init_y_pos']) and is_number(stim_params['v_x']) and is_number(stim_params['v_y']) and is_number_between_0_and_1(stim_params['moving_dot_brightness']) and is_number_between_0_and_1(stim_params['background_brightness'])) elif stim_type == "Combined Dots": stim_params_are_valid = (is_nonnegative_number(stim_params['radius']) and is_number(stim_params['moving_dot_init_x_pos']) and is_number(stim_params['moving_dot_init_y_pos']) and is_number(stim_params['v_x']) and is_number(stim_params['v_y']) and is_number_between_0_and_1(stim_params['moving_dot_brightness']) and is_number(stim_params['looming_dot_init_x_pos']) and is_number(stim_params['looming_dot_init_y_pos']) and is_positive_number(stim_params['l_v']) and is_number_between_0_and_1(stim_params['looming_dot_brightness']) and is_number_between_0_and_1(stim_params['background_brightness'])) elif stim_type == "Optomotor Grating": stim_params_are_valid = (is_positive_number(stim_params['frequency']) and is_nonnegative_number(stim_params['merging_pos']) and is_number(stim_params['init_phase']) and is_number(stim_params['velocity']) and is_number_between_0_and_1(stim_params['contrast']) and is_number_between_0_and_1(stim_params['brightness']) and is_number(stim_params['angle'])) elif stim_type in ["Grating", "Broadband Grating"]: stim_params_are_valid = (is_positive_number(stim_params['frequency']) and is_number(stim_params['init_phase']) and is_number(stim_params['velocity']) and is_number_between_0_and_1(stim_params['contrast']) and is_number_between_0_and_1(stim_params['brightness']) and is_number(stim_params['angle'])) elif stim_type == "White Flash": stim_params_are_valid = is_number_between_0_and_1(stim_params['brightness']) elif stim_type in ("Delay", "Black Flash"): stim_params_are_valid = True return stim_params_are_valid def add_invalid_params_text(self): if not self.invalid_params_label: # add invalid params label self.invalid_params_label = Label() self.invalid_params_label.Parent = self.save_button_panel self.invalid_params_label.Font = ERROR_FONT self.invalid_params_label.Padding = Padding(5) self.invalid_params_label.ForeColor = Color.Red self.invalid_params_label.AutoSize = True # set invalid param label text self.invalid_params_label.Text = "Invalid parameters." def remove_invalid_params_text(self): if self.invalid_params_label: # clear invalid param label text self.invalid_params_label.Text = ""
class TTLDialog(): ''' Dialog window for setting TTL pulse parameters. ''' def ShowDialog(self, controller): # set controller self.controller = controller # initialize success bool self.success = False # initialize invalid params label self.invalid_params_label = None # get current TTL params self.TTL_params = controller.config_params['TTL_params'] # create the form self.dialog_window = Form() self.dialog_window.FormBorderStyle = FormBorderStyle.FixedSingle self.dialog_window.Text = "TTL Parameters" self.dialog_window.StartPosition = FormStartPosition.CenterScreen self.dialog_window.Width = 200 # add & populate TTL param panel self.add_TTL_param_panel() self.populate_TTL_param_panel() # add save button panel self.add_save_button_panel() # auto-size the window self.dialog_window.AutoSize = True # show the dialog self.dialog_window.ShowDialog() # return success boolean return self.success def add_TTL_param_panel(self): # create TTL param panel self.TTL_param_panel = FlowLayoutPanel() self.TTL_param_panel.Parent = self.dialog_window self.TTL_param_panel.BackColor = PARAM_PANEL_COLOR self.TTL_param_panel.Dock = DockStyle.Bottom self.TTL_param_panel.Padding = Padding(10) self.TTL_param_panel.FlowDirection = FlowDirection.TopDown self.TTL_param_panel.WrapContents = False self.TTL_param_panel.AutoScroll = True self.TTL_param_panel.Font = BODY_FONT self.TTL_param_panel.AutoSize = True def populate_TTL_param_panel(self): # initialize TTL param text controls dict self.TTL_param_textboxes = {} # add heading label add_heading_label("TTL Parameters", self.TTL_param_panel) # add param labels & textboxes self.add_TTL_param_to_window('delay', 'Delay (ms)') self.add_TTL_param_to_window('frequency', 'Frequency (Hz)') self.add_TTL_param_to_window('pulse_width', 'Pulse width (ms)') self.add_TTL_param_to_window('duration', 'Duration (s)') def add_TTL_param_to_window(self, name, label_text): # add param label add_param_label(label_text + ':', self.TTL_param_panel) # add param textbox self.TTL_param_textboxes[name] = TextBox() self.TTL_param_textboxes[name].Parent = self.TTL_param_panel self.TTL_param_textboxes[name].Text = str(self.TTL_params[name]) self.TTL_param_textboxes[name].Width = 150 self.TTL_param_textboxes[name].BackColor = TEXTBOX_COLOR self.TTL_param_textboxes[name].AutoSize = True self.TTL_param_textboxes[name].Font = Font(BODY_FONT.FontFamily, 18) def add_save_button_panel(self): # create save button panel self.save_button_panel = FlowLayoutPanel() self.save_button_panel.Parent = self.dialog_window self.save_button_panel.BackColor = BUTTON_PANEL_COLOR self.save_button_panel.Dock = DockStyle.Bottom self.save_button_panel.Padding = Padding(10) self.save_button_panel.WrapContents = False self.save_button_panel.AutoSize = True self.save_button_panel.Font = BODY_FONT # add save button self.save_button = Button() self.save_button.Parent = self.save_button_panel self.save_button.Text = "Save" self.save_button.Click += self.on_save_button_click self.save_button.BackColor = BUTTON_COLOR self.save_button.AutoSize = True # save button is activated when user presses Enter self.dialog_window.AcceptButton = self.save_button # add close button self.close_button = Button() self.close_button.Parent = self.save_button_panel self.close_button.Text = "Close" self.close_button.DialogResult = DialogResult.Cancel self.close_button.BackColor = BUTTON_COLOR self.close_button.AutoSize = True def on_save_button_click(self, sender, event): # save TTL params self.success = self.save_TTL_params(sender, event) if self.success: # saving was successful; close the window self.dialog_window.Close() def save_TTL_params(self, sender, event): # get contents of param textboxes self.TTL_param_textbox_values = { key: value.Text for (key, value) in self.TTL_param_textboxes.items() } if self.are_valid_params(self.TTL_param_textbox_values): # the params are valid # remove any invalid params text self.remove_invalid_params_text() # create new parameters dicts new_TTL_params = { key: float(value) for (key, value) in self.TTL_param_textbox_values.items() } # update controller's TTL params self.controller.config_params['TTL_params'] = new_TTL_params # save TTL params self.controller.save_config_params() return True else: # the params are invalid; add invalid params text self.add_invalid_params_text() return False def are_valid_params(self, TTL_params): # check that all of the params are valid stim_params_are_valid = ( is_nonnegative_number(TTL_params['delay']) and is_positive_number(TTL_params['frequency']) and is_positive_number(TTL_params['pulse_width']) and is_positive_number(TTL_params['duration'])) return stim_params_are_valid def add_invalid_params_text(self): if not self.invalid_params_label: # add invalid params label self.invalid_params_label = Label() self.invalid_params_label.Parent = self.save_button_panel self.invalid_params_label.Font = ERROR_FONT self.invalid_params_label.Padding = Padding(5) self.invalid_params_label.ForeColor = Color.Red self.invalid_params_label.AutoSize = True # set invalid param label text self.invalid_params_label.Text = "Invalid parameters." def remove_invalid_params_text(self): if self.invalid_params_label: # clear invalid param label text self.invalid_params_label.Text = ""
class ConfigNameDialog(): ''' Dialog window for inputting a configuration name. Used when creating or renaming a configuration. ''' def ShowDialog(self, controller, title, text, default_input, config_index): # set controller self.controller = controller # set exp index self.config_index = config_index # initialize exp name variable self.config_name = None # initialize invalid name label self.invalid_name_label = None # create the form self.dialog_window = Form() self.dialog_window.AutoSize = True self.dialog_window.Width = 400 self.dialog_window.StartPosition = FormStartPosition.CenterScreen self.dialog_window.Text = title self.dialog_window.MaximumSize = Size(400, 160) self.dialog_window.FormBorderStyle = FormBorderStyle.FixedSingle # create the main panel self.panel = FlowLayoutPanel() self.panel.Parent = self.dialog_window self.panel.BackColor = DIALOG_COLOR self.panel.Dock = DockStyle.Top self.panel.Padding = Padding(10, 10, 0, 10) self.panel.FlowDirection = FlowDirection.TopDown self.panel.WrapContents = False self.panel.AutoSize = True self.panel.Font = BODY_FONT # add the dialog text config_name_label = Label() config_name_label.Parent = self.panel config_name_label.Text = text config_name_label.Width = self.panel.Width config_name_label.AutoSize = True config_name_label.Margin = Padding(0, 5, 0, 0) # add the textbox self.config_name_box = TextBox() self.config_name_box.Text = default_input self.config_name_box.Parent = self.panel self.config_name_box.Width = self.dialog_window.Width - 30 self.config_name_box.AutoSize = True self.config_name_box.BackColor = BUTTON_PANEL_COLOR self.config_name_box.Font = Font(BODY_FONT.FontFamily, 9) # add save button panel self.add_save_button_panel() # show the dialog self.dialog_window.ShowDialog() # return the config name return self.config_name def add_save_button_panel(self): # create save button panel self.save_button_panel = FlowLayoutPanel() self.save_button_panel.Parent = self.dialog_window self.save_button_panel.BackColor = BUTTON_PANEL_COLOR self.save_button_panel.Dock = DockStyle.Bottom self.save_button_panel.Padding = Padding(10, 0, 10, 10) self.save_button_panel.WrapContents = False # self.save_button_panel.Height = 40 self.save_button_panel.Font = BODY_FONT self.save_button_panel.FlowDirection = FlowDirection.LeftToRight self.save_button_panel.AutoSize = True # add save button self.save_button = Button() self.save_button.Parent = self.save_button_panel self.save_button.Text = "Save" self.save_button.Click += self.on_save_button_click self.save_button.BackColor = BUTTON_COLOR self.save_button.AutoSize = True # save button is activated when user presses Enter self.dialog_window.AcceptButton = self.save_button def on_save_button_click(self, sender, event): # get what's in the text box config_name = self.config_name_box.Text if self.config_index == None: # we are creating a new config; check if none of the existing configs have the same name success = not (config_name in self.controller.configs['configs_list']) else: # we are renaming an config; check if none of the other configs have the same name other_configs = [ exp for exp in self.controller.configs['configs_list'] if not exp == "config_name" ] success = not (config_name in other_configs) if success: # the exp name is valid; set exp name & close the window self.config_name = config_name self.dialog_window.Close() else: # the exp name is invalid; add invalid name text self.add_invalid_name_text() def add_invalid_name_text(self): if not self.invalid_name_label: # add invalid name label self.invalid_name_label = Label() self.invalid_name_label.Parent = self.save_button_panel self.invalid_name_label.Font = ERROR_FONT self.invalid_name_label.Padding = Padding(5) self.invalid_name_label.ForeColor = Color.Red self.invalid_name_label.AutoSize = True # set invalid name label text self.invalid_name_label.Text = "Config name is taken." def remove_invalid_name_text(self): if self.invalid_name_label: # clear invalid name label text self.invalid_name_label.Text = ""