Пример #1
0
    def attr_list_entry_callback(self, _event, attrib, entry_box: ttk.Entry,
                                 selected_widget):
        if selected_widget is not None:
            values: str = entry_box.get()
            values.strip(' ')
            if values.startswith('[') and values.endswith(']'):
                values = values[1:-1].strip(' ')
                value_list = []
                while len(values) > 0:
                    if values[0] == "'":
                        next_value = values[1:].split("'")[0]
                        value_list.append(next_value)
                        values = values[(2 + len(next_value)):].strip(' ')
                        if values.startswith(','):
                            values = values[1:].strip(' ')
                    else:
                        entry_box.delete(0, tkinter.END)
                        entry_box.insert(0,
                                         str(getattr(selected_widget, attrib)))
                        raise Exception(
                            'entry should be a list of str, comma separated')

            else:
                value_list = [values]
            return_value = self.pyted_core.update_widget_attribute(
                selected_widget, attrib, value_list)
            # self.update_attr_frame()
            if selected_widget.tk_name is not None:
                self.handles.place_selected_widget_handles(
                    selected_widget.tk_name)
            if return_value is not None:
                messagebox.showwarning(
                    'Renaming problem',
                    'Name already exists for another widget and Name not changed'
                )
Пример #2
0
class Content(Frame):
    def __init__(self, master):
        super().__init__(master)
        Label(self, text='Name:').grid(row=0, column=0, padx=5, sticky=SW)
        Label(self, text='Email:').grid(row=0, column=1, padx=5, sticky=SW)
        Label(self, text='Comments:').grid(row=2, column=0, padx=5, sticky=SW)
        self.entry_name = Entry(self, width=24, font=INPUT_FONT)
        self.entry_name.grid(row=1, column=0, padx=5)
        self.entry_email = Entry(self, width=24, font=INPUT_FONT)
        self.entry_email.grid(row=1, column=1, padx=5)
        self.text_comments = Text(self, width=50, height=10, font=INPUT_FONT)
        self.text_comments.grid(row=3, column=0, columnspan=2, padx=5)
        Button(self, text='Submit', command=self.submit) \
            .grid(row=4, column=0, padx=5, pady=5, sticky=E)
        Button(self, text='Clear', command=self.clear) \
            .grid(row=4, column=1, padx=5, pady=5, sticky=W)

    def submit(self):
        print(f'Name: {self.entry_name.get()}')
        print(f'Email: {self.entry_email.get()}')
        print(f'Comments: {self.text_comments.get(1.0, END)}')
        self.clear()
        messagebox.showinfo(title='Explore California Feedback',
                            message='Comments Submitted!')

    def clear(self):
        self.entry_name.delete(0, END)
        self.entry_email.delete(0, END)
        self.text_comments.delete(1.0, END)
Пример #3
0
 def set_date(this, calendar: Calendar, enty: Entry,
              frame: tkinter.Toplevel):
     enty.config(state="normal")
     enty.delete(0, "end")
     enty.insert(0, calendar.selection_get())
     enty.config(state="disabled")
     frame.destroy()
Пример #4
0
 def apagar_caracter_invalido(evento_char, ref_entry: ttk.Entry, caracteres_inválidos: str):
     """ Método estático para apagar os caracteres inseridos em um Entry no momento da digitação(evento Release).
     Pode ser usado sem instanciação.
     :param evento_char: event.char do evento.
     :param ref_entry: Referência do componente Entry.
     :param caracteres_inválidos: string de caracteres que deseja-se que sejam apagados.
     :return: none. """
     if evento_char in caracteres_inválidos:
         ref_entry.delete(len(ref_entry.get()) - 1, 'end')
Пример #5
0
def select_file(entry: ttk.Entry, title: str, extension: str) -> None:

    window = tk.Toplevel()
    window.withdraw()

    path = tkinter.filedialog.askopenfilename(title=title, filetypes=(("{0} files".format(extension),"*.{0}".format(extension)),("all files","*.*")))

    window.destroy()
    entry.delete(0, tk.END)
    entry.insert(0, path)
    return
Пример #6
0
def mac_auto(is_auto: tk.BooleanVar, entry: ttk.Entry):
    logging.info("mac auto clicked")
    if is_auto.get():
        logging.info("disabling mac")
        entry.delete(0, tk.END)
        entry.insert(tk.END, "")
        entry.config(state=tk.DISABLED)
    else:
        entry.delete(0, tk.END)
        entry.insert(tk.END, "00:00:00:00:00:00")
        entry.config(state=tk.NORMAL)
Пример #7
0
class EntryOptionsWindow:
    def __init__(self, ls: str, tk: Tk, select_path=False) -> None:
        self.select_path = select_path
        self.List = ls
        self.Tk = tk
        self.Root = Toplevel(self.Tk)
        self.Root.withdraw()
        self.Frame = Frame(self.Root)
        self.Box = Listbox(self.Frame, selectmode='extended', width=54, height=24)
        for i in globals()[self.List]:
            self.Box.insert(END, i)
        self.Scroll = Scrollbar(self.Frame, command=self.Box.yview)
        self.Entry = Entry(self.Frame)
        self.ButtonAdd = Button(self.Frame, text='Добавить', command=self.__add_item)
        self.ButtonDel = Button(self.Frame, text='Удалить', command=self.__del_item)
        self.ButtonDone = Button(self.Frame, text='Готово', command=self.__save_list)
        self.ButtonExit = Button(self.Frame, text='Отмена', command=self.Root.destroy)

    def __add_item(self) -> None:
        if self.select_path:
            text = filedialog.askdirectory()
        else:
            text = self.Entry.get()
        if text:
            self.Box.insert(END, text)
            self.Entry.delete(0, END)

    def __del_item(self) -> None:
        select = list(self.Box.curselection())
        select.reverse()
        for i in select:
            self.Box.delete(i)

    def __save_list(self) -> None:
        globals()[self.List] = list(self.Box.get(0, END))
        self.Root.destroy()

    def main(self) -> None:
        center_win(self.Root, '500x400')
        self.Root.deiconify()
        self.Root.title(f'Editing {self.List}')
        self.Box.pack(side='left', expand=True)
        self.Scroll.pack(side='left', fill='y')
        self.Box.config(yscrollcommand=self.Scroll.set)
        self.Frame.pack(side='left', padx=10)
        if not self.select_path:
            self.Entry.pack(anchor='n')
        self.ButtonAdd.pack(fill='x')
        self.ButtonDel.pack(fill='x')
        self.ButtonDone.pack(fill='x')
        self.ButtonExit.pack(fill='x')
        self.Root.mainloop()
    def login(self, username_entry: ttk.Entry, password_entry: ttk.Entry):

        data: dict = {"Usuario": str, "Contrasena": str}
        data["Usuario"] = username_entry.get()
        data["Contrasena"] = password_entry.get()

        response = self.makeRequest("POST", "login", data)
        if response.status_code == 200:
            self.interface_state = response.json()["account_type"]
        elif response.status_code == 401:
            messagebox.showerror("", "usuario o contrasena invalidos")
            password_entry.delete(0, tk.END)
        else:
            print("error en el server, help!!!")
Пример #9
0
class SelectKernel(Toplevel):
    """About Toplevel."""
    def __init__(self, master):
        """Create the Toplevel to select an existing Jupyter kernel."""
        Toplevel.__init__(self, master, class_=master.winfo_class(), padx=10, pady=10)
        self.resizable(True, False)
        self.title("Select existing kernel")
        self.columnconfigure(0, minsize=22)
        self.columnconfigure(1, weight=1)
        self.columnconfigure(2, weight=1)

        self.selected_kernel = ''

        Label(self,
              text="Select the .json connection file or enter the existing kernel's id:").grid(columnspan=4,
                                                                                               sticky='w',
                                                                                               pady=4)
        self.entry = Entry(self, width=10)
        self.entry.grid(row=1, columnspan=3, sticky='ew', pady=4)
        self.entry.bind('<Return>', lambda e: self.validate())
        Button(self, text='...', width=2, padding=0,
               command=self.select_file).grid(row=1, column=3, sticky='sn', pady=4)

        Button(self, text='Ok', command=self.validate).grid(row=2, column=1,
                                                            padx=4, pady=4,
                                                            sticky='e')
        Button(self, text='Cancel', command=self.destroy).grid(row=2, column=2,
                                                               padx=4, pady=4,
                                                               sticky='w')
        self.transient(master)
        self.entry.focus_set()
        self.update_idletasks()
        self.grab_set()

    def validate(self):
        self.selected_kernel = self.entry.get()
        self.destroy()

    def select_file(self):
        filename = askopenfilename(self, "Select connection file",
                                   initialdir=jupyter_runtime_dir(),
                                   defaultextension='.json',
                                   filetypes=[('JSON', '*.json'), ('All files', '*')])
        if filename:
            self.entry.delete(0, 'end')
            self.entry.insert(0, filename)
    def concrete_inc_dec(cls, entry: ttk.Entry, operand: str):
        """Concrete function to add or substract the value from an input"""
        old_value = entry.get().split()[0]
        value = float(old_value)
        if operand == "+":
            value += 1
        else:
            value -= 1
            if value < 0:
                value = 0
        if value == int(value):
            value = int(value)

        entry["state"] = "normal"
        entry.delete(0, "end")
        entry.insert(0, value)
        entry["state"] = "readonly"
Пример #11
0
class ArgEntry(Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.__namelabel = Label(self)
        self.__namelabel.pack(expand='yes', fill='x')
        self.__entry = Entry(self)
        self.__entry.pack(side='left', expand='yes', fill='x')
        self.__btn = Button(self)
        self.__btn.pack(side='right')
        self.__btn['state'] = 'disabled'

    @property
    def entry(self):
        return self.__entry

    @property
    def entry_text(self):
        return self.__entry.get()

    @entry_text.setter
    def entry_text(self, value):
        self.__entry.delete(0, 'end')
        self.__entry.insert(0, value)

    @property
    def button(self):
        return self.__btn

    @property
    def arg_name(self):
        return self.__namelabel['text']

    @arg_name.setter
    def arg_name(self, val):
        self.__namelabel['text'] = val

    @property
    def arg_value(self):
        return self.__entry.get()

    @arg_value.setter
    def arg_value(self, val):
        self.__entry.delete(0, 'end')
        self.__entry.insert(0, val)
Пример #12
0
    def escrever_mascara_entry(evento_char, ref_entry: ttk.Entry, mascara: str, especial: str = ''):
        """ Método estático para formatar a entrada de um Entry de acordo com mascara.
        Pode ser usado sem instanciação.
        :param evento_char: event.char do evento.
        :param ref_entry: Referência do componente Entry.
        :param mascara: Mascara base para a formatação. Deve apresentar espacos onde quer ser inseridos os números.
        :return: none. """
        # Apaga o Entry caso o conteúdo seja o default.
        if ref_entry.get() == mascara:
            ref_entry.delete(0, 'end')

        # O event.char captura de tecla literalmente, mas não captura teclas de controle(Backspace, ...).
        # O event.keysym usa uma nomenclatura diferente, e captura todas as teclas.
        # Tkinter 8.5 reference gui for Python. 161-162.
        if evento_char in string.digits or evento_char in especial:
            pos = len(ref_entry.get())
            try:
                if mascara[pos] != ' ':
                    ref_entry.insert('end', mascara[pos])
            except IndexError:
                ref_entry.delete(0, 'end')
class PasswordGenerator(Frame):

    def __init__(self):
        super().__init__()

        self.initUI()

        self.my_pw = ''

    def initUI(self):
        self.master.title('Simple PasswordGenerator v1.0')
        self.pack(fill=BOTH, expand=False)

        frame1 = Frame(self)
        frame1.pack(fill=X)

        lbl1 = Label(frame1, text='Input password lenght: (6-15)', width=27)
        lbl1.pack(side=LEFT, padx=5, pady=10)

        self.entry1 = Entry(frame1, width=10)
        self.entry1.pack(side=LEFT, padx=2)

        frame2 = Frame(self)
        frame2.pack(fill=X)

        lbl2 = Label(frame2, text='Generated password:'******'end')
        try:
            pw_length = int(self.entry1.get())
            if pw_length >= 6 and pw_length <=15:
                for i in range(pw_length):
                    index = random.randrange(len(possible_chars))
                    self.my_pw += possible_chars[index]
                self.entry2.insert(1, self.my_pw)
                self.my_pw = ''
            else:
                messagebox.showwarning('Error!', 'Password must be 6-15 characters!')
                self.entry1.delete(0, 'end')
        except:
            messagebox.showwarning('Error!', 'Field must contain a number!')
            self.entry1.delete(0, 'end')
Пример #14
0
    def initui(self):
        self.nombre = Nombre(0,10)

        self.master.title("convertisseur")
        self.pack(fill=BOTH, expand=1)
        frame1 = Frame(self, relief = RAISED, borderwidth = 1)
        frame1.pack(fill = BOTH)
        label1 = Label(frame1, text = "Valeur")
        label1.pack(side = LEFT)
        self.valeur = StringVar()
        self.ecriture = StringVar()

        disp_valeur = Entry(frame1, textvar = self.valeur )
        disp_valeur.delete(0, END)
        self.valeur.set(str(self.nombre.valeur))
#        disp_valeur.insert(0, self.nombre.valeur)
        disp_valeur.pack(side = RIGHT, expand = True)
        disp_valeur.bind("<Return>", self.update_valeur)

        frame2 = Frame(self, relief = RAISED, borderwidth = 1)
        frame2.pack(fill = BOTH)
        label2 = Label(frame2, text = "Base")
        self.base = StringVar()
        label2.pack(side = LEFT)
        disp_base = Entry(frame2, textvar = self.base)
        disp_base.delete(0, END)
        self.base.set(self.nombre.base)
#        disp_base.insert(0, self.nombre.base)
        disp_base.pack(side = RIGHT, expand=True)
        disp_base.bind("<Return>", self.update_base)


        frame3 = Frame(self, relief=RAISED, borderwidth=1)
        frame3.pack(fill=BOTH)
        self.ecriture = StringVar()
        label3 = Label(frame3, text="Ecriture")
        label3.pack(side=LEFT)
        disp_ecriture = Entry(frame3, textvar = self.ecriture)
        disp_ecriture.delete(0, END)
        self.ecriture.set(self.nombre.represente())
#        disp_base.insert(0, self.nombre.represente())
        disp_ecriture.pack(side=RIGHT, expand=True)
        disp_ecriture.bind("<Return>", self.update_ecriture)
Пример #15
0
class ClientSetUp(Frame):
    """
    Dialog box that presents 3 inputs to the user:
        Username
        Server IP
        Port

    Extends the Frame class in tkinter.
    """

    def __init__(self):
        super().__init__()
        self.name = ''
        self.ip = ''
        self.port = ''
        self.initUI()

    def initUI(self):
        """
        Initialises the dialog box. The box will contain fields for the
        username, server ip and server port. A button is used to submit
        the data.
        """
        self.master.title("Connection Details")
        self.pack(fill=BOTH, expand=True)
        # Prepare the username field
        frame1 = Frame(self)
        frame1.pack(fill=X)

        lbl1 = Label(frame1, text="Username", width=14)
        lbl1.pack(side=LEFT, padx=5, pady=10)

        self.entry1 = Entry(frame1, textvariable=self.name)
        self.entry1.pack(fill=X, padx=5, expand=True)
        # Prepare the server address field
        frame2 = Frame(self)
        frame2.pack(fill=X)

        lbl2 = Label(frame2, text="Server Address", width=14)
        lbl2.pack(side=LEFT, padx=5, pady=10)

        self.entry2 = Entry(frame2, textvariable=self.ip)
        self.entry2.pack(fill=X, padx=5, expand=True)
        # Prepare the server port field
        frame3 = Frame(self)
        frame3.pack(fill=X)

        lbl3 = Label(frame3, text="Server Port", width=14)
        lbl3.pack(side=LEFT, padx=5, pady=10)

        self.entry3 = Entry(frame3, textvariable=self.port)
        self.entry3.pack(fill=X, padx=5, expand=True)

        frame4 = Frame(self)
        frame4.pack(fill=X)

        # Command tells the form what to do when the button is clicked
        btn = Button(frame4, text="Submit", command=self.onSubmit)
        btn.pack(padx=5, pady=10)

        # Give entry1 focus
        self.entry1.focus()

        # Set up what happens when Return is pressed
        # All entries will move onto the next except port which will submit
        # Note: These didn't work if they weren't lambdas (don't know why)
        self.entry1.bind('<Return>', lambda event: self.entry2.focus())
        self.entry2.bind('<Return>', lambda event: self.entry3.focus())
        self.entry3.bind('<Return>', lambda event: self.onSubmit())

        photo = PhotoImage(file=resource_path('icon.png'))
        self.master.iconphoto(False, photo)

    def onSubmit(self):
        """
        When clicked, the user input is stored in the instance variables
        and the boxes are cleared. The widget is then destroyed.
        """
        self.name = self.entry1.get()
        self.ip = self.entry2.get()
        self.port = self.entry3.get()

        self.entry1.delete(0, END)
        self.entry2.delete(0, END)
        self.entry3.delete(0, END)
        self.quit()

    def retry(self, name='', ip='', port=''):
        """
        Used if the user enters incorrect data. It will repopulate
        the fields where the data was correct. The mainloop is started
        once the fields are populated.

        Parameters:
            name (str): Name of client
            ip (str): IP of server
            port (str): Port of server
        """
        self.entry1.insert(0, name)
        self.entry2.insert(0, ip)
        self.entry3.insert(0, port)

        self.mainloop()
        self.quit()

    def on_close(self):
        self.destroy()
        sys.exit()
Пример #16
0
class DaxModelParameter():
    '''The UI elements and logic to set model parameter values.

    For this application; all model parameters are assumed to be
    floats (or ints cast to floats). Strings and Logicals need not
    apply.
    '''

    def __init__(self, parent, label_frame, sherpa_model_parameter):
        '''Create model parameter UI element'''
        self.sherpa_par = sherpa_model_parameter
        self.parent = parent
        self.label_frame = label_frame
        self.initial_value = {'val': self.sherpa_par.val,
                              'min': self.sherpa_par.min,
                              'max': self.sherpa_par.max}
        self.render_ui()

    def _freeze_thaw(self):
        '''ACTION: set the freeze() or thaw() based on the
        checkbox value.'''
        if 1 == self.fz_box.get():
            self.sherpa_par.freeze()
        else:
            self.sherpa_par.thaw()

    @staticmethod
    def __format_val(val):
        'Format parameter values'
        retval = "{:.5g}".format(val)
        return retval

    def reset(self):
        """Reset values to original"""
        for field in ['max', 'min', 'val']:
            to_mod = getattr(self, field)
            to_mod.delete(0, END)
            to_mod.insert(0, self.__format_val(self.initial_value[field]))
            to_mod.configure(foreground="black")
            setattr(self.sherpa_par, field, self.initial_value[field])

    def update(self):
        """Reset values to original"""
        for field in ['max', 'min', 'val']:
            to_mod = getattr(self, field)
            newval = getattr(self.sherpa_par, field)
            to_mod.delete(0, END)
            to_mod.insert(0, self.__format_val(newval))
            # ~ to_mod.configure(foreground="black")
            # ~ setattr(self.sherpa_par, field, self.initial_value[field])

    def entry_callback(self, keyevt, field):
        '''ACTION: set the model parameter value when the user
        type <<Return>>.  Otherwise, when user edits value
        it turns red so user knows it hasn't been set yet.

        All values are cast|set to doubles.

        There is no validation in the UI against the min|max
        values.  Sherpa raises an exception if you try to go beyond
        the limits so the color remains red until valid value is
        entered.
        '''
        from sherpa.utils.err import ParameterErr

        # Note: use .char instead of .keysym because Return
        # and Enter on the keypad are different keysym's but both
        # generate CR. This makes sense since can remap keyboard
        # keys -- the action we want is CR, whichever key generates it.
        # Update: Unfortunately the .char field is cleared 
        # in newer versions of python in the KeyRelease callback, and
        # the Key callback doesn't work (order of callback doesn't change
        # text color correctly).  So, I'm back to using the keysym.

        to_mod = getattr(self, field)

        if keyevt.keysym in ['Return', 'KP_Enter', 'Enter']:
            try:
                fval = float(to_mod.get())
                setattr(self.sherpa_par, field, fval)
                to_mod.configure(foreground="black")
                to_mod.last_value = to_mod.get()
            except (ValueError, ParameterErr) as val_err:
                messagebox.showerror("DAX Model Editor", str(val_err))

        else:
            if to_mod.get() != to_mod.last_value:
                to_mod.configure(foreground="red")

    def render_ui(self):
        '''Render the parameter UI elements and attach bindings'''

        row = self.parent.get_row()
        win = self.label_frame

        # The parameter name
        lab = Label(win, text=self.sherpa_par.name,
                    width=12, anchor="e")
        lab.grid(row=row, column=0, padx=(5, 5), pady=2)

        # The current parameter value
        self.val_str = StringVar()
        self.val = Entry(win, textvariable=self.val_str,
                         foreground="black", width=12, justify="right")
        self.val.grid(row=row, column=1, padx=(5, 5), pady=2)
        self.val.delete(0, END)
        self.val.insert(0, self.__format_val(self.sherpa_par.val))
        self.val.last_value = self.val.get()
        self.val.bind("<KeyRelease>",
                      lambda x: self.entry_callback(x, field='val'))

        # Frozen|Thawed checkbox.  Checked if frozen.
        self.fz_box = IntVar()
        if self.sherpa_par.frozen is True:
            self.fz_box.set(1)
        else:
            self.fz_box.set(0)
        fzbtn = Checkbutton(win, text="", variable=self.fz_box,
                            command=self._freeze_thaw)
        fzbtn.grid(row=row, column=2, padx=(5, 5), pady=2)

        # The min value
        self.min_str = StringVar()
        self.min = Entry(win, textvariable=self.min_str,
                         foreground="black", width=12, justify="right")
        self.min.grid(row=row, column=3, padx=(5, 5), pady=2)
        self.min.delete(0, END)
        self.min.insert(0, self.__format_val(self.sherpa_par.min))
        self.min.last_value = self.min.get()
        self.min.bind("<KeyRelease>",
                      lambda x: self.entry_callback(x, field='min'))

        # The max value
        self.max_str = StringVar()
        self.max = Entry(win, textvariable=self.max_str,
                         foreground="black", width=12, justify="right")
        self.max.grid(row=row, column=4, padx=(5, 5), pady=2)
        self.max.delete(0, END)
        self.max.insert(0, self.__format_val(self.sherpa_par.max))
        self.max.last_value = self.max.get()
        self.max.bind("<KeyRelease>",
                      lambda x: self.entry_callback(x, field='max'))

        # The units of the parameter
        par_units = Label(win, text="{}".format(self.sherpa_par.units),
                          width=20, anchor="e")
        par_units.grid(row=row, column=5, padx=(5, 5), pady=2)
Пример #17
0
class loginPage(Frame):
    ''' This is a frame class and thus it implements Frame,
        it also contains the required showItems and hideItems function which I believe are present in each frame class
        tomake things a bit simpler
'''
    def __init__(self, parent, main, **kw):
        Frame.__init__(self, parent, **kw)
        self.main = main
        self.label_head = Label(text='Welcome to the Stock Exchange',
                                font=LARGE_FONT)
        self.l_user = Label(text='Username')
        self.user = Entry()
        self.l_pass = Label(text='Password')
        self.password = Entry(show='*')
        self.login_back_b = Button(text='Login',
                                   command=lambda: self.login_check())
        self.sign_up_b = Button(
            text='Sign Up',
            command=lambda: self.main.show_frame(SIGN_UP_PAGE, LOGIN_PAGE))
        self.view_stock_b = Button(
            text='View Stock',
            command=lambda: self.main.show_frame(VIEW_STOCK, LOGIN_PAGE))
        self.main.bind('<Return>', self.keyPress)

    def showItems(self, main):
        self.label_head.grid(column=0, row=0, columnspan=2)
        self.l_user.grid(column=0, row=1)
        self.user.grid(column=1, row=1)
        self.l_pass.grid(column=0, row=2)
        self.password.grid(column=1, row=2)
        self.login_back_b.grid(column=0, row=3, columnspan=2)
        self.sign_up_b.grid(column=0, row=4)
        self.view_stock_b.grid(column=1, row=4)

    def hideItems(self, main):
        self.label_head.grid_forget()
        self.l_user.grid_forget()
        self.user.grid_forget()
        self.l_pass.grid_forget()
        self.password.grid_forget()
        self.login_back_b.grid_forget()
        self.sign_up_b.grid_forget()
        self.view_stock_b.grid_forget()

    def keyPress(self, event):
        ''' this is the binding fucntion for any keyPress event
'''
        if event.keysym == 'Return':
            self.login_check()

    def login_check(self):
        ''' This function checks whether a given user is registered on the system or not
            then continues to chech whether they have entered the correct password.
            it also creates messagesboxes to give the proper error message to the user
'''
        main = self.main
        user = str(self.user.get())
        password = bytes(str(self.password.get()), 'utf-8')
        main.login = False
        check1 = user in main.pass_dict
        check3 = user == 'admin' and password == 'admin'
        if check1:
            check2 = main.pass_dict[user] == password
        if check1 and check2:
            main.login = True
        else:
            main.login = False
        if check3:
            main.login = True
            main.admin = True
            main.show_frame(ADMIN_PAGE, LOGIN_PAGE)
        if main.login:
            if not check3:
                main.present_user, main.p_user_dict = user, main.users_dict[
                    user]
                main.show_frame(VIEW_STOCK, LOGIN_PAGE)
            self.password.delete(0, END)
            self.user.delete(0, END)
        else:
            self.password.delete(0, END)
            showinfo(message='Incorrect Username or Password entered')
Пример #18
0
class retailPage(Frame):
    ''' the main page that actually branches into 2 seperate frames for buying and selling
        this frame provides the use with the
'''
    def __init__(self, parent, main, **kw):
        Frame.__init__(self, parent, **kw)
        self.main = main
        self.Head = Label(font=MED_FONT)

        self.retail_back_b = Button(text='BACK', command=self.back_fb)
        self.stock_name = StringVar()
        self.stock_name_ = ''
        self.stock_name_c = Combobox(textvariable=self.stock_name)
        self.stock_name_l = Label(text='Stock name: ')
        self.amount = Entry()
        self.amount_l = Label(text='Number of stocks :')

        self.check_avail_b = Button(text='Check')
        self.cost_l = Label(text='Cost: ')
        self.cost = Label()
        self.buy_stock_b = Button(text='BUY Stock')
        self.sellp_l = Label(text='Current Selling Price: ')
        self.profit_l = Label(text='PROFIT/LOSS')
        self.sell_stock_b = Button(text='SELL Stock')
        self.page = None

    def showItems(self, main):
        self.Head.grid(column=0, row=0, columnspan=2)
        self.Head.configure(text='logged in as %s' % main.present_user)
        if self.page == 'b':
            self.buy()
        elif self.page == 's':
            self.sell()

    def hideItems(self, main):
        self.Head.grid_forget()

    def buy(self):
        '''This function creates the buy window
'''
        self.retail_back_b.grid(column=1, row=5)
        self.retail_back_b.config(command=self.back_fb)
        self.stock_name_l.grid(column=0, row=1)
        self.stock_name_c.grid(column=1, row=1)
        self.stock_name_c.config(values=self.main.shares_dict.keys())
        self.amount_l.grid(column=0, row=2)
        self.amount.grid(column=1, row=2)
        self.check_avail_b.config(command=self.check_avail_buy)
        self.check_avail_b.grid(column=0, row=3)
        self.cost_l.grid(column=0, row=4, columnspan=2)
        self.buy_stock_b.config(command=self.buy_stock,
                                text='Buy Stock',
                                state='disabled')
        self.buy_stock_b.grid(column=0, row=5)

    def sell(self):
        '''This function creats the sell window'''
        self.retail_back_b.grid(column=1, row=6)
        self.retail_back_b.config(command=self.back_fs)
        self.check_avail_b.config(command=self.check_avail_sell)
        self.stock_name_l.grid(column=0, row=1)
        self.stock_name_c.grid(column=1, row=1)
        self.stock_name_c.config(values=self.main.shares_dict.keys())
        self.amount_l.grid(column=0, row=2)
        self.amount.grid(column=1, row=2)
        self.check_avail_b.grid(column=0, row=3)
        self.sellp_l.grid(column=0, row=4, columnspan=2)
        self.profit_l.grid(column=0, row=5, columnspan=2)
        self.sell_stock_b.config(command=self.sell_stock,
                                 state='disabled',
                                 text='Check')
        self.sell_stock_b.grid(column=0, row=6)

    def back_fb(self):
        '''Back from buy i.e. removes all the items needed to make buy'''
        self.retail_back_b.grid_forget()
        self.Head.grid(column=0, row=0, columnspan=2)
        self.stock_name_l.grid_forget()
        self.stock_name_c.grid_forget()
        self.amount_l.grid_forget()
        self.amount.grid_forget()
        self.check_avail_b.grid_forget()
        self.cost_l.grid_forget()
        self.buy_stock_b.grid_forget()
        self.buy_stock_b.config(state='disabled')

        self.main.show_frame(VIEW_STOCK, RETAIL_PAGE)
        self.stock_name.set('')
        self.amount.delete(0, END)

    def back_fs(self):
        ''' Back from sell i.e. removes all the items needed to make it sell window'''
        self.Head.grid(column=0, row=0, columnspan=2)
        self.retail_back_b.grid_forget()
        self.check_avail_b.grid_forget()
        self.stock_name_l.grid_forget()
        self.stock_name_c.grid_forget()
        self.amount_l.grid_forget()
        self.amount.grid_forget()
        self.sellp_l.grid_forget()
        self.profit_l.grid_forget()
        self.sell_stock_b.grid_forget()

        self.main.show_frame(VIEW_STOCK, RETAIL_PAGE)
        self.stock_name.set('')
        self.amount.delete(0, END)
        self.check_avail_b.grid_forget()

    def check_avail_buy(self):
        ''' Performs a check whether the number of shares requisted are available or not and then check whether the
            person has the required amounts of fund or not for the transaction to go through'''
        name = self.stock_name.get()
        l2 = self.main.accounts[self.main.present_user]

        if name in self.main.shares_dict.keys():
            li = self.main.shares_dict[name]
        else:
            self.stock_name.delete(0, END)
            showinfo(meassage='Enter a Valid Stock name')

        available_num = int(li['tot_amount']) - int(li['tot_sold'])
        req = int(self.amount.get())
        cost = req * int(li['cost'])

        if req < 0:
            showinfo(message='Enter a Valid amount')
        elif req > available_num:
            showinfo(message='Enter an amount less than ' + str(available_num))
        elif cost > int(l2['balance']):
            showinfo(message='You have only %s in you account' % l2['balance'])
        else:
            self.cost_l.config(text='Cost: \t' + li['cost'] + '*' + str(req) +
                               '=' + str(cost))
            self.buy_stock_b.config(state='normal')

    def check_avail_sell(self):
        ''' Performs a check whether the user has enough stocks to sell'''
        name = self.stock_name.get()
        if name in self.main.shares_dict.keys():
            li = self.main.shares_dict[name]
        else:
            self.stock_name.delete(0, END)
            showinfo(message='Enter a Valid Stock name')
        req = int(self.amount.get())
        if req < 0:
            showinfo(message='Please Enter a Valid amount')
            self.amount.delete(0, END)
        li = self.main.p_user_dict
        ok = False
        for i in li:
            if name == i['name']:
                ok = True
                buff = i

        if req > int(buff['tot_owned']):
            showinfo(message='You dont have that many stocks try less than ' +
                     buff['tot_owned'])
            self.amount.delete(0, END)
        cost = self.main.shares_dict[name]['cost']
        tot_cost = req * int(cost)
        try:
            spent = req * float(buff['money_spent']) / int(buff['tot_owned'])
        except:
            spent = 0
        pol = tot_cost - spent

        if pol >= 0:
            self.profit_l.config()
        elif pol < 0:
            self.profit_l.config()
        if req <= int(buff['tot_owned']):
            self.sellp_l.config(text='Current Selling Price: \t' + cost)
            self.profit_l.config(text="PROFIT-LOSS: \t" + str(pol))
            self.sell_stock_b.config(command=self.sell_stock,
                                     text='Sell Stock')
            showinfo(message=
                     'Everthing is ok, \nClicking Sell will execute the trade')
            self.sell_stock_b.config(state='normal')

    def buy_stock(self):
        '''Finally Executes the transaction and asks the user for conformation one last time'''
        name = self.stock_name.get()
        li = self.main.shares_dict[name]

        for i in range(len(self.main.p_user_dict)):
            if name == self.main.p_user_dict[i]['name']:
                index = i
        req = int(self.amount.get())
        tot_cost = req * int(li['cost'])
        self.main.shares_dict[name]['tot_sold'] = str(
            int(self.main.shares_dict[name]['tot_sold']) + req)
        self.main.p_user_dict[index]['tot_owned'] = str(
            int(self.main.p_user_dict[index]['tot_owned']) + req)
        self.main.p_user_dict[index]['money_spent'] = str(
            int(self.main.p_user_dict[index]['money_spent']) + tot_cost)
        self.main.users_dict[
            self.main.present_user][index] = self.main.p_user_dict[index]
        balance = int(self.main.accounts[self.main.present_user]['balance'])
        self.main.accounts[self.main.present_user]['balance'] = str(balance -
                                                                    tot_cost)
        self.cost_l.config(text='Cost: ')

        showinfo(
            message='You have just bought %s of the stock %s at the price %s' %
            (str(req), name, str(tot_cost)))
        self.stock_name.set('')
        self.main.show_frame(VIEW_STOCK, RETAIL_PAGE)
        self.back_fb()

    def sell_stock(self):
        '''Asks the user for conformation and then completes the transaction
        of selling, at this point the profit field is
        also updated'''
        name = self.stock_name.get()
        req = int(self.amount.get())

        li = self.main.p_user_dict
        for i in range(len(li)):
            if name == li[i]['name']:
                ok = True
                buff = i

        tot_cost = req * int(self.main.shares_dict[name]['cost'])
        try:
            spent = req * float(
                self.main.p_user_dict[buff]['money_spent']) / int(
                    self.main.p_user_dict[buff]['tot_owned'])
        except ZeroDivisionError:
            spent = 0
        self.main.shares_dict[name]['tot_sold'] = str(
            int(self.main.shares_dict[name]['tot_sold']) - req)
        self.main.p_user_dict[buff]['tot_owned'] = str(
            int(self.main.p_user_dict[buff]['tot_owned']) - req)
        pol = tot_cost - spent
        diff = int(self.main.p_user_dict[buff]['money_spent']) - tot_cost
        self.main.p_user_dict[buff]['money_spent'] = str(
            diff) if diff > 0 else '0'
        self.main.users_dict[self.main.present_user] = self.main.p_user_dict
        profit = int(self.main.accounts[self.main.present_user]['profit'])
        self.main.accounts[self.main.present_user]['profit'] = str(
            int(profit + pol))
        balance = int(self.main.accounts[self.main.present_user]['balance'])
        self.main.accounts[self.main.present_user]['balance'] = str(balance +
                                                                    tot_cost)
        self.retail_back_b.grid_forget()
        self.profit_l.config(text='PROFIT/LOSS: ')
        self.sellp_l.config(text='Current selling price: ')
        self.main.show_frame(VIEW_STOCK, RETAIL_PAGE)
        self.back_fs()
Пример #19
0
class MainView(Frame):
    def __init__(self, root, model: Model):
        super().__init__(root)
        root.columnconfigure(0, weight=1)
        root.rowconfigure(0, weight=1)
        self.model = model
        self.__init_components()
        self.grid(sticky='nsew')
        self.__bind_action_events()

    def __init_components(self):
        # Instanciar widgets
        self.panel_form = Labelframe(self, text='Tarea')
        self.panel_tasks = Labelframe(self, text='Tareas por hacer')
        self.panel_complete_tasks = Labelframe(self, text='Tareas completas')

        self.label_name = Label(self.panel_form, text='Nombre:')
        self.label_description = Label(self.panel_form, text='Descripción:')
        self.entry_name = Entry(self.panel_form)
        self.entry_description = Entry(self.panel_form)

        self.btn_modify_task = Button(self.panel_form,
                                      text='Editar tarea',
                                      state=DISABLED,
                                      command=self.__modify_task)
        self.btn_new_task = Button(self.panel_form,
                                   text='Nueva tarea',
                                   command=self.__new_task)
        self.btn_delete_task = Button(self.panel_form,
                                      text='Eliminar tarea',
                                      state=DISABLED,
                                      command=self.__delete_task)
        self.btn_clear_form = Button(self.panel_form,
                                     text='Limpiar campos',
                                     command=self.__clear_form)
        self.btn_complete_task = Button(self.panel_form,
                                        text='Completar tarea',
                                        state=DISABLED,
                                        command=self.__complete_task)

        self.scroll_tasks = Scrollbar(self.panel_tasks, orient=VERTICAL)
        self.scroll_complete_tasks = Scrollbar(self.panel_complete_tasks,
                                               orient=VERTICAL)
        self.list_tasks = Listbox(self.panel_tasks,
                                  selectmode=SINGLE,
                                  height=10,
                                  width=25,
                                  yscrollcommand=self.scroll_tasks.set)
        self.list_complete_tasks = Listbox(
            self.panel_complete_tasks,
            selectmode=SINGLE,
            height=10,
            width=25,
            yscrollcommand=self.scroll_complete_tasks.set)

        # Posicionar los widgets
        # Panel de formulario de tareas
        self.panel_form.pack(fill='both',
                             expand='yes',
                             padx=10,
                             pady=5,
                             ipadx=5,
                             ipady=5)
        self.panel_form.columnconfigure(0, weight=1)
        self.panel_form.rowconfigure(0, weight=1)

        self.label_name.grid(row=0, column=0, padx=5, sticky='w')
        self.entry_name.grid(row=0, column=1, padx=5, sticky='w')
        self.label_description.grid(row=1,
                                    column=0,
                                    padx=5,
                                    pady=5,
                                    sticky='w')
        self.entry_description.grid(row=1,
                                    column=1,
                                    padx=5,
                                    pady=10,
                                    sticky='w')

        # Botones
        self.btn_modify_task.grid(row=2, column=0, ipady=4, sticky='we')
        self.btn_new_task.grid(row=2, column=1, ipady=4, sticky='we')
        self.btn_delete_task.grid(row=3, column=0, ipady=4, sticky='we')
        self.btn_clear_form.grid(row=3, column=1, ipady=4, sticky='we')
        self.btn_complete_task.grid(row=4,
                                    column=0,
                                    columnspan=2,
                                    ipady=4,
                                    sticky='we')

        config_list = {
            'fill': 'both',
            'expand': 'yes',
            'padx': 10,
            'pady': 5,
            'ipadx': 5,
            'ipady': 5
        }

        # Panel de lista de tareas pendientes
        self.panel_tasks.pack(config_list)
        self.panel_tasks.columnconfigure(0, weight=20)
        self.panel_tasks.columnconfigure(1, weight=1)
        self.list_tasks.grid(row=0, column=0, sticky='we')
        self.scroll_tasks.configure(command=self.list_tasks.yview)
        self.scroll_tasks.grid(row=0, column=1, sticky='ns')

        # Panel de lista de tareas completas
        self.panel_complete_tasks.pack(config_list)
        self.panel_complete_tasks.columnconfigure(0, weight=20)
        self.panel_complete_tasks.columnconfigure(1, weight=1)
        self.list_complete_tasks.grid(row=0, column=0, sticky='we')
        self.scroll_complete_tasks.configure(
            command=self.list_complete_tasks.yview)
        self.scroll_complete_tasks.grid(row=0, column=1, sticky='ns')

    def __bind_action_events(self):
        # self.btn_new_task.bind('<Button>', self.__new_task)
        # self.btn_modify_task.bind('<Button>', self.__modify_task)
        # self.btn_delete_task.bind('<Button>', self.__delete_task)
        # self.btn_complete_task.bind('<Button>', self.__complete_task)

        self.list_tasks.bind('<<ListboxSelect>>', self.__select_task)

    def __new_task(self):
        log_info('Botón {} pulsado'.format(
            self.btn_new_task.config('text')[-1]))

        name_value = self.entry_name.get()
        description_value = self.entry_description.get()

        if name_value:
            self.model.new_task(name_value, description_value)
            self.update_tables()
        else:
            messagebox.showwarning('AVISO', 'Complete el campo de nombre')

    def __modify_task(self):
        index = self.__selected_task()

        name_value = self.entry_name.get()
        description_value = self.entry_description.get()

        if index != -1:
            task = self.model.get_task(self.model.tasks[index].get_id())
            complete = self.model.modify_task(task.get_id(), name_value,
                                              description_value)

            if complete:
                messagebox.showinfo('Aviso',
                                    'Tarea: {} editada'.format(task.name))

        self.update_tables()

    def __delete_task(self):
        index = self.__selected_task()

        if index != -1:
            task = self.model.get_task(self.model.tasks[index].get_id())
            self.model.delete_task(task.get_id())
            self.update_tables()

    def __complete_task(self):
        index = self.__selected_task()

        if index != -1:
            task = self.model.get_task(self.model.tasks[index].get_id())
            task.close_todo()
            complete = self.model.modify_task(task.get_id(), task.name,
                                              task.description)

            if complete:
                messagebox.showinfo('Aviso',
                                    'Tarea: {} completa'.format(task.name))

        self.update_tables()

    def __clear_form(self):
        self.clear_form()
        self.__change_state_btn(DISABLED)

    def __select_task(self, event):
        self.clear_form()
        index = self.__selected_task()

        if index != -1:
            task = self.model.get_task(self.model.tasks[index].get_id())
            self.set_form(task)
            self.__change_state_btn(NORMAL)

    def __change_state_btn(self, state: str):
        self.btn_new_task.config(
            state=NORMAL if state == DISABLED else DISABLED)
        self.btn_delete_task.config(state=state)
        self.btn_modify_task.config(state=state)
        self.btn_complete_task.config(state=state)

    def __selected_task(self):
        try:
            return self.list_tasks.curselection()[0]
        except IndexError:
            self.__change_state_btn(DISABLED)
            self.list_complete_tasks.activate(-1)
            return -1

    def update_tables(self):
        log_info('Actualizando tablas')
        self.list_tasks.delete(0, END)
        self.list_complete_tasks.delete(0, END)

        for index in range(len(self.model.tasks)):
            self.list_tasks.insert(index, self.model.tasks[index].name)

        for index in range(len(self.model.complete_tasks)):
            self.list_complete_tasks.insert(
                index, self.model.complete_tasks[index].name)

    def clear_form(self):
        self.entry_name.delete(0, END)
        self.entry_description.delete(0, END)

    def set_form(self, task: Task):
        if task is not None:
            self.entry_name.insert(0, task.name)
            self.entry_description.insert(0, task.description)
        else:
            log_error('No se encontró la tarea seleccionada')
Пример #20
0
class MultipleABFEditor(Toplevel):
    def __init__(self, parent, paths, dispatch_event):
        """
        Initialises a window for loading datasets.
        First loads a datafile.
        After waiting for this process, creates a window to select different channels and specify the unit of the data.
        """
        super().__init__(None)
        self.parent = parent
        self.dispatch_event = dispatch_event
        self.title("ABF data set")
        self.editor = None
        self.paths = paths
        self.paths_new = []
        if not test1.alpha:
            self.path = paths[0]
            self.abf = ABF(self.path)
            self.values = None
            self.values1 = None
            self.resizable(width=False, height=False)
            waiting_popup = Toplevel(parent)
            waiting_popup.attributes("-topmost", True)
            waiting_label = Label(waiting_popup, text="Preparing data...")
            waiting_label.pack()
            waiting_popup.update()
            waiting_popup.destroy()
        try:
            if not test1.alpha:
                self.attributes("-topmost", True)
                dividends = self.abf._abf[self.abf._abf.names.index("channelNames")]
                units = self.abf._abf[self.abf._abf.names.index("channelUnits")]
                for i in range(len(self.abf._abf[self.abf._abf.names.index("channelNames")])):
                    dividends[i] = dividends[i] + " " + \
                                   self.abf._abf[self.abf._abf.names.index("channelUnits")][i]
                divisors = dividends + 'None'
                self.dividend_var = StringVar(self)  # usually contains current
                self.divisor_var = StringVar(self)  # usually contains voltage
                self.dividend_var.set(dividends[0])
                self.divisor_var.set(divisors[1])
                self.dividends = dividends
                self.units = units
                self.divisors = divisors
                path_lab = Label(self, text="Path: {}".format(self.paths[0]))
                path_lab.pack(padx=10, pady=10)
                channel_selector = Frame(self)
                warning = Label(channel_selector,
                                text="Select a channel, optionally divide it by another channel")

                dividend_menu = OptionMenu(channel_selector,
                                           self.dividend_var,
                                           *dividends)
                divisor_menu = OptionMenu(channel_selector,
                                          self.divisor_var,
                                          *divisors)
                warning.pack()
                dividend_menu.pack()
                divisor_menu.pack()
                channel_selector.pack(padx=10, pady=10)

                unit_selector = Frame(self)
                unit_lab = Label(unit_selector, text="Unit: ")
                unit_lab.pack(side="left", fill="x")
                self.unit = Entry(unit_selector)
                self.unit.delete(0, "end")
                self.unit.insert(0, "nS")
                self.unit.pack(side="left", fill="x")
                unit_selector.pack(padx=10, pady=10)

                self.ok = Button(self, text="Load", command=self.load)

                self.ok.pack(side="left", fill="x", padx=10, pady=10)

                self.ok_all = Button(self, text="Load All", command=self.load_all)

                self.ok_all.pack()
                self.ok_all.place(relx=0.5, rely=0.88, anchor=CENTER)
                cancel = Button(self, text="Don't load", command=self.destroy)
                cancel.pack(side="right", fill="x", padx=10, pady=10)
                self.bind('<Return>', self.load)


        except:
            messagebox.showwarning(
                "Error loading file", self.paths[0] + " could not be loaded.")
            self.destroy()

    @property
    def params(self):
        """
        Parameters which will be given to the controller.
        """

        dividend = self.dividend_var.get()
        # print(dividend)
        divisor = self.divisor_var.get()
        # print(divisor)
        channels = [self.dividends.index(dividend) + 1]
        # print("Channels", channels)

        if divisor != 'None':
            channels += [self.divisors.index(divisor) + 1]
        return dict(sweep=1, channels=channels, unit=self.unit.get())

    def load(self, test=None):
        """
        Gives the dataset and parameters to the controller, then destroys the editor.
        """
        self.ok.config(state = DISABLED)
        self.ok_all.config(state=DISABLED)
        if len(self.paths) > 0:
            del self.paths[0]

        self.dispatch_event("add_abf_dataset", self.abf, self.params)
        if len(self.paths) != 0:
            test1.alpha = False
            Editor = MultipleABFEditor
            self.editor = Editor(self.parent, self.paths, self.dispatch_event)


        else:
            test1.alpha = True
        self.destroy()

    def load_all(self, test=None):
        """
        Gives the datasets with same channel names , channel units , same number of channels and parameters( same
        parameters to all data sets with same  channel names , channel units , same number of channels  )to the
        controller, then destroys the editor.
        """
        self.ok_all.config(state = DISABLED)
        self.ok.config(state=DISABLED)
        self.values = ABF.names1(self.abf)
        values = set(self.values)
        self.values1 = ABF.units1(self.abf)
        values1 = set(self.values1)
        params1 = self.params
        for path in self.paths:
            self.abf = ABF(path)
            dividends = self.abf._abf[self.abf._abf.names.index("channelNames")]
            for i in range(len(self.abf._abf[self.abf._abf.names.index("channelNames")])):
                dividends[i] = dividends[i] + " " + \
                               self.abf._abf[self.abf._abf.names.index("channelUnits")][i]
            units = self.abf._abf[self.abf._abf.names.index("channelUnits")]
            dividends = set(dividends)
            units = set(units)

            if len(self.values) == len(dividends) and len(self.values1) == len(units) and (values == dividends) and (
                    values1 == units):
                self.dispatch_event("add_abf_dataset", self.abf, params1)

            else:
                self.paths_new.append(path)

        if len(self.paths_new) != 0:
            test1.alpha = False
            Editor = MultipleABFEditor
            self.editor = Editor(self.parent, self.paths_new, self.dispatch_event)

        else:
            test1.alpha = True

        self.destroy()
Пример #21
0
class MarkovDemo(Frame):

    "MarkovDemo(master=None, **kw) -> MarkovDemo instance"

    TEXT = dict(height=2, width=46, wrap=WORD)  # Text Options
    GRID = dict(padx=5, pady=5)                 # Grid Options

    # Initialize a MarkovDemo instance with a GUI for interaction.

    def __init__(self, master=None, **kw):
        "Initialize the MarkovDemo instance's widgets and settings."
        super().__init__(master, **kw)
        self.build_widgets()
        self.place_widgets()
        self.setup_widgets()
        self.grid_rowconfigure(2, weight=1)
        self.grid_rowconfigure(3, weight=1)
        self.grid_columnconfigure(0, weight=1)
        self.key = self.primer = None

    def build_widgets(self):
        "Build the various widgets that will be used in the program."
        # Create processing frame widgets.
        self.processing_frame = LabelFrame(self, text='Processing Mode:')
        self.mode_var = StringVar(self, 'encode')
        self.decode_button = Radiobutton(self.processing_frame,
                                         text='Decode Cipher-Text',
                                         command=self.handle_radiobuttons,
                                         value='decode',
                                         variable=self.mode_var)
        self.encode_button = Radiobutton(self.processing_frame,
                                         text='Encode Plain-Text',
                                         command=self.handle_radiobuttons,
                                         value='encode',
                                         variable=self.mode_var)
        self.freeze_var = BooleanVar(self, False)
        self.freeze_button = Checkbutton(self.processing_frame,
                                         text='Freeze Key & Primer',
                                         command=self.handle_checkbutton,
                                         offvalue=False,
                                         onvalue=True,
                                         variable=self.freeze_var)
        # Create encoding frame widgets.
        self.encoding_frame = LabelFrame(self, text='Encoding Options:')
        self.chain_size_label = Label(self.encoding_frame, text='Chain Size:')
        self.chain_size_entry = Entry(self.encoding_frame)
        self.plain_text_label = Label(self.encoding_frame, text='Plain-Text:')
        self.plain_text_entry = Entry(self.encoding_frame)
        # Create input frame widgets.
        self.input_frame = LabelFrame(self, text='Input Area:')
        self.input_text = ScrolledText(self.input_frame, **self.TEXT)
        # Create output frame widgets.
        self.output_frame = LabelFrame(self, text='Output Area:')
        self.output_text = ScrolledText(self.output_frame, **self.TEXT)

    def place_widgets(self):
        "Place the widgets where they belong in the MarkovDemo frame."
        # Locate processing frame widgets.
        self.processing_frame.grid(sticky=EW, **self.GRID)
        self.decode_button.grid(row=0, column=0, **self.GRID)
        self.encode_button.grid(row=0, column=1, **self.GRID)
        self.freeze_button.grid(row=0, column=2, **self.GRID)
        # Locate encoding frame widgets.
        self.encoding_frame.grid(sticky=EW, **self.GRID)
        self.chain_size_label.grid(row=0, column=0, sticky=W, **self.GRID)
        self.chain_size_entry.grid(row=0, column=1, sticky=EW, **self.GRID)
        self.plain_text_label.grid(row=1, column=0, sticky=W, **self.GRID)
        self.plain_text_entry.grid(row=1, column=1, sticky=EW, **self.GRID)
        self.encoding_frame.grid_columnconfigure(1, weight=1)
        # Locate input frame widgets.
        self.input_frame.grid(sticky=NSEW, **self.GRID)
        self.input_text.grid(sticky=NSEW, **self.GRID)
        self.input_frame.grid_rowconfigure(0, weight=1)
        self.input_frame.grid_columnconfigure(0, weight=1)
        # Locate output frame widgets.
        self.output_frame.grid(sticky=NSEW, **self.GRID)
        self.output_text.grid(sticky=NSEW, **self.GRID)
        self.output_frame.grid_rowconfigure(0, weight=1)
        self.output_frame.grid_columnconfigure(0, weight=1)

    def setup_widgets(self):
        "Setup each widget's configuration for the events they handle."
        self.input_text.bind('<Key>', self.handle_key_events)
        self.input_text.bind('<Control-Key-a>', self.handle_control_a)
        self.input_text.bind('<Control-Key-/>', lambda event: 'break')
        self.output_text['state'] = DISABLED
        self.output_text.bind('<Control-Key-a>', self.handle_control_a)
        self.output_text.bind('<Control-Key-/>', lambda event: 'break')

    ########################################################################

    # Take care of any special event needing dedicated processing.

    def handle_radiobuttons(self):
        "Change the interface based on the encoding / decoding setting."
        if self.encrypting:
            self.freeze_button.grid()
            if not self.freeze_var.get():
                self.encoding_frame.grid()
        else:
            self.freeze_button.grid_remove()
            if not self.freeze_var.get():
                self.encoding_frame.grid_remove()
        self.handle_key_events(None)

    def handle_checkbutton(self):
        "Change the interface based on the key / primer freeze setting."
        if self.freeze_var.get():
            self.encoding_frame.grid_remove()
        else:
            self.encoding_frame.grid()

    def handle_key_events(self, event):
        "Schedule refreshing the output area after an input area event."
        if event is None or event.char and event.state | 0o11 == 0o11:
            self.after_idle(self.refresh)

    @staticmethod
    def handle_control_a(event):
        "Select all text in the widget associated with the given event."
        event.widget.tag_add(SEL, 1.0, END + '-1c')
        return 'break'

    ########################################################################

    # Handle interface's updates when either encoding or decoding.

    def refresh(self):
        "Refresh the output based on the value of the input."
        text = self.input_text.get(1.0, END + '-1c')
        if not text:
            self.output = text
        elif self.encrypting:
            self.encode(text)
        else:
            self.decode(text)

    def output(self, value):
        "Set the text in the output area to the string value."
        self.output_text['state'] = NORMAL
        self.output_text.delete(1.0, END)
        self.output_text.insert(END, value)
        if self.encrypting and self.freeze_var.get():
            self.output_text.see(END)
        self.output_text['state'] = DISABLED

    output = property(fset=output, doc='Output area property.')

    @property
    def chain_size(self):
        "Chain size for the Markov chains used when encrypting."
        try:
            value = ast.literal_eval(self.chain_size_entry.get())
            assert isinstance(value, int) and 2 <= value <= 256
            return value
        except:
            self.chain_size_entry.delete(0, END)
            self.chain_size_entry.insert(0, '2')
            return 2

    @property
    def plain_text(self):
        "Plain text or ignored characters in encryption process."
        try:
            value = self.repr_to_obj(self.plain_text_entry.get(), '')
            assert isinstance(value, str)
            return value
        except:
            self.plain_text_entry.delete(0, END)
            return ''

    ########################################################################

    # Encrypt a string for display in the interface's output area.

    def encode(self, string):
        "Encode the string and show the cipher-text in the output."
        try:
            cipher = self.build_cipher(string)
        except ValueError:
            self.output = ''
        except:
            self.output = traceback.format_exc()
        else:
            self.output = self.build_header() + '\n\n' + cipher

    def build_cipher(self, string):
        "Build cipher-text based on plain-text and return answer."
        if self.key and self.freeze_var.get():
            cipher, primer = me.encrypt_str(string, self.key, self.primer)
        else:
            args = string, self.chain_size, self.plain_text
            cipher, self.key, self.primer = me.auto_encrypt_str(*args)
        return cipher

    def build_header(self):
        "Build header from key and primer values in current use."
        header = '\n'.join(map(self.bytes_to_repr, self.key.data))
        header += '\n' + self.bytes_to_repr(self.primer.data)
        return header

    ########################################################################

    # Decrypt a string for display in the interface's output area.

    def decode(self, string):
        "Decode encrypted message and display plain-text in output."
        try:
            cipher = self.extract_keys(string)
            text = self.extract_text(cipher)
        except ValueError:
            self.output = ''
        except:
            self.output = traceback.format_exc()
        else:
            self.output = text

    def extract_keys(self, string):
        "Extract keys to decryption and return the cipher-text area."
        header, cipher = string.split('\n\n', 1)
        *key, primer = map(self.repr_to_obj, header.split('\n'))
        self.key, self.primer = me.Key(tuple(key)), me.Primer(primer)
        return cipher

    def extract_text(self, string):
        "Extract text message from string using built key and primer."
        text, primer = me.decrypt_str(string, self.key, self.primer)
        return text

    ########################################################################

    # Provide some special methods to simplify the program's code.

    @property
    def encrypting(self):
        "Encrypting boolean stating current operations mode."
        return {'encode': True, 'decode': False}[self.mode_var.get()]

    @staticmethod
    def bytes_to_repr(obj):
        "Convert bytes object into suitable representation."
        if not isinstance(obj, bytes):
            raise TypeError('Object must be a bytes instance!')
        return repr(obj)[2:-1]

    @staticmethod
    def repr_to_obj(string, prefix='b'):
        "Convert representation into an equivalent object."
        for template in '{}"{}"', "{}'{}'":
            try:
                return ast.literal_eval(template.format(prefix, string))
            except:
                pass
        raise ValueError('Cannot convert {!r} to object!'.format(string))

    @classmethod
    def main(cls):
        "Create context for demo and run a test instance."
        NoDefaultRoot()
        root = Tk()
        root.minsize(420, 330)
        root.title('Markov Demo 2')
        test = cls(root)
        test.grid(sticky=NSEW)
        root.grid_rowconfigure(0, weight=1)
        root.grid_columnconfigure(0, weight=1)
        root.mainloop()
Пример #22
0
class DateEntry(Frame):

    def __init__(self,
                 master=None,
                 dateformat='%B %d, %Y',
                 firstweekday=6,
                 startdate=None,
                 **kw
                 ):
        """
        Args:
            master (Widget): The parent widget.
            dateformat (str): The format string used to render the text in the entry widget. Default is '%B %d, %Y'.
            firstweekday (int): Specifies the first day of the week. ``0`` is Monday, ``6`` is Sunday (the default).
            startdate (datetime): The date to be in focus when the calendar is displayed. Current date is default.
            **kw: Optional keyword arguments to be passed to containing frame widget.
        """
        super().__init__(master=master, **kw)
        self.dateformat = dateformat
        self.firstweekday = firstweekday
        self.image = self.draw_button_image('white')
        self.startdate = startdate or datetime.today()

        # widget layout
        self.entry = Entry(self, name='date-entry')
        self.entry.pack(side='left', fill='x', expand='yes')
        self.button = ttk.Button(self, image=self.image, command=self.on_date_ask, padding=(2, 2))
        self.button.pack(side='left')

        # TODO consider adding data validation: https://www.geeksforgeeks.org/python-tkinter-validating-entry-widget/
        self.entry.insert('end', self.startdate.strftime(dateformat))  # starting entry value

    def draw_button_image(self, color):
        """Draw a calendar button image of the specified color

        Image reference: https://www.123rf.com/photo_117523637_stock-vector-modern-icon-calendar-button-applications.html

        Args:
            color (str): the color to draw the image foreground.

        Returns:
            PhotoImage: the image created for the calendar button.
        """
        im = Image.new('RGBA', (21, 22))
        draw = ImageDraw.Draw(im)

        # outline
        draw.rounded_rectangle([1, 3, 20, 21], radius=2, outline=color, width=1)

        # page spirals
        draw.rectangle([4, 1, 5, 5], fill=color)
        draw.rectangle([10, 1, 11, 5], fill=color)
        draw.rectangle([16, 1, 17, 5], fill=color)

        # row 1
        draw.rectangle([7, 9, 9, 11], fill=color)
        draw.rectangle([11, 9, 13, 11], fill=color)
        draw.rectangle([15, 9, 17, 11], fill=color)

        # row 2
        draw.rectangle([3, 13, 5, 15], fill=color)
        draw.rectangle([7, 13, 9, 15], fill=color)
        draw.rectangle([11, 13, 13, 15], fill=color)
        draw.rectangle([15, 13, 17, 15], fill=color)

        # row 3
        draw.rectangle([3, 17, 5, 19], fill=color)
        draw.rectangle([7, 17, 9, 19], fill=color)
        draw.rectangle([11, 17, 13, 19], fill=color)

        return ImageTk.PhotoImage(im)

    def on_date_ask(self):
        """A callback for when a date is requested from the widget button.
        
        Try to grab the initial date from the entry if possible. However, if this date is note valid, use the current
        date and print a message to the console.
        """
        try:
            self.startdate = datetime.strptime(self.entry.get(), self.dateformat)
        except Exception as e:
            print(e)
            self.startdate = datetime.today()
        olddate = datetime.strptime(self.entry.get() or self.startdate, self.dateformat)
        newdate = ask_date(self.entry, startdate=olddate, firstweekday=self.firstweekday)
        self.entry.delete('0', 'end')
        self.entry.insert('end', newdate.strftime(self.dateformat))
        self.entry.focus_force()
class MarkovDemo(Frame):

    "MarkovDemo(master=None, **kw) -> MarkovDemo instance"

    TEXT = dict(height=2, width=46, wrap=WORD)  # Text Options
    GRID = dict(padx=5, pady=5)  # Grid Options

    # Initialize a MarkovDemo instance with a GUI for interaction.

    def __init__(self, master=None, **kw):
        "Initialize the MarkovDemo instance's widgets and settings."
        super().__init__(master, **kw)
        self.build_widgets()
        self.place_widgets()
        self.setup_widgets()
        self.grid_rowconfigure(2, weight=1)
        self.grid_rowconfigure(3, weight=1)
        self.grid_columnconfigure(0, weight=1)
        self.key = self.primer = None

    def build_widgets(self):
        "Build the various widgets that will be used in the program."
        # Create processing frame widgets.
        self.processing_frame = LabelFrame(self, text='Processing Mode:')
        self.mode_var = StringVar(self, 'encode')
        self.decode_button = Radiobutton(self.processing_frame,
                                         text='Decode Cipher-Text',
                                         command=self.handle_radiobuttons,
                                         value='decode',
                                         variable=self.mode_var)
        self.encode_button = Radiobutton(self.processing_frame,
                                         text='Encode Plain-Text',
                                         command=self.handle_radiobuttons,
                                         value='encode',
                                         variable=self.mode_var)
        self.freeze_var = BooleanVar(self, False)
        self.freeze_button = Checkbutton(self.processing_frame,
                                         text='Freeze Key & Primer',
                                         command=self.handle_checkbutton,
                                         offvalue=False,
                                         onvalue=True,
                                         variable=self.freeze_var)
        # Create encoding frame widgets.
        self.encoding_frame = LabelFrame(self, text='Encoding Options:')
        self.chain_size_label = Label(self.encoding_frame, text='Chain Size:')
        self.chain_size_entry = Entry(self.encoding_frame)
        self.plain_text_label = Label(self.encoding_frame, text='Plain-Text:')
        self.plain_text_entry = Entry(self.encoding_frame)
        # Create input frame widgets.
        self.input_frame = LabelFrame(self, text='Input Area:')
        self.input_text = ScrolledText(self.input_frame, **self.TEXT)
        # Create output frame widgets.
        self.output_frame = LabelFrame(self, text='Output Area:')
        self.output_text = ScrolledText(self.output_frame, **self.TEXT)

    def place_widgets(self):
        "Place the widgets where they belong in the MarkovDemo frame."
        # Locate processing frame widgets.
        self.processing_frame.grid(sticky=EW, **self.GRID)
        self.decode_button.grid(row=0, column=0, **self.GRID)
        self.encode_button.grid(row=0, column=1, **self.GRID)
        self.freeze_button.grid(row=0, column=2, **self.GRID)
        # Locate encoding frame widgets.
        self.encoding_frame.grid(sticky=EW, **self.GRID)
        self.chain_size_label.grid(row=0, column=0, sticky=W, **self.GRID)
        self.chain_size_entry.grid(row=0, column=1, sticky=EW, **self.GRID)
        self.plain_text_label.grid(row=1, column=0, sticky=W, **self.GRID)
        self.plain_text_entry.grid(row=1, column=1, sticky=EW, **self.GRID)
        self.encoding_frame.grid_columnconfigure(1, weight=1)
        # Locate input frame widgets.
        self.input_frame.grid(sticky=NSEW, **self.GRID)
        self.input_text.grid(sticky=NSEW, **self.GRID)
        self.input_frame.grid_rowconfigure(0, weight=1)
        self.input_frame.grid_columnconfigure(0, weight=1)
        # Locate output frame widgets.
        self.output_frame.grid(sticky=NSEW, **self.GRID)
        self.output_text.grid(sticky=NSEW, **self.GRID)
        self.output_frame.grid_rowconfigure(0, weight=1)
        self.output_frame.grid_columnconfigure(0, weight=1)

    def setup_widgets(self):
        "Setup each widget's configuration for the events they handle."
        self.input_text.bind('<Key>', self.handle_key_events)
        self.input_text.bind('<Control-Key-a>', self.handle_control_a)
        self.input_text.bind('<Control-Key-/>', lambda event: 'break')
        self.output_text['state'] = DISABLED
        self.output_text.bind('<Control-Key-a>', self.handle_control_a)
        self.output_text.bind('<Control-Key-/>', lambda event: 'break')

    ########################################################################

    # Take care of any special event needing dedicated processing.

    def handle_radiobuttons(self):
        "Change the interface based on the encoding / decoding setting."
        if self.encrypting:
            self.freeze_button.grid()
            if not self.freeze_var.get():
                self.encoding_frame.grid()
        else:
            self.freeze_button.grid_remove()
            if not self.freeze_var.get():
                self.encoding_frame.grid_remove()
        self.handle_key_events(None)

    def handle_checkbutton(self):
        "Change the interface based on the key / primer freeze setting."
        if self.freeze_var.get():
            self.encoding_frame.grid_remove()
        else:
            self.encoding_frame.grid()

    def handle_key_events(self, event):
        "Schedule refreshing the output area after an input area event."
        if event is None or event.char and event.state | 0o11 == 0o11:
            self.after_idle(self.refresh)

    @staticmethod
    def handle_control_a(event):
        "Select all text in the widget associated with the given event."
        event.widget.tag_add(SEL, 1.0, END + '-1c')
        return 'break'

    ########################################################################

    # Handle interface's updates when either encoding or decoding.

    def refresh(self):
        "Refresh the output based on the value of the input."
        text = self.input_text.get(1.0, END + '-1c')
        if not text:
            self.output = text
        elif self.encrypting:
            self.encode(text)
        else:
            self.decode(text)

    def output(self, value):
        "Set the text in the output area to the string value."
        self.output_text['state'] = NORMAL
        self.output_text.delete(1.0, END)
        self.output_text.insert(END, value)
        if self.encrypting and self.freeze_var.get():
            self.output_text.see(END)
        self.output_text['state'] = DISABLED

    output = property(fset=output, doc='Output area property.')

    @property
    def chain_size(self):
        "Chain size for the Markov chains used when encrypting."
        try:
            value = ast.literal_eval(self.chain_size_entry.get())
            assert isinstance(value, int) and 2 <= value <= 256
            return value
        except:
            self.chain_size_entry.delete(0, END)
            self.chain_size_entry.insert(0, '2')
            return 2

    @property
    def plain_text(self):
        "Plain text or ignored characters in encryption process."
        try:
            value = self.repr_to_obj(self.plain_text_entry.get(), '')
            assert isinstance(value, str)
            return value
        except:
            self.plain_text_entry.delete(0, END)
            return ''

    ########################################################################

    # Encrypt a string for display in the interface's output area.

    def encode(self, string):
        "Encode the string and show the cipher-text in the output."
        try:
            cipher = self.build_cipher(string)
        except ValueError:
            self.output = ''
        except:
            self.output = traceback.format_exc()
        else:
            self.output = self.build_header() + '\n\n' + cipher

    def build_cipher(self, string):
        "Build cipher-text based on plain-text and return answer."
        if self.key and self.freeze_var.get():
            cipher, primer = me.encrypt_str(string, self.key, self.primer)
        else:
            args = string, self.chain_size, self.plain_text
            cipher, self.key, self.primer = me.auto_encrypt_str(*args)
        return cipher

    def build_header(self):
        "Build header from key and primer values in current use."
        header = '\n'.join(map(self.bytes_to_repr, self.key.data))
        header += '\n' + self.bytes_to_repr(self.primer.data)
        return header

    ########################################################################

    # Decrypt a string for display in the interface's output area.

    def decode(self, string):
        "Decode encrypted message and display plain-text in output."
        try:
            cipher = self.extract_keys(string)
            text = self.extract_text(cipher)
        except ValueError:
            self.output = ''
        except:
            self.output = traceback.format_exc()
        else:
            self.output = text

    def extract_keys(self, string):
        "Extract keys to decryption and return the cipher-text area."
        header, cipher = string.split('\n\n', 1)
        *key, primer = map(self.repr_to_obj, header.split('\n'))
        self.key, self.primer = me.Key(tuple(key)), me.Primer(primer)
        return cipher

    def extract_text(self, string):
        "Extract text message from string using built key and primer."
        text, primer = me.decrypt_str(string, self.key, self.primer)
        return text

    ########################################################################

    # Provide some special methods to simplify the program's code.

    @property
    def encrypting(self):
        "Encrypting boolean stating current operations mode."
        return {'encode': True, 'decode': False}[self.mode_var.get()]

    @staticmethod
    def bytes_to_repr(obj):
        "Convert bytes object into suitable representation."
        if not isinstance(obj, bytes):
            raise TypeError('Object must be a bytes instance!')
        return repr(obj)[2:-1]

    @staticmethod
    def repr_to_obj(string, prefix='b'):
        "Convert representation into an equivalent object."
        for template in '{}"{}"', "{}'{}'":
            try:
                return ast.literal_eval(template.format(prefix, string))
            except:
                pass
        raise ValueError('Cannot convert {!r} to object!'.format(string))

    @classmethod
    def main(cls):
        "Create context for demo and run a test instance."
        NoDefaultRoot()
        root = Tk()
        root.minsize(420, 330)
        root.title('Markov Demo 2')
        test = cls(root)
        test.grid(sticky=NSEW)
        root.grid_rowconfigure(0, weight=1)
        root.grid_columnconfigure(0, weight=1)
        root.mainloop()
Пример #24
0
class AudioDemo(Frame):
    """ A demo application class that provides simple GUI for testing featurizer+classifier on
    microphone or wav file input. """
    def __init__(self,
                 featurizer_model=None,
                 classifier_model=None,
                 auto_scale=True,
                 sample_rate=None,
                 channels=None,
                 input_device=None,
                 categories=None,
                 image_width=80,
                 threshold=None,
                 wav_file=None,
                 clear=5,
                 serial=None,
                 vad_model=None,
                 smoothing=None,
                 ignore_list=None):
        """ Initialize AudioDemo object
        featurizer_model - the path to the ELL featurizer
        classifier_model - the path to the ELL classifier
        auto_scale - auto scale audio input to range [-1, 1]
        sample_rate - sample rate to featurizer is expecting
        channels - number of channels featurizer is expecting
        input_device - optional id of microphone to use
        categories - path to file containing category labels
        image_width - width of the spectrogram image
        threshold - ignore predictions that have confidence below this number (e.g. 0.5)
        wav_file - optional wav_file to use  when you click Play
        serial - optional serial input, reading numbers from the given serial port.
        vad_model - optional ELL model containing VoiceActivityDetector
        smoothing - controls the size of the smoothing window (defaults to 0).
        ignore_list - list of category labels to ignore (like 'background' or 'silence')
        """
        super().__init__()

        self.CLASSIFIER_MODEL_KEY = "classifier_model"
        self.FEATURIZER_MODEL_KEY = "featurizer_model"
        self.WAV_FILE_KEY = "wav_file"
        self.CATEGORY_FILE_KEY = "categories"

        self.get_settings_file_name()
        self.load_settings()
        self.reading_input = False
        self.featurizer_model = None
        self.serial_port = serial
        self.smoothing = smoothing
        self.ignore_list = ignore_list

        if featurizer_model:
            self.featurizer_model = featurizer_model
            self.settings[self.FEATURIZER_MODEL_KEY] = featurizer_model
        elif self.FEATURIZER_MODEL_KEY in self.settings:
            self.featurizer_model = self.settings[self.FEATURIZER_MODEL_KEY]

        self.classifier_model = None
        if classifier_model:
            self.classifier_model = classifier_model
            self.settings[self.CLASSIFIER_MODEL_KEY] = classifier_model
        elif self.CLASSIFIER_MODEL_KEY in self.settings:
            self.classifier_model = self.settings[self.CLASSIFIER_MODEL_KEY]

        self.wav_filename = wav_file
        if self.wav_filename is None and self.WAV_FILE_KEY in self.settings:
            self.wav_filename = self.settings[self.WAV_FILE_KEY]

        self.wav_file_list = None
        self.auto_scale = auto_scale
        self.sample_rate = sample_rate if sample_rate is not None else 16000
        self.channels = channels if channels is not None else 1
        self.input_device = input_device
        self.num_classifier_features = None

        self.vad = None
        self.vad_reset = (vad_model is not None)
        self.previous_vad = 0
        self.vad_latch = 3  # only reset after 3 vad=0 signals to smooth vad signal a bit.
        if not categories and self.CATEGORY_FILE_KEY in self.settings:
            categories = self.settings[self.CATEGORY_FILE_KEY]

        self.categories = categories
        if categories:
            self.settings[self.CATEGORY_FILE_KEY] = categories

        self.save_settings()  # in case we just changed it.
        self.audio_level = 0
        self.min_level = 0
        self.max_level = 0
        self.threshold = threshold

        self.output_clear_time = int(clear * 1000) if clear else 5000

        self.featurizer = None
        self.classifier = None
        self.wav_file = None
        self.speaker = None
        self.microphone = None
        self.animation = None
        self.show_classifier_output = True
        self.last_prediction = None
        self.probability = 0

        # Threads
        self.read_input_thread = None
        self.lock = Lock()
        self.main_thread = get_ident()
        self.message_queue = []

        # UI components
        self.max_spectrogram_width = image_width
        self.features_entry = None
        self.classifier_feature_data = None
        self.spectrogram_image_data = None

        self.init_ui()

        if self.featurizer_model:
            self.load_featurizer_model(os.path.abspath(self.featurizer_model))
        else:
            self.show_output("Please specify and load a feature model")

        if smoothing == "vad":
            # smooth up to 1 second worth of predictions
            self.smoothing = int(self.sample_rate / self.featurizer.input_size)
            if vad_model is None:
                vad_model = make_vad.make_vad("vad.ell", self.sample_rate,
                                              self.featurizer.input_size,
                                              self.featurizer.output_size,
                                              None)

        if self.classifier_model:
            self.load_classifier(self.classifier_model)
            self.setup_spectrogram_image()
        else:
            self.show_output("Please specify and load a classifier model")

        if vad_model:
            self.vad = vad.VoiceActivityDetector(vad_model)

    def get_settings_file_name(self):
        """ this app stores the various UI field values in a settings file in your temp folder
        so you don't always have to specify the full command line options """
        import tempfile
        temp = tempfile.gettempdir()
        self.settings_file_name = os.path.join(temp, "ELL", "Audio",
                                               "viewaudio.json")

    def load_settings(self):
        """ load the previously saved settings from disk, if any """
        self.settings = {}
        print("loading settings from: {}".format(self.settings_file_name))
        if os.path.isfile(self.settings_file_name):
            with open(self.settings_file_name, "r") as f:
                self.settings = json.load(f)

    def save_settings(self):
        """ save the current settings to disk """
        settings_dir = os.path.dirname(self.settings_file_name)
        if not os.path.isdir(settings_dir):
            os.makedirs(settings_dir)
        with open(self.settings_file_name, "w") as f:
            json.dump(self.settings, f, indent=2)

    def load_featurizer_model(self, featurizer_model):
        """ load the given compiled ELL featurizer for use in processing subsequent audio input """
        if featurizer_model:
            self.featurizer = featurizer.AudioTransform(featurizer_model, 40)
            self.setup_spectrogram_image()

            self.show_output("Feature input size: {}, output size: {}".format(
                self.featurizer.input_size, self.featurizer.output_size))
            if self.features_entry.get() != featurizer_model:
                self.features_entry.delete(0, END)
                self.features_entry.insert(0, featurizer_model)

        self.init_data()

    def load_classifier(self, classifier_path):
        """ load the given compiled ELL classifier for use in processing subsequent audio input """
        if classifier_path:
            self.classifier = classifier.AudioClassifier(
                classifier_path,
                self.categories,
                self.threshold,
                smoothing_window=self.smoothing,
                ignore_list=self.ignore_list)
            self.show_output(
                "Classifier input size: {}, output size: {}".format(
                    self.classifier.input_size, self.classifier.output_size))
            if self.classifier_entry.get() != classifier_path:
                self.classifier_entry.delete(0, END)
                self.classifier_entry.insert(0, classifier_path)
        self.init_data()

    def init_data(self):
        """ initialize the spectrogram_image_data and classifier_feature_data based on the newly loaded model info """
        if self.featurizer:
            dim = (self.featurizer.output_size, self.max_spectrogram_width)
            self.spectrogram_image_data = np.zeros(dim, dtype=float)
            if self.spectrogram_widget:
                self.spectrogram_widget.clear(self.spectrogram_image_data)

            if self.classifier:
                self.num_classifier_features = self.classifier.input_size // self.featurizer.output_size
                dim = (self.num_classifier_features,
                       self.featurizer.output_size)
                self.classifier_feature_data = np.zeros(dim, dtype=float)

    def accumulate_feature(self, feature_data):
        """ accumulate the feature data and pass feature data to classifier """
        if self.vad:
            vad_signal = self.vad.predict(feature_data)
            if self.previous_vad != vad_signal:
                if vad_signal == 0:
                    if self.vad_latch > 0:
                        # wait for 2 more to smooth the vad signal a bit.
                        self.vad_latch -= 1
                    else:
                        self.vad_latch = 3
                        self.previous_vad = vad_signal

                        if self.vad_reset:
                            self.show_output("--- reset ---")
                            self.classifier.reset()
                        else:
                            self.show_output("--- clear history ---")
                            self.classifier.clear_smoothing()

                elif vad_signal == 1:
                    self.vad_latch = 3
                    self.previous_vad = vad_signal

        self.audio_level = np.sum([x * x for x in feature_data])

        if self.classifier and self.show_classifier_output:
            self.classifier_feature_data = np.vstack(
                (self.classifier_feature_data,
                 feature_data))[-self.num_classifier_features:, :]
            self.evaluate_classifier()

    def accumulate_spectrogram_image(self, feature_data):
        """ accumulate the feature data into the spectrogram image """
        image_data = self.spectrogram_image_data
        feature_data = np.reshape(feature_data, [-1, 1])
        new_image = np.hstack(
            (image_data, feature_data))[:, -image_data.shape[1]:]
        image_data[:, :] = new_image

    def update_rgb_led(self):
        # This helper function uses the RGB led UI to give an indication of audio levels (brightness)
        # and voice activity (red)
        level = self.audio_level
        if level < self.min_level:
            self.min_level = level
        if level > self.max_level:
            self.max_level = level

        red = 0.0
        green = 0.0
        blue = 0.0

        range = self.max_level - self.min_level
        if range == 0:
            range = 1.0
        brightness = 128 * (level - self.min_level) / range
        if self.previous_vad:
            red = brightness + 127
        else:
            green = brightness + 127
        rgb = "#{:02x}{:02x}{:02x}".format(int(red), int(green), int(blue))
        self.rgb_canvas.itemconfig(self.rgb_oval, fill=rgb)

    def on_ui_update(self):
        # this is an animation callback to update the UI every 33 milliseconds.
        self.update_rgb_led()
        self.process_output()
        result = self.set_spectrogram_image()
        if not self.reading_input:
            self.after(1, self.on_stopped)
        return (result, )

    def set_spectrogram_image(self):
        """ update the spectrogram image and the min/max values """
        self.lock.acquire()  # protect access to the shared state
        result = self.spectrogram_widget.show(self.spectrogram_image_data)
        self.lock.release()
        return result

    def get_correct_shape(self, shape):
        """ for some reason keras stores input shape as (None,80,40), and numpy hates that
        so we have to change this to (1,80,40) """
        shape = list(shape)
        fix = [x if x else 1 for x in shape]
        return tuple(fix)

    def clear_output(self):
        """ remove some of the Output based a the timeout callback  """
        self.output_text.delete(1.0, 2.0)

    def process_output(self):
        """ show output that was queued by background thread """
        self.lock.acquire()
        messages = self.message_queue
        self.message_queue = []
        self.lock.release()
        for msg in messages:
            self.show_output(msg)

    def show_output(self, message):
        """ show output message, or queue it if we are on a background thread """
        if self.main_thread != get_ident():
            self.message_queue += [message]
            return

        for line in str(message).split('\n'):
            self.output_text.insert(END, "{}\n".format(line))

        self.output_text.see("end")  # scroll to end
        self.after(self.output_clear_time, self.clear_output)

    def evaluate_classifier(self):
        """ run the classifier model on the current feature data and show the prediction, if any """
        if self.evaluate_classifier and self.classifier and self.classifier_feature_data is not None:
            prediction, probability, label, _ = self.classifier.predict(
                self.classifier_feature_data.ravel())
            if prediction is not None:
                percent = int(100 * probability)
                if self.last_prediction != prediction or self.probability < probability:
                    self.last_prediction = prediction
                    self.probability = probability
                    self.show_output(" DETECTED ({}) {}% {}".format(
                        prediction, percent, label))

    def start_playing(self, filename):
        """ Play a wav file, and classify the audio. Note we use a background thread to read the
        wav file and we setup a UI animation function to draw the sliding spectrogram image, this way
        the UI update doesn't interfere with the smoothness of the audio playback """
        if self.speaker is None:
            self.speaker = speaker.Speaker()

        self.stop()
        self.reading_input = False
        self.wav_file = wav_reader.WavReader(self.sample_rate, self.channels,
                                             self.auto_scale)
        self.wav_file.open(filename, self.featurizer.input_size, self.speaker)

        def update_func(frame_index):
            return self.on_ui_update()

        if self.animation:
            self.animation.event_source.stop()
        self.reading_input = True

        # Start animation timer for updating the UI (e.g. spectrogram image)
        self.animation = self.spectrogram_widget.begin_animation(update_func)

        # start background thread to read and classify the audio.
        self.featurizer.open(self.wav_file)
        self.read_input_thread = Thread(target=self.on_read_features, args=())
        self.read_input_thread.daemon = True
        self.read_input_thread.start()

    def start_recording(self):
        """ Start recording audio from the microphone nd classify the audio. Note we use a background thread to
        process the audio and we setup a UI animation function to draw the sliding spectrogram image, this way
        the UI update doesn't interfere with the smoothness of the microphone readings """

        self.stop()

        input_channel = None

        if self.serial_port:
            import serial_reader
            self.serial = serial_reader.SerialReader(0.001)
            self.serial.open(self.featurizer.input_size, self.serial_port)
            input_channel = self.serial
        else:
            if self.microphone is None:
                self.microphone = microphone.Microphone(
                    auto_scale=self.auto_scale, console=False)

            num_channels = 1
            self.microphone.open(self.featurizer.input_size, self.sample_rate,
                                 num_channels, self.input_device)
            input_channel = self.microphone

        def update_func(frame_index):
            return self.on_ui_update()

        if self.animation:
            self.animation.event_source.stop()

        self.reading_input = True
        # Start animation timer for updating the UI (e.g. spectrogram image)
        self.animation = self.spectrogram_widget.begin_animation(update_func)

        # start background thread to read and classify the recorded audio.
        self.featurizer.open(input_channel)
        self.read_input_thread = Thread(target=self.on_read_features, args=())
        self.read_input_thread.daemon = True
        self.read_input_thread.start()

    def on_read_features(self):
        """ this is the background thread entry point.  So we read the feature data in a loop
        and pass it to the classifier """
        try:
            while self.reading_input and self.featurizer:
                feature_data = self.featurizer.read()
                if feature_data is None:
                    break  # eof
                else:
                    self.lock.acquire()
                    self.accumulate_feature(feature_data)
                    self.accumulate_spectrogram_image(feature_data)
                    self.lock.release()
        except:
            errorType, value, traceback = sys.exc_info()
            print("### Exception reading input: " + str(errorType) + ": " +
                  str(value) + " " + str(traceback))
            while traceback:
                print(traceback.tb_frame.f_code)
                traceback = traceback.tb_next

        self.reading_input = False
        if self.classifier:
            self.classifier.reset()  # good time to reset.

    def stop(self):
        """ called when user clicks the stop button, or we reach the end of a wav file input """
        # close streams
        if self.animation:
            self.animation.event_source.stop()
            self.animation = None
        if self.microphone:
            self.microphone.close()
        if self.speaker:
            self.speaker.close()
        if self.wav_file:
            self.wav_file.close()
            self.wav_file = None
        self.reading_input = False
        self.last_prediction = None
        self.probability = 0
        if self.classifier:
            self.classifier.reset()  # good time to reset.

    def on_rec_button_click(self):
        """ called when user clicks the record button, same button is used to "stop" recording. """
        if self.rec_button["text"] == "Rec":
            self.rec_button["text"] = "Stop"
            self.play_button["text"] = "Play"
            self.start_recording()
        else:
            self.rec_button["text"] = "Rec"
            self.on_stop()

    def on_play_button_click(self):
        """ called when user clicks the record button, same button is used to "stop" playback """
        if self.play_button["text"] == "Play":
            self.play_button["text"] = "Stop"
            self.rec_button["text"] = "Rec"
            self.on_play()
        else:
            self.play_button["text"] = "Play"
            self.on_stop()

    def on_play(self):
        """ called when user clicks the Play button """
        filename = self.wav_filename_entry.get()
        filename = filename.strip('"')
        self.wav_filename = filename
        self.settings[self.WAV_FILE_KEY] = filename
        self.save_settings()
        self.start_playing(filename)

    def on_stop(self):
        """ called when user clicks the Stop button """
        self.reading_input = False
        if self.wav_file:
            self.wav_file.close()
            self.wav_file = None
        if self.read_input_thread:
            self.read_input_thread.join()
            self.read_input_thread = None
        self.stop()

    def on_stopped(self):
        """ called when we reach the end of the wav file playback """
        self.play_button["text"] = "Play"
        self.stop()

    def get_wav_list(self):
        if self.wav_filename and os.path.isfile(self.wav_filename):
            dir_name = os.path.dirname(self.wav_filename)
            if not self.wav_file_list:
                self.wav_file_list = [
                    x for x in os.listdir(dir_name)
                    if os.path.splitext(x)[1] == ".wav"
                ]
                self.wav_file_list.sort()
        return self.wav_file_list

    def select_wav_file(self, filename):
        self.wav_filename = filename
        # show the file in the UI
        self.wav_filename_entry.delete(0, END)
        if self.wav_filename:
            self.wav_filename_entry.insert(0, self.wav_filename)
        # and automatically play the file.
        self.on_play()

    def on_minus_key(self, event):
        """ When user presses the plus button we reverse to the previous wav file in the current folder.
        This way you can easily step through all the training wav files """
        if self.get_wav_list():
            i = self.wav_file_list.index(os.path.basename(self.wav_filename))
            if i - 1 >= 0:
                next_wav_file = self.wav_file_list[i - 1]
                dir_name = os.path.dirname(self.wav_filename)
                self.select_wav_file(os.path.join(dir_name, next_wav_file))

    def on_plus_key(self, event):
        """ When user presses the plus button we advance to the next wav file in the current folder.
        This way you can easily step through all the training wav files """
        if self.get_wav_list():
            i = self.wav_file_list.index(os.path.basename(self.wav_filename))
            if i + 1 < len(self.wav_file_list):
                next_wav_file = self.wav_file_list[i + 1]
                dir_name = os.path.dirname(self.wav_filename)
                self.select_wav_file(os.path.join(dir_name, next_wav_file))

    def init_ui(self):
        """ setup the GUI for the app """
        self.master.title("Test")
        self.pack(fill=BOTH, expand=True)

        # Input section
        input_frame = LabelFrame(self, text="Input")
        input_frame.bind("-", self.on_minus_key)
        input_frame.bind("+", self.on_plus_key)
        input_frame.pack(fill=X)
        self.play_button = Button(input_frame,
                                  text="Play",
                                  command=self.on_play_button_click)
        self.play_button.pack(side=RIGHT, padx=4)

        self.rgb_canvas = tk.Canvas(input_frame, width=20, height=20, bd=0)
        self.rgb_oval = self.rgb_canvas.create_oval(2,
                                                    2,
                                                    20,
                                                    20,
                                                    fill='#FF0000',
                                                    width=0)
        self.rgb_canvas.pack(side=RIGHT, padx=4)

        self.rec_button = Button(input_frame,
                                 text="Rec",
                                 command=self.on_rec_button_click)
        self.rec_button.pack(side=RIGHT, padx=4)

        self.wav_filename_entry = Entry(input_frame, width=24)
        self.wav_filename_entry.pack(fill=X)
        self.wav_filename_entry.delete(0, END)
        if self.wav_filename:
            self.wav_filename_entry.insert(0, self.wav_filename)

        # Feature section
        features_frame = LabelFrame(self, text="Features")
        features_frame.pack(fill=X)

        features_control_frame = Frame(features_frame)
        features_control_frame.pack(fill=X)
        load_features_button = Button(features_control_frame,
                                      text="Load",
                                      command=self.on_load_featurizer_model)
        load_features_button.pack(side=RIGHT)
        self.features_entry = Entry(features_control_frame, width=8)
        self.features_entry.pack(fill=X)
        self.features_entry.delete(0, END)
        if self.featurizer_model:
            self.features_entry.insert(0, self.featurizer_model)

        self.spectrogram_widget = SpectrogramImage(features_frame)
        self.spectrogram_widget.pack(fill=X)

        # Classifier section
        classifier_frame = LabelFrame(self, text="Classifier")
        classifier_frame.pack(fill=X)
        load_classifier_button = Button(classifier_frame,
                                        text="Load",
                                        command=self.on_load_classifier)
        load_classifier_button.pack(side=RIGHT)
        self.classifier_entry = Entry(classifier_frame, width=8)
        self.classifier_entry.pack(fill=X)
        self.classifier_entry.delete(0, END)
        if self.classifier_model:
            self.classifier_entry.insert(0, self.classifier_model)

        # Output section
        output_frame = LabelFrame(self, text="Output")
        output_frame.pack(fill=BOTH, expand=True)

        self.output_text = Text(output_frame)
        self.output_text.pack(fill=BOTH, padx=4, expand=True)

    def setup_spectrogram_image(self):
        """ this needs to be called if you load a new feature model, because the featurizer output size might have
        changed. """
        if self.featurizer:
            dim = (self.featurizer.output_size, self.max_spectrogram_width)
            self.spectrogram_image_data = np.zeros(dim, dtype=float)
            self.spectrogram_widget.show(self.spectrogram_image_data)

    def on_load_featurizer_model(self):
        """ called when user clicks the Load button for the feature model """
        filename = self.features_entry.get()
        filename = filename.strip('"')
        self.settings[self.FEATURIZER_MODEL_KEY] = filename
        self.save_settings()
        self.stop()
        self.load_featurizer_model(filename)

    def on_load_classifier(self):
        """ called when user clicks the Load button for the feature model """
        self.classifier_model = self.classifier_entry.get()
        self.settings[self.CLASSIFIER_MODEL_KEY] = self.classifier_model
        self.save_settings()
        self.stop()
        self.load_classifier(self.classifier_model)
Пример #25
0
class ManagerWindow:
    """
    主管理界面入口类,直接无参数创建对象即可。
    """

    # 窗口宽高
    WIN_WIDTH = 800
    WIN_HEIGHT = 600

    def __init__(self):
        # 界面根节点
        self.root = Tk()

        # 主窗口标题
        self.root.title(MANAGER_TITLE)

        # 读取config
        self.config_dict = ConfigOperation.get_dir_from_file()

        # 主窗口分辨率
        self.root.geometry("%sx%s+%s+%s" % (
            self.WIN_WIDTH,
            self.WIN_HEIGHT,
            int((self.root.winfo_screenwidth() - self.WIN_WIDTH) / 2),
            int((self.root.winfo_screenheight() - self.WIN_HEIGHT) / 2)
        ))
        self.root.minsize(self.WIN_WIDTH, self.WIN_HEIGHT)

        # 选项卡
        self.tab_main = Notebook(self.root)
        self.tab_main.pack(expand=True, fill=BOTH)

        # 登录选项卡
        self.frame_login = Frame(self.tab_main, bg=BG_COLOR)
        self.frame_login.pack(side=TOP)
        self.tab_main.add(self.frame_login, text=TAB_NAME_LIST["login"]["text"])

        # 管理选项卡
        self.frame_manage = Frame(self.tab_main, bg=BG_COLOR)
        self.tab_main.add(self.frame_manage, text=TAB_NAME_LIST["manage"]["text"])

        # 好友选项卡
        self.frame_friend = Frame(self.tab_main, bg=BG_COLOR)
        self.frame_friend.pack(side=TOP)
        self.tab_main.add(self.frame_friend, text=TAB_NAME_LIST["friends"]["text"])

        # 群选项卡
        self.frame_group = Frame(self.tab_main, bg=BG_COLOR)
        self.frame_group.pack(side=TOP)
        self.tab_main.add(self.frame_group, text=TAB_NAME_LIST["groups"]["text"])

        # 插件选项卡
        self.frame_plugin = Frame(self.tab_main, bg=BG_COLOR)
        self.frame_plugin.pack(side=TOP)
        self.tab_main.add(self.frame_plugin, text=TAB_NAME_LIST["plugins"]["text"])

        # 初始化登录选项卡
        self.__init_login_tab()

        # 初始化好友选项卡
        self.__init_friend_tab()

        # 初始化群选项卡
        self.__init_group_tab()

        # 初始化管理选项卡
        self.__init_manage_tab()

        # 初始化插件选项卡
        self.__init_plugin_tab()

        # 关闭窗口自动释放Session
        self.root.protocol("WM_DELETE_WINDOW", lambda: self.__on_close_root())

        # 刷新显示
        self.__refresh()

        # 运行相关线程
        fetch_message_thread = FetchMessageThread()
        fetch_message_thread.daemon = True
        fetch_message_thread.start()

        # 运行插件初始化方法
        PluginHandler.call_init()

        # 执行自动连接一次
        self.__auto_connect()

        # 显示
        self.root.mainloop()

    def __init_login_tab(self):
        """
        初始化登录选项卡界面

        :return: 无
        """
        # 左边列表的frame
        frame_login_list = Frame(self.frame_login, bg=BG_COLOR)
        frame_login_list.pack(
            side=LEFT,
            expand=True,
            fill=BOTH,
            padx=5,
            pady=5
        )

        # 列表,用于保存连接记录
        self.treeview_login_list = Treeview(
            frame_login_list,
            columns=[
                LOGIN_GUIDE["host"],
                LOGIN_GUIDE["port"],
                LOGIN_GUIDE["authkey"],
                LOGIN_GUIDE["qq"]
            ],
            show="headings",
            selectmode=BROWSE
        )
        self.treeview_login_list.pack(
            expand=True,
            fill=BOTH,
            side=LEFT
        )
        self.treeview_login_list.column(
            LOGIN_GUIDE["host"],
            width=0
        )
        self.treeview_login_list.heading(
            LOGIN_GUIDE["host"],
            text=LOGIN_GUIDE["host"]
        )
        self.treeview_login_list.column(
            LOGIN_GUIDE["port"],
            width=0
        )
        self.treeview_login_list.heading(
            LOGIN_GUIDE["port"],
            text=LOGIN_GUIDE["port"]
        )
        self.treeview_login_list.column(
            LOGIN_GUIDE["authkey"],
            width=40
        )
        self.treeview_login_list.heading(
            LOGIN_GUIDE["authkey"],
            text=LOGIN_GUIDE["authkey"]
        )
        self.treeview_login_list.column(
            LOGIN_GUIDE["qq"],
            width=0
        )
        self.treeview_login_list.heading(
            LOGIN_GUIDE["qq"],
            text=LOGIN_GUIDE["qq"]
        )

        # 设定双击事件
        self.treeview_login_list.bind(
            "<Double-Button-1>",
            lambda event: self.__on_double_click_login_list_content()
        )

        # 设定登录列表的滚动条
        scrollbar_login_list = Scrollbar(frame_login_list)
        scrollbar_login_list.pack(fill="y", expand=True)
        self.treeview_login_list.config(yscrollcommand=scrollbar_login_list.set)
        scrollbar_login_list.config(command=self.treeview_login_list.yview)

        # 设置列表右键菜单
        self.treeview_login_list.bind("<Button-3>", self.__show_login_list_pop_up_menu)

        # 登录界面显示的那一坨
        frame_login_menu = Frame(self.frame_login, bg=BG_COLOR)
        frame_login_menu.pack(side=LEFT, padx=5, pady=5)

        # mirai端地址
        Label(frame_login_menu, text=LOGIN_GUIDE["host"], bg=BG_COLOR).grid(row=0, sticky=E, padx=5, pady=5)
        self.entry_host = Entry(frame_login_menu)
        self.entry_host.grid(row=0, column=1, sticky=W, padx=5, pady=5)

        # mirai端端口号
        Label(frame_login_menu, text=LOGIN_GUIDE["port"], bg=BG_COLOR).grid(row=1, sticky=E, padx=5, pady=5)
        self.entry_port = Entry(frame_login_menu)
        self.entry_port.grid(row=1, column=1, sticky=W, padx=5, pady=5)

        # mirai端http授权码
        Label(frame_login_menu, text=LOGIN_GUIDE["authkey"], bg=BG_COLOR).grid(
            row=2,
            sticky=E,
            padx=5,
            pady=5
        )
        self.entry_authkey = Entry(frame_login_menu, show=PWD_CHAR_CIRCLE)
        self.entry_authkey.grid(row=2, column=1, sticky=W, padx=5, pady=5)

        # 用于激活sessioKey的qq号码
        Label(frame_login_menu, text=LOGIN_GUIDE["qq"], bg=BG_COLOR).grid(
            row=3,
            sticky=E,
            padx=5,
            pady=5
        )
        self.entry_qq = Entry(frame_login_menu)
        self.entry_qq.grid(row=3, column=1, sticky=W, padx=5, pady=5)

        # 自动连接复选框
        self.auto_connect_var = BooleanVar()
        self.checkbutton_auto_connect = Checkbutton(
            frame_login_menu,
            text=AUTO_CONNECT_GUIDE,
            onvalue=True,
            offvalue=False,
            variable=self.auto_connect_var,
            bg=BG_COLOR
        )
        self.checkbutton_auto_connect.grid(row=4, column=0, padx=5, pady=5, columnspan=2)

        # 连接按钮
        self.btn_connect = Button(
            frame_login_menu,
            text=BTN_TEXT_CONN["connect"],
            width=15,
            command=lambda: self.__on_click_connect_event(),
        )
        self.btn_connect.grid(row=5, columnspan=2, padx=5, pady=5)

        # 添加到登录列表按钮
        self.btn_save_login = Button(
            frame_login_menu,
            width=15,
            text=BTN_TEXT_ADD_LOGIN,
            command=lambda: self.__on_click_add_to_login_list()
        )
        self.btn_save_login.grid(row=6, columnspan=2, padx=5, pady=5)

        # 状态栏
        self.label_login_status_bar = Label(
            self.root,
            text=LOGIN_STATUS_BAR_TEXT["notConnect"],
            fg=STATUS_BAR_COLOR["normal"]
        )
        self.label_login_status_bar.pack(side=LEFT)

        # 下面开始从config中将内容填充进文本框中
        self.entry_host.delete(0, END)
        self.entry_host.insert(END, self.config_dict["lastConnection"]["host"])
        self.entry_port.delete(0, END)
        self.entry_port.insert(END, self.config_dict["lastConnection"]["port"])
        self.entry_authkey.delete(0, END)
        self.entry_authkey.insert(END, self.config_dict["lastConnection"]["authkey"])
        self.entry_qq.delete(0, END)
        self.entry_qq.insert(END, self.config_dict["lastConnection"]["qq"])

        # 自动连接复选框内容
        self.auto_connect_var.set(self.config_dict["autoConnect"])

    def __init_friend_tab(self):
        """
        初始化好友选项卡内容

        :return: 无
        """

        # 创建好友列表框架
        frame_friend_list = Frame(self.frame_friend, bg=BG_COLOR)
        frame_friend_list.pack(
            side=LEFT,
            expand=True,
            fill=BOTH,
            padx=5,
            pady=5
        )

        # 创建消息测试发送框架
        frame_friend_send = Frame(self.frame_friend, bg=BG_COLOR)
        frame_friend_send.pack(
            side=LEFT,
            padx=5,
            pady=5
        )

        # 设置列表
        self.treeview_friend_list = Treeview(
            frame_friend_list,
            columns=[
                FRIEND_GUIDE["qq"],
                FRIEND_GUIDE["nickname"],
                FRIEND_GUIDE["remark"]
            ],
            show="headings",
            selectmode=BROWSE
        )
        self.treeview_friend_list.pack(
            expand=True,
            fill=BOTH,
            side=LEFT
        )
        self.treeview_friend_list.column(
            FRIEND_GUIDE["qq"],
            width=0
        )
        self.treeview_friend_list.heading(
            FRIEND_GUIDE["qq"],
            text=FRIEND_GUIDE["qq"]
        )
        self.treeview_friend_list.column(
            FRIEND_GUIDE["nickname"],
            width=0
        )
        self.treeview_friend_list.heading(
            FRIEND_GUIDE["nickname"],
            text=FRIEND_GUIDE["nickname"]
        )
        self.treeview_friend_list.column(
            FRIEND_GUIDE["remark"],
            width=0
        )
        self.treeview_friend_list.heading(
            FRIEND_GUIDE["remark"],
            text=FRIEND_GUIDE["remark"]
        )

        # 设定好友列表的滚动条
        scrollbar_friend_list = Scrollbar(frame_friend_list)
        scrollbar_friend_list.pack(fill="y", expand=True)
        self.treeview_friend_list.config(yscrollcommand=scrollbar_friend_list.set)
        scrollbar_friend_list.config(command=self.treeview_friend_list.yview)

        # 刷新列表按钮
        Button(
            frame_friend_send,
            text=BTN_FRIEND_REFRESH,
            command=lambda: self.__on_click_refresh_friend_list_event()
        ).grid(row=0, padx=5, pady=5)

        # 发送纯文本窗口标题
        Label(frame_friend_send, text=SEND_TITLE, bg=BG_COLOR).grid(row=1, padx=5, pady=5)

        # 发送纯文本窗口
        self.text_friend_send = Text(frame_friend_send, width=30, height=5)
        self.text_friend_send.grid(row=2, padx=5, pady=5)

        # 发送按钮
        Button(
            frame_friend_send,
            text=BTN_SEND,
            command=lambda: self.__on_click_send_friend_message()
        ).grid(row=3, padx=5, pady=5)

    def __init_group_tab(self):
        """
        初始化群选项卡内容

        :return: 无
        """

        # 创建好友列表框架
        frame_group_list = Frame(self.frame_group, bg=BG_COLOR)
        frame_group_list.pack(
            side=LEFT,
            expand=True,
            fill=BOTH,
            padx=5,
            pady=5
        )

        # 创建消息测试发送框架
        frame_group_send = Frame(self.frame_group, bg=BG_COLOR)
        frame_group_send.pack(
            side=LEFT,
            padx=5,
            pady=5
        )

        # 设置列表
        self.treeview_group_list = Treeview(
            frame_group_list,
            columns=[
                GROUP_GUIDE["group"],
                GROUP_GUIDE["name"],
                GROUP_GUIDE["permission"]
            ],
            show="headings",
            selectmode=BROWSE
        )
        self.treeview_group_list.pack(
            expand=True,
            fill=BOTH,
            side=LEFT
        )
        self.treeview_group_list.column(
            GROUP_GUIDE["group"],
            width=0
        )
        self.treeview_group_list.heading(
            GROUP_GUIDE["group"],
            text=GROUP_GUIDE["group"]
        )
        self.treeview_group_list.column(
            GROUP_GUIDE["name"],
            width=0
        )
        self.treeview_group_list.heading(
            GROUP_GUIDE["name"],
            text=GROUP_GUIDE["name"]
        )
        self.treeview_group_list.column(
            GROUP_GUIDE["permission"],
            width=0
        )
        self.treeview_group_list.heading(
            GROUP_GUIDE["permission"],
            text=GROUP_GUIDE["permission"]
        )

        # 设定群列表的滚动条
        scrollbar_group_list = Scrollbar(frame_group_list)
        scrollbar_group_list.pack(fill="y", expand=True)
        self.treeview_group_list.config(yscrollcommand=scrollbar_group_list.set)
        scrollbar_group_list.config(command=self.treeview_group_list.yview)

        # 刷新列表按钮
        Button(
            frame_group_send,
            text=BTN_GROUP_REFRESH,
            command=lambda: self.__on_click_refresh_group_list_event()
        ).grid(row=0, padx=5, pady=5)

        # 发送纯文本窗口标题
        Label(frame_group_send, text=SEND_TITLE, bg=BG_COLOR).grid(row=1, padx=5, pady=5)

        # 发送纯文本窗口
        self.text_group_send = Text(frame_group_send, width=30, height=5)
        self.text_group_send.grid(row=2, padx=5, pady=5)

        # 发送按钮
        Button(
            frame_group_send,
            text=BTN_SEND,
            command=lambda: self.__on_click_send_group_message()
        ).grid(row=3, padx=5, pady=5)

    def __init_manage_tab(self):
        """
        初始化管理选项卡

        :return: 无
        """

        f_manage = Frame(self.frame_manage, bg=BG_COLOR)
        f_manage.pack(padx=5, pady=5, expand=True)

        # 指定头指示
        Label(f_manage, text=MANAGE_GUIDE["commandHead"], bg=BG_COLOR).grid(
            row=0,
            column=0,
            padx=5,
            pady=5,
            sticky=E
        )

        # 指令头文本框
        self.entry_command_head = Entry(f_manage)
        self.entry_command_head.grid(row=0, column=1, padx=5, pady=5, sticky=EW)

        # 调试复选框
        self.debug_var = BooleanVar()
        checkbutton_debug = Checkbutton(
            f_manage,
            text=MANAGE_GUIDE["debug"],
            onvalue=True,
            offvalue=False,
            variable=self.debug_var,
            bg=BG_COLOR
        )
        checkbutton_debug.grid(row=1, column=0, columnspan=3, padx=5, pady=5)

        # 启用机器人
        self.enable_var = BooleanVar()
        checkbutton_enable = Checkbutton(
            f_manage,
            text=MANAGE_GUIDE["enable"],
            onvalue=True,
            offvalue=False,
            variable=self.enable_var,
            bg=BG_COLOR
        )
        checkbutton_enable.grid(row=2, column=0, columnspan=3, padx=5, pady=5)

        # 配置保存
        Button(
            f_manage,
            text=MANAGE_GUIDE["saveConfig"],
            command=self.__on_click_save_config
        ).grid(
            row=3,
            column=1,
            padx=5,
            pady=5,
            sticky=EW
        )

        # bot管理qq列表
        self.treeview_op_list = Treeview(
            f_manage,
            columns=[
                MANAGE_GUIDE["botOpQQ"]
            ],
            show="headings",
            selectmode=BROWSE
        )
        self.treeview_op_list.column(MANAGE_GUIDE["botOpQQ"], width=200)
        self.treeview_op_list.heading(MANAGE_GUIDE["botOpQQ"], text=MANAGE_GUIDE["botOpQQ"])
        self.treeview_op_list.grid(
            row=4,
            column=0,
            columnspan=3,
            rowspan=10,
            sticky=EW
        )

        # 列表右键
        self.treeview_op_list.bind("<Button-3>", self.__show_op_list_pop_up_menu)

        # 添加管理标签
        Label(f_manage, text=MANAGE_GUIDE["addOpQQ"], bg=BG_COLOR).grid(row=14, column=0, padx=5, pady=5)

        # 添加管理文本框
        self.entry_add_op = Entry(f_manage)
        self.entry_add_op.grid(row=14, column=1, padx=5, pady=5)

        # 添加添加按钮
        Button(
            f_manage,
            text=MANAGE_GUIDE["btnAddOpQQ"],
            command=lambda: self.__on_click_add_op()
        ).grid(row=14, column=2, padx=5, pady=5, sticky=EW)

    def __init_plugin_tab(self):
        """
        初始化插件选项卡

        :return: 无
        """

        # 指示标签
        Label(self.frame_plugin, text=PLUGIN_LABEL_TEXT, bg=BG_COLOR).pack(side=TOP)

        # 插件列表frame
        frame_plugin_list = Frame(self.frame_plugin, bg=BG_COLOR)
        frame_plugin_list.pack(
            side=TOP,
            expand=True,
            fill=BOTH,
            padx=5,
            pady=5
        )

        # 插件列表
        self.treeview_plugin_list = Treeview(
            frame_plugin_list,
            columns=[
                PLUGIN_GUIDE["pluginName"]
            ],
            show="headings",
            selectmode=BROWSE
        )
        self.treeview_plugin_list.pack(fill=BOTH, expand=True, side=LEFT)
        self.treeview_plugin_list.column(PLUGIN_GUIDE["pluginName"])
        self.treeview_plugin_list.heading(PLUGIN_GUIDE["pluginName"], text=PLUGIN_GUIDE["pluginName"])

        # 设定插件列表滚动条
        scrollbar_plugin_list = Scrollbar(frame_plugin_list)
        scrollbar_plugin_list.pack(fill="y", expand=True)
        self.treeview_plugin_list.config(yscrollcommand=scrollbar_plugin_list.set)
        scrollbar_plugin_list.config(command=self.treeview_plugin_list.yview)

    def __on_click_connect_event(self):
        """
        点击连接按钮事件

        :return: 无
        """

        if not GlobalValues.is_connected:
            # 如果是要连接

            # 存到全局使用变量
            GlobalValues.conn_host = self.entry_host.get()
            GlobalValues.conn_port = self.entry_port.get()
            GlobalValues.conn_authkey = self.entry_authkey.get()
            try:
                # 转换为整型后保存
                GlobalValues.conn_qq = int(self.entry_qq.get())
            except ValueError:
                self.label_login_status_bar.config(text=LOGIN_STATUS_BAR_TEXT["wrongQQ"], fg=STATUS_BAR_COLOR["failed"])
                return

            # 修改界面上的一些内容为不可修改
            self.__set_login_tools_active(False)

            # 修改按钮内容
            self.btn_connect.config(text=BTN_TEXT_CONN["disconnect"])

            # 修改状态栏内容
            self.label_login_status_bar.config(text=LOGIN_STATUS_BAR_TEXT["connecting"], fg=STATUS_BAR_COLOR["normal"])

            # 调用连接
            try:
                Conn.new_session_key()
            except (
                requests.exceptions.InvalidURL,
                requests.exceptions.ConnectionError,
            ):
                # 连接错误

                # 错误信息显示到状态栏
                self.label_login_status_bar.config(
                    text=LOGIN_STATUS_BAR_TEXT["connectFailed"],
                    fg=STATUS_BAR_COLOR["failed"]
                )

                # 修改文本框为可修改
                self.__set_login_tools_active(True)
                self.btn_connect.config(text=BTN_TEXT_CONN["connect"])
                return
            except WrongAuthkeyException:
                # 授权码错误

                # 显示到状态栏
                self.label_login_status_bar.config(
                    text=LOGIN_STATUS_BAR_TEXT["wrongAuthkey"],
                    fg=STATUS_BAR_COLOR["failed"]
                )

                # 修改文本框为可修改
                self.__set_login_tools_active(True)
                self.btn_connect.config(text=BTN_TEXT_CONN["connect"])
                return

            except BotNotExistException:
                # bot不存在错误
                self.label_login_status_bar.config(
                    text=LOGIN_STATUS_BAR_TEXT["qqNotExist"],
                    fg=STATUS_BAR_COLOR["failed"]
                )

                # 修改文本框为可修改
                self.__set_login_tools_active(True)
                self.btn_connect.config(text=BTN_TEXT_CONN["connect"])
                return

            self.label_login_status_bar.config(
                text=LOGIN_STATUS_BAR_TEXT["connected"],
                fg=STATUS_BAR_COLOR["passed"]
            )

            # 修改连接状态值
            GlobalValues.is_connected = True

            # 修改上次连接键值对
            ConfigOperation.modify_dict("lastConnection", {
                "host": GlobalValues.conn_host,
                "port": GlobalValues.conn_port,
                "authkey": GlobalValues.conn_authkey,
                "qq": GlobalValues.conn_qq
            })

            # 修改文件中自动连接开关
            ConfigOperation.modify_dict("autoConnect", self.auto_connect_var.get())

        else:
            # 如果要断开连接

            # 修改文本框为可修改
            self.__set_login_tools_active(True)

            # 修改按钮名称
            self.btn_connect.config(text=BTN_TEXT_CONN["connect"])

            # 修改属性值
            self.label_login_status_bar.config(
                text=LOGIN_STATUS_BAR_TEXT["disconnectSuccess"],
                fg=STATUS_BAR_COLOR["normal"]
            )

            # 释放session
            Conn.release_session_key()

            # 修改连接状态值
            GlobalValues.is_connected = False

    def __set_login_tools_active(self, active: bool):
        """
        修改界面上的一些内容为不可修改

        :param active: bool,如果为False则禁用掉文本框,否则启用
        :return: 无
        """
        if active:
            self.entry_host.config(state=ACTIVE)
            self.entry_port.config(state=ACTIVE)
            self.entry_authkey.config(state=ACTIVE)
            self.entry_qq.config(state=ACTIVE)
            self.checkbutton_auto_connect.config(state=ACTIVE)
        else:
            self.entry_host.config(state=DISABLED)
            self.entry_port.config(state=DISABLED)
            self.entry_authkey.config(state=DISABLED)
            self.entry_qq.config(state=DISABLED)
            self.checkbutton_auto_connect.config(state=DISABLED)

    def __on_close_root(self):
        """
        关闭窗口的事件
        :return: 无
        """

        # 如果正在连接则释放连接
        if GlobalValues.is_connected:
            Conn.release_session_key()

        # 杀掉root
        self.root.destroy()

    def __refresh(self):
        """
        用于刷新界面,在必要时调用

        :return: 无
        """

        def refresh_login_list():
            """
            刷新登录列表

            :return: 无
            """

            # 删除目前表中的所有内容
            self.treeview_login_list.delete(*self.treeview_login_list.get_children())

            # 把登录列表内容添加到显示中
            for one_record in LoginListOperation.get_list_from_file():
                self.treeview_login_list.insert("", index=END, values=(
                    one_record["host"],
                    one_record["port"],
                    one_record["authkey"],
                    one_record["qq"]
                ))

        def refresh_op_list():
            """
            刷新bot管理员qq列表

            :return: 无
            """

            # 删除目前表中的所有内容
            self.treeview_op_list.delete(*self.treeview_op_list.get_children())

            # 把内容添加到显示中
            for one_record in OpListOperation.get_list():
                self.treeview_op_list.insert("", index=END, values=(
                    one_record
                ))

        def refresh_config():
            """
            刷新配置

            :return: 无
            """
            # 重新获取config
            self.config_dict = ConfigOperation.get_dir_from_file()

            # 将文件中的内容显示到界面中
            self.entry_command_head.delete(0, END)
            self.entry_command_head.insert(END, self.config_dict["commandHead"])

            # 设置复选框默认勾选
            self.debug_var.set(self.config_dict["debug"])
            self.enable_var.set(self.config_dict["enable"])

            # 将内容设置到全局变量
            GlobalValues.command_head = self.config_dict["commandHead"]
            GlobalValues.debug_var = self.debug_var
            GlobalValues.enable_var = self.enable_var

        def refresh_plugin_list():
            # 获取插件名称
            plugin_names = PluginHandler.get_plugin_name_list()

            # 显示
            self.treeview_plugin_list.delete(*self.treeview_plugin_list.get_children())
            for name in plugin_names:
                self.treeview_plugin_list.insert("", index=END, values=(
                    name
                ))

        # 调用刷新登录列表
        refresh_login_list()

        # 调用刷新op列表
        refresh_op_list()

        # 刷新config显示
        refresh_config()

        # 刷新插件列表显示
        refresh_plugin_list()

    def __on_click_add_to_login_list(self):
        """
        将填写内容添加到列表中
        :return: 无
        """

        # 非空检测
        if [
            self.entry_host.get(),
            self.entry_port.get(),
            self.entry_authkey.get(),
            self.entry_qq.get()
        ] == [""] * 4:
            return

        # 调用添加登录项方法
        LoginListOperation.add_to_list(
            self.entry_host.get(),
            self.entry_port.get(),
            self.entry_authkey.get(),
            self.entry_qq.get()
        )

        # 刷新显示
        self.__refresh()

    def __on_double_click_login_list_content(self):
        """
        双击登录列表项目时,自动填充到右侧

        :return: 无
        """

        # 获取item的值
        item_list = self.treeview_login_list.item(self.treeview_login_list.focus(), "values")

        # 获取需要的项目并设置
        self.entry_host.delete(0, END)
        self.entry_host.insert(END, item_list[0])
        self.entry_port.delete(0, END)
        self.entry_port.insert(END, item_list[1])
        self.entry_authkey.delete(0, END)
        self.entry_authkey.insert(END, item_list[2])
        self.entry_qq.delete(0, END)
        self.entry_qq.insert(END, item_list[3])

    def __show_login_list_pop_up_menu(self, event):
        """
        显示右键菜单

        :param event: 事件
        :return: 无
        """
        def on_delete_event(item_id):
            """
            删除选项的事件

            :return: 无
            """

            # 删除该项
            LoginListOperation.remove_from_list(*self.treeview_login_list.item(item_id, "values"))
            self.treeview_login_list.delete(item_id)
            self.__refresh()

        # 获取选择对象
        iid = self.treeview_login_list.identify_row(event.y)

        # 如果有选择,则弹出右键菜单
        if iid:
            self.treeview_login_list.selection_set(iid)
            menu_pop_up = Menu(self.treeview_login_list, tearoff=False)
            menu_pop_up.add_command(
                label=POP_UP_MENU_DELETE_STR,
                command=lambda: on_delete_event(iid)
            )
            menu_pop_up.post(event.x_root, event.y_root)

    def __on_click_refresh_friend_list_event(self):
        """
        点击刷新好友列表事件

        :return: 无
        """
        try:
            # 如果未连接,则可能会抛出异常,此处直接弹出错误消息框
            friend_list = Conn.get_friend_list()
        except:
            messagebox.showerror(message=REFRESH_ERROR_MSG)
            return

        # 删除列表内容
        self.treeview_friend_list.delete(*self.treeview_friend_list.get_children())

        # 解析friend_list
        for friend_block in friend_list:
            self.treeview_friend_list.insert("", index=END, values=(
                friend_block["id"],
                friend_block["nickname"],
                friend_block["remark"]
            ))

    def __on_click_refresh_group_list_event(self):
        """
        点击刷新群列表事件

        :return: 无
        """
        try:
            # 如果未连接,则可能会抛出异常,此处直接弹出错误消息框
            group_list = Conn.get_group_list()
        except:
            messagebox.showerror(message=REFRESH_ERROR_MSG)
            return

        # 删除列表内容
        self.treeview_group_list.delete(*self.treeview_group_list.get_children())

        # 解析group_list
        for group_block in group_list:
            self.treeview_group_list.insert("", index=END, values=(
                group_block["id"],
                group_block["name"],
                group_block["permission"]
            ))

    def __on_click_send_friend_message(self):
        """
        点击发送消息给好友按钮

        :return: 无
        """

        # 获取到选中好友的值列表
        value_list = self.treeview_friend_list.item(self.treeview_friend_list.focus(), "values")

        try:
            # 获取qq并发送消息
            qq = value_list[0]
            message_chain = MessageChain()
            text = self.text_friend_send.get(1.0, END)
            if text == "\n":
                return
            message_chain.add_plain_text(text)
            Conn.send_friend_message(qq, message_chain)
            self.text_friend_send.delete(1.0, END)
        except:
            messagebox.showerror(message=SEND_ERROR_MSG)
            return

    def __on_click_send_group_message(self):
        """
        点击发送消息给群按钮

        :return: 无
        """

        # 获取到选中群的值列表
        value_list = self.treeview_group_list.item(self.treeview_group_list.focus(), "values")

        try:
            # 获取qq并发送消息
            qq = value_list[0]
            message_chain = MessageChain()
            text = self.text_group_send.get(1.0, END)
            if text == "\n":
                return
            message_chain.add_plain_text(text)
            Conn.send_group_message(qq, message_chain)
            self.text_group_send.delete(1.0, END)
        except:
            messagebox.showerror(message=SEND_ERROR_MSG)
            return



    def __on_click_add_op(self):
        """
        点击添加op按钮事件

        :return: 无
        """

        content = self.entry_add_op.get()

        # 如果添加op的文本框中没有东西,则不添加
        if content == "":
            return

        # 如果转换数字出错则不添加
        try:
            op_qq = int(content)
        except ValueError:
            return

        # 添加到op列表中
        OpListOperation.add_to_list(op_qq)

        # 刷新显示
        self.__refresh()

    def __show_op_list_pop_up_menu(self, event):
        """
        op列表右键菜单

        :return: 无
        """

        def on_delete_event():
            """
            删除选项的事件

            :return: 无
            """

            # 删除该项
            # 注意此处的强转,由于能够保证显示出来的内容一定只含有数字,故可以直接转换
            OpListOperation.remove_from_list(int(self.treeview_op_list.item(op_qq, "values")[0]))
            self.treeview_op_list.delete(op_qq)
            self.__refresh()

        # 获取选择对象
        op_qq = self.treeview_op_list.identify_row(event.y)

        # 如果有选择,则弹出右键菜单
        if op_qq:
            menu_pop_up = Menu(self.treeview_op_list, tearoff=False)
            self.treeview_op_list.selection_set(op_qq)
            menu_pop_up.add_command(
                label=POP_UP_MENU_DELETE_STR,
                command=lambda: on_delete_event()
            )
            menu_pop_up.post(event.x_root, event.y_root)

    def __on_click_save_config(self):
        """
        点击保存配置事件

        :return: 无
        """

        content = self.entry_command_head.get()

        # 如果为空,则不存入,但是刷新
        # 这样是为了保证点击后会显示原来的设置
        if content == "":
            self.__refresh()
            return

        ConfigOperation.modify_dict("commandHead", content)
        ConfigOperation.modify_dict("debug", self.debug_var.get())
        ConfigOperation.modify_dict("enable", self.enable_var.get())

        # 刷新
        self.__refresh()

        # 弹出对话框
        messagebox.showinfo(message=MANAGE_GUIDE["successSaveCommandHeadMsg"])

    def __auto_connect(self):
        if self.config_dict["autoConnect"]:
            self.__on_click_connect_event()
Пример #26
0
class PageOne(tk.Frame):
    def __init__(self, master):

        tk.Frame.__init__(self, master)
        # tk.Frame.configure(self,bg='blue')
        # tk.Label(self, text="Page de jeu", font=('Helvetica', 18, "bold")).pack(side="top", fill=BOTH, pady=5)

        frame_left = Frame(self)
        self.frame_left = frame_left
        frame_left.pack(fill=BOTH, side=LEFT)

        # add entry to this frame
        self.label = tk.Label(frame_left,
                              text="",
                              font=('Helvetica', 10),
                              fg='red')
        self.label.pack()

        self.bagniere_bleu = tk.Canvas(frame_left, width=50, height=3)
        self.bagniere_bleu.pack(side='top', anchor='c')
        self.bagniere_bleu.create_rectangle(0, 3, 50, 0, fill='blue')

        self.Nombre_1 = Entry(frame_left)
        self.Nombre_1.pack(side='top', anchor='w')

        # bagnier pour differencier les couleurs
        self.bagniere_bleu = tk.Canvas(frame_left, width=50, height=3)
        self.bagniere_bleu.pack(side='top', anchor='c')
        self.bagniere_bleu.create_rectangle(0, 3, 50, 0, fill='red')

        self.Nombre_2 = Entry(frame_left)
        self.Nombre_2.pack(side='top', anchor='w')

        tk.Button(
            frame_left,
            text="Go back to start page",
            command=lambda: master.switch_frame(StartPage)).pack(side='bottom')

        self.frame1 = Frame(self)
        self.frame1.pack(fill='x')
        self.rectangle = tk.Canvas(self.frame1)
        self.rectangle.pack()
        self.create_ret(self.rectangle)

        # self.update_clock()
        self.master = master
        self.commencer_un_jeu()

    def create_circle(self, r, canvasName, color):  #center coordinates, radius
        x = random.randint(20, 300)
        y = random.randint(20, 250)
        x0 = x - r
        y0 = y - r
        x1 = x + r
        y1 = y + r
        return canvasName.create_oval(x0, y0, x1, y1, fill=color)

    def create_ret(self, canvas):
        return canvas.create_rectangle(0, 500, 500, 0, fill="#fdffdb")

    def update_clock(self):
        self.temps_de_rect = (time.time() - self.debut)
        self.temps_de_rect = time.strftime("%H:%M:%S",
                                           time.gmtime(self.temps_de_rect))
        self.label.configure(text=self.temps_de_rect)
        if self.fin:
            self.master.after(1000, self.update_clock)

    def commencer_un_jeu(self):
        self.fin = True
        try:
            self.rejouer.destroy()
            self.label.config(text='')
            self.Nombre_2.delete(0, END)
            self.Nombre_1.delete(0, END)

        except:
            pass

        self.bt_valider = tk.Button(self.frame_left,
                                    text='valider',
                                    command=lambda: self.fin_du_jeu())
        self.bt_valider.pack(side='top', anchor='w')

        self.debut = time.time()
        self.temps_de_rect = (time.time() - self.debut)
        self.temps_de_rect = time.strftime("%H:%M:%S",
                                           time.gmtime(self.temps_de_rect))
        self.label.configure(text=self.temps_de_rect)
        self.update_clock()

        self.rectangle.destroy()
        self.rectangle = tk.Canvas(self.frame1)
        self.rectangle.pack()
        self.create_ret(self.rectangle)

        self.nombre_j1 = random.randint(1, 10)
        self.nombre_j2 = random.randint(1, 10)
        for _ in range(self.nombre_j2):
            self.create_circle(20, self.rectangle, 'red')
        for _ in range(self.nombre_j1):
            self.create_circle(20, self.rectangle, 'blue')

    def fin_du_jeu(self):
        self.fin = False
        if (int(self.Nombre_1.get()) == self.nombre_j1) and (int(
                self.Nombre_2.get()) == self.nombre_j2):
            #jeu gagné

            self.bt_valider.destroy()
            self.rejouer = Button(self.frame_left,
                                  text="Rejouer",
                                  command=lambda: self.commencer_un_jeu())

            self.rejouer.pack(side='top', fill='x')

            self.temps_de_rect = (time.time() - self.debut)
            self.temps_de_rect = time.strftime("%H:%M:%S",
                                               time.gmtime(self.temps_de_rect))
            self.label.configure(text=self.temps_de_rect)
            self.rectangle.create_text(200,
                                       150,
                                       fill="darkblue",
                                       font="Times 20 italic bold",
                                       text="Victoire")
        else:

            self.bt_valider.destroy()
            self.rejouer = Button(self.frame_left,
                                  text="Rejouer",
                                  command=lambda: self.commencer_un_jeu())

            self.rejouer.pack(side='top', fill='x')

            self.temps_de_rect = (time.time() - self.debut)
            self.temps_de_rect = time.strftime("%H:%M:%S",
                                               time.gmtime(self.temps_de_rect))
            self.label.configure(text=self.temps_de_rect)
            self.rectangle.create_text(200,
                                       150,
                                       fill="darkblue",
                                       font="Times 20 italic bold",
                                       text="Defaite")
Пример #27
0
class AudioDemo(Frame):
    """ A demo application class that provides simple GUI for testing featurizer+classifier on 
    microphone or wav file input. """

    def __init__(self, featurizer_model=None, classifier_model=None, 
                sample_rate=None, channels=None, input_device=None, categories=None, 
                image_width=80, ignore_label=None, threshold=None, wav_file=None, clear=5):
        """ Initialize AudioDemo object
        featurizer_model - the path to the compiled ELL featurizer
        classifier_model - the path to the compiled ELL classifier
        sample_rate - sample rate to featurizer is expecting
        channels - number of channels featurizer is expecting
        input_device - optional id of microphone to use
        categories - path to file containing category labels
        image_width - width of the spectrogram image
        ignore_label - list of predictions to ignore (e.g. [0] ignores prediction 0)
        threshold - ignore predictions that have confidence below this number (e.g. 0.5)
        wav_file - optional wav_file to process when you click Play
        """
        input_device
        super().__init__()

        self.CLASSIFIER_MODEL_KEY = "classifier_model"
        self.FEATURIZER_MODEL_KEY = "featurizer_model"
        self.WAV_FILE_KEY = "wav_file"
        self.CATEGORY_FILE_KEY = "categories"

        self.get_settings_file_name()
        self.load_settings()
        self.reading_input = False
        self.featurizer_model = None
        if featurizer_model:
            self.featurizer_model = featurizer_model
            self.settings[self.FEATURIZER_MODEL_KEY] = featurizer_model
        elif self.FEATURIZER_MODEL_KEY in self.settings:
            self.featurizer_model = self.settings[self.FEATURIZER_MODEL_KEY]        

        self.classifier_model = None
        if classifier_model:
            self.classifier_model = classifier_model
            self.settings[self.CLASSIFIER_MODEL_KEY] = classifier_model
        elif self.CLASSIFIER_MODEL_KEY in self.settings:
            self.classifier_model = self.settings[self.CLASSIFIER_MODEL_KEY]

        self.wav_filename = wav_file
        if self.wav_filename is None and self.WAV_FILE_KEY in self.settings:
            self.wav_filename = self.settings[self.WAV_FILE_KEY]

        self.wav_file_list = None

        self.sample_rate = sample_rate if sample_rate is not None else 16000
        self.channels = channels if channels is not None else 1
        self.input_device = input_device
        self.num_classifier_features = None

        if not categories and self.CATEGORY_FILE_KEY in self.settings:
            categories = self.settings[self.CATEGORY_FILE_KEY]

        self.categories = categories
        if categories:
            self.settings[self.CATEGORY_FILE_KEY] = categories
            
        self.save_settings() # in case we just changed it.
        self.min_value = 0.0
        self.max_value = 1.0
        self.update_minmax = True
        
        self.ignore_list = []
        if ignore_label:
            self.ignore_list = [ ignore_label ]
        self.threshold = threshold

        self.output_clear_time = int(clear * 1000) if clear else 5000

        self.featurizer = None
        self.classifier = None
        self.wav_file = None
        self.speaker = None
        self.microphone = None
        self.animation = None
        self.show_spectrogram = True
        self.colormap_name = "plasma"
        self.show_classifier_output = True
        self.last_prediction = None
        self.probability = 0

        # Threads
        self.read_input_thread = None
        self.lock = Lock()
        self.main_thread = get_ident()
        self.message_queue = []

        # UI components
        self.max_spectrogram_width = image_width
        self.features_entry = None
        self.spectrogram_image = None
        self.classifier_feature_data = None
        self.spectrogram_image_data = None
        
        self.init_ui()
        
        if self.featurizer_model:
            self.load_featurizer_model(os.path.abspath(self.featurizer_model))
        else:
            self.show_output("Please specify and load a feature model")

        if self.classifier_model:   
            self.load_classifier(self.classifier_model)    
            self.setup_spectrogram_image()   
        else:
            self.show_output("Please specify and load a classifier model")

    def get_settings_file_name(self):
        """ this app stores the various UI field values in a settings file in your temp folder 
        so you don't always have to specify the full command line options """
        import tempfile
        temp = tempfile.gettempdir() 
        self.settings_file_name = os.path.join(temp, "ELL", "Audio", "viewaudio.json")

    def load_settings(self):
        """ load the previously saved settings from disk, if any """
        self.settings = {}
        if os.path.isfile(self.settings_file_name):
            with open(self.settings_file_name, "r") as f:
                self.settings = json.load(f)

    def save_settings(self):
        """ save the current settings to disk """
        settings_dir = os.path.dirname(self.settings_file_name)
        if not os.path.isdir(settings_dir):
            os.makedirs(settings_dir)
        with open(self.settings_file_name, "w") as f:
            f.write(json.dumps(self.settings))            

    def load_featurizer_model(self, featurizer_model):
        """ load the given compiled ELL featurizer for use in processing subsequent audio input """
        if featurizer_model:
            self.featurizer = featurizer.AudioTransform(featurizer_model, 40)
            self.setup_spectrogram_image()
            
            self.show_output("Feature input size: {}, output size: {}".format(
                self.featurizer.input_size, 
                self.featurizer.output_size))
            if self.features_entry.get() != featurizer_model:
                self.features_entry.delete(0, END)
                self.features_entry.insert(0, featurizer_model)
            
        self.init_data()

    def load_classifier(self, classifier_path):
        """ load the given compiled ELL classifier for use in processing subsequent audio input """
        if classifier_path:
            self.classifier = classifier.AudioClassifier(classifier_path, self.categories, 
                                                         self.ignore_list, self.threshold)
            self.show_output("Classifier input size: {}, output size: {}".format(
                self.classifier.input_size,
                self.classifier.output_size))
            if self.classifier_entry.get() != classifier_path:
                self.classifier_entry.delete(0, END)
                self.classifier_entry.insert(0, classifier_path)
        self.init_data()

    def init_data(self):
        """ initialize the spectrogram_image_data and classifier_feature_data based on the newly loaded model info """
        if self.featurizer:
            dim = (self.featurizer.output_size, self.max_spectrogram_width)
            self.spectrogram_image_data = np.zeros(dim, dtype=float)  
            if self.spectrogram_image is not None:
                self.spectrogram_image.set_data(self.spectrogram_image_data)

            if self.classifier:
                self.num_classifier_features = self.classifier.input_size // self.featurizer.output_size
                dim = (self.num_classifier_features, self.featurizer.output_size)
                self.classifier_feature_data = np.zeros(dim, dtype=float)

    def accumulate_feature(self, feature_data):
        """ accumulate the feature data and pass feature data to classifier """
        if self.classifier and self.show_classifier_output:
            self.classifier_feature_data = np.vstack((self.classifier_feature_data, 
                feature_data))[-self.num_classifier_features:,:]
            self.evaluate_classifier()
            
    def accumulate_spectrogram_image(self, feature_data):
        """ accumulate the feature data into the spectrogram image """
        image_data = self.spectrogram_image_data
        feature_data = np.reshape(feature_data, [-1,1])
        new_image = np.hstack((image_data, feature_data))[:,-image_data.shape[1]:]
        image_data[:,:] = new_image

    def set_spectrogram_image(self):
        """ update the spectrogram image and the min/max values """
        self.lock.acquire() # protect access to the shared state 
        if self.update_minmax and self.show_spectrogram:
            min_value = np.min(self.spectrogram_image_data)
            max_value = np.max(self.spectrogram_image_data)
            if np.isfinite(min_value) and np.isfinite(max_value):
                self.min_value = min_value
                self.max_value = max_value
                eps = 0.1
                if self.max_value - self.min_value < eps:
                    self.max_value = self.min_value + eps

            self.spectrogram_image.set_clim(self.min_value, self.max_value)
        self.spectrogram_image.set_data(self.spectrogram_image_data)
        self.lock.release()

    def get_correct_shape(self, shape):
        """ for some reason keras stores input shape as (None,80,40), and numpy hates that
        so we have to change this to (1,80,40) """
        shape = list(shape)
        fix = [x if x else 1 for x in shape]
        return tuple(fix)

    def clear_output(self):
        """ remove some of the Output based a the timeout callback  """
        self.output_text.delete(1.0, 2.0)

    def process_output(self):
        """ show output that was queued by background thread """
        self.lock.acquire()
        messages = self.message_queue
        self.message_queue = []        
        self.lock.release()
        for msg in messages:
            self.show_output(msg)
    
    def show_output(self, message):
        """ show output message, or queue it if we are on a background thread """
        if self.main_thread != get_ident():
            self.message_queue += [message]
            return

        for line in str(message).split('\n'):
            self.output_text.insert(END, "{}\n".format(line))

        self.output_text.see("end") # scroll to end
        self.after(self.output_clear_time, self.clear_output)

    def evaluate_classifier(self):
        """ run the classifier model on the current feature data and show the prediction, if any """
        if self.evaluate_classifier and self.classifier and self.classifier_feature_data is not None:
            prediction, probability, label = self.classifier.predict(self.classifier_feature_data.ravel())
            if prediction is not None:
                percent = int(100*probability)
                if self.last_prediction != prediction or self.probability < probability:
                    self.last_prediction = prediction
                    self.probability = probability
                    self.show_output("<<< DETECTED ({}) {}% {} >>>".format(prediction, percent, label))
    
    def start_playing(self, filename):
        """ Play a wav file, and classify the audio. Note we use a background thread to read the
        wav file and we setup a UI animation function to draw the sliding spectrogram image, this way
        the UI update doesn't interfere with the smoothness of the audio playback """
        if self.speaker is None:
            self.speaker = speaker.Speaker()
    
        self.stop()
        self.reading_input = False
        self.wav_file = wav_reader.WavReader(self.sample_rate, self.channels)
        self.wav_file.open(filename, self.featurizer.input_size, self.speaker)

        def update_func(frame_index):
            self.process_output()
            if not self.reading_input:
                self.after(1, self.on_stopped)
            self.set_spectrogram_image()
            return (self.spectrogram_image,)

        if self.animation:
            self.animation.event_source.stop()
        self.reading_input = True       

        # Start animation timer for updating the UI (e.g. spectrogram image) (30 fps is usually fine) 
        self.animation = animation.FuncAnimation(self.features_figure, update_func, interval=33, blit=True) 

        # start background thread to read and classify the audio.
        self.featurizer.open(self.wav_file)
        self.read_input_thread = Thread(target=self.on_read_features, args=())
        self.read_input_thread.daemon = True
        self.read_input_thread.start()        

    def start_recording(self):
        """ Start recording audio from the microphone nd classify the audio. Note we use a background thread to 
        process the audio and we setup a UI animation function to draw the sliding spectrogram image, this way
        the UI update doesn't interfere with the smoothness of the microphone readings """ 
        if self.microphone is None:
            self.microphone = microphone.Microphone(False)
    
        self.stop()
        num_channels = 1
        self.microphone.open(self.featurizer.input_size, self.sample_rate, num_channels, self.input_device)        
        
        def update_func(frame_index):
            # this is an animation callback to update the UI every 33 milliseconds.
            self.process_output()
            self.set_spectrogram_image()
            if not self.reading_input:
                self.after(1, self.on_stopped)
            return (self.spectrogram_image,)

        if self.animation:
            self.animation.event_source.stop()
            
        self.reading_input = True
        # Start animation timer for updating the UI (e.g. spectrogram image) (30 fps is usually fine) 
        self.animation = animation.FuncAnimation(self.features_figure, update_func, interval=33, blit=True)
        
        # start background thread to read and classify the recorded audio.
        self.featurizer.open(self.microphone)
        self.read_input_thread = Thread(target=self.on_read_features, args=())
        self.read_input_thread.daemon = True
        self.read_input_thread.start()            

    def on_read_features(self): 
        """ this is the background thread entry point.  So we read the feature data in a loop
        and pass it to the classifier """
        try:           
            while self.reading_input and self.featurizer:
                feature_data = self.featurizer.read()                
                if feature_data is None:
                    break # eof
                else:                    
                    self.lock.acquire()
                    self.accumulate_feature(feature_data)
                    if self.show_spectrogram:
                        self.accumulate_spectrogram_image(feature_data)
                    self.lock.release()
        except:
            errorType, value, traceback = sys.exc_info()
            print("### Exception reading input: " + str(errorType) + ": " + str(value) + " " + str(traceback))
            while traceback:
                print(traceback.tb_frame.f_code)
                traceback = traceback.tb_next

        self.reading_input = False
    
    def stop(self):
        """ called when user clicks the stop button, or we reach the end of a wav file input """
        # close streams
        if self.animation:
            self.animation.event_source.stop()
            self.animation = None
        if self.microphone:
            self.microphone.close()
        if self.speaker:
            self.speaker.close()
        if self.wav_file:
            self.wav_file.close()
            self.wav_file = None
        self.reading_input = False
        self.last_prediction = None
        self.probability = 0
    
    def on_rec_button_click(self):
        """ called when user clicks the record button, same button is used to "stop" recording. """
        if self.rec_button["text"] == "Rec":
            self.rec_button["text"] = "Stop"
            self.play_button["text"] = "Play"
            self.start_recording()
        else:
            self.rec_button["text"] = "Rec"
            self.on_stop()

    def on_play_button_click(self):
        """ called when user clicks the record button, same button is used to "stop" playback """
        if self.play_button["text"] == "Play":
            self.play_button["text"] = "Stop"
            self.rec_button["text"] = "Rec"
            self.on_play()
        else:
            self.play_button["text"] = "Play"
            self.on_stop()

    def on_play(self):
        """ called when user clicks the Play button """
        filename = self.wav_filename_entry.get()
        filename = filename.strip('"')
        self.wav_filename = filename
        self.settings[self.WAV_FILE_KEY] = filename
        self.save_settings()
        self.start_playing(filename)

    def on_stop(self):
        """ called when user clicks the Stop button """
        self.reading_input = False
        if self.wav_file:
            self.wav_file.close()
            self.wav_file = None
        if self.read_input_thread:
            self.read_input_thread.join()
            self.read_input_thread = None  
        self.stop()

    def on_stopped(self):
        """ called when we reach the end of the wav file playback """
        self.play_button["text"] = "Play"
        self.stop()

    def get_wav_list(self):
        if self.wav_filename and os.path.isfile(self.wav_filename):
            dir_name = os.path.dirname(self.wav_filename)
            if not self.wav_file_list:
                self.wav_file_list = [x for x in os.listdir(dir_name) if os.path.splitext(x)[1] == ".wav"]
                self.wav_file_list.sort()
        return self.wav_file_list

    def select_wav_file(self, filename):
        self.wav_filename = filename
        # show the file in the UI                                
        self.wav_filename_entry.delete(0, END)
        if self.wav_filename:
            self.wav_filename_entry.insert(0, self.wav_filename)
        # and automatically play the file.
        self.on_play()

    def on_minus_key(self, event):
        """ When user presses the plus button we reverse to the previous wav file in the current folder.
        This way you can easily step through all the training wav files """
        if self.get_wav_list():
            i = self.wav_file_list.index(os.path.basename(self.wav_filename))
            if i - 1 >= 0:
                next_wav_file = self.wav_file_list[i - 1]
                dir_name = os.path.dirname(self.wav_filename)
                self.select_wav_file(os.path.join(dir_name, next_wav_file))

    def on_plus_key(self, event):
        """ When user presses the plus button we advance to the next wav file in the current folder.
        This way you can easily step through all the training wav files """
        if self.get_wav_list():
            i = self.wav_file_list.index(os.path.basename(self.wav_filename))
            if i + 1 < len(self.wav_file_list):
                next_wav_file = self.wav_file_list[i + 1]
                dir_name = os.path.dirname(self.wav_filename)
                self.select_wav_file(os.path.join(dir_name, next_wav_file))

    def init_ui(self):
        """ setup the GUI for the app """
        self.master.title("Test")
        self.pack(fill=BOTH, expand=True)

        # Input section
        input_frame = LabelFrame(self, text="Input")
        input_frame.bind("-", self.on_minus_key)
        input_frame.bind("+", self.on_plus_key)
        input_frame.pack(fill=X)
        self.play_button = Button(input_frame, text="Play", command=self.on_play_button_click)
        self.play_button.pack(side=RIGHT, padx=4)
        self.rec_button = Button(input_frame, text="Rec", command=self.on_rec_button_click)
        self.rec_button.pack(side=RIGHT, padx=4)
        self.wav_filename_entry = Entry(input_frame, width=24)
        self.wav_filename_entry.pack(fill=X)
        self.wav_filename_entry.delete(0, END)
        if self.wav_filename:
            self.wav_filename_entry.insert(0, self.wav_filename)

        # Feature section
        features_frame = LabelFrame(self, text="Features")
        features_frame.pack(fill=X)

        features_control_frame = Frame(features_frame)
        features_control_frame.pack(fill=X)
        load_features_button = Button(features_control_frame, text="Load", command=self.on_load_featurizer_model)
        load_features_button.pack(side=RIGHT)
        self.features_entry = Entry(features_control_frame, width=8)
        self.features_entry.pack(fill=X)
        self.features_entry.delete(0, END)
        if self.featurizer_model:
            self.features_entry.insert(0, self.featurizer_model)

        viz_frame = Frame(features_frame)
        viz_frame.pack(fill=X)
        self.features_figure = Figure(figsize=(5, 4), dpi=100)
        self.subplot = self.features_figure.add_subplot(111)

        canvas = FigureCanvasTkAgg(self.features_figure, master=viz_frame)
        canvas.draw()
        canvas.show()
        canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=True)

        # Classifier section
        classifier_frame = LabelFrame(self, text="Classifier")
        classifier_frame.pack(fill=X)
        load_classifier_button = Button(classifier_frame, text="Load", command=self.on_load_classifier)
        load_classifier_button.pack(side=RIGHT)
        self.classifier_entry = Entry(classifier_frame, width=8)
        self.classifier_entry.pack(fill=X)
        self.classifier_entry.delete(0, END)
        if self.classifier_model:
            self.classifier_entry.insert(0, self.classifier_model)

        # Output section
        output_frame = LabelFrame(self, text="Output")
        output_frame.pack(fill=BOTH, expand=True)

        self.output_text = Text(output_frame)
        self.output_text.pack(fill=BOTH, padx=4, expand=True) 

    def setup_spectrogram_image(self):
        """ this need to be called if you load a new feature model, because the featurizer output size might have 
        changed. """
        if self.featurizer:
            dim = (self.featurizer.output_size, self.max_spectrogram_width)
            self.spectrogram_image_data = np.zeros(dim, dtype=float)  
            self.subplot.clear()
            self.spectrogram_image = self.subplot.imshow(self.spectrogram_image_data, vmin=self.min_value, 
                vmax=self.max_value, origin="lower", animated=True, cmap=pyplot.get_cmap(self.colormap_name))

    def on_load_featurizer_model(self):
        """ called when user clicks the Load button for the feature model """
        filename = self.features_entry.get()
        filename = filename.strip('"')
        self.settings[self.FEATURIZER_MODEL_KEY] = filename
        self.save_settings()
        self.stop()
        self.load_featurizer_model(filename)

    def on_load_classifier(self):
        """ called when user clicks the Load button for the feature model """
        self.classifier_model = self.classifier_entry.get()
        self.settings[self.CLASSIFIER_MODEL_KEY] = self.classifier_model
        self.save_settings()
        self.stop()
        self.load_classifier(self.classifier_model)
Пример #28
0
class App(Tk):  #the main class for the main window
    def __init__(self):
        Tk.__init__(self)

        # window properties
        self.title(string="Screen Recorder")
        self.iconbitmap("icon.ico")
        self.resizable(width=False, height=False)

        ffmpegAvailable = False
        for item in os.listdir():
            if item == "ffmpeg.exe":
                ffmpegAvailable = True
                break
        if not ffmpegAvailable:
            self.withdraw()
            if messagebox.askyesno(
                    "FFmpeg Not Found",
                    "ffmpeg.exe could not be found in screen recorder's directory. Do you want to be redirected to the ffmpeg download website?"
            ):
                webbrowser.open_new_tab("https://ffmpeg.zeranoe.com/builds/")
            exit()
        self.cmdGen = cmdGen(
        )  # create a command generator object to store settings

        # file name
        label1 = Label(self, text="File Name:")
        label1.grid(row=0, column=0, sticky="")
        self.entry1 = Entry(self)
        self.entry1.grid(row=0, column=1, sticky="ew")

        # ensure the existance of the "ScreenCaptures" directory
        try:
            os.mkdir("ScreenCaptures")
        except FileExistsError:
            pass
        os.chdir("ScreenCaptures")

        # find a default file name that is currently available.
        defaultFile = "ScreenCapture.mp4"
        available = False
        fileNum = 0
        while available == False:
            hasMatch = False
            for item in os.listdir():
                if item == defaultFile:
                    hasMatch = True
                    break
            if not hasMatch:
                available = True
            else:
                fileNum += 1
                defaultFile = "ScreenCapture" + str(fileNum) + ".mp4"
        os.chdir("..")
        self.entry1.insert(END, defaultFile)

        # radio buttons determine what to record
        self.what = StringVar()
        self.what.set("desktop")
        self.radio2 = Radiobutton(self,
                                  text="record the window with the title of: ",
                                  variable=self.what,
                                  value="title",
                                  command=self.enDis1)
        self.radio1 = Radiobutton(self,
                                  text="record the entire desktop",
                                  variable=self.what,
                                  value="desktop",
                                  command=self.enDis)
        self.radio1.grid(row=1, column=0, sticky="w")
        self.radio2.grid(row=2, column=0, sticky="w")
        self.entry2 = Entry(self, state=DISABLED)
        self.entry2.grid(row=2, column=1, sticky="ew")

        # initialize webcam
        self.webcamdevices = Webcam.listCam()
        self.webcamrecorder = Webcam.capturer("")

        # "record from webcam" checkbox
        self.rcchecked = IntVar()
        self.recordcam = Checkbutton(self,
                                     text="Record from webcam",
                                     command=self.checkboxChanged,
                                     variable=self.rcchecked)
        self.recordcam.grid(row=3, column=0)

        # a drop-down allowing you to select the webcam device from the available directshow capture devices
        self.devicename = StringVar(self)
        if self.webcamdevices:
            self.devicename.set(self.webcamdevices[0])
            self.deviceselector = OptionMenu(self, self.devicename,
                                             *self.webcamdevices)
            self.deviceselector.config(state=DISABLED)
            self.deviceselector.grid(row=3, column=1)
        else:
            self.devicename.set("NO DEVICES AVAILABLE")
            self.recordcam.config(state=DISABLED)
            self.deviceselector = OptionMenu(self, self.devicename,
                                             "NO DEVICES AVAILABLE")
            self.deviceselector.config(state=DISABLED)
            self.deviceselector.grid(row=3, column=1)

        self.opButton = Button(self,
                               text="⚙ Additional Options...",
                               command=self.openSettings)
        self.opButton.grid(row=4, column=1, sticky='e')

        # the "start recording" button
        self.startButton = Button(self,
                                  text="⏺ Start Recording",
                                  command=self.startRecord)
        self.startButton.grid(row=5, column=0, columnspan=2)

        # some variables
        self.recording = False  # are we recording?
        self.proc = None  # the popen object for ffmpeg (during screenrecord)
        self.recorder = recordFile.recorder(
        )  # the "recorder" object for audio (see recordFile.py)
        self.mergeProcess = None  # the popen object for ffmpeg (while merging video and audio files)

        # start the ffmpeg monitoring callback
        self.pollClosed()

    def openSettings(self):
        self.settings = settingsWin(self, self.cmdGen, self.recorder)

    def pollClosed(self):
        """callback that repeats itself every 100ms. Automatically determines if ffmpeg is still running."""
        if self.recording:
            if self.proc.poll() != None:
                self.startRecord()
                messagebox.showerror(
                    "ffmpeg error", "ffmpeg has stopped working. ERROR: \n" +
                    str(self.proc.stderr.read()).replace('\\r\\n', '\n'))
            if self.recorder.error:
                self.startRecord()
        if self.mergeProcess and self.recording == False:
            if self.mergeProcess.poll() != None:
                self.startButton.config(text="⏺ Start Recording", state=NORMAL)
                self.title(string="Screen Recorder")
        self.after(100, self.pollClosed)

    def enDis(self):
        """Called when the "desktop" radio button is pressed"""
        self.entry2.config(state=DISABLED)
        # self.what.set("desktop")

    def enDis1(self):
        """Called when the "window title" radio button is pressed"""
        self.entry2.config(state=NORMAL)
        # self.what.set("title")

    def checkboxChanged(self):
        """Called when the "record webcam" checkbox is checked or unchecked."""
        #self.rcchecked = not self.rcchecked
        if self.rcchecked.get():
            self.deviceselector.config(state=NORMAL)
        else:
            self.deviceselector.config(state=DISABLED)

    def startRecord(self):
        """toggles recording. Will start conversion subprocess on recording completion"""
        if self.recording == False:
            # change the window
            self.title(string="Screen Recorder (Recording...)")
            self.startButton.config(text="⏹️ Stop Recording")
            self.filename = self.entry1.get()

            # disable interface
            self.entry1.config(state=DISABLED)
            self.radio1.config(state=DISABLED)
            self.radio2.config(state=DISABLED)
            self.deviceselector.config(state=DISABLED)
            self.opButton.config(state=DISABLED)
            if self.what.get() == "title":
                self.entry2.config(state=DISABLED)

            # ensure the existence of the "tmp" directory
            try:
                os.mkdir("tmp")
            except FileExistsError:
                pass

            # start screen recording process
            self.recording = True
            startupinfo = subprocess.STARTUPINFO()
            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            startupinfo.wShowWindow = subprocess.SW_HIDE

            # self.cmdGen.setFps(60)
            # self.cmdGen.setEncode('nvenc_h264') # CPU: mpeg4 // NVIDIA: h264_nvenc // AMD: no.
            self.cmdGen.setSource(self.what.get() == "title",
                                  self.entry2.get())
            command = self.cmdGen.getCmd("tmp/tmp.mkv")
            self.proc = subprocess.Popen(args=command,
                                         startupinfo=startupinfo,
                                         stderr=subprocess.PIPE)

            # start audio recording
            self.recorder.record("tmp/tmp.wav")

            # start webcam recording, if checked
            self.recordcam.config(state=DISABLED)
            if self.rcchecked.get() and self.webcamdevices:
                self.webcamrecorder.setDevice(str(self.devicename.get()))
                self.webcamrecorder.startCapture("tmp/webcamtmp.mkv")

            # minimize the window to get it out of the way of the recording
            self.iconify()
        elif self.recording == True:
            self.deiconify()
            defaultFile = self.filename

            # re-enable interface
            self.entry1.config(state=NORMAL)
            self.radio1.config(state=NORMAL)
            self.radio2.config(state=NORMAL)
            self.opButton.config(state=NORMAL)
            if self.webcamdevices:
                self.recordcam.config(state=NORMAL)
                if self.rcchecked.get():
                    self.deviceselector.config(state=NORMAL)
            if self.what.get() == "title":
                self.entry2.config(state=NORMAL)

            available = False
            fileNum = 0

            # stop all recording processes
            self.recording = False
            self.proc.terminate()
            self.recorder.stop_recording()
            if self.rcchecked.get() and self.webcamdevices:
                self.webcamrecorder.stopCapture()
            try:
                os.mkdir("ScreenCaptures")
            except FileExistsError:
                pass

            # change the window title and button text to reflect the current process
            self.title(string="Screen Recorder (converting...)")
            self.startButton.config(
                text="converting your previous recording, please wait...",
                state=DISABLED)

            # start the video conversion process
            startupinfo = subprocess.STARTUPINFO()
            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            startupinfo.wShowWindow = subprocess.SW_HIDE
            self.cmdGen.config(audList=self.recorder.devices)
            command = self.cmdGen.getCvtCmd("ScreenCaptures/" + self.filename)
            if not self.recorder.error:
                self.mergeProcess = subprocess.Popen(args=command,
                                                     startupinfo=startupinfo)

            # if self.rcchecked.get():
            #     self.mergeProcess = subprocess.Popen(args= ["ffmpeg","-i",'tmp/tmp.mkv','-i','tmp/tmp.wav','-i','tmp/webcamtmp.mkv','-filter_complex','[2:v] scale=640:-1 [inner]; [0:0][inner] overlay=0:0 [out]',"-shortest",'-map','[out]','-y',"ScreenCaptures/"+self.filename])
            # else:
            #     self.mergeProcess = subprocess.Popen(args= ["ffmpeg","-i",'tmp/tmp.mkv','-i','tmp/tmp.wav',"-shortest",'-y',"ScreenCaptures/"+self.filename], startupinfo=startupinfo)

            # change the screen capture name to something that is not taken
            os.chdir("ScreenCaptures")
            while True:
                matches = 0
                for item in os.listdir():
                    if item == defaultFile:
                        matches += 1
                if matches == 0:
                    self.entry1.delete(0, END)
                    self.entry1.insert(END, defaultFile)
                    break
                else:
                    fileNum += 1
                    file = self.filename.split(".")
                    defaultFile = file[0].rstrip("1234567890") + str(
                        fileNum) + "." + file[1]

            os.chdir("../")
Пример #29
0
class Sync(Tk):
    """FolderSync main window."""
    def __init__(self):
        Tk.__init__(self, className='FolderSync')
        self.title("FolderSync")
        self.geometry("%ix%i" %
                      (self.winfo_screenwidth(), self.winfo_screenheight()))
        self.protocol("WM_DELETE_WINDOW", self.quitter)
        self.icon = PhotoImage(master=self, file=IM_ICON)
        self.iconphoto(True, self.icon)
        self.rowconfigure(2, weight=1)
        self.columnconfigure(0, weight=1)

        # --- icons
        self.img_about = PhotoImage(master=self, file=IM_ABOUT)
        self.img_open = PhotoImage(master=self, file=IM_OPEN)
        self.img_plus = PhotoImage(master=self, file=IM_PLUS)
        self.img_moins = PhotoImage(master=self, file=IM_MOINS)
        self.img_sync = PhotoImage(master=self, file=IM_SYNC)
        self.img_prev = PhotoImage(master=self, file=IM_PREV)
        self.img_expand = PhotoImage(master=self, file=IM_EXPAND)
        self.img_collapse = PhotoImage(master=self, file=IM_COLLAPSE)

        self.original = ""
        self.sauvegarde = ""
        # list of files / folders to delete before starting the copy because
        # they are not of the same type on the original and the backup
        self.pb_chemins = []
        self.err_copie = False
        self.err_supp = False

        # --- init log files
        l = [f for f in listdir(PATH) if match(r"foldersync[0-9]+.pid", f)]
        nbs = []
        for f in l:
            with open(join(PATH, f)) as fich:
                old_pid = fich.read().strip()
            if exists("/proc/%s" % old_pid):
                nbs.append(int(search(r"[0-9]+", f).group()))
            else:
                remove(join(PATH, f))
        if not nbs:
            i = 0
        else:
            nbs.sort()
            i = 0
            while i in nbs:
                i += 1
        self.pidfile = PID_FILE % i
        open(self.pidfile, 'w').write(str(getpid()))

        self.log_copie = LOG_COPIE % i
        self.log_supp = LOG_SUPP % i

        self.logger_copie = setup_logger("copie", self.log_copie)
        self.logger_supp = setup_logger("supp", self.log_supp)
        date = datetime.now().strftime('%d/%m/%Y %H:%M')
        self.logger_copie.info("\n###  %s  ###\n" % date)
        self.logger_supp.info("\n###  %s  ###\n" % date)

        # --- filenames and extensions that will not be copied
        exclude_list = split(r'(?<!\\) ',
                             CONFIG.get("Defaults", "exclude_copie"))
        self.exclude_names = []
        self.exclude_ext = []
        for elt in exclude_list:
            if elt:
                if elt[:2] == "*.":
                    self.exclude_ext.append(elt[1:])
                else:
                    self.exclude_names.append(elt.replace("\ ", " "))

        # --- paths that will not be deleted
        self.exclude_path_supp = [
            ch.replace("\ ", " ") for ch in split(
                r'(?<!\\) ', CONFIG.get("Defaults", "exclude_supp")) if ch
        ]
        #        while "" in self.exclude_path_supp:
        #            self.exclude_path_supp.remove("")

        self.q_copie = Queue()
        self.q_supp = Queue()
        # True if a copy / deletion is running
        self.is_running_copie = False
        self.is_running_supp = False

        self.style = Style(self)
        self.style.theme_use("clam")
        self.style.configure("TProgressbar",
                             troughcolor='lightgray',
                             background='#387EF5',
                             lightcolor="#5D95F5",
                             darkcolor="#2758AB")
        self.style.map("TProgressbar",
                       lightcolor=[("disabled", "white")],
                       darkcolor=[("disabled", "gray")])
        self.style.configure("folder.TButton", padding=0)
        # --- menu
        self.menu = Menu(self, tearoff=False)
        self.configure(menu=self.menu)

        # -------- recents
        self.menu_recent = Menu(self.menu, tearoff=False)
        if RECENT:
            for ch_o, ch_s in RECENT:
                self.menu_recent.add_command(
                    label="%s -> %s" % (ch_o, ch_s),
                    command=lambda o=ch_o, s=ch_s: self.open(o, s))
        else:
            self.menu.entryconfigure(0, state="disabled")

        # -------- favorites
        self.menu_fav = Menu(self.menu, tearoff=False)
        self.menu_fav_del = Menu(self.menu_fav, tearoff=False)
        self.menu_fav.add_command(label=_("Add"),
                                  image=self.img_plus,
                                  compound="left",
                                  command=self.add_fav)
        self.menu_fav.add_cascade(label=_("Remove"),
                                  image=self.img_moins,
                                  compound="left",
                                  menu=self.menu_fav_del)
        for ch_o, ch_s in FAVORIS:
            label = "%s -> %s" % (ch_o, ch_s)
            self.menu_fav.add_command(
                label=label, command=lambda o=ch_o, s=ch_s: self.open(o, s))
            self.menu_fav_del.add_command(
                label=label, command=lambda nom=label: self.del_fav(nom))
        if not FAVORIS:
            self.menu_fav.entryconfigure(1, state="disabled")

        # -------- log files
        menu_log = Menu(self.menu, tearoff=False)
        menu_log.add_command(label=_("Copy"), command=self.open_log_copie)
        menu_log.add_command(label=_("Removal"),
                             command=self.open_log_suppression)

        # -------- settings
        menu_params = Menu(self.menu, tearoff=False)
        self.copy_links = BooleanVar(self,
                                     value=CONFIG.getboolean(
                                         "Defaults", "copy_links"))
        self.show_size = BooleanVar(self,
                                    value=CONFIG.getboolean(
                                        "Defaults", "show_size"))
        menu_params.add_checkbutton(label=_("Copy links"),
                                    variable=self.copy_links,
                                    command=self.toggle_copy_links)
        menu_params.add_checkbutton(label=_("Show total size"),
                                    variable=self.show_size,
                                    command=self.toggle_show_size)
        self.langue = StringVar(self, CONFIG.get("Defaults", "language"))
        menu_lang = Menu(menu_params, tearoff=False)
        menu_lang.add_radiobutton(label="English",
                                  value="en",
                                  variable=self.langue,
                                  command=self.change_language)
        menu_lang.add_radiobutton(label="Français",
                                  value="fr",
                                  variable=self.langue,
                                  command=self.change_language)
        menu_params.add_cascade(label=_("Language"), menu=menu_lang)
        menu_params.add_command(label=_("Exclude from copy"),
                                command=self.exclusion_copie)
        menu_params.add_command(label=_("Exclude from removal"),
                                command=self.exclusion_supp)

        self.menu.add_cascade(label=_("Recents"), menu=self.menu_recent)
        self.menu.add_cascade(label=_("Favorites"), menu=self.menu_fav)
        self.menu.add_cascade(label=_("Log"), menu=menu_log)
        self.menu.add_cascade(label=_("Settings"), menu=menu_params)
        self.menu.add_command(image=self.img_prev,
                              compound="center",
                              command=self.list_files_to_sync)
        self.menu.add_command(image=self.img_sync,
                              compound="center",
                              state="disabled",
                              command=self.synchronise)
        self.menu.add_command(image=self.img_about,
                              compound="center",
                              command=lambda: About(self))
        # --- tooltips
        wrapper = TooltipMenuWrapper(self.menu)
        wrapper.add_tooltip(4, _('Preview'))
        wrapper.add_tooltip(5, _('Sync'))
        wrapper.add_tooltip(6, _('About'))

        # --- path selection
        frame_paths = Frame(self)
        frame_paths.grid(row=0, sticky="ew", pady=(10, 0))
        frame_paths.columnconfigure(0, weight=1)
        frame_paths.columnconfigure(1, weight=1)
        f1 = Frame(frame_paths, height=26)
        f2 = Frame(frame_paths, height=26)
        f1.grid(row=0, column=0, sticky="ew")
        f2.grid(row=0, column=1, sticky="ew")
        f1.grid_propagate(False)
        f2.grid_propagate(False)
        f1.columnconfigure(1, weight=1)
        f2.columnconfigure(1, weight=1)

        # -------- path to original
        Label(f1, text=_("Original")).grid(row=0, column=0, padx=(10, 4))
        f11 = Frame(f1)
        f11.grid(row=0, column=1, sticky="nsew", padx=(4, 0))
        self.entry_orig = Entry(f11)
        self.entry_orig.place(x=1, y=0, bordermode='outside', relwidth=1)
        self.b_open_orig = Button(f1,
                                  image=self.img_open,
                                  style="folder.TButton",
                                  command=self.open_orig)
        self.b_open_orig.grid(row=0, column=2, padx=(0, 7))
        # -------- path to backup
        Label(f2, text=_("Backup")).grid(row=0, column=0, padx=(8, 4))
        f22 = Frame(f2)
        f22.grid(row=0, column=1, sticky="nsew", padx=(4, 0))
        self.entry_sauve = Entry(f22)
        self.entry_sauve.place(x=1, y=0, bordermode='outside', relwidth=1)
        self.b_open_sauve = Button(f2,
                                   image=self.img_open,
                                   width=2,
                                   style="folder.TButton",
                                   command=self.open_sauve)
        self.b_open_sauve.grid(row=0, column=5, padx=(0, 10))

        paned = PanedWindow(self, orient='horizontal')
        paned.grid(row=2, sticky="eswn")
        paned.rowconfigure(0, weight=1)
        paned.columnconfigure(1, weight=1)
        paned.columnconfigure(0, weight=1)

        # --- left side
        frame_left = Frame(paned)
        paned.add(frame_left, weight=1)
        frame_left.rowconfigure(3, weight=1)
        frame_left.columnconfigure(0, weight=1)

        # -------- files to copy
        f_left = Frame(frame_left)
        f_left.columnconfigure(2, weight=1)
        f_left.grid(row=2,
                    columnspan=2,
                    pady=(4, 2),
                    padx=(10, 4),
                    sticky="ew")

        Label(f_left, text=_("To copy")).grid(row=0, column=2)
        frame_copie = Frame(frame_left)
        frame_copie.rowconfigure(0, weight=1)
        frame_copie.columnconfigure(0, weight=1)
        frame_copie.grid(row=3,
                         column=0,
                         sticky="eswn",
                         columnspan=2,
                         pady=(2, 4),
                         padx=(10, 4))
        self.tree_copie = CheckboxTreeview(frame_copie,
                                           selectmode='none',
                                           show='tree')
        self.b_expand_copie = Button(f_left,
                                     image=self.img_expand,
                                     style="folder.TButton",
                                     command=self.tree_copie.expand_all)
        TooltipWrapper(self.b_expand_copie, text=_("Expand all"))
        self.b_expand_copie.grid(row=0, column=0)
        self.b_expand_copie.state(("disabled", ))
        self.b_collapse_copie = Button(f_left,
                                       image=self.img_collapse,
                                       style="folder.TButton",
                                       command=self.tree_copie.collapse_all)
        TooltipWrapper(self.b_collapse_copie, text=_("Collapse all"))
        self.b_collapse_copie.grid(row=0, column=1, padx=4)
        self.b_collapse_copie.state(("disabled", ))
        self.tree_copie.tag_configure("warning", foreground="red")
        self.tree_copie.tag_configure("link",
                                      font="tkDefaultFont 9 italic",
                                      foreground="blue")
        self.tree_copie.tag_bind("warning", "<Button-1>", self.show_warning)
        self.tree_copie.grid(row=0, column=0, sticky="eswn")
        self.scroll_y_copie = Scrollbar(frame_copie,
                                        orient="vertical",
                                        command=self.tree_copie.yview)
        self.scroll_y_copie.grid(row=0, column=1, sticky="ns")
        self.scroll_x_copie = Scrollbar(frame_copie,
                                        orient="horizontal",
                                        command=self.tree_copie.xview)
        self.scroll_x_copie.grid(row=1, column=0, sticky="ew")
        self.tree_copie.configure(yscrollcommand=self.scroll_y_copie.set,
                                  xscrollcommand=self.scroll_x_copie.set)
        self.pbar_copie = Progressbar(frame_left,
                                      orient="horizontal",
                                      mode="determinate")
        self.pbar_copie.grid(row=4,
                             columnspan=2,
                             sticky="ew",
                             padx=(10, 4),
                             pady=4)
        self.pbar_copie.state(("disabled", ))

        # --- right side
        frame_right = Frame(paned)
        paned.add(frame_right, weight=1)
        frame_right.rowconfigure(3, weight=1)
        frame_right.columnconfigure(0, weight=1)

        # -------- files to delete
        f_right = Frame(frame_right)
        f_right.columnconfigure(2, weight=1)
        f_right.grid(row=2,
                     columnspan=2,
                     pady=(4, 2),
                     padx=(4, 10),
                     sticky="ew")
        Label(f_right, text=_("To remove")).grid(row=0, column=2)
        frame_supp = Frame(frame_right)
        frame_supp.rowconfigure(0, weight=1)
        frame_supp.columnconfigure(0, weight=1)
        frame_supp.grid(row=3,
                        columnspan=2,
                        sticky="eswn",
                        pady=(2, 4),
                        padx=(4, 10))
        self.tree_supp = CheckboxTreeview(frame_supp,
                                          selectmode='none',
                                          show='tree')
        self.b_expand_supp = Button(f_right,
                                    image=self.img_expand,
                                    style="folder.TButton",
                                    command=self.tree_supp.expand_all)
        TooltipWrapper(self.b_expand_supp, text=_("Expand all"))
        self.b_expand_supp.grid(row=0, column=0)
        self.b_expand_supp.state(("disabled", ))
        self.b_collapse_supp = Button(f_right,
                                      image=self.img_collapse,
                                      style="folder.TButton",
                                      command=self.tree_supp.collapse_all)
        TooltipWrapper(self.b_collapse_supp, text=_("Collapse all"))
        self.b_collapse_supp.grid(row=0, column=1, padx=4)
        self.b_collapse_supp.state(("disabled", ))
        self.tree_supp.grid(row=0, column=0, sticky="eswn")
        self.scroll_y_supp = Scrollbar(frame_supp,
                                       orient="vertical",
                                       command=self.tree_supp.yview)
        self.scroll_y_supp.grid(row=0, column=1, sticky="ns")
        self.scroll_x_supp = Scrollbar(frame_supp,
                                       orient="horizontal",
                                       command=self.tree_supp.xview)
        self.scroll_x_supp.grid(row=1, column=0, sticky="ew")
        self.tree_supp.configure(yscrollcommand=self.scroll_y_supp.set,
                                 xscrollcommand=self.scroll_x_supp.set)
        self.pbar_supp = Progressbar(frame_right,
                                     orient="horizontal",
                                     mode="determinate")
        self.pbar_supp.grid(row=4,
                            columnspan=2,
                            sticky="ew",
                            padx=(4, 10),
                            pady=4)
        self.pbar_supp.state(("disabled", ))

        # --- bindings
        self.entry_orig.bind("<Key-Return>", self.list_files_to_sync)
        self.entry_sauve.bind("<Key-Return>", self.list_files_to_sync)

    def exclusion_supp(self):
        excl = ExclusionsSupp(self)
        self.wait_window(excl)
        # paths that will not be deleted
        self.exclude_path_supp = [
            ch.replace("\ ", " ") for ch in split(
                r'(?<!\\) ', CONFIG.get("Defaults", "exclude_supp")) if ch
        ]

    def exclusion_copie(self):
        excl = ExclusionsCopie(self)
        self.wait_window(excl)
        exclude_list = CONFIG.get("Defaults", "exclude_copie").split(" ")
        self.exclude_names = []
        self.exclude_ext = []
        for elt in exclude_list:
            if elt:
                if elt[:2] == "*.":
                    self.exclude_ext.append(elt[2:])
                else:
                    self.exclude_names.append(elt)

    def toggle_copy_links(self):
        CONFIG.set("Defaults", "copy_links", str(self.copy_links.get()))

    def toggle_show_size(self):
        CONFIG.set("Defaults", "show_size", str(self.show_size.get()))

    def open_log_copie(self):
        open_file(self.log_copie)

    def open_log_suppression(self):
        open_file(self.log_supp)

    def quitter(self):
        rep = True
        if self.is_running_copie or self.is_running_supp:
            rep = askokcancel(
                _("Confirmation"),
                _("A synchronization is ongoing, do you really want to quit?"),
                parent=self)
        if rep:
            self.destroy()

    def del_fav(self, nom):
        self.menu_fav.delete(nom)
        self.menu_fav_del.delete(nom)
        FAVORIS.remove(tuple(nom.split(" -> ")))
        save_config()
        if not FAVORIS:
            self.menu_fav.entryconfigure(1, state="disabled")

    def add_fav(self):
        sauvegarde = self.entry_sauve.get()
        original = self.entry_orig.get()
        if original != sauvegarde and original and sauvegarde:
            if exists(original) and exists(sauvegarde):
                if not (original, sauvegarde) in FAVORIS:
                    FAVORIS.append((original, sauvegarde))
                    save_config()
                    label = "%s -> %s" % (original, sauvegarde)
                    self.menu_fav.entryconfigure(1, state="normal")
                    self.menu_fav.add_command(label=label,
                                              command=lambda o=original, s=
                                              sauvegarde: self.open(o, s))
                    self.menu_fav_del.add_command(
                        label=label,
                        command=lambda nom=label: self.del_fav(nom))

    def open(self, ch_o, ch_s):
        self.entry_orig.delete(0, "end")
        self.entry_orig.insert(0, ch_o)
        self.entry_sauve.delete(0, "end")
        self.entry_sauve.insert(0, ch_s)
        self.list_files_to_sync()

    def open_sauve(self):
        sauvegarde = askdirectory(self.entry_sauve.get(), parent=self)
        if sauvegarde:
            self.entry_sauve.delete(0, "end")
            self.entry_sauve.insert(0, sauvegarde)

    def open_orig(self):
        original = askdirectory(self.entry_orig.get(), parent=self)
        if original:
            self.entry_orig.delete(0, "end")
            self.entry_orig.insert(0, original)

    def sync(self, original, sauvegarde):
        """ peuple tree_copie avec l'arborescence des fichiers d'original à copier
            vers sauvegarde et tree_supp avec celle des fichiers de sauvegarde à
            supprimer """
        errors = []
        copy_links = self.copy_links.get()
        excl_supp = [
            path for path in self.exclude_path_supp
            if commonpath([path, sauvegarde]) == sauvegarde
        ]

        def get_name(elt):
            return elt.name.lower()

        def lower(char):
            return char.lower()

        def arbo(tree, parent, n):
            """ affiche l'arborescence complète de parent et renvoie la longueur
                maximale des items (pour gérer la scrollbar horizontale) """
            m = 0
            try:
                with scandir(parent) as content:
                    l = sorted(content, key=get_name)
                for item in l:
                    chemin = item.path
                    nom = item.name
                    if item.is_symlink():
                        if copy_links:
                            tree.insert(parent,
                                        'end',
                                        chemin,
                                        text=nom,
                                        tags=("whole", "link"))
                            m = max(m, len(nom) * 9 + 20 * (n + 1))
                    elif ((nom not in self.exclude_names)
                          and (splitext(nom)[-1] not in self.exclude_ext)):
                        tree.insert(parent,
                                    'end',
                                    chemin,
                                    text=nom,
                                    tags=("whole", ))
                        m = max(m, len(nom) * 9 + 20 * (n + 1))
                        if item.is_dir():
                            m = max(m, arbo(tree, chemin, n + 1))
            except NotADirectoryError:
                pass
            except Exception as e:
                errors.append(str(e))
            return m

        def aux(orig, sauve, n, search_supp):
            m_copie = 0
            m_supp = 0
            try:
                lo = listdir(orig)
                ls = listdir(sauve)
            except Exception as e:
                errors.append(str(e))
                lo = []
                ls = []
            lo.sort(key=lambda x: x.lower())
            ls.sort(key=lambda x: x.lower())
            supp = False
            copie = False
            if search_supp:
                for item in ls:
                    chemin_s = join(sauve, item)
                    if chemin_s not in excl_supp and item not in lo:
                        supp = True
                        self.tree_supp.insert(sauve,
                                              'end',
                                              chemin_s,
                                              text=item,
                                              tags=("whole", ))
                        m_supp = max(m_supp, int(len(item) * 9 + 20 * (n + 1)),
                                     arbo(self.tree_supp, chemin_s, n + 1))

            for item in lo:
                chemin_o = join(orig, item)
                chemin_s = join(sauve, item)
                if ((item not in self.exclude_names)
                        and (splitext(item)[-1] not in self.exclude_ext)):
                    if item not in ls:
                        # le dossier / fichier n'est pas dans la sauvegarde
                        if islink(chemin_o):
                            if copy_links:
                                copie = True
                                self.tree_copie.insert(orig,
                                                       'end',
                                                       chemin_o,
                                                       text=item,
                                                       tags=("whole", "link"))
                                m_copie = max(
                                    m_copie,
                                    (int(len(item) * 9 + 20 * (n + 1))))
                        else:
                            copie = True
                            self.tree_copie.insert(orig,
                                                   'end',
                                                   chemin_o,
                                                   text=item,
                                                   tags=("whole", ))
                            m_copie = max(
                                m_copie, (int(len(item) * 9 + 20 * (n + 1))),
                                arbo(self.tree_copie, chemin_o, n + 1))
                    elif islink(chemin_o) and exists(chemin_o):
                        # checking the existence prevent from copying broken links
                        if copy_links:
                            if not islink(chemin_s):
                                self.pb_chemins.append(chemin_o)
                                tags = ("whole", "warning", "link")
                            else:
                                tags = ("whole", "link")
                            self.tree_copie.insert(orig,
                                                   'end',
                                                   chemin_o,
                                                   text=item,
                                                   tags=tags)
                            m_copie = max(m_copie,
                                          int(len(item) * 9 + 20 * (n + 1)))
                            copie = True
                    elif isfile(chemin_o):
                        # first check if chemin_s is also a file
                        if isfile(chemin_s):
                            if getmtime(chemin_o) // 60 > getmtime(
                                    chemin_s) // 60:
                                # le fichier f a été modifié depuis la dernière sauvegarde
                                copie = True
                                self.tree_copie.insert(orig,
                                                       'end',
                                                       chemin_o,
                                                       text=item,
                                                       tags=("whole", ))
                        else:
                            self.pb_chemins.append(chemin_o)
                            self.tree_copie.insert(orig,
                                                   'end',
                                                   chemin_o,
                                                   text=item,
                                                   tags=("whole", "warning"))
                    elif isdir(chemin_o):
                        # to avoid errors due to unrecognized item types (neither file nor folder nor link)
                        if isdir(chemin_s):
                            self.tree_copie.insert(orig,
                                                   'end',
                                                   chemin_o,
                                                   text=item)
                            self.tree_supp.insert(sauve,
                                                  'end',
                                                  chemin_s,
                                                  text=item)
                            c, s, mc, ms = aux(
                                chemin_o, chemin_s, n + 1, search_supp
                                and (chemin_s not in excl_supp))
                            supp = supp or s
                            copie = copie or c
                            if not c:
                                # nothing to copy
                                self.tree_copie.delete(chemin_o)
                            else:
                                m_copie = max(
                                    m_copie, mc,
                                    int(len(item) * 9 + 20 * (n + 1)))
                            if not s:
                                # nothing to delete
                                self.tree_supp.delete(chemin_s)
                            else:
                                m_supp = max(m_supp, ms,
                                             int(len(item) * 9 + 20 * (n + 1)))
                        else:
                            copie = True
                            self.pb_chemins.append(chemin_o)
                            self.tree_copie.insert(orig,
                                                   'end',
                                                   chemin_o,
                                                   text=item,
                                                   tags=("whole", "warning"))
                            m_copie = max(
                                m_copie, (int(len(item) * 9 + 20 * (n + 1))),
                                arbo(self.tree_copie, chemin_o, n + 1))
            return copie, supp, m_copie, m_supp

        self.tree_copie.insert("",
                               0,
                               original,
                               text=original,
                               tags=("checked", ),
                               open=True)
        self.tree_supp.insert("",
                              0,
                              sauvegarde,
                              text=sauvegarde,
                              tags=("checked", ),
                              open=True)
        c, s, mc, ms = aux(original, sauvegarde, 1, True)
        if not c:
            self.tree_copie.delete(original)
            self.tree_copie.column("#0", minwidth=0, width=0)
        else:
            mc = max(len(original) * 9 + 20, mc)
            self.tree_copie.column("#0", minwidth=mc, width=mc)
        if not s:
            self.tree_supp.delete(sauvegarde)
            self.tree_supp.column("#0", minwidth=0, width=0)
        else:
            ms = max(len(sauvegarde) * 9 + 20, mc)
            self.tree_supp.column("#0", minwidth=ms, width=ms)
        return errors

    def show_warning(self, event):
        if "disabled" not in self.b_open_orig.state():
            x, y = event.x, event.y
            elem = event.widget.identify("element", x, y)
            if elem == "padding":
                orig = self.tree_copie.identify_row(y)
                sauve = orig.replace(self.original, self.sauvegarde)
                showwarning(
                    _("Warning"),
                    _("%(original)s and %(backup)s are not of the same kind (folder/file/link)"
                      ) % {
                          'original': orig,
                          'backup': sauve
                      },
                    master=self)

    def list_files_to_sync(self, event=None):
        """Display in a treeview the file to copy and the one to delete."""
        self.pbar_copie.configure(value=0)
        self.pbar_supp.configure(value=0)
        self.sauvegarde = self.entry_sauve.get()
        self.original = self.entry_orig.get()
        if self.original != self.sauvegarde and self.original and self.sauvegarde:
            if exists(self.original) and exists(self.sauvegarde):
                o_s = (self.original, self.sauvegarde)
                if o_s in RECENT:
                    RECENT.remove(o_s)
                    self.menu_recent.delete("%s -> %s" % o_s)
                RECENT.insert(0, o_s)
                self.menu_recent.insert_command(
                    0,
                    label="%s -> %s" % o_s,
                    command=lambda o=self.original, s=self.sauvegarde: self.
                    open(o, s))
                if len(RECENT) == 10:
                    del (RECENT[-1])
                    self.menu_recent.delete(9)
                save_config()
                self.menu.entryconfigure(0, state="normal")
                self.configure(cursor="watch")
                self.toggle_state_gui()
                self.update_idletasks()
                self.update()
                self.efface_tree()
                err = self.sync(self.original, self.sauvegarde)
                self.configure(cursor="")
                self.toggle_state_gui()
                c = self.tree_copie.get_children("")
                s = self.tree_supp.get_children("")
                if not (c or s):
                    self.menu.entryconfigure(5, state="disabled")
                    self.b_collapse_copie.state(("disabled", ))
                    self.b_expand_copie.state(("disabled", ))
                    self.b_collapse_supp.state(("disabled", ))
                    self.b_expand_supp.state(("disabled", ))
                elif not c:
                    self.b_collapse_copie.state(("disabled", ))
                    self.b_expand_copie.state(("disabled", ))
                elif not s:
                    self.b_collapse_supp.state(("disabled", ))
                    self.b_expand_supp.state(("disabled", ))
                if err:
                    showerror(_("Errors"), "\n".join(err), master=self)
                notification_send(_("Scan is finished."))
                warnings = self.tree_copie.tag_has('warning')
                if warnings:
                    showwarning(
                        _("Warning"),
                        _("Some elements to copy (in red) are not of the same kind on the original and the backup."
                          ),
                        master=self)
            else:
                showerror(_("Error"), _("Invalid path!"), master=self)

    def efface_tree(self):
        """Clear both trees."""
        c = self.tree_copie.get_children("")
        for item in c:
            self.tree_copie.delete(item)
        s = self.tree_supp.get_children("")
        for item in s:
            self.tree_supp.delete(item)
        self.b_collapse_copie.state(("disabled", ))
        self.b_expand_copie.state(("disabled", ))
        self.b_collapse_supp.state(("disabled", ))
        self.b_expand_supp.state(("disabled", ))

    def toggle_state_gui(self):
        """Toggle the state (normal/disabled) of key elements of the GUI."""
        if "disabled" in self.b_open_orig.state():
            state = "!disabled"
            for i in range(7):
                self.menu.entryconfigure(i, state="normal")
        else:
            state = "disabled"
            for i in range(7):
                self.menu.entryconfigure(i, state="disabled")
        self.tree_copie.state((state, ))
        self.tree_supp.state((state, ))
        self.entry_orig.state((state, ))
        self.entry_sauve.state((state, ))
        self.b_expand_copie.state((state, ))
        self.b_collapse_copie.state((state, ))
        self.b_expand_supp.state((state, ))
        self.b_collapse_supp.state((state, ))
        self.b_open_orig.state((state, ))
        self.b_open_sauve.state((state, ))

    def update_pbar(self):
        """
        Dislay the progress of the copy and deletion and put the GUI back in
        normal state once both processes are done.
        """
        if not self.is_running_copie and not self.is_running_supp:
            notification_send(_("Sync is finished."))
            self.toggle_state_gui()
            self.pbar_copie.configure(value=self.pbar_copie.cget("maximum"))
            self.pbar_supp.configure(value=self.pbar_supp.cget("maximum"))
            self.menu.entryconfigure(5, state="disabled")
            self.configure(cursor="")
            self.efface_tree()
            msg = ""
            if self.err_copie:
                msg += _(
                    "There were errors during the copy, see %(file)s for more details.\n"
                ) % {
                    'file': self.log_copie
                }
            if self.err_supp:
                msg += _(
                    "There were errors during the removal, see %(file)s for more details.\n"
                ) % {
                    'file': self.log_supp
                }
            if msg:
                showerror(_("Error"), msg, master=self)
        else:
            if not self.q_copie.empty():
                self.pbar_copie.configure(value=self.q_copie.get())
            if not self.q_supp.empty():
                self.pbar_supp.configure(value=self.q_supp.get())
            self.update()
            self.after(50, self.update_pbar)

    @staticmethod
    def get_list(tree):
        """Return the list of files/folders to copy/delete (depending on the tree)."""
        selected = []

        def aux(item):
            tags = tree.item(item, "tags")
            if "checked" in tags and "whole" in tags:
                selected.append(item)
            elif "checked" in tags or "tristate" in tags:
                ch = tree.get_children(item)
                for c in ch:
                    aux(c)

        ch = tree.get_children("")
        for c in ch:
            aux(c)
        return selected

    def synchronise(self):
        """
        Display the list of files/folders that will be copied / deleted
        and launch the copy and deletion if the user validates the sync.
        """
        # get files to delete and folder to delete if they are empty
        a_supp = self.get_list(self.tree_supp)
        # get files to copy
        a_copier = self.get_list(self.tree_copie)
        a_supp_avant_cp = []
        for ch in self.pb_chemins:
            if ch in a_copier:
                a_supp_avant_cp.append(
                    ch.replace(self.original, self.sauvegarde))
        if a_supp or a_copier:
            Confirmation(self, a_copier, a_supp, a_supp_avant_cp,
                         self.original, self.sauvegarde, self.show_size.get())

    def copie_supp(self, a_copier, a_supp, a_supp_avant_cp):
        """Launch sync."""
        self.toggle_state_gui()
        self.configure(cursor="watch")
        self.update()
        self.pbar_copie.state(("!disabled", ))
        self.pbar_supp.state(("!disabled", ))
        nbtot_copie = len(a_copier) + len(a_supp_avant_cp)
        self.pbar_copie.configure(maximum=nbtot_copie, value=0)
        nbtot_supp = len(a_supp)
        self.pbar_supp.configure(maximum=nbtot_supp, value=0)
        self.is_running_copie = True
        self.is_running_supp = True
        process_copie = Thread(target=self.copie,
                               name="copie",
                               daemon=True,
                               args=(a_copier, a_supp_avant_cp))
        process_supp = Thread(target=self.supp,
                              daemon=True,
                              name="suppression",
                              args=(a_supp, ))
        process_copie.start()
        process_supp.start()
        self.pbar_copie.configure(value=0)
        self.pbar_supp.configure(value=0)
        self.update_pbar()

    def copie(self, a_copier, a_supp_avant_cp):
        """
        Copie tous les fichiers/dossiers de a_copier de original vers
        sauvegarde en utilisant la commande système cp. Les erreurs
        rencontrées au cours du processus sont inscrites dans
        ~/.foldersync/copie.log
        """
        self.err_copie = False
        orig = abspath(self.original) + "/"
        sauve = abspath(self.sauvegarde) + "/"
        chdir(orig)
        self.logger_copie.info(
            _("\n###### Copy: %(original)s -> %(backup)s\n") % {
                'original': self.original,
                'backup': self.sauvegarde
            })
        n = len(a_supp_avant_cp)
        self.logger_copie.info(_("Removal before copy:"))
        for i, ch in zip(range(1, n + 1), a_supp_avant_cp):
            self.logger_copie.info(ch)
            p_copie = run(["rm", "-r", ch], stderr=PIPE)
            self.q_copie.put(i)
            err = p_copie.stderr.decode()
            if err:
                self.err_copie = True
                self.logger_copie.error(err.strip())
        self.logger_copie.info(_("Copy:"))
        for i, ch in zip(range(n + 1, n + 1 + len(a_copier)), a_copier):
            ch_o = ch.replace(orig, "")
            self.logger_copie.info("%s -> %s" % (ch_o, sauve))
            p_copie = run(["cp", "-ra", "--parents", ch_o, sauve], stderr=PIPE)
            self.q_copie.put(i)
            err = p_copie.stderr.decode()
            if err:
                self.err_copie = True
                self.logger_copie.error(err.strip())
        self.is_running_copie = False

    def supp(self, a_supp):
        """
        Supprime tous les fichiers/dossiers de a_supp de original vers
        sauvegarde en utilisant la commande système rm. Les erreurs
        rencontrées au cours du processus sont inscrites dans
        ~/.foldersync/suppression.log.
        """
        self.err_supp = False
        self.logger_supp.info(
            _("\n###### Removal:  %(original)s -> %(backup)s\n") % {
                'original': self.original,
                'backup': self.sauvegarde
            })
        for i, ch in enumerate(a_supp):
            self.logger_supp.info(ch)
            p_supp = run(["rm", "-r", ch], stderr=PIPE)
            self.q_supp.put(i + 1)
            err = p_supp.stderr.decode()
            if err:
                self.logger_supp.error(err.strip())
                self.err_supp = True
        self.is_running_supp = False

    def unlink(self):
        """Unlink pidfile."""
        unlink(self.pidfile)

    def change_language(self):
        """Change app language."""
        CONFIG.set("Defaults", "language", self.langue.get())
        showinfo(
            _("Information"),
            _("The language setting will take effect after restarting the application"
              ))
Пример #30
0
class VadTest(Frame):
    """ A demo application class that provides simple GUI for testing voice activity detection on microphone or wav file input. """
    def __init__(self, featurizer_path, input_device, wav_file, sample_rate):
        """ Initialize the VadTest object
        featurizer_path - path to the ELL featurizer to use
        input_device - id of the microphone to use
        wav_file - optional wav_file to use when you click play
        sample_rate - the sample rate to resample the incoming audio
        """
        super().__init__()

        self.FEATURIZER_PATH_KEY = "featurizer_path"
        self.WAV_FILE_KEY = "wav_file"
        self.main_thread = get_ident()
        self.output_clear_time = 5000
        self.channels = 1
        self.init_ui()

        self.get_settings_file_name()
        self.load_settings()

        self.max_spectrogram_width = 120
        self.spectrogram_image = None
        self.spectrogram_image_data = None
        self.show_spectrogram = True
        self.colormap_name = "inferno"
        self.min_value = 0.0
        self.max_value = 1.0
        self.update_minmax = True

        self.levels = []
        self.signals = []
        self.featurizer_path = None
        self.featurizer = None
        self.reading_input = False

        # Threads
        self.read_input_thread = None
        self.lock = Lock()
        self.main_thread = get_ident()
        self.message_queue = []
        self.animation = None

        # featurizer
        if featurizer_path:
            self.featurizer_path = featurizer_path
            self.settings[self.FEATURIZER_PATH_KEY] = featurizer_path
        elif self.FEATURIZER_PATH_KEY in self.settings:
            self.featurizer_path = self.settings[self.FEATURIZER_PATH_KEY]
        self.sample_rate = sample_rate

        self.input_device = input_device
        self.wav_filename = None
        self.wav_file = None
        if wav_file:
            self.wav_filename = wav_file
            self.settings[self.WAV_FILE_KEY] = wav_file
        if self.wav_filename is None and self.WAV_FILE_KEY in self.settings:
            self.wav_filename = self.settings[self.WAV_FILE_KEY]

        self.wav_file_list = None
        self.speaker = None
        self.microphone = None
        self.save_settings()  # in case we just changed it.

        if self.featurizer_path:
            self.load_featurizer_model(os.path.abspath(self.featurizer_path))
        else:
            self.show_output("Please specify and load a feature model")

        self.update_ui()

    def init_ui(self):
        self.master.title("VAD Test")
        self.pack(side="top", fill=BOTH, expand=True)

        # VAD Controls section for controlling these VAD settings:
        controls_frame = LabelFrame(self, text="Controls", height=30)
        Label(controls_frame, text="tau_up:").grid(row=0, column=0)
        self.tau_up = Entry(controls_frame, width=15)
        self.tau_up.grid(row=1, column=0)

        Label(controls_frame, text="tau_down:").grid(row=0, column=1)
        self.tau_down = Entry(controls_frame, width=15)
        self.tau_down.grid(row=1, column=1)

        Label(controls_frame, text="threshold_up:").grid(row=0, column=2)
        self.threshold_up = Entry(controls_frame, width=15)
        self.threshold_up.grid(row=1, column=2)

        Label(controls_frame, text="threshold_down:").grid(row=0, column=3)
        self.threshold_down = Entry(controls_frame, width=15)
        self.threshold_down.grid(row=1, column=3)

        Label(controls_frame, text="large_input:").grid(row=0, column=4)
        self.large_input = Entry(controls_frame, width=15)
        self.large_input.grid(row=1, column=4)

        Label(controls_frame, text="gain_att:").grid(row=0, column=5)
        self.gain_att = Entry(controls_frame, width=15)
        self.gain_att.grid(row=1, column=5)

        Label(controls_frame, text="level_threshold:").grid(row=0, column=6)
        self.level_threshold = Entry(controls_frame, width=15)
        self.level_threshold.grid(row=1, column=6)
        controls_frame.pack(side=TOP)

        # Input section
        input_frame = LabelFrame(self, text="Input")
        input_frame.bind("-", self.on_minus_key)
        input_frame.bind("+", self.on_plus_key)
        input_frame.pack(fill=X)
        self.play_button = Button(input_frame,
                                  text="Play",
                                  command=self.on_play_button_click)
        self.play_button.pack(side=RIGHT, padx=4)
        self.rec_button = Button(input_frame,
                                 text="Rec",
                                 command=self.on_rec_button_click)
        self.rec_button.pack(side=RIGHT, padx=4)
        self.wav_filename_entry = Entry(input_frame, width=24)
        self.wav_filename_entry.pack(fill=X)
        self.wav_filename_entry.delete(0, END)

        # Feature section
        features_frame = LabelFrame(self, text="Features")
        features_frame.pack(fill=X)

        features_control_frame = Frame(features_frame)
        features_control_frame.pack(fill=X)
        load_features_button = Button(features_control_frame,
                                      text="Load",
                                      command=self.on_load_featurizer_model)
        load_features_button.pack(side=RIGHT)
        self.features_entry = Entry(features_control_frame, width=8)
        self.features_entry.pack(fill=X)
        self.features_entry.delete(0, END)

        viz_frame = Frame(features_frame)
        viz_frame.bind("%w", self.on_resized)
        viz_frame.pack(fill=X)
        self.features_figure = Figure(figsize=(5, 4), dpi=96)
        self.subplot = self.features_figure.add_subplot(211)

        self.subplot2 = self.features_figure.add_subplot(212)

        self.canvas = FigureCanvasTkAgg(self.features_figure, master=viz_frame)
        self.canvas.draw()
        self.canvas.show()
        self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=True)

        # Output section
        output_frame = LabelFrame(self, text="Output")
        output_frame.pack(fill=BOTH, expand=True)
        self.bind("<Configure>", self.on_resized)

        self.output_text = Text(output_frame)
        self.output_text.pack(fill=BOTH, padx=4, expand=True)

    def on_resized(self, event):
        window_size = event.width
        box = self.spectrogram_image.get_window_extent()
        scale = (box.x1 - box.x0) / self.max_spectrogram_width
        self.max_spectrogram_width = int((window_size * 0.8) / scale)
        self.setup_spectrogram_image()

    def load_featurizer_model(self, featurizer_path):
        """ load the given compiled ELL featurizer for use in processing subsequent audio input """
        if featurizer_path:
            self.featurizer = featurizer.AudioTransform(featurizer_path, 40)
            self.setup_spectrogram_image()

            self.vad = vad.VoiceActivityDetector(self.sample_rate,
                                                 self.featurizer.output_size)

            self.show_output("Feature input size: {}, output size: {}".format(
                self.featurizer.input_size, self.featurizer.output_size))

        self.init_data()

    def setup_spectrogram_image(self):
        """ this need to be called if you load a new feature model, because the featurizer output size might have 
        changed. """
        if self.featurizer:
            dim = (self.featurizer.output_size, self.max_spectrogram_width)
            self.spectrogram_image_data = np.zeros(dim, dtype=float)
            self.subplot.clear()
            self.spectrogram_image = self.subplot.imshow(
                self.spectrogram_image_data,
                vmin=self.min_value,
                vmax=self.max_value,
                origin="lower",
                animated=True,
                cmap=pyplot.get_cmap(self.colormap_name))

    def accumulate_spectrogram_image(self, feature_data):
        """ accumulate the feature data into the spectrogram image """
        image_data = self.spectrogram_image_data
        feature_data = np.reshape(feature_data, [-1, 1])
        new_image = np.hstack(
            (image_data, feature_data))[:, -image_data.shape[1]:]
        image_data[:, :] = new_image

    def set_spectrogram_image(self):
        """ update the spectrogram image and the min/max values """
        self.lock.acquire()  # protect access to the shared state
        if self.update_minmax and self.show_spectrogram:
            min_value = np.min(self.spectrogram_image_data)
            max_value = np.max(self.spectrogram_image_data)
            if np.isfinite(min_value) and np.isfinite(max_value):
                self.min_value = min_value
                self.max_value = max_value
                eps = 0.1
                if self.max_value - self.min_value < eps:
                    self.max_value = self.min_value + eps

            self.spectrogram_image.set_clim(self.min_value, self.max_value)
        self.spectrogram_image.set_data(self.spectrogram_image_data)
        self.lock.release()

    def on_load_featurizer_model(self):
        """ called when user clicks the Load button for the feature model """
        filename = self.features_entry.get()
        filename = filename.strip('"')
        self.featurizer_path = filename
        self.get_sample_rate()
        self.settings[self.FEATURIZER_PATH_KEY] = filename
        self.save_settings()
        self.stop()
        self.load_featurizer_model(filename)

    def set_entry(self, e, value):
        s = str(value)
        if e.get() != s:
            e.delete(0, END)
            e.insert(0, s)

    def get_entry(self, e):
        v = e.get()
        return float(v)

    def update_ui(self):
        self.set_entry(self.wav_filename_entry, self.wav_filename)
        self.set_entry(self.features_entry, self.featurizer_path)
        self.set_entry(self.tau_up, vad.DEFAULT_TAU_UP)
        self.set_entry(self.tau_down, vad.DEFAULT_TAU_DOWN)
        self.set_entry(self.threshold_up, vad.DEFAULT_THRESHOLD_UP)
        self.set_entry(self.threshold_down, vad.DEFAULT_THRESHOLD_DOWN)
        self.set_entry(self.large_input, vad.DEFAULT_LARGE_INPUT)
        self.set_entry(self.gain_att, vad.DEFAULT_GAIN_ATT)
        self.set_entry(self.level_threshold, vad.DEFAULT_LEVEL_THRESHOLD)

    def read_ui_settings(self):
        self.vad.configure(self.get_entry(self.tau_up),
                           self.get_entry(self.tau_down),
                           self.get_entry(self.threshold_up),
                           self.get_entry(self.threshold_down),
                           self.get_entry(self.large_input),
                           self.get_entry(self.gain_att),
                           self.get_entry(self.level_threshold))

    def init_data(self):
        """ initialize the spectrogram_image_data based on the newly loaded model info """
        if self.featurizer:
            dim = (self.featurizer.output_size, self.max_spectrogram_width)
            self.spectrogram_image_data = np.zeros(dim, dtype=float)
            if self.spectrogram_image is not None:
                self.spectrogram_image.set_data(self.spectrogram_image_data)

    def get_settings_file_name(self):
        """ this app stores the various UI field values in a settings file in your temp folder 
        so you don't always have to specify the full command line options """
        import tempfile
        temp = tempfile.gettempdir()
        self.settings_file_name = os.path.join(temp, "ELL", "Audio",
                                               "vad_test.json")

    def load_settings(self):
        """ load the previously saved settings from disk, if any """
        self.settings = {}
        try:
            if os.path.isfile(self.settings_file_name):
                with open(self.settings_file_name, "r") as f:
                    self.settings = json.load(f)
        except:
            self.show_output("error loading settings: {}".format(
                self.settings_file_name))
            self.settings = {}

    def save_settings(self):
        """ save the current settings to disk """
        settings_dir = os.path.dirname(self.settings_file_name)
        if not os.path.isdir(settings_dir):
            os.makedirs(settings_dir)
        with open(self.settings_file_name, "w") as f:
            f.write(json.dumps(self.settings))

    def on_rec_button_click(self):
        """ called when user clicks the record button, same button is used to "stop" recording. """
        if self.rec_button["text"] == "Rec":
            self.rec_button["text"] = "Stop"
            self.play_button["text"] = "Play"
            self.start_recording()
        else:
            self.rec_button["text"] = "Rec"
            self.on_stopped()

    def on_play_button_click(self):
        """ called when user clicks the record button, same button is used to "stop" playback """
        if self.play_button["text"] == "Play":
            self.play_button["text"] = "Stop"
            self.rec_button["text"] = "Rec"
            self.on_play()
        else:
            self.play_button["text"] = "Play"
            self.on_stopped()

    def on_play(self):
        """ called when user clicks the Play button """
        filename = self.wav_filename_entry.get()
        filename = filename.strip('"')
        self.wav_filename = filename
        self.settings[self.WAV_FILE_KEY] = filename
        self.save_settings()
        self.start_playing(filename)

    def on_stop(self):
        """ called when user clicks the Stop button """
        self.reading_input = False
        if self.wav_file:
            self.wav_file.close()
            self.wav_file = None
        if self.read_input_thread:
            self.read_input_thread.join()
            self.read_input_thread = None
        self.stop()

    def on_stopped(self):
        """ called when we reach the end of the wav file playback """
        self.play_button["text"] = "Play"
        self.on_stop()
        self.subplot2.clear()
        if (len(self.levels) > 0):
            levels = np.array(self.levels)
            levels /= np.max(levels)
            signals = np.array(self.signals)
            self.subplot2.plot(levels)
            self.subplot2.plot(signals)
            self.vad.reset()
        self.canvas.draw()
        self.canvas.show()
        self.levels = []
        self.signals = []

    def stop(self):
        """ called when user clicks the stop button, or we reach the end of a wav file input """
        # close streams
        if self.animation:
            self.animation.event_source.stop()
            self.animation = None
        if self.microphone:
            self.microphone.close()
        if self.speaker:
            self.speaker.close()
        if self.wav_file:
            self.wav_file.close()
            self.wav_file = None
        self.reading_input = False

    def get_wav_list(self):
        if self.wav_filename and os.path.isfile(self.wav_filename):
            full_path = os.path.abspath(self.wav_filename)
            dir_name = os.path.dirname(full_path)
            if not self.wav_file_list:
                print("wav file name: {}".format(full_path))
                print("looking for wav files in: {}".format(dir_name))
                self.wav_file_list = [
                    x for x in os.listdir(dir_name)
                    if os.path.splitext(x)[1] == ".wav"
                ]
                self.wav_file_list.sort()
        return self.wav_file_list

    def select_wav_file(self, filename):
        self.wav_filename = filename
        # show the file in the UI
        self.wav_filename_entry.delete(0, END)
        if self.wav_filename:
            self.wav_filename_entry.insert(0, self.wav_filename)
        # and automatically play the file.
        self.on_play()

    def on_minus_key(self, event):
        """ When user presses the plus button we reverse to the previous wav file in the current folder.
        This way you can easily step through all the training wav files """
        if self.get_wav_list():
            i = self.wav_file_list.index(os.path.basename(self.wav_filename))
            if i - 1 >= 0:
                next_wav_file = self.wav_file_list[i - 1]
                dir_name = os.path.dirname(self.wav_filename)
                self.select_wav_file(os.path.join(dir_name, next_wav_file))

    def on_plus_key(self, event):
        """ When user presses the plus button we advance to the next wav file in the current folder.
        This way you can easily step through all the training wav files """
        if self.get_wav_list():
            i = self.wav_file_list.index(os.path.basename(self.wav_filename))
            if i + 1 < len(self.wav_file_list):
                next_wav_file = self.wav_file_list[i + 1]
                dir_name = os.path.dirname(self.wav_filename)
                self.select_wav_file(os.path.join(dir_name, next_wav_file))

    def clear_output(self):
        """ remove some of the Output based a the timeout callback  """
        self.output_text.delete(1.0, 2.0)

    def process_output(self):
        """ show output that was queued by background thread """
        self.lock.acquire()
        messages = self.message_queue
        self.message_queue = []
        self.lock.release()
        for msg in messages:
            self.show_output(msg)

    def show_output(self, message):
        """ show output message, or queue it if we are on a background thread """
        if self.main_thread != get_ident():
            self.message_queue += [message]
            return

        for line in str(message).split('\n'):
            self.output_text.insert(END, "{}\n".format(line))

        self.output_text.see("end")  # scroll to end
        self.after(self.output_clear_time, self.clear_output)

    def start_playing(self, filename):
        """ Play a wav file, and classify the audio. Note we use a background thread to read the
        wav file and we setup a UI animation function to draw the sliding spectrogram image, this way
        the UI update doesn't interfere with the smoothness of the audio playback """
        #if self.speaker is None:
        #    self.speaker = speaker.Speaker()

        self.stop()

        self.read_ui_settings()
        self.reading_input = False
        self.wav_file = wav_reader.WavReader(self.sample_rate, self.channels)
        self.wav_file.open(filename, self.featurizer.input_size, self.speaker)
        self.setup_spectrogram_image()

        def update_func(frame_index):
            self.process_output()
            if not self.reading_input:
                self.after(1, self.on_stopped)
            self.set_spectrogram_image()
            return (self.spectrogram_image, )

        if self.animation:
            self.animation.event_source.stop()
        self.reading_input = True

        # Start animation timer for updating the UI (e.g. spectrogram image) (30 fps is usually fine)
        self.animation = animation.FuncAnimation(self.features_figure,
                                                 update_func,
                                                 interval=33,
                                                 blit=True)

        # start background thread to read and classify the audio.
        self.featurizer.open(self.wav_file)
        self.read_input_thread = Thread(target=self.on_read_features, args=())
        self.read_input_thread.daemon = True
        self.read_input_thread.start()

    def start_recording(self):
        """ Start recording audio from the microphone nd classify the audio. Note we use a background thread to 
        process the audio and we setup a UI animation function to draw the sliding spectrogram image, this way
        the UI update doesn't interfere with the smoothness of the microphone readings """
        if self.microphone is None:
            self.microphone = microphone.Microphone(False)

        self.stop()
        self.read_ui_settings()
        num_channels = 1
        self.microphone.open(self.featurizer.input_size, self.sample_rate,
                             num_channels, self.input_device)

        def update_func(frame_index):
            # this is an animation callback to update the UI every 33 milliseconds.
            self.process_output()
            self.set_spectrogram_image()
            if not self.reading_input:
                self.after(1, self.on_stopped)
            return (self.spectrogram_image, )

        if self.animation:
            self.animation.event_source.stop()

        self.reading_input = True
        # Start animation timer for updating the UI (e.g. spectrogram image) (30 fps is usually fine)
        self.animation = animation.FuncAnimation(self.features_figure,
                                                 update_func,
                                                 interval=33,
                                                 blit=True)

        # start background thread to read and classify the recorded audio.
        self.featurizer.open(self.microphone)
        self.read_input_thread = Thread(target=self.on_read_features, args=())
        self.read_input_thread.daemon = True
        self.read_input_thread.start()

    def on_read_features(self):
        """ this is the background thread entry point.  So we read the feature data in a loop """
        try:

            while self.reading_input and self.featurizer:
                feature_data = self.featurizer.read()
                if feature_data is None:
                    break  # eof
                else:
                    signal = self.vad.process(feature_data)
                    self.levels += [self.vad.level]
                    self.signals += [signal]
                    self.lock.acquire()
                    if self.show_spectrogram:
                        self.accumulate_spectrogram_image(feature_data)
                    self.lock.release()
        except:
            errorType, value, traceback = sys.exc_info()
            print("### Exception reading input: " + str(errorType) + ": " +
                  str(value) + " " + str(traceback))
            while traceback:
                print(traceback.tb_frame.f_code)
                traceback = traceback.tb_next

        self.reading_input = False
Пример #31
0
class Tampil(Tk):
    def __init__(self):
        super().__init__()
        self.dekPrev = myDek()
        self.dekNext = myDek()
        self.dekHistory = myDek()
        self.current = ""

        #Windows
        self.title("Stack Using Deque")
        self.geometry("400x160")

        #Label
        self.lblTitle = Label(self,
                              text="Bejjo Parse Browser",
                              foreground="blue")
        self.lblTitle.grid(row=0, column=2, pady=5, padx=2)

        self.lblBody = Label(self, text="Welcome")
        self.lblBody.grid(row=2, column=0, columnspan=4, pady=5, padx=2)

        #Button
        self.btnPrev = Button(self,
                              text="<",
                              width=5,
                              state="disabled",
                              command=self.prev)
        self.btnPrev.grid(row=1, column=0, pady=5, padx=2)

        self.btnNext = Button(self,
                              text=">",
                              width=5,
                              state="disabled",
                              command=self.next)
        self.btnNext.grid(row=1, column=1, pady=5, padx=2)

        self.btnSearch = Button(self,
                                text="Search",
                                width=8,
                                command=self.search)
        self.btnSearch.grid(row=1, column=3, pady=5, padx=2)

        self.btnHistory = Button(self,
                                 text="History",
                                 width=12,
                                 command=self.history)
        self.btnHistory.grid(row=0, column=0, columnspan=2, pady=5, padx=2)

        self.btnParse = Button(self, text="Parse", width=8, command=self.parse)
        self.btnParse.grid(row=0, column=3, pady=5, padx=2)

        #Entry
        self.txtSearch = Entry(self, width=40)
        self.txtSearch.grid(row=1, column=2, pady=5, padx=2)

    def parse(self):
        if self.btnParse["text"] == "Parse":
            teks = "Welcome"
            if self.current != "":
                teks = ""
                url = Url(self.current)
                for x in url:
                    teks += x + "\n"
            self.lblBody["text"] = teks
            self.btnParse["text"] = "Close"
            self.btnHistory["text"] = "History"
        else:
            self.btnParse["text"] = "Parse"
            self.switch_attr()

    def history(self):
        if self.btnHistory["text"] == "History":
            self.lblBody["text"] = self.dekHistory
            self.btnHistory["text"] = "Close"
            self.btnParse["text"] = "Parse"
        else:
            self.btnHistory["text"] = "History"
            self.switch_attr()

    def data(self):
        teks = "Welcome"
        if self.current != "":
            url = Url(self.current)
            teks = url.title()
        return teks

    def switch_attr(self):
        if len(self.dekPrev) == 0:
            self.btnPrev["state"] = "disabled"
        else:
            self.btnPrev["state"] = "normal"

        if len(self.dekNext) == 0:
            self.btnNext["state"] = "disabled"
        else:
            self.btnNext["state"] = "normal"

        self.txtSearch.delete(0, "end")
        self.txtSearch.insert(0, self.current)

        teks = self.data()
        self.lblBody["text"] = teks

    def prev(self):
        self.dekNext + self.current
        self.current = self.dekPrev.back()
        self.btnHistory["text"] = "History"
        self.btnParse["text"] = "Parse"
        self.switch_attr()

    def next(self):
        self.dekPrev + self.current
        self.current = self.dekNext.back()
        self.btnHistory["text"] = "History"
        self.btnParse["text"] = "Parse"
        self.switch_attr()

    def search(self):
        url = self.txtSearch.get().strip()
        if self.current == url and url:
            self.btnHistory["text"] = "History"
            self.btnParse["text"] = "Parse"
            self.switch_attr()
        elif "http" == url[:4] and len(url.replace(
                "http", "", 1).strip()) > 5 and "." in url:
            self.dekPrev + self.current
            self.current = url
            self.dekNext = myDek()
            self.dekHistory.history(self.current)
            self.btnHistory["text"] = "History"
            self.btnParse["text"] = "Parse"
            self.switch_attr()
        else:
            messagebox.showinfo("Warning", "Incorrect URL")