def makeFrameWidget(self): ''' makes frame widget ''' #make regular frame stuff -- label and tier self.frames_canvas = Canvas(self.canvas_frame, width=self.canvas_width, height=self.canvas_height, background='gray', highlightthickness=0) frames_label = Canvas(self.frame, width=self.label_width, height=self.canvas_height, highlightthickness=0, background='gray') frames_label.create_text(self.label_width, 0, anchor='ne', justify='center', text='frames: ', width=self.label_width, activefill='blue') # make subframe to go on top of label canvas sbframe = Frame(frames_label) #put new widgets onto subframe offset = self.app.Data.getFileLevel('offset') if offset != None: self.frame_shift.set(offset) go_btn = Button(sbframe, text='Offset', command=self.shiftFrames, takefocus=0) # minmax = len(self.app.Audio.sfile)*1000 txtbox = Spinbox(sbframe, textvariable=self.frame_shift, width=7, from_=-10000000, to=10000000) txtbox.bind('<Escape>', lambda ev: sbframe.focus()) txtbox.bind('<Return>', lambda ev: self.shiftFrames()) go_btn.grid(row=0, column=0, sticky='e') txtbox.grid(row=0, column=1, sticky='e') # put subframe on canvas window = frames_label.create_window(self.label_width * .3, self.canvas_height / 3, anchor='nw', window=sbframe) self.TkWidgets.append({ 'name': self.frameTierName, 'frames': self.frames_canvas, 'frames-label': frames_label }) self.frames_canvas.bind("<Button-1>", self.getClickedFrame)
class MainGUI: def __init__(self, master): self.master = master self.backgroundColor = "pink" master.title("A simple GUI.") self.leftLabel = Label(master, text="LEFT", width=10, height=10, relief=RAISED, background="grey") self.leftLabel.pack(side=LEFT) self.rightLabel = Label(master, text="RIGHT", width=10, height=10, relief=RAISED, background="grey") self.rightLabel.pack(side=RIGHT) self.topLabel = Label(master, text="TOP", width=10, height=10, relief=RAISED, background="grey") self.topLabel.pack(side=TOP) self.bottomLabel = Label(master, text="BOTTOM", width=10, height=10, relief=RAISED, background="grey") self.bottomLabel.pack(side=BOTTOM) self.frame = Frame(master, width=100, height=100) self.frame.bind('<KeyRelease>', self.keyPressed) self.frame.pack() self.frame.focus() print("hello") def keyPressed(self, event): self.clearLabelColor() if event.char == "a" or event.char == "A": self.leftLabel.configure(background="pink") if event.char == "s" or event.char == "S": self.bottomLabel.configure(background="pink") if event.char == "d" or event.char == "D": self.rightLabel.configure(background="pink") if event.char == "w" or event.char == "W": self.topLabel.configure(background="pink") return def clearLabelColor(self): self.leftLabel.configure(background="grey") self.bottomLabel.configure(background="grey") self.topLabel.configure(background="grey") self.rightLabel.configure(background="grey")
class GraphyInspector: def __init__(self, parent): self.parent = parent self.width = self.parent.right_frame_width self.padding = self.parent.right_frame_padding self.frame = Frame(master=self.parent.right_frame) self.frame.pack(side='top', fill='y', ) # "Inspector" title bar self.title_frame = Frame(master=self.frame) self.title_frame.pack(side='top') self.title_label = Label(master=self.title_frame, text="Inspector", width=self.width, bg='lightgray') self.title_label.pack() # identifier for type of object selected self.type_frame = Frame(master=self.frame, relief='sunken') self.type_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.type_label1 = Label(master=self.type_frame, width=int(self.width/2)-self.padding, text='Object:') self.type_label1.pack(side='left', padx=self.padding, pady=self.padding) self.type_label2 = Label(master=self.type_frame, width=int(self.width / 2) - self.padding, text='', bg='white') self.type_label2.pack(side='right', padx=self.padding, pady=self.padding) # label of selected object (i.e. name user gives them, no canvas IDs here) self.label_frame = Frame(master=self.frame, relief='sunken') self.label_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.label_label = Label(master=self.label_frame, width=int(self.width/2)-self.padding, text="Label:") self.label_label.pack(side='left', padx=self.padding, pady=self.padding) self.label_var = StringVar() self.label_var.set('') self.label_entry = Entry(self.label_frame, width=int(self.width/2)-self.padding, textvariable=self.label_var) self.label_entry.pack(side='right', padx=self.padding, pady=self.padding) self.label_entry.bind('<Button-1>', self.select_label_text) self.label_entry.bind('<Return>', self.drop_widget_focus) # status identifier (for vertices and layers) self.status_frame = Frame(master=self.frame, relief='sunken') self.status_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.status_label1 = Label(master=self.status_frame, width=int(self.width/2)-self.padding, text='Status:') self.status_label1.pack(side='left', padx=self.padding, pady=self.padding) self.status_label2 = Label(master=self.status_frame, width=int(self.width/2)-self.padding, text='', bg='white') self.status_label2.pack(side='right', padx=self.padding, pady=self.padding) self.activation_var = StringVar() self.activation_var.set('') self.activation_menu = OptionMenu(self.status_frame, self.activation_var, "Identity", "Sigmoid", "ReLU", "Logarithmic", "Exponential") self.activation_menu.pack(side='right', padx=self.padding, pady=self.padding) # weight identifier (for edges only) self.weight_frame = Frame(master=self.frame, relief='sunken') self.weight_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.weight_label = Label(master=self.weight_frame, width=int(self.width/2)-self.padding, text="Weight:") self.weight_label.pack(side='left', padx=self.padding, pady=self.padding) self.weight_var = DoubleVar() self.weight_entry = Entry(self.weight_frame, width=int(self.width / 2) - self.padding, textvariable=self.weight_var) self.weight_entry.pack(side='right', padx=self.padding, pady=self.padding) self.weight_entry.bind('<Button-1>', self.select_weight_text) self.weight_entry.bind('<Return>', self.drop_widget_focus) # node count identifier (for layers only) self.node_frame = Frame(master=self.frame, relief='sunken') self.node_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.node_label = Label(master=self.node_frame, width=int(self.width/2)-self.padding, text="Node Count:") self.node_label.pack(side='left', padx=self.padding, pady=self.padding) self.node_var = IntVar() self.node_entry = Entry(self.node_frame, width=int(self.width / 2) - self.padding, textvariable=self.node_var) self.node_entry.pack(side='right', padx=self.padding, pady=self.padding) self.node_entry.bind('<Button-1>', self.select_node_text) self.node_entry.bind('<Return>', self.drop_widget_focus) # leakiness self.leakiness_frame = Frame(master=self.frame, relief='sunken') self.leakiness_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.leakiness_label = Label(master=self.leakiness_frame, width=int(self.width/2)-self.padding, text="Leakiness") self.leakiness_label.pack(side='left', padx=self.padding, pady=self.padding) self.leakiness_var = DoubleVar() self.leakiness_entry = Entry(self.leakiness_frame, width=int(self.width / 2) - self.padding, textvariable=self.leakiness_var) self.leakiness_entry.pack(side='right', padx=self.padding, pady=self.padding) self.leakiness_entry.bind('<Button-1>', self.select_leakiness_text) self.leakiness_entry.bind('<Return>', self.drop_widget_focus) # bias self.bias_frame = Frame(master=self.frame, relief='sunken') self.bias_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.bias_label = Label(master=self.bias_frame, width=int(self.width/2)-self.padding, text="Bias:") self.bias_label.pack(side='left', padx=self.padding, pady=self.padding) self.bias_var = DoubleVar() self.bias_entry = Entry(self.bias_frame, width=int(self.width / 2) - self.padding, textvariable=self.bias_var) self.bias_entry.pack(side='right', padx=self.padding, pady=self.padding) self.bias_entry.bind('<Button-1>', self.select_bias_text) self.bias_entry.bind('<Return>', self.drop_widget_focus) # output bound self.bound_frame = Frame(master=self.frame, relief='sunken') self.bound_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.bound_label = Label(master=self.bound_frame, width=int(self.width/2)-self.padding, text="Output Bound:") self.bound_label.pack(side='left', padx=self.padding, pady=self.padding) self.bound_var = DoubleVar() self.bound_entry = Entry(self.bound_frame, width=int(self.width / 2) - self.padding, textvariable=self.bound_var) self.bound_entry.pack(side='right', padx=self.padding, pady=self.padding) self.bound_entry.bind('<Button-1>', self.select_bound_text) self.bound_entry.bind('<Return>', self.drop_widget_focus) # noise self.noise_frame = Frame(master=self.frame, relief='sunken') self.noise_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.noise_label = Label(master=self.noise_frame, width=int(self.width/2)-self.padding, text="Weight Noise:") self.noise_label.pack(side='left', padx=self.padding, pady=self.padding) self.noise_var = DoubleVar() self.noise_entry = Entry(self.noise_frame, width=int(self.width / 2) - self.padding, textvariable=self.noise_var) self.noise_entry.pack(side='right', padx=self.padding, pady=self.padding) self.noise_entry.bind('<Button-1>', self.select_noise_text) self.noise_entry.bind('<Return>', self.drop_widget_focus) # input / output self.input_output_frame = Frame(master=self.frame, relief='sunken') self.input_output_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.input_var = BooleanVar() self.input_var.set(False) self.output_var = BooleanVar() self.output_var.set(False) self.input_toggle = Checkbutton(master=self.input_output_frame, text="Is Input", variable=self.input_var) self.output_toggle = Checkbutton(master=self.input_output_frame, text="Is Output", variable=self.output_var) self.input_toggle.pack(side='left', padx=self.padding, pady=self.padding) self.output_toggle.pack(side='left', padx=self.padding, pady=self.padding) self.selected = None self.selected_type = None self.set_unselected() self.label_var.trace('w', self.set_selected_label) self.weight_var.trace('w', self.set_selected_weight) self.activation_var.trace('w', self.set_selected_activation) self.leakiness_var.trace('w', self.set_selected_leakiness) self.node_var.trace('w', self.set_selected_node_count) self.bias_var.trace('w', self.set_selected_bias) self.bound_var.trace('w', self.set_selected_bound) self.noise_var.trace('w', self.set_selected_noise) self.input_var.trace('w', self.set_input) self.output_var.trace('w', self.set_output) # mode self.mode = parent.mode self.set_mode(parent.mode) # object is a vertex or edge, type is 'vertex' or 'edge'... def set_selected(self, selected_object, selected_object_type): self.selected = selected_object self.selected_type = selected_object_type if self.mode == "Graph": if selected_object_type == 'vertex': self.type_label2.config(text="Vertex") self.status_label2.config(text=selected_object.status) self.type_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.label_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.status_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.label_var.set(selected_object.label) elif selected_object_type == 'edge': self.type_label2.config(text="Edge") self.type_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.label_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.weight_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.label_var.set(selected_object.label) self.weight_var.set(selected_object.weight) else: print('dafuq is going on') elif self.mode == "Net": if selected_object_type == 'vertex': self.type_label2.config(text="Layer") self.status_label2.config(text=selected_object.status) self.type_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.label_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.status_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.node_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.leakiness_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.bias_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.bound_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.input_output_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.label_var.set(selected_object.label) self.node_var.set(selected_object.node_count) self.activation_var.set(selected_object.status) self.leakiness_var.set(selected_object.leakiness) self.bias_var.set(selected_object.bias) self.bound_var.set(selected_object.bound) self.input_var.set(selected_object.is_input_layer) self.output_var.set(selected_object.is_output_layer) elif selected_object_type == 'edge': self.type_label2.config(text="Weights") self.type_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.label_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.weight_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.noise_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.label_var.set(selected_object.label) self.weight_var.set(selected_object.weight) self.noise_var.set(selected_object.noise) else: print('This will never happen.') # nothing is selected def set_unselected(self): self.type_frame.pack_forget() self.label_frame.pack_forget() self.status_frame.pack_forget() self.weight_frame.pack_forget() self.node_frame.pack_forget() self.bias_frame.pack_forget() self.bound_frame.pack_forget() self.noise_frame.pack_forget() self.input_output_frame.pack_forget() self.leakiness_frame.pack_forget() self.selected = None self.selected_type = None # set label of selected object def set_selected_label(self, *args): self.selected.set_label(self.label_var.get()) # set weight of selected object def set_selected_weight(self, *args): self.selected.set_weight(self.weight_var.get()) def set_selected_activation(self, *args): self.selected.set_status(self.activation_var.get()) def set_selected_node_count(self, *args): self.selected.set_node_count(self.node_var.get()) def set_selected_bias(self, *args): self.selected.set_bias(self.bias_var.get()) def set_selected_bound(self, *args): self.selected.set_bound(self.bound_var.get()) def set_selected_noise(self, *args): self.selected.set_noise(self.noise_var.get()) def set_input(self, *args): self.selected.set_input_layer(self.input_var.get()) def set_output(self, *args): self.selected.set_output_layer(self.output_var.get()) def set_selected_leakiness(self, *args): self.selected.set_leakiness(self.leakiness_var.get()) def update(self): if self.selected: selected = self.selected type = self.selected_type self.set_unselected() self.set_selected(selected, type) def select_label_text(self, event): if event: # delay so the default click doesn't undo selection, then recall and fall through to "else" self.parent.tk.after(50, self.select_label_text, False) else: self.label_entry.select_range(0, 'end') self.label_entry.icursor(0) def select_weight_text(self, event): if event: # delay so the default click doesn't undo selection, then recall and fall through to "else" self.parent.tk.after(50, self.select_weight_text, False) else: self.weight_entry.select_range(0, 'end') self.weight_entry.icursor(0) def select_node_text(self, event): if event: self.parent.tk.after(50, self.select_node_text, False) else: self.node_entry.select_range(0, 'end') self.node_entry.icursor(0) def select_bias_text(self, event): if event: self.parent.tk.after(50, self.select_bias_text, False) else: self.bias_entry.select_range(0, 'end') self.bias_entry.icursor(0) def select_noise_text(self, event): if event: self.parent.tk.after(50, self.select_noise_text, False) else: self.noise_entry.select_range(0, 'end') self.noise_entry.icursor(0) def select_bound_text(self, event): if event: self.parent.tk.after(50, self.select_bound_text, False) else: self.bound_entry.select_range(0, 'end') self.bound_entry.icursor(0) def select_leakiness_text(self, event): if event: self.parent.tk.after(50, self.select_leakiness_text, False) else: self.leakiness_entry.select_range(0, 'end') self.leakiness_entry.icursor(0) def drop_widget_focus(self, event): self.frame.focus() def set_mode(self, mode): if mode == "Graph": self.mode = mode self.weight_label.config(text="Weight:") self.status_label1.config(text="Status:") self.activation_menu.pack_forget() self.input_output_frame.pack_forget() self.status_label2.pack(side='right', padx=self.padding, pady=self.padding) elif mode == "Net": self.mode = mode self.weight_label.config(text="Start Weight:") self.status_label1.config(text="Activation:") self.status_label2.pack_forget() self.activation_menu.pack(side='right', padx=self.padding, pady=self.padding) else: print("This will never happen.")
class ABSingleLickGui: def __init__(self, root=Tk()): self.root = root self.root.title("Apple/Banana Single Lick") self.root.configure(bg=BACKGROUND_COLOR) self.titleLabel = Label(self.root, text='AppleBanana Single Lick Experiment', font=STATUS_FONT, bg=BACKGROUND_COLOR) self.ard = None self.experiment = None self.isConfigLoaded = False self.isArdConnected = False self.isPumpOn = False self.estTime = StringVar() self.lickCount = IntVar() self.lickCount.set(0) self.isSoundOn = BooleanVar() self.stopUpdating = threading.Event() self.ardUpdater = threading.Thread(target=self.updateVariable) port = MouseArduino.getUnoPort() #Frames self.master = Frame(root, bg=BACKGROUND_COLOR) self.master.grid_rowconfigure(0) self.master.grid_rowconfigure(1) self.master.grid_rowconfigure(2, weight=5) self.master.grid_columnconfigure(0, weight=1) self.master.grid_columnconfigure(1, weight=1) self.master.grid_columnconfigure(2, weight=1) self.ardInitFrame = Frame(self.master, bd=3, relief='groove', bg=BACKGROUND_COLOR) self.ardControlFrame = Frame(self.master, bd=3, relief='groove', bg=BACKGROUND_COLOR) self.initFrame = Frame(self.master, bd=3, relief='groove', bg=BACKGROUND_COLOR) self.argFrame = Frame(self.master, bd=3, relief='groove', bg=BACKGROUND_COLOR) self.finalControlFrame = Frame(self.master, bd=3, relief='groove', bg=BACKGROUND_COLOR) #ardInitFrame self.ardInitFrameLabel = Label(self.ardInitFrame, text="Connect to Hardware", bg=HEADER_COLOR, font=LARGE_FONT, fg='white', relief='solid', borderwidth=2, width=40) self.comLabel = Label(self.ardInitFrame, bg=BACKGROUND_COLOR, text="Com Port:", font=TEXT_FONT) self.comEntry = Entry(self.ardInitFrame, font=TEXT_FONT) self.baudrateLabel = Label(self.ardInitFrame, bg=BACKGROUND_COLOR, text="Baudrate:", font=TEXT_FONT) self.baudrateEntry = Entry(self.ardInitFrame, font=TEXT_FONT) self.connectButton = Button(self.ardInitFrame, text="Connect", font=STATUS_FONT, command=self.connect) self.ardInitFrameLabel.grid(row=0, columnspan=2, padx=40, pady=10) self.comLabel.grid(row=1, column=0, sticky=tk.E) self.comEntry.grid(row=1, column=1, sticky=tk.W) self.baudrateLabel.grid(row=2, column=0, sticky=tk.E) self.baudrateEntry.grid(row=2, column=1, sticky=tk.W) self.connectButton.grid(row=3, columnspan=2, pady=10) self.comEntry.insert(0, port) self.baudrateEntry.insert(0, 115200) #ardControlFrame self.ardControlFrameLabel = Label(self.ardControlFrame, text='Pre-experiment Control', bg=HEADER_COLOR, font=LARGE_FONT, fg='white', relief='solid', borderwidth=2, width=40) self.sendStringEntry = Entry(self.ardControlFrame, font=TEXT_FONT, width=20) self.sendStringButton = Button(self.ardControlFrame, text='Send String', font=TEXT_FONT, bg=BACKGROUND_COLOR, command=self.sendString) self.rewardButton = Button(self.ardControlFrame, text='Reward(R)', font=STATUS_FONT, width=10, bg=BACKGROUND_COLOR, command=self.deliverReward, height=1) self.pumpButton = Button(self.ardControlFrame, text='Pump Water(P)', font=STATUS_FONT, command=self.togglePump, bg=OFF_COLOR, width=12, height=1) self.lickLabel = Label(self.ardControlFrame, text='LICK', bg=LICK_OFF_COLOR, font=LICK_FONT, width=10, height=1) self.lickCountLabel = Label(self.ardControlFrame, text='Lick Count :', bg=BACKGROUND_COLOR, font=LARGE_FONT) self.lickCountButton = Button(self.ardControlFrame, textvariable=self.lickCount, font=LARGE_FONT, bg=BACKGROUND_COLOR, command=lambda: self.lickCount.set(0)) self.soundCheckButton = Checkbutton(self.ardControlFrame, text='Lick Sound', variable=self.isSoundOn, bg=BACKGROUND_COLOR) self.ardControlFrameLabel.grid(row=0, columnspan=2, padx=40, pady=10) self.sendStringEntry.bind('<Return>', self.sendString) self.sendStringEntry.grid(row=1, column=0, padx=5, sticky=tk.E) self.sendStringEntry.bind('<Escape>', lambda x: self.master.focus()) self.sendStringButton.grid(row=1, column=1, padx=5, sticky=tk.W) self.rewardButton.grid(row=2, column=0, pady=10) self.pumpButton.grid(row=2, column=1, pady=10) self.lickLabel.grid(row=3, columnspan=2, pady=15) self.lickCountLabel.grid(row=4, column=0, sticky=tk.E) self.lickCountButton.grid(row=4, column=1, sticky=tk.W) self.soundCheckButton.grid(row=5, columnspan=2) #initFrame self.initFrameLabel = Label(self.initFrame, text="Session Configuration", font=LARGE_FONT, bg=HEADER_COLOR, fg='white', relief='solid', borderwidth=2, width=40) self.loadButton = Button(self.initFrame, text="Load Config(L)", font=STATUS_FONT, command=self.selectFile) self.sessionNameLabel = Label(self.initFrame, text="Session Name:", font=TEXT_FONT, bg=BACKGROUND_COLOR) self.sessionNameEntry = Entry(self.initFrame, font=TEXT_FONT) self.numOfTrialsLabel = Label(self.initFrame, text="Number of Trials:", font=TEXT_FONT, bg=BACKGROUND_COLOR) self.numOfTrialsEntry = Entry(self.initFrame, font=TEXT_FONT) self.numOfTrialsEntry.bind('<KeyRelease>', self.updateTime) self.numOfTrialsEntry.bind('<Escape>', lambda x: self.master.focus()) self.initFrameLabel.grid(row=0, columnspan=2, padx=40, pady=10) self.sessionNameLabel.grid(row=1, column=0, sticky=tk.E) self.sessionNameEntry.grid(row=1, column=1, sticky=tk.W) self.sessionNameEntry.bind('<Escape>', lambda x: self.master.focus()) self.numOfTrialsLabel.grid(row=2, column=0, sticky=tk.E) self.numOfTrialsEntry.grid(row=2, column=1, sticky=tk.W) self.loadButton.grid(row=3, columnspan=2, pady=10) #finalControlFrame self.finalControlFrameLabel = Label(self.finalControlFrame, text='Experiment Control', bg=HEADER_COLOR, font=LARGE_FONT, fg='white', relief='solid', bd=2, width=40) self.estTimeLabel = Label(self.finalControlFrame, textvariable=self.estTime, font=STATUS_FONT, bg=BACKGROUND_COLOR) self.startButton = Button(self.finalControlFrame, text="START EXPERIMENT", font='Helvetica 20 bold', command=self.startExperiment) self.finalControlFrameLabel.grid(padx=40, pady=10) self.estTimeLabel.grid(pady=10) self.startButton.grid(pady=15) #master self.titleLabel.pack(pady=5) self.master.pack(padx=20, pady=20) self.initFrame.grid(row=0, column=0) self.ardInitFrame.grid(row=1, column=0) self.finalControlFrame.grid(row=2, column=0, sticky='NSWE') self.argFrame.grid(row=0, column=1, rowspan=3, sticky='NSWE') for frame in [ self.master, self.initFrame, self.ardInitFrame, self.finalControlFrame, self.argFrame ]: frame.bind('r', self.deliverReward) frame.bind('p', self.togglePump) frame.bind('l', self.selectFile) frame.bind('R', self.deliverReward) frame.bind('P', self.togglePump) frame.bind('L', self.selectFile) frame.bind("<Button-1>", lambda e: self.master.focus_set()) self.updateTime() def run(self): self.master.mainloop() def selectFile(self, event=None): fileName = filedialog.askopenfilename() self.configFileName = fileName for widget in self.argFrame.winfo_children(): widget.destroy() self.argFrameLabel = Label(self.argFrame, text="Experiment Configuration: " + os.path.basename(fileName), font=LARGE_FONT, bg=HEADER_COLOR, fg='white', relief='solid', bd=2, width=40).grid(columnspan=2, padx=40, pady=10) try: with open(fileName) as f: self.args = json.load(f) except Exception as e: print(e) argToLen = lambda x: len(str(x)) maxArgNameLength = argToLen( max(self.args.keys(), key=lambda x: argToLen(x))) maxArgValueLength = argToLen( max(self.args.values(), key=lambda x: argToLen(x))) correctTrialDuration = self.args["Cue duration"] + \ self.args["Correct response visual duration"] + \ (self.args["Stimulus duration"] if self.args["Give reward without lick"] == 0 else 0) wrongTrialDuration = self.args["Cue duration"] +\ self.args["Stimulus duration"] + \ self.args["Wrong response flash duration"] + \ self.args["Wrong response rest duration"] self.trialDuration = correctTrialDuration * self.args[ "A image probability"] + wrongTrialDuration * ( 1 - self.args["A image probability"]) for i, (argName, value) in enumerate( sorted(self.args.items(), key=lambda item: item[0])): lName = Label(self.argFrame, text=str(argName) + " :", font='Helvetica 12 bold', bg=BACKGROUND_COLOR).grid(row=i + 3, column=0, sticky=tk.E) lValue = Label(self.argFrame, text=str(value), bg=BACKGROUND_COLOR).grid( row=i + 3, column=1, sticky=tk.W, ) self.updateTime() self.isConfigLoaded = True def connect(self): try: comport = self.comEntry.get() baudrate = self.baudrateEntry.get() if comport == "" or baudrate == "": raise Exception("Please fill in all values") baudrate = int(baudrate) self.ard = MouseArduino(comport, baudrate) self.ard.start() self.ardInitFrame.destroy() self.ardUpdater.start() self.ardControlFrame.grid(row=1, column=0) self.isArdConnected = True except Exception as e: messagebox.showerror( "Error", "Could not connect to Arduino. Make sure port is correct or other program isn't grabbing the port :" + str(e)) def deliverReward(self, event=None): self.ard.deliverReward() def sendString(self, event=None): self.ard.write(self.sendStringEntry.get()) self.sendStringEntry.delete(0, 'end') def updateVariable(self): while not self.stopUpdating.is_set(): if self.ard.newMsg.wait(1): while not self.ard.msgQueue.empty(): self.ard.newMsg.clear() msg = self.ard.msgQueue.get() print(msg) args = Utilities.parse(msg) arg = args[1].strip() if arg == 'LK': self.lickCount.set(self.lickCount.get() + 1) self.lickLabel.configure(bg=ON_COLOR) if self.isSoundOn.get(): Sound.cue(0.05) time.sleep(0.2) self.lickLabel.configure(bg=LICK_OFF_COLOR) elif arg == 'startpump': self.pumpButton.configure(bg=ON_COLOR) self.isPumpOn = True elif arg == 'stoppump': self.pumpButton.configure(bg=OFF_COLOR) self.isPumpOn = False def togglePump(self, event=None): if self.isPumpOn: self.ard.stopPump() else: self.ard.startPump() def updateTime(self, event=None): numOfTrials = self.numOfTrialsEntry.get() try: totalDuration = self.trialDuration * int(numOfTrials) tmin = totalDuration // 60 tsec = totalDuration % 60 timeStr = "{:.0f} Min {:.0f} Sec".format(tmin, tsec) except Exception as e: timeStr = "" print(e) self.estTime.set("Estimated duration: {:>10}".format(timeStr)) def startExperiment(self): if not self.isConfigLoaded: messagebox.showerror("Error", "Please load configuration file") elif not self.isArdConnected: messagebox.showerror("Error", "Please connect Arduino") else: try: sessionName = self.sessionNameEntry.get() numOfTrials = self.numOfTrialsEntry.get() if sessionName == "" or numOfTrials == "": raise Exception("Please fill in all values") numOfTrials = int(numOfTrials) self.experiment = ABSingleLick(self.ard) self.experiment.startExperiment(sessionName, numOfTrials, self.configFileName) except Exception as e: messagebox.showerror("Error", e)
class Spectrogram(Module): def __init__(self, app): info(' - initializing module: Spectrogram') self.app = app self.frame = Frame(self.app.BOTTOM) self.frame.grid(row=0, column=1, pady=(self.app.pady * 2, self.app.pady / 2)) self.axis_frame = Frame(self.app.BOTTOM) self.axis_frame.grid(row=0, column=0, sticky='e', pady=(self.app.pady * 2, self.app.pady / 2)) self.canvas_width = self.app.TextGrid.canvas_width self.canvas_height = 106 self.canvas = Canvas(self.frame, width=self.canvas_width, height=self.canvas_height, background='gray', highlightthickness=0) self.spectrogram = None self.spec_freq_max = DoubleVar() self.wl = DoubleVar() self.dyn_range = DoubleVar() self.clicktime = -1 self.specClick = False self.oldSelected = None self.doDefaults() #make spinboxes & buttons for spectrogram specs self.spinwin = Frame(self.axis_frame) #spinboxes axis_ceil_box = Spinbox(self.spinwin, textvariable=self.spec_freq_max, command=self.drawSpectrogram, width=7, increment=100, from_=0, to_=100000) axis_ceil_box.bind('<Return>', self.drawSpectrogram) axis_ceil_box.bind('<Escape>', lambda ev: self.spinwin.focus()) wl_box = Spinbox(self.spinwin, textvariable=self.wl, command=self.drawSpectrogram, width=7, increment=0.0005, from_=0, to_=1) wl_box.bind('<Return>', self.drawSpectrogram) wl_box.bind('<Escape>', lambda ev: self.spinwin.focus()) dyn_range_box = Spinbox(self.spinwin, textvariable=self.dyn_range, command=self.drawSpectrogram, width=7, increment=10, from_=0, to_=10000) dyn_range_box.bind('<Return>', self.drawSpectrogram) dyn_range_box.bind('<Escape>', lambda ev: self.spinwin.focus()) #buttons default_btn = Button(self.spinwin, text='Standards', command=self.restoreDefaults, takefocus=0) apply_btn = Button(self.spinwin, text='Apply', command=self.drawSpectrogram, takefocus=0) # self.axis_frame.create_window(wwidth,self.canvas_height, window=self.spinwin, anchor='ne') #grid spinboxes & buttons on subframe axis_ceil_box.grid(row=0, columnspan=2, sticky='ne') wl_box.grid(row=1, columnspan=2, sticky='ne') dyn_range_box.grid(row=2, columnspan=2, sticky='ne') default_btn.grid(row=3) apply_btn.grid(row=3, column=1) self.grid() self.canvas.bind('<Button-1>', self.jumpToFrame) # self.canvas.bind('<Shift-Button-1>', self.jumpToFrame) def doDefaults(self): self.spec_freq_max.set(5000.0) self.wl.set(0.005) self.dyn_range.set(90) def restoreDefaults(self): self.doDefaults() self.drawSpectrogram() def update(self): ''' Removes and redraws lines on top of Spectrogram corresponding to selected interval(s) ''' self.canvas.delete('line') self.drawInterval() def reset(self): self.drawSpectrogram() self.drawInterval() def drawSpectrogram(self, event=None): ''' Extracts spectrogram data from sound, and draws it to canvas ''' if not LIBS_INSTALLED: return if self.app.Audio.current: sound = parselmouth.Sound(self.app.Audio.current) ts_fac = decimal.Decimal(10000.0) wl = decimal.Decimal(self.wl.get()) start_time = decimal.Decimal(self.app.TextGrid.start) end_time = decimal.Decimal(self.app.TextGrid.end) # the spectrogram is for the audio file, so it makes sense # to get the duration from the audio file and not from the # textgrid -JNW if start_time == end_time: end_time = decimal.Decimal(sound.get_total_duration()) duration = end_time - start_time #duration = decimal.Decimal(sound.get_total_duration()) # in case there isn't a TextGrid or there's some other issue: -JNW self.ts = duration / ts_fac # the amount taken off in spectrogram creation seems to be # ( 2 * ts * floor( wl / ts ) ) + ( duration % ts ) # but we've defined ts as duration / 10000, so duration % ts = 0 # so the amount to increase the length by is ts * floor( wl / ts ) # at either end - D.S. extra = self.ts * math.floor(wl / self.ts) start_time = max(0, start_time - extra) end_time = min(end_time + extra, sound.get_total_duration()) sound_clip = sound.extract_part(from_time=start_time, to_time=end_time) spec = sound_clip.to_spectrogram( window_length=wl, time_step=self.ts, maximum_frequency=self.spec_freq_max.get()) self.spectrogram = 10 * np.log10(np.flip(spec.values, 0)) # self.spectrogram += self.spectrogram.min() # self.spectrogram *= (60.0 / self.spectrogram.max()) mx = self.spectrogram.max() dyn = self.dyn_range.get() # debug(self.spectrogram.min(), self.spectrogram.max()) self.spectrogram = self.spectrogram.clip(mx - dyn, mx) - mx # debug(self.spectrogram.min(), self.spectrogram.max()) self.spectrogram *= (-255.0 / dyn) # self.spectrogram += 60 # debug(self.spectrogram.min(), self.spectrogram.max()) img = PIL.Image.fromarray(self.spectrogram) if img.mode != 'RGB': img = img.convert('RGB') # contrast = ImageEnhance.Contrast(img) # img = contrast.enhance(5) # self.canvas_height = img.height img = img.resize((self.canvas_width, self.canvas_height)) photo_img = ImageTk.PhotoImage(img) self.canvas.config(height=self.canvas_height) # self.canvas.create_image(0,0, anchor='nw', image=photo_img) # self.canvas.create_image(self.canvas_width/2,self.canvas_height/2, image=photo_img) if self.app.TextGrid.selectedItem: tags = self.app.TextGrid.selectedItem[0].gettags( self.app.TextGrid.selectedItem[1]) self.canvas.delete('all') img = self.canvas.create_image(self.canvas_width, self.canvas_height, anchor='se', image=photo_img) self.img = photo_img #pass on selected-ness if self.app.TextGrid.selectedItem: if self.app.TextGrid.selectedItem[0] == self.canvas: self.app.TextGrid.selectedItem = (self.canvas, img) #pass on tags for tag in tags: self.canvas.addtag_all(tag) def drawInterval(self): ''' Adapted with permission from https://courses.engr.illinois.edu/ece590sip/sp2018/spectrograms1_wideband_narrowband.html by Mark Hasegawa-Johnson ''' if self.app.TextGrid.selectedItem: widg = self.app.TextGrid.selectedItem[0] itm = self.app.TextGrid.selectedItem[1] if widg in self.app.TextGrid.tier_pairs: #if widg is label itvl_canvas = self.app.TextGrid.tier_pairs[widg] for i in itvl_canvas.find_withtag('line'): loc = itvl_canvas.coords(i)[0] self.canvas.create_line(loc, 0, loc, self.canvas_height, tags='line', fill='blue') elif widg in self.app.TextGrid.tier_pairs.values( ): #if widg is textgrid canvas if itm - 1 in widg.find_all(): l_loc = widg.coords(itm - 1)[0] self.canvas.create_line(l_loc, 0, l_loc, self.canvas_height, tags='line', fill='blue') if itm + 1 in widg.find_all(): r_loc = widg.coords(itm + 1)[0] self.canvas.create_line(r_loc, 0, r_loc, self.canvas_height, tags='line', fill='blue') elif widg == self.canvas: l_time, r_time = self.app.TextGrid.getMinMaxTime() l_loc = self.timeToX(float(l_time)) r_loc = self.timeToX(float(r_time)) self.canvas.create_line(l_loc, 0, l_loc, self.canvas_height, tags='line', fill='blue') self.canvas.create_line(r_loc, 0, r_loc, self.canvas_height, tags='line', fill='blue') #draw selected frame if self.app.TextGrid.firstFrame <= self.app.frame <= self.app.TextGrid.lastFrame: xcoord = self.app.TextGrid.frames_canvas.coords( self.app.TextGrid.highlighted_frame)[0] self.canvas.create_line(xcoord, 0, xcoord, self.canvas_height, tags='line', fill='red') #draw line where user last clicked on spectrogram if self.clicktime != -1 and self.specClick == False: x = self.timeToX(self.clicktime) self.canvas.create_line(x, 0, x, self.canvas_height, tags='line', fill='green') def jumpToFrame(self, event): ''' ''' #restore textgrid selected interval between clicks if not self.app.TextGrid.selectedItem: key = next(iter(self.app.TextGrid.tier_pairs)) wdg = self.app.TextGrid.tier_pairs[key] self.app.TextGrid.selectedItem = (wdg, wdg.find_all()[0]) self.app.TextGrid.setSelectedIntvlFrames( self.app.TextGrid.selectedItem) if self.app.TextGrid.selectedItem[0] == self.canvas: self.app.TextGrid.selectedItem = self.oldSelected self.app.TextGrid.setSelectedIntvlFrames( self.app.TextGrid.selectedItem) #prevents wiping of canvases because of mouse click # self.app.resized = False # draw line at click location x = self.canvas.canvasx(event.x) self.clicktime = self.xToTime(x) #jump to new frame frame = self.app.TextGrid.my_find_closest( self.app.TextGrid.frames_canvas, self.canvas.canvasx(event.x)) framenum = self.app.TextGrid.frames_canvas.gettags(frame)[0][5:] self.app.frame = int(framenum) self.app.framesUpdate() #remember which interval was selected before specgram click if event.state == 1: self.oldSelected = self.app.TextGrid.selectedItem #for selecting & zooming interval (w/ shift) self.specClick = True def xToTime(self, x): ''' converts from a x coordinate (relative to the canvas) to the timestamp at that coordinate''' return (x * float(self.app.TextGrid.end - self.app.TextGrid.start) / self.canvas_width) + float(self.app.TextGrid.start) def timeToX(self, time): ''' converts from a time to the x coordinate on a canvas representing that time''' return self.canvas_width * (time - float( self.app.TextGrid.start)) / float(self.app.TextGrid.end - self.app.TextGrid.start) def grid(self): ''' Put tkinter items on app ''' self.canvas.grid(row=0, column=0, sticky='news') self.spinwin.grid(row=0, column=0, sticky='ne') # self.axis_canvas.grid(row=0,column=0,sticky='se') def grid_remove(self): self.canvas.grid_remove() self.spinwin.grid_remove()
class NumPad(object): """ dialog for number entry """ def __init__(self, master, base, init): """ initialize dialog """ self.master=master self.dialog = Toplevel(master) # set [X] behavior self.dialog.protocol("WM_DELETE_WINDOW", self.Cancel) # create dialog frame self.frame = Frame(self.dialog) self.frame.focus() self.base = base self.Position(self.dialog) # member variables self.init = init self.symList = [] # initial method calls self.Make_symList() self.Add_Widgets() def Position(widget, dialog, pos=(0.5, 0.5)): """ determine where on screen dialog will open parameters: widget - self if called by __init__, None or some widget otherwise dialog - Toplevel to be positioned pos - tuple containing relative screen position of dialog center - default: (0,5,0.5) called in __init__ method, so does not need to be expicitlly called when creating dialog. However, Position can be used to position the main application window, too. """ # hide dialog dialog.withdraw() # clean up dialog tasks dialog.update_idletasks() # get dialog dimensions # get dialog gemometry ("widthxheight+offx+offy") temp_str = dialog.geometry() # find index of "x" i = temp_str.index("x") # width is the geometry string before the "x" (divide by 2 to get center) d_x = int(temp_str[:i])/2 # remove the width and "x" from geometry string temp_str = temp_str[i+1:] # find the index of the first "+" i = temp_str.index("+") # height is the geometry string before the "+" (divide by 2 to get center) d_y = int(temp_str[:i])/2 # get screen dimensions s_x = dialog.winfo_screenwidth() s_y = dialog.winfo_screenheight() # find offset to position dialog x = s_x*pos[0] - d_x y = s_y*pos[1] - d_y # position dialog dialog.geometry("+%d+%d" % (x, y)) # show dialog dialog.deiconify() def Cancel(self): """ set rtn to initial value and close the dialog window """ self.rtn = self.init self.dialog.destroy() def Close(self): """ set rtn to display and close the dialog window """ self.rtn = self.disp["text"] self.dialog.destroy() def Make_symList(self): """ create list of symbols from base """ for i in range(self.base): if i < 10: # use 0-9 self.symList.append(str(i) ) elif i<37: # use letters for values over 9 (up to 36) self.symList.append(chr(55+i)) def Add_Widgets(self): """ add widgets to frame """ # display the value self.disp = Label(self.frame, text="", width=15 ) self.disp.grid(columnspan=4) # add value buttons for val in range (self.base): if self.base == 2: span = 2 else: span = 1 Button(self.frame, text=self.symList[val], width="3", command=partial(self.Press, self.symList[val] ) ).grid(column=val%4*span, row=int(val/4)+1, columnspan=span) # add backspace button Button(self.frame, text="←", command=self.Bksp ).grid(columnspan=4) # add clear button Button(self.frame, text="clear", command=self.Clear ).grid(columnspan=4) # add nib button if self.base == 2: Button(self.frame, text="new nibble", command=partial(self.Press, "n") ).grid(columnspan=4) #add accept button Button(self.frame, text="accept", command=self.Close ).grid(columnspan=4) def Press(self, sym): """ append character to the display parameter: sym - the character to be appended """ temp = self.disp["text"] # base 2 is a special case if self.base != 2: # append character to display, but don't allow leading zeros if not((temp == "") & (sym == "0")): self.disp["text"] = temp+sym else: # split string into nibbles nibList = temp.split(" ") # nib button not pressed if sym != "n": # get the last nibble tmp2 = nibList.pop() # strip leading zeros i = tmp2.find("1") if i == -1: tmp2 = "" else: tmp2 = tmp2[i:] # if the last nibble is still 4 bits, put it back and start fresh if len(tmp2) == 4: nibList.append(tmp2) tmp2 = "" # append character tmp2 += sym # pad nibble with leadin zeros tmp2 = tmp2.zfill(4) # nib button was pressed else: if (nibList[0] != "0000") and (nibList[0] != ""): # start a new nibble tmp2 = "0000" else: tmp2 = None # create string of nibbles separated by space temp = "" for nib in nibList: temp += nib + " " # append last nibble if tmp2 != None: temp += tmp2 else: temp = temp[:-1] # set display to string of nibbles self.disp["text"] = temp # widen dialog if needed self.Resize() def Bksp(self): """ delete the last character on the display """ if self.disp["text"] != "": # binary is a special case if self.base != 2: self.disp["text"] = self.disp["text"][:-1] else: # split string into nibbles nibList = self.disp["text"].split(" ") # remove nibble if zero if nibList[-1] == "0000": nibList = nibList[:-1] # otherwise remove last bit of nibble else: nibList[-1] = "0" + nibList[-1][:-1] # set display to nibbles temp = "" for nib in nibList: temp += nib + " " self.disp["text"] = temp[:-1] def Clear(self): """ dclear the display """ self.disp["text"] = "" def Resize(self): """ Change frame width if needed """ # get height geo = self.dialog.geometry() y = int(geo[geo.find("x")+1:geo.find("+")]) # find needed width w = len(self.disp["text"]) if w > 15: self.disp["width"] = w x = self.disp["width"]*8 #change frame width self.dialog.geometry("%dx%d" % (x,y)) def Go(self): """ returnable method to be called in main program """ # make modal self.dialog.transient(self.master) self.dialog.grab_set() # show dialog frame self.frame.grid() # wait for dialog to close self.dialog.wait_window(self.dialog) # return value return self.rtn
class App(ThemedTk): ''' This class is neatly wraps all the functionality of our application. By itself, it's is responsible for handling command line input, navigating between files, navigating between frames, handling of some events, and coordinating other core functionality (which is handled by individual `modules`). Note: - when we change files, modules should execute MODULE.reset() methods - when we change frames, modules should execute MODULE.update() methods - modules that are responsible for managing their own widgets should have MODULE.grid() and MODULE.grid_remove() methods to wrap corresponding functionality for their widgets ''' def __init__(self): info('initializing UltraTrace') # do the normal Tk init stuff if util.get_platform() == 'Linux': try: info('Loading platform-specific enhancements for Linux') import xrp # pip3 install xparser from pathlib import Path Xresources = xrp.parse_file( os.path.join(str(Path.home()), '.Xresources')) if '*TtkTheme' in Xresources.resources: ttktheme = Xresources.resources['*TtkTheme'] info("Setting Linux Ttk theme to {}".format(ttktheme)) elif '*TkTheme' in Xresources.resources: ttktheme = Xresources.resources['*TkTheme'] info("Setting Linux Tk theme to {}".format(ttktheme)) else: ttktheme = "clam" # alt, clam, classic, default info("Falling back to default Linux Tk theme: {}".format( ttktheme)) super().__init__(theme=ttktheme) except Exception as e: error(e) super().__init__() else: super().__init__() self.title('UltraTrace') # check if we were passed a command line argument parser = argparse.ArgumentParser(prog='UltraTrace') parser.add_argument( 'path', help= 'path (unique to a participant) where subdirectories contain raw data', default=None, nargs='?') args = parser.parse_args() # initialize data module self.Data = modules.Metadata(self, args.path) # initialize the main app widgets self.setWidgetDefaults() self.buildWidgetSkeleton() # initialize other modules self.Control = modules.Control(self) self.Trace = modules.Trace(self) self.Dicom = modules.Dicom(self) self.Audio = modules.Playback(self) self.TextGrid = modules.TextGrid(self) self.Spectrogram = modules.Spectrogram(self) self.Search = modules.Search(self) info(' - loading widgets') self.filesUpdate() # self.framesUpdate() # self.TextGrid.startup() #NOTE why does modules.TextGrid have to reset a second time? Is there a more economical way to do this? # to deal with resize handler being called multiple times # in a single window resize self.isResizing = False self.oldwidth = self.winfo_width() self.after(300, self.afterstartup) def setWidgetDefaults(self): ''' Need to set up some defaults here before building Tk widgets (this is specifically true w/r/t the StringVars) ''' self.currentFID = 0 # file index w/in list of sorted files self.frame = 0 # current frame of dicom file self.isClicked = False # used in handling of canvas click events self.isDragging = False # used in handling of canvas click events # self.resized = False #for changing widgets after window resize self.selectBoxX = False self.selectBoxY = False # declare string variables self.currentFileSV = StringVar(self) self.frameSV = StringVar(self) # initialize string variables self.currentFileSV.set(self.Data.files[self.currentFID]) self.frameSV.set('1') def buildWidgetSkeleton(self): ''' Builds the basic skeleton of our app widgets. - items marked with (*) are built directly in this function - items marked with (~) are built by the individual modules # WARNING: out of date diagram .________________________________________. | ROOT | | .____________________________________. | | | TOP* | | | | ._______________. .______________. | | | | | LEFT* | | RIGHT* | | | | | | - file nav* | | - dicom~ | | | | | | - frame nav*| | | | | | | | - traces~ | | | | | | | | - undo~ | | | | | | | \_______________/ \______________/ | | | \____________________________________/ | | | | .____________________________________. | | | BOTTOM* | | | | - spectrogram~ | | | | - textgrid~ | | | \____________________________________/ | \________________________________________/ ''' # main Frame skeleton self.TOP = Frame(self) self.TOP.columnconfigure(1, weight=1, minsize=320) self.TOP.rowconfigure(0, weight=1, minsize=240) self.LEFT = Frame(self.TOP) # self.LEFT.rowconfigure(0,weight=1) # self.LEFT.columnconfigure(0,weight=1) self.RIGHT = Frame(self.TOP) self.RIGHT.rowconfigure(0, weight=1) self.RIGHT.columnconfigure(0, weight=1) self.BOTTOM = Frame(self) # self.BOTTOM.columnconfigure(0,weight=1) self.BOTTOM.columnconfigure(1, weight=1) # self.BOTTOM.rowconfigure(0,weight=1) # self.TOP.grid( row=0, column=0, sticky='nw') # self.LEFT.grid( row=0, sticky='n' ) # self.RIGHT.grid( row=0, column=1) # self.BOTTOM.grid( row=1, column=0, sticky='e') self.TOP.grid(row=0, column=0, sticky='nesw') self.LEFT.grid(row=0, sticky='nesw') self.RIGHT.grid(row=0, column=1, sticky='nesw') self.BOTTOM.grid(row=1, column=0, sticky='nesw') self.pady = 3 self.columnconfigure(0, weight=1) self.rowconfigure(0, weight=1) # navigate between all available filenames in this directory self.filesFrame = Frame(self.LEFT) #, pady=7) self.filesPrevBtn = Button(self.filesFrame, text='<', command=self.filesPrev, takefocus=0) self.filesJumpToMenu = OptionMenu(self.filesFrame, self.currentFileSV, *self.Data.files, command=self.filesJumpTo) self.filesNextBtn = Button(self.filesFrame, text='>', command=self.filesNext, takefocus=0) self.filesFrame.grid(row=1) self.filesPrevBtn.grid(row=1, column=0) self.filesJumpToMenu.grid(row=1, column=1) self.filesNextBtn.grid(row=1, column=2) Header(self.filesFrame, text="Choose a file:").grid(row=0, column=0, columnspan=3) # navigate between frames self.framesFrame = Frame(self.LEFT) #, pady=7) self.framesSubframe = Frame(self.framesFrame) self.framesPrevBtn = Button(self.framesSubframe, text='<', command=self.framesPrev, takefocus=0) self.framesEntryText = Entry(self.framesSubframe, width=5, textvariable=self.frameSV) self.framesEntryBtn = Button(self.framesSubframe, text='Go', command=self.framesJumpTo, takefocus=0) self.framesNextBtn = Button(self.framesSubframe, text='>', command=self.framesNext, takefocus=0) self.framesHeader = Header(self.framesFrame, text="Choose a frame:") self.framesFrame.grid(row=3) self.framesSubframe.grid(row=1) # non-module-specific bindings if util.get_platform() == 'Linux': self.bind('<Control-Left>', self.filesPrev) self.bind('<Control-Right>', self.filesNext) else: self.bind('<Option-Left>', self.filesPrev) self.bind('<Option-Right>', self.filesNext) self.bind('<Left>', self.framesPrev) self.bind('<Right>', self.framesNext) self.bind('<BackSpace>', self.onBackspace) self.bind('<Button-1>', self.getWinSize) self.bind('<ButtonRelease-1>', self.onRelease) self.bind('<Double-Button-1>', self.onDoubleClick) self.bind('<Escape>', self.onEscape) # self.count = 0 self.framesEntryText.bind('<Return>', self.unfocusAndJump) self.framesEntryText.bind('<Escape>', lambda ev: self.framesFrame.focus()) # force window to front self.lift() def lift(self): ''' Bring window to front (doesn't shift focus to window) ''' self.attributes('-topmost', 1) self.attributes('-topmost', 0) def afterstartup(self): ''' ''' self.bind('<Configure>', self.alignBottomLeftWrapper) self.alignBottomLeft() self.getWinSize() self.alignBottomRight(self.oldwidth - self.leftwidth) if self.Dicom.zframe.image: self.Dicom.zframe.setImage(self.Dicom.zframe.image) def alignBottomLeftWrapper(self, event=None): if self.isResizing: return self.isResizing = True self.after(100, lambda: self.alignBottomLeft(event)) def alignBottomLeft(self, event=None): ''' Makes the length of the canvases on the lower left the same length as the pane of controls in self.LEFT ''' self.leftwidth = self.LEFT.winfo_width() for t in range(len(self.TextGrid.TkWidgets)): tierWidgets = self.TextGrid.TkWidgets[t] if 'frames' in tierWidgets: tierWidgets['frames-label'].config(width=self.leftwidth) tierWidgets['frames-label'].coords( 'all', (self.leftwidth, tierWidgets['frames-label'].coords(1)[1])) if 'canvas' in tierWidgets: tierWidgets['canvas-label'].config(width=self.leftwidth) tierWidgets['canvas-label'].coords( 'all', (self.leftwidth, tierWidgets['canvas-label'].coords(1)[1])) if event == None or event.widget == self: self.alignBottomRight(self.winfo_width() - self.leftwidth) if self.Dicom.zframe.image: self.Dicom.zframe.setImage(self.Dicom.zframe.image) self.isResizing = False def alignBottomRight(self, x): ''' ''' self.Spectrogram.canvas_width = x self.Spectrogram.canvas.config(width=x) self.TextGrid.canvas_width = x for t in range(len(self.TextGrid.TkWidgets)): tierWidgets = self.TextGrid.TkWidgets[t] canvas = None if 'frames' in tierWidgets: tierWidgets['frames'].config(width=x) elif 'canvas' in tierWidgets: tierWidgets['canvas'].config(width=x) if 'times' in tierWidgets: tierWidgets['times'].config(width=x) tierWidgets['times'].coords( 2, (x, tierWidgets['times'].coords(2)[1])) #move end time tierWidgets['times'].coords( 3, (x / 2, tierWidgets['times'].coords(3)[1])) self.TextGrid.fillCanvases() #calls Spectrogram.reset # def onWindowResize(self, event): # ''' # Handle moving or resizing the app window # ''' # self.alignBottomLeft() # # self.resized=True def getWinSize(self, event=None): self.oldwidth = self.winfo_width() def onDoubleClick(self, event): ''' select only crosshairs that's double clicked''' nearby = self.Trace.getNearClickAllTraces((event.x, event.y)) if nearby != None: self.Trace.unselectAll() self.Trace.select(nearby) def onClickZoom(self, event): ''' Handle clicking within the zoomframe canvas ''' if self.Dicom.isLoaded(): self.click = (event.x, event.y) self.isDragging = False # get nearby crosshairs from this trace nearby = self.Trace.getNearClickAllTraces(self.click) # if we didn't click near anything ... if nearby == None: self.Trace.unselectAll() if event.state != 1: # unselect crosshairs self.isClicked = True ch = self.Trace.add(*self.click) self.Control.push({'type': 'add', 'chs': [ch]}) else: self.selectBoxX = self.Dicom.zframe.canvas.canvasx(event.x) self.selectBoxY = self.Dicom.zframe.canvas.canvasy(event.y) return # NOTE: only get here if we clicked near something # if holding option key, unselect the guy we clicked on # if event.state == 16: # if holding shift key, and ch is selected, unselect it if event.state == 1 and nearby in self.Trace.selected: nearby.unselect() if nearby in self.Trace.selected: self.Trace.selected.remove(nearby) # otherwise, if not selected, add it to our selection elif nearby not in self.Trace.selected: if event.state != 1: #and nearby.isSelected == False: self.Trace.unselectAll() # add this guy to our current selection self.Trace.select(nearby) #through all of these operations, if clicked ch is selected, is ready to be dragged if nearby in self.Trace.selected: # set dragging variables self.isDragging = True self.dragClick = self.click def onReleaseZoom(self, event): ''' Handle releasing a click within the zoomframe canvas ''' if self.Dicom.isLoaded(): # select multiple crosshairs if self.selectBoxX != False: canvas = self.Dicom.zframe.canvas x1 = self.selectBoxX x2 = canvas.canvasx(event.x) y1 = self.selectBoxY y2 = canvas.canvasy(event.y) self.selectBoxX = False self.selectBoxY = False trace = self.Trace.getCurrentTraceName() coords = [] x1True = None if trace in self.Trace.crosshairs: for ch in self.Trace.crosshairs[trace]: if x1True == None: x1True, y1True = ch.transformCoordsToTrue(x1, y1) x2True, y2True = ch.transformCoordsToTrue(x2, y2) if ch.isVisible: x, y = ch.getTrueCoords() if min(x1True, x2True) < x < max(x1True, x2True) and min( y1True, y2True) < y < max( y1True, y2True): self.Trace.select(ch) self.isDragging = False self.isClicked = False self.Trace.write() def onReleaseSpec(self, event): '''shift + release zooms textgrid & spectrogram to selected interval''' if self.Spectrogram.specClick == True: # if event.state==257: canvas = self.Spectrogram.canvas t1 = self.Spectrogram.clicktime t2 = self.Spectrogram.xToTime(canvas.canvasx(event.x)) # self.TextGrid.start = decimal.Decimal(min(t1,t2)) # self.TextGrid.end = decimal.Decimal(max(t1,t2)) # for itm in canvas.find_all()[0]: # for tag in canvas.gettags(itm): #canvas.dtag() does not seem to work with one argument if max(t1, t2) - min( t1, t2 ) > self.Spectrogram.ts: #if selected area is larger than one strip of Spectrogram #gets rid of previous tags for tag in canvas.gettags(canvas.find_all()[0]): canvas.dtag(canvas.find_all()[0], tag) a = self.Spectrogram.timeToX(self.Spectrogram.clicktime) b = event.x x1 = min(a, b) x2 = max(a, b) #find all frames within range, and add them as tags frame_i = self.TextGrid.frames_canvas.find_all()[0] current_loc = self.TextGrid.frames_canvas.coords(frame_i)[0] while current_loc < x2: if current_loc > x1: tag = self.TextGrid.frames_canvas.gettags(frame_i)[0] canvas.addtag_all(tag) frame_i += 1 current_loc = self.TextGrid.frames_canvas.coords( frame_i)[0] canvas.addtag_all('minTime' + str(self.Spectrogram.xToTime(x1))) canvas.addtag_all('maxTime' + str(self.Spectrogram.xToTime(x2))) self.TextGrid.selectedItem = (canvas, canvas.find_all()[0]) self.TextGrid.setSelectedIntvlFrames( self.TextGrid.selectedItem) # self.TextGrid.paintCanvases() # self.Spectrogram.drawInterval(l_loc=x1,r_loc=x2) # debug(canvas.gettags('all')) # specgram = self.Spectrogram.canvas.find_all()[0] # self.TextGrid.fillCanvases() self.TextGrid.genFrameList(widg=canvas, x_loc=x2, SI=True) self.Spectrogram.specClick = False self.Spectrogram.clicktime = -1 def onRelease(self, event): ''' ''' #runs if click happened on specific canvases self.onReleaseZoom(event) self.onReleaseSpec(event) #runs if window resized # if self.resized == True and self.Dicom.zframe.shown == True: #shouldn't trigger when frame not displayed if self.winfo_width( ) != self.oldwidth and self.Dicom.zframe.shown == True: #shouldn't trigger when frame not displayed # self.resized = False #resize dicom image png_loc = self.Data.getPreprocessedDicom(self.frame) image = PIL.Image.open(png_loc) self.Dicom.zframe.setImage(image) # x = self.Dicom.zframe.width x = self.winfo_width() - self.LEFT.winfo_width() # y = self.Dicom.zframe.height #resize TextGrid tiers and spectrogram self.alignBottomRight(x) #move Traces self.Trace.move() #save layout ot geometry manager geometry = self.geometry() self.Data.setTopLevel('geometry', geometry) def onMotion(self, event): ''' Handle mouse movement within the zoomframe canvas ''' if self.Dicom.isLoaded(): if self.isDragging: # dragging selection thisClick = (event.x, event.y) selected = list(self.Trace.selected) coords = [] # move all currently selected crosshairs for sch in selected: # keep their relative distance constant center = (sch.x, sch.y ) # canvas coordinates not true coordinates newX = event.x + center[0] - self.dragClick[0] newY = event.y + center[1] - self.dragClick[1] sch.dragTo((newX, newY)) coords.append(center) self.dragClick = thisClick self.Control.push({ 'type': 'move', 'chs': selected, 'coords': coords }) elif self.isClicked: # no selection, mouse clicked lastClick = self.click thisClick = (event.x, event.y) # enforce minimum distance b/w new crosshairs dx = abs(thisClick[0] - lastClick[0]) / self.Dicom.zframe.imgscale dy = abs(thisClick[1] - lastClick[1]) / self.Dicom.zframe.imgscale if dx > util.CROSSHAIR_DRAG_BUFFER or dy > util.CROSSHAIR_DRAG_BUFFER: self.click = thisClick ch = self.Trace.add(*self.click) self.Control.push({'type': 'add', 'chs': [ch]}) def onEscape(self, event): ''' Handle <Esc> key : empties the current selection ''' self.isDragging = False self.isClicked = False self.Trace.unselectAll() def onBackspace(self, event): ''' Handle <Backspace> key : removes current selection ''' for sch in self.Trace.selected: self.Trace.remove(sch) self.Control.push({'type': 'delete', 'chs': self.Trace.selected}) self.Trace.unselectAll() def filesUpdate(self): ''' Changes to be executed every time we change files ''' # update variables self.currentFileSV.set(self.Data.files[self.currentFID]) self.frame = 1 self.frames = 1 # reset modules self.Control.reset() self.Trace.reset() self.Dicom.reset( ) # need this after Trace.reset() #NOTE is this still true? self.Audio.reset() self.TextGrid.reset() self.Spectrogram.reset() # check if we can pan left/right self.filesPrevBtn['state'] = 'disabled' if self.Data.getFileLevel( '_prev') == None else 'normal' self.filesNextBtn['state'] = 'disabled' if self.Data.getFileLevel( '_next') == None else 'normal' #load first frame self.framesUpdate() def filesPrev(self, event=None): ''' controls self.filesPrevBtn for panning between available files ''' if self.Data.getFileLevel('_prev') != None: # change the index of the current file self.currentFID -= 1 # update self.filesUpdate() def filesNext(self, event=None): ''' controls self.filesNextBtn for panning between available files ''' if self.Data.getFileLevel('_next') != None: # change the index of the current file self.currentFID += 1 # update self.filesUpdate() def filesJumpTo(self, choice): ''' jump directly to an available file (from the OptionMenu widget) ''' self.currentFID = self.Data.files.index(choice) self.filesUpdate() def framesUpdate(self): ''' Changes to be executed every time we change frames ''' # frameTier = self.TextGrid.TextGrid.getFirst(self.TextGrid.frameTierName) # if # update variables self.frameSV.set(str(self.frame)) # update modules self.Control.update() self.Dicom.update() self.Trace.update() self.Audio.update() self.TextGrid.update() self.Spectrogram.update() # check if we can pan left/right self.framesPrevBtn[ 'state'] = 'disabled' if self.frame == self.TextGrid.startFrame else 'normal' self.framesNextBtn[ 'state'] = 'disabled' if self.frame == self.TextGrid.endFrame else 'normal' def framesPrev(self, event=None): ''' controls self.framesPrevBtn for panning between frames ''' # if self.Dicom.isLoaded and self.frame > self.TextGrid.startFrame: if isinstance(self.focus_get(), (Entry, Spinbox)): return if self.frame > self.TextGrid.startFrame: self.frame -= 1 # if len(self.TextGrid.selectedIntvlFrames) != 0: # while str(self.frame) not in self.TextGrid.selectedIntvlFrames or self.frame > self.TextGrid.last_frame: # if self.frame <= int(self.TextGrid.selectedIntvlFrames[0]): # self.frame = int(self.TextGrid.selectedIntvlFrames[0]) # break # self.frame -= 1 self.framesUpdate() def framesNext(self, event=None): ''' controls self.framesNextBtn for panning between frames ''' # if self.Dicom.isLoaded and self.frame < self.TextGrid.endFrame: if isinstance(self.focus_get(), (Entry, Spinbox)): return if self.frame < self.TextGrid.endFrame: self.frame += 1 # if len(self.TextGrid.selectedIntvlFrames) != 0: # while str(self.frame) not in self.TextGrid.selectedIntvlFrames or self.frame < self.TextGrid.first_frame: # if self.frame >= int(self.TextGrid.selectedIntvlFrames[-1]): # self.frame = int(self.TextGrid.selectedIntvlFrames[-1]) # break # self.frame += 1 self.framesUpdate() def unfocusAndJump(self, event): self.framesJumpTo() self.framesFrame.focus() def framesJumpTo(self): ''' jump directly to a frame (from the Entry widget) ''' try: choice = int(self.frameSV.get()) if choice < 1: self.frame = 1 elif choice > self.frames: self.frame = self.frames else: self.frame = choice self.framesUpdate() except ValueError: error('Please enter an integer!')