Beispiel #1
0
    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)
Beispiel #2
0
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.")
Beispiel #4
0
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)
Beispiel #5
0
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()
Beispiel #6
0
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
Beispiel #7
0
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!')