コード例 #1
0
 def runtime_select(self, button: ttk.Button):
     logging.debug("selecting runtime button: %s", button)
     self.runtime_select_button.state(["!pressed"])
     self.stop_button.state(["!pressed"])
     self.runtime_marker_button.state(["!pressed"])
     self.run_command_button.state(["!pressed"])
     button.state(["pressed"])
コード例 #2
0
ファイル: toolbar.py プロジェクト: montag451/core
 def design_select(self, button: ttk.Button):
     logging.debug("selecting design button: %s", button)
     self.select_button.state(["!pressed"])
     self.link_button.state(["!pressed"])
     self.node_button.state(["!pressed"])
     self.network_button.state(["!pressed"])
     self.annotation_button.state(["!pressed"])
     button.state(["pressed"])
コード例 #3
0
class AutoCorrectConfig(Frame):
    """Configuration window for autocorrect."""
    def __init__(self, master, app, **kwargs):
        Frame.__init__(self, master, padding=4, **kwargs)
        self.rowconfigure(2, weight=1)
        self.columnconfigure(0, weight=1)
        self.columnconfigure(1, weight=1)

        self.tree = Treeview(self,
                             columns=('replace', 'by'),
                             show='',
                             selectmode='browse')
        scroll_x = AutoScrollbar(self,
                                 orient='horizontal',
                                 command=self.tree.xview)
        scroll_y = AutoScrollbar(self,
                                 orient='vertical',
                                 command=self.tree.yview)
        self.tree.configure(xscrollcommand=scroll_x.set,
                            yscrollcommand=scroll_y.set)

        self.reset()

        self.replace = StringVar(self)
        self.by = StringVar(self)

        add_trace(self.replace, 'write', self._trace_replace)
        add_trace(self.by, 'write', self._trace_by)

        b_frame = Frame(self)
        self.b_add = Button(b_frame, text=_('New'), command=self.add)
        self.b_rem = Button(b_frame, text=_('Delete'), command=self.remove)
        self.b_add.state(('disabled', ))
        self.b_rem.state(('disabled', ))
        self.b_add.pack(pady=4, fill='x')
        self.b_rem.pack(pady=4, fill='x')
        Button(b_frame, text=_('Reset'), command=self.reset).pack(pady=8,
                                                                  fill='x')

        Label(self, text=_('Replace')).grid(row=0,
                                            column=0,
                                            sticky='w',
                                            pady=4)
        Label(self, text=_('By')).grid(row=0, column=1, sticky='w', pady=4)
        Entry(self, textvariable=self.replace).grid(row=1,
                                                    column=0,
                                                    sticky='ew',
                                                    pady=4,
                                                    padx=(0, 4))
        Entry(self, textvariable=self.by).grid(row=1,
                                               column=1,
                                               sticky='ew',
                                               pady=4)
        self.tree.grid(row=2, columnspan=2, sticky='ewsn', pady=(4, 0))
        scroll_x.grid(row=3, columnspan=2, sticky='ew', pady=(0, 4))
        scroll_y.grid(row=2, column=2, sticky='ns', pady=(4, 0))
        b_frame.grid(row=1, rowspan=2, padx=(4, 0), sticky='nw', column=3)

        self.tree.bind('<<TreeviewSelect>>', self._on_treeview_select)

    def _trace_by(self, *args):
        key = self.replace.get().strip()
        val = self.by.get().strip()
        self.by.set(val)
        if key in self.tree.get_children(''):
            if val != self.tree.set(key, 'by'):
                self.b_add.state(('!disabled', ))
            else:
                self.b_add.state(('disabled', ))
        else:
            self.b_add.state(('!disabled', ))
        if not val:
            self.b_add.state(('disabled', ))

    def _trace_replace(self, *args):
        key = self.replace.get().strip()
        val = self.by.get().strip()
        self.replace.set(key)
        if not key:
            self.b_add.state(('disabled', ))
            self.b_rem.state(('disabled', ))
        else:
            self.b_add.state(('!disabled', ))
            sel = self.tree.selection()
            if key in self.tree.get_children(''):
                if key not in sel:
                    self.tree.selection_set(key)
                self.b_add.configure(text=_('Replace'))
                self.b_rem.state(('!disabled', ))
                if val != self.tree.set(key, 'by'):
                    self.b_add.state(('!disabled', ))
                else:
                    self.b_add.state(('disabled', ))
            else:
                self.b_rem.state(('disabled', ))
                self.b_add.configure(text=_('New'))
                if sel:
                    self.tree.selection_remove(*sel)
        if not val:
            self.b_add.state(('disabled', ))

    def _on_treeview_select(self, event):
        sel = self.tree.selection()
        if sel:
            key, val = self.tree.item(sel[0], 'values')
            self.replace.set(key)
            self.by.set(val)

    def reset(self):
        self.tree.delete(*self.tree.get_children(''))
        keys = list(AUTOCORRECT.keys())
        keys.sort()
        for key in keys:
            self.tree.insert('', 'end', key, values=(key, AUTOCORRECT[key]))

    def add(self):
        key = self.replace.get().strip()
        val = self.by.get().strip()
        if key in self.tree.get_children(''):
            self.tree.item(key, values=(key, val))
        elif key and val:
            self.tree.insert('', 'end', key, values=(key, val))

    def remove(self):
        key = self.replace.get()
        if key in self.tree.get_children(''):
            self.tree.delete(key)

    def ok(self):
        keys = self.tree.get_children('')
        AUTOCORRECT.clear()
        for key in keys:
            AUTOCORRECT[key] = self.tree.set(key, 'by')
コード例 #4
0
ファイル: toolbar.py プロジェクト: q9512268/core
 def select_radio(self, selected: ttk.Button) -> None:
     for button in self.radio_buttons:
         button.state(["!pressed"])
     selected.state(["pressed"])
コード例 #5
0
ファイル: sync.py プロジェクト: j4321/FolderSync
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"
              ))
コード例 #6
0
class GUI:

    x0 = y0 = x1 = y1 = x_start = y_start = rect_x0 = rect_y0 = rect_x2 = rect_y2 = -1
    polygone = []
    # polygonesPointsCollection = []
    polygone_id_collection = {}
    polygoneCollection = {}
    colorCollection = [
        'black', 'red', 'green', 'blue', 'cyan', 'yellow', 'magenta'
    ]
    randomColor = random.choice(colorCollection)
    # fontText = tkFont.Font(family='Helvetica')
    textHeight = 30
    line_tmp = None
    # format de text in the text-area
    text_format = "txt"
    check_mark = u"\u2713"
    rect = None
    canvasLigneCollection = []
    the_last_draw = None

    def __init__(self, master):
        self.master = master
        self.master.title("Points indicator")

        # Mode de draw
        self.draw_mode = "point"

        # Image
        self.origin_image = None
        self.scale = 1.0
        self.img = None
        self.img_id = None

        # Menu
        self.menu_bar = Menu(self.master)
        self.create_menus()

        # main frame
        self.frame = Frame(self.master,
                           relief=SUNKEN,
                           bg="red",
                           width=self.master.winfo_width())
        self.frame.grid_rowconfigure(0, weight=1)
        self.frame.grid_columnconfigure(0, weight=1)
        self.frame.pack(fill=None, expand=False)

        # scroll bars
        self.xscrollbar = Scrollbar(self.frame, orient=HORIZONTAL)
        self.xscrollbar.grid(row=2, column=0, columnspan=2, sticky=EW)

        self.yscrollbar = Scrollbar(self.frame)
        self.yscrollbar.grid(row=1, column=1, sticky=NS)

        # canvas to put image
        self.canvas = Canvas(self.frame,
                             bg="black",
                             bd=0,
                             height=(self.master.winfo_height() - 250),
                             width=(self.master.winfo_width() - 100),
                             xscrollcommand=self.xscrollbar.set,
                             yscrollcommand=self.yscrollbar.set)
        self.canvas.grid(row=1, column=0, sticky=NSEW)
        self.canvas.config(state=DISABLED)
        self.xscrollbar.config(command=self.canvas.xview)
        self.yscrollbar.config(command=self.canvas.yview)

        # zoom utility buttons
        self.zoom_in_button = Button(self.frame,
                                     text="+",
                                     command=lambda: self.zoom(self.scale * 2),
                                     width=3)
        self.zoom_out_button = Button(
            self.frame,
            text="-",
            command=lambda: self.zoom(self.scale * 0.5),
            width=3)
        self.zoom_scale = Scale(self.frame,
                                from_=1,
                                to=3,
                                length=80,
                                command=self.on_scale,
                                orient=VERTICAL)

        self.zoom_in_button.place(relx=0.93, rely=0.03)
        self.zoom_scale.place(relx=0.93, rely=0.07)
        self.zoom_out_button.place(relx=0.93, rely=0.18)
        self.change_zoom_state(0)

        # Frame to put text
        self.textFrame = Frame(self.frame,
                               relief=SUNKEN,
                               bg=self.master.cget('bg'),
                               width=(self.master.winfo_width()),
                               height=(self.master.winfo_height() -
                                       int(self.canvas['height']) - 50))
        self.textFrame.grid_rowconfigure(0, weight=1)
        self.textFrame.grid_columnconfigure(0, weight=1)
        self.textFrame.grid(row=3, column=0, columnspan=2, sticky=NSEW)
        self.textFrame.grid_propagate(False)

        # text area widget
        self.textContent = Text(self.textFrame, font='Arial')
        self.textContent.grid(row=4, column=0, padx=25, pady=25, sticky=NSEW)
        self.set_text_color_tags()

        # popup entry value
        self.roomLabel = StringVar()

        self.open_file()
        # bind mouse-click event
        self.go_to_point_mode()  # point mode by default
        self.canvas.bind("<MouseWheel>", self.on_mouse_wheel)

    def create_menus(self):
        """
        create pull-down menus, and add it to the menu bar
        """
        # menu File
        file_menu = Menu(self.menu_bar, tearoff=0)
        file_menu.add_command(label="New Image", command=self.open_file)
        file_menu.add_command(label="Save Data", command=self.save_to)
        file_menu.add_command(label="Load From File",
                              command=self.load_work_from_file)
        file_menu.add_separator()
        file_menu.add_command(label="Exit", command=self.master.quit)
        self.menu_bar.add_cascade(label="File", menu=file_menu)
        self.master.config(menu=self.menu_bar)

        # menu Format
        convert_menu = Menu(self.menu_bar, tearoff=0)
        convert_menu.add_command(label="Json",
                                 command=lambda: self.generate_text("json"))
        convert_menu.add_command(label="Html",
                                 command=lambda: self.generate_text("html"))
        convert_menu.add_command(label="Text",
                                 command=lambda: self.generate_text("txt"))
        self.menu_bar.add_cascade(label="Format", menu=convert_menu)
        self.master.config(menu=self.menu_bar)

        # menu Tools
        tools_menu = Menu(self.menu_bar, tearoff=0)
        tools_menu.add_command(label="Copy text to clipboard",
                               command=self.copy_text_to_clipboard)
        self.menu_bar.add_cascade(label="Tools", menu=tools_menu)
        self.master.config(menu=self.menu_bar)
        """
        menu Mode (draw mode): 
        [Point mode]: dessiner des polygones par des points
        [Drag mode]: dessiner des polygones par le tire d'une aire de polygone sur l'interface
        """
        draw_mode_menu = Menu(self.menu_bar, tearoff=0)
        draw_mode_menu.add_command(
            label=self.check_mark + " Point",
            command=lambda: self.change_draw_mode(draw_mode_menu, 'point'))
        draw_mode_menu.add_command(
            label=" Drag",
            command=lambda: self.change_draw_mode(draw_mode_menu, 'drag'))
        self.menu_bar.add_cascade(label="Mode", menu=draw_mode_menu)
        self.master.config(menu=self.menu_bar)

        # menu Undo
        menu_undo = Menu(self.menu_bar, tearoff=0)
        menu_undo.add_command(label="Remove the last polygone",
                              command=self.undo_last_polygone)
        self.menu_bar.add_cascade(label="Undo", menu=menu_undo)
        self.master.config(menu=self.menu_bar)

    def end_draw_cycle(self, popup):
        if self.roomLabel.get() in self.polygoneCollection:
            self.popup("Id already exist and they are unique.", "#ff3838")
            self.roomLabel.set('')
        elif self.roomLabel.get() == '':
            self.popup("The id can't be empty.", "#ff3838")
        else:
            popup.destroy()
            polygone_str = self.get_formatted_coordinates(self.polygone)
            # self.textContent.insert('end', self.roomLabel.get() + ': ' + polygone_str+'\n', self.randomColor)
            self.polygoneCollection[self.roomLabel.get()] = polygone_str
            self.roomLabel.set('')
            self.polygone = []
            self.remove_polygone_lignes()
            self.randomColor = self.get_no_repeat_color()
            self.generate_text(self.text_format)
            print('restart!')
            print(self.polygoneCollection)

    def popup_entry(self):
        popup = Toplevel()
        popup.wm_title("Entry an id")
        popup.wm_geometry("%dx%d%+d%+d" % (220, 80, 450, 300))

        popup_label = Label(popup, text="Id : ")
        popup_label.grid(row=0, padx=10, pady=10)

        entry = Entry(popup, textvariable=self.roomLabel)
        entry.grid(row=0, column=1, padx=10, pady=10)
        entry.focus_set()
        entry.bind("<Return>", (lambda event: self.end_draw_cycle(popup)))

        button = Button(popup,
                        text="Ok",
                        command=lambda: self.end_draw_cycle(popup))
        button.grid(row=1, columnspan=2, padx=10, pady=10)
        popup.protocol("WM_DELETE_WINDOW",
                       lambda: self.close_entry_popup_window(popup))

    def get_formatted_coordinates(self, the_polygone):
        """
        Retourne un String de coordonées de polygone sous forme: "x0, y0 x1, y1 ... "
        """
        formatted_coordinates = ''
        for i in range(len(self.polygone)):
            if i % 2 == 0:  # indice paire (x)
                formatted_coordinates += (str(the_polygone[i]) + ',')
            else:  # impaire (y)
                formatted_coordinates += (str(the_polygone[i]) + ' ')
        return formatted_coordinates

    def get_no_repeat_color(self):
        """
        Retourne une couleur dans la colorCollection autre que la variable 'randomColor'
        Pour éviter d'avoir 2 fois la suite la meme couleur
        """
        color_collection_copie = self.colorCollection.copy()
        color_collection_copie.remove(self.randomColor)
        return random.choice(color_collection_copie)

    def add_line(self, event):
        """
        MouseClick event pour tracer les polygones
        """
        if self.x0 == -1 and self.y0 == -1:  # start drawing (start point: x0, y0)
            self.change_zoom_state(0)
            self.x0 = event.x
            self.y0 = event.y
            self.x_start = self.x0
            self.y_start = self.y0
            """
            self.x_start = self.mutate_point(self.x0)
            if self.x_start != self.x0:
                self.y_start = self.mutate_point(self.y0)
            else:
                self.y_start = self.y0
            print("after: " + str(self.x_start) + ", " + str(self.y_start))
            """
            self.polygone.append(
                self.canvas.canvasx(self.x_start) / self.scale)
            self.polygone.append(
                self.canvas.canvasy(self.y_start) / self.scale)
        else:  # in drawing
            self.x1 = event.x
            self.y1 = event.y
            canvas_ligne = self.canvas.create_line(
                self.canvas.canvasx(self.x0),
                self.canvas.canvasy(self.y0),
                self.canvas.canvasx(self.x1),
                self.canvas.canvasy(self.y1),
                fill=self.randomColor)
            self.canvasLigneCollection.append(canvas_ligne)
            if ((self.x_start - 5) <= self.x1 <=
                (self.x_start + 5)) and ((self.y_start - 5) <= self.y1 <=
                                         (self.y_start + 5)):
                # endPoint ~ start point (in a range of 5 pixels ): end 1 cycle draw
                self.x0 = -1
                self.y0 = -1
                self.the_last_draw = self.canvas.create_polygon(
                    ' '.join(
                        str(points * self.scale) for points in self.polygone),
                    fill=self.randomColor)
                self.polygone_id_collection[
                    self.the_last_draw] = self.randomColor
                # self.polygonesPointsCollection.append(self.polygone)
                self.popup_entry()
                self.change_zoom_state(1)
            else:
                self.x0 = self.x1
                self.y0 = self.y1
                """
                print(str(self.x1) + ', ' + str(self.y1))
                self.x0 = self.mutate_point(self.x1)
                self.y0 = self.mutate_point(self.y1)
                print("after: " + str(self.x0) + ", " + str(self.y0))
                """
                self.polygone.append(
                    self.canvas.canvasx(self.x0, 0.5) / self.scale)
                self.polygone.append(
                    self.canvas.canvasy(self.y0, 0.5) / self.scale)

    """ [TODO]: draw with a polygone near by
    def mutate_point(self, point):
        if self.polygonesPointsCollection:
            for polygonPoints in self.polygonesPointsCollection:
                for pt in polygonPoints:
                    if (pt-10) <= point <= (pt+10):
                        return pt
        return point
    """

    def cancel_draw(self, e):
        self.x0 = self.y0 = self.x_start = self.y_start = -1
        self.polygone = []
        self.popup("Draw cancelled.")
        self.remove_polygone_lignes()
        self.change_zoom_state(1)

    def popup(self, msg, text_color=None):
        popup = Toplevel(self.master)
        # popup window par rapport a l'écran TODO: center to root window
        popup.wm_title("Info")
        popup.wm_geometry("%dx%d%+d%+d" % (220, 80, 450, 300))

        if text_color:
            popup_label = Label(popup, text=msg, fg=text_color)
        else:
            popup_label = Label(popup, text=msg)
        popup_label.grid(row=0, padx=10, pady=10)

        button = Button(popup, text="Ok", command=popup.destroy)
        button.grid(row=1, padx=65, pady=10)

    def on_mouse_wheel(self, event):
        self.canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")

    def generate_text(self, text_format):
        formatted_text = ''

        if text_format == "json":
            formatted_text = '{\n\t"value": "", "label": "", \n\t"zones": [\n\t'
            for key, value in self.polygoneCollection.items():
                formatted_text += '\t{"id": "' + key + '", "points": "' + value + '"},\n\t'
            formatted_text = self.r_replace(formatted_text, ',', '', 1)
            formatted_text += ']\n}'
            self.text_format = "json"
        elif text_format == 'html':
            for key, value in self.polygoneCollection.items():
                formatted_text += '<polygon id="' + key + '" class="st0" points="' + value + '"/>\n'
            self.text_format = "html"
        else:
            for key, value in self.polygoneCollection.items():
                formatted_text += (key + ': ' + value + '\n')
            self.text_format = "txt"
        self.reload_text_content(formatted_text)

    def reload_text_content(self, text):
        self.textContent.delete('1.0', END)
        self.textContent.insert('1.0', text)

    def copy_text_to_clipboard(self):
        text_area_values = self.textContent.get('1.0', END)
        self.master.clipboard_clear()
        self.master.clipboard_append(text_area_values)
        self.popup("Text copied to clipboard, [Ctrl]+[V] to paste.")

    def set_text_color_tags(self):
        """
        Créer un textTag pour chaque couleur dans 'colorCollection'
        """
        for color in self.colorCollection:
            self.textContent.tag_config(color, foreground=color)

    def r_replace(self, s, old, new, occurrence):
        """
        Inversement remplacer le 'old' character 'occurrence' fois par 'new' caracter dans le string 's'
        """
        li = s.rsplit(old, occurrence)
        return new.join(li)

    def preview_line(self, event):
        if self.line_tmp:
            self.canvas.delete(self.line_tmp)
            self.line_tmp = None
        if self.x0 != -1 and self.y0 != -1:
            x_start = self.x0
            y_start = self.y0
            self.line_tmp = self.canvas.create_line(
                self.canvas.canvasx(x_start),
                self.canvas.canvasy(y_start),
                self.canvas.canvasx(event.x),
                self.canvas.canvasy(event.y),
                fill=self.randomColor)

    def zoom(self, zoom_scale):
        if 1 <= zoom_scale <= 4:
            self.scale = zoom_scale
            self.zoom_scale.set(self.scale)
            self.load_image()
            self.redraw_all_polygone()

    def load_image(self):
        self.canvas.delete("all")
        image_width, image_height = self.origin_image.size
        size = int(image_width * self.scale), int(image_height * self.scale)
        self.img = ImageTk.PhotoImage(self.origin_image.resize(size))
        self.img_id = self.canvas.create_image(0, 0, image=self.img, anchor=NW)

        # tell the canvas to scale up/down the vector objects
        self.canvas.scale(ALL, 0, 0, self.scale, self.scale)
        self.canvas.configure(scrollregion=self.canvas.bbox("all"))

    def open_file(self):
        """
        [menu][File][Open] changer l'image sur la quelle qu'on travail
        """
        """
        # [DEV] Chemin seulement pour faciliter le développement: à changer en prod
        file = Image.open("C:/Users/gs63vr/Documents/Grener/app/src/assets/img/confort/N4.png")
        """
        file = askopenfilename(parent=self.master,
                               initialdir="C:/",
                               title='Choose a file to open',
                               filetypes=FileHandler.IMAGE_FILE_TYPES)
        if file:
            try:
                self.origin_image = Image.open(file)
                self.polygoneCollection = {}
                self.reload_text_content('')
                self.load_image()
                self.change_zoom_state(1)
            except IOError:
                self.popup("Can't open the image file!", "#ff3838")

    def on_button_press(self, event):
        """
        Set x0, y0 du rectangle
        :param event: on click event
        """
        self.rect = None
        self.rect_x0 = self.canvas.canvasx(event.x) / self.scale
        self.rect_y0 = self.canvas.canvasy(event.y) / self.scale

    def on_move_press(self, event):
        if self.rect:
            self.canvas.delete(self.rect)
        self.rect_x2 = self.canvas.canvasx(event.x)
        self.rect_y2 = self.canvas.canvasy(event.y)
        # expand rectangle as you drag the mouse
        self.rect = self.canvas.create_rectangle(self.rect_x0 * self.scale,
                                                 self.rect_y0 * self.scale,
                                                 self.rect_x2,
                                                 self.rect_y2,
                                                 fill=self.randomColor)

    def on_button_release(self, event):
        self.polygone.extend([
            self.rect_x0, self.rect_y0, self.rect_x2 / self.scale,
            self.rect_y0, self.rect_x2 / self.scale, self.rect_y2 / self.scale,
            self.rect_x0, self.rect_y2 / self.scale
        ])
        self.the_last_draw = self.rect
        self.polygone_id_collection[self.the_last_draw] = self.randomColor
        self.popup_entry()

    def save_to(self):
        """
        [menu][File][Save] export de fichier
        """
        my_file_handler = FileHandler(self.master)
        my_file_handler.export_file(self.textContent.get(1.0, END),
                                    self.text_format)

    def load_work_from_file(self):
        """
        [menu][File][Load_from_file] export de fichier
        """
        my_file_handler = FileHandler(self.master)
        content = my_file_handler.load_file()
        if my_file_handler.file_extension:
            self.reinit_variables_from_content(content,
                                               my_file_handler.file_extension)
            self.text_format = my_file_handler.file_extension.replace('.', '')
        self.reload_text_content(content)

    def reinit_variables_from_content(self, content_text, extension):
        # clean global variables
        self.clean_global_variables()
        self.polygone_id_collection = {}
        self.polygoneCollection = {}

        # Initialisation datas quand reload
        if extension == '.json':
            json_obj = json.loads(content_text)
            for zone in json_obj['zones']:
                points = zone['points'].replace(',', ' ')
                polygone = self.canvas.create_polygon(points,
                                                      fill=self.randomColor)
                self.polygone_id_collection[polygone] = ''
                self.polygoneCollection[zone['id']] = zone['points']
        elif extension == '.html':
            my_html_parser = MyHTMLParser()
            my_html_parser.feed(content_text)
            for polygone in my_html_parser.polygone_collection:
                points = polygone[2][1].replace(',', ' ')
                polygone = self.canvas.create_polygon(points,
                                                      fill=self.randomColor)
                self.polygone_id_collection[polygone] = ''
                self.polygoneCollection[polygone[0][1]] = polygone[2][1]
        elif extension == '.txt':
            structured_data = content_text.rstrip('\n')
            structured_data = [
                s.split(': ') for s in structured_data.splitlines()
            ]
            for polygon in structured_data:
                points = polygon[1].replace(',', ' ')
                polygone = self.canvas.create_polygon(points,
                                                      fill=self.randomColor)
                self.polygone_id_collection[polygone] = ''
                self.polygoneCollection[polygon[0]] = polygon[1]
        self.randomColor = self.get_no_repeat_color()

    def on_scale(self, event):
        value = self.zoom_scale.get()
        if int(value) != value:
            self.zoom_scale.set(round(value))
            self.scale = round(value)
            self.zoom(round(value))

    def undo_last_polygone(self):
        """
        clean the last drawn
        """
        if self.polygone_id_collection:
            self.canvas.delete(
                list(self.polygone_id_collection.items())[-1][0])
            self.polygone_id_collection.popitem()
            # self.canvas.delete(self.the_last_draw)
            self.clean_global_variables()
            self.polygoneCollection.popitem()
            self.generate_text(self.text_format)

    def change_draw_mode(self, menu, mode_to_change):
        """
        Fonction qui change le mode de draw entre "point mode" et "drag mode"
        """
        if mode_to_change != self.draw_mode:
            # TODO: [clean] separer le switch de menu dans 2 fonction differents
            if mode_to_change == 'point':
                self.go_to_point_mode()
                menu.entryconfig(0, label=self.check_mark + ' Point')
                menu.entryconfig(2, label=' Drag')
            elif mode_to_change == 'drag':
                self.go_to_drag_mode()
                menu.entryconfig(0, label=' Point')
                menu.entryconfig(2, label=self.check_mark + ' Drag')
            self.draw_mode = mode_to_change

    def go_to_point_mode(self):
        self.clean_global_variables()
        self.canvas.unbind("<ButtonRelease 1>")
        self.canvas.unbind("<B1-Motion>")
        self.canvas.bind("<Button 1>", self.add_line)
        self.canvas.bind("<Motion>", self.preview_line)
        self.canvas.bind("<Button 3>", self.cancel_draw)

    def go_to_drag_mode(self):
        """
        [Drag Mode]
        """
        self.clean_global_variables()
        self.canvas.unbind("<Button 3>")
        self.canvas.bind("<Button 1>", self.on_button_press)
        self.canvas.bind("<B1-Motion>", self.on_move_press)
        self.canvas.bind("<ButtonRelease 1>", self.on_button_release)

    def clean_global_variables(self):
        """
        Réinitialiser les variables globales sauf la collection de polygone :polygoneCollection
        """
        self.x0 = self.y0 = self.x1 = self.y1 = self.x_start = self.y_start = self.rect_x0 = self.rect_y0 = self.rect_x2 = self.rect_y2 = -1
        self.polygone = []
        self.randomColor = self.get_no_repeat_color()
        self.line_tmp = None
        self.rect = None

    def close_entry_popup_window(self, popup, event=None):
        popup.destroy()
        self.canvas.delete(self.the_last_draw)
        self.remove_polygone_lignes()
        self.clean_global_variables()

    def remove_polygone_lignes(self):
        for canvasLigne in self.canvasLigneCollection:
            self.canvas.delete(canvasLigne)
        self.canvasLigneCollection = []

    def redraw_all_polygone(self):
        for polygone_coordinates in self.polygoneCollection.values():
            points = polygone_coordinates.replace(',', ' ').rstrip().split(' ')
            points = [float(point) * self.scale for point in points]
            canvas_coord = ' '.join(str(s) for s in points)
            polygone = self.canvas.create_polygon(canvas_coord,
                                                  fill=self.randomColor)
            self.polygone_id_collection[polygone] = ''

    def change_zoom_state(self, state):
        if state == 0:
            self.zoom_in_button.state(['disabled'])
            self.zoom_out_button.state(['disabled'])
            self.zoom_scale.state(['disabled'])
        else:
            self.zoom_in_button.state(['!disabled'])
            self.zoom_out_button.state(['!disabled'])
            self.zoom_scale.state(['!disabled'])
コード例 #7
0
class ClientGUI:
    def __init__(self):
        self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.client_socket.settimeout(5.0)
        self.port = 2345
        self.client_running = False
        self.client_connected = False
        self.root = tk.Tk()
        self.root.resizable(width=False, height=False)
        self.root.title('Chat Client')
        self.createGUI()
        self.root.protocol('WM_DELETE_WINDOW', self.onShuttingDownClient)
        self.root.mainloop()
        return

    def createGUI(self):
        tk.ttk.Style().configure('TLabel', font=('Times New Roman', 16))
        tk.ttk.Style().configure('TButton', font=('Times New Roman', 16))
        self.frame = Frame(self.root)

        self.user_name_label = Label(self.frame, text='User Name')
        self.user_name_label.grid(row=0, column=0, sticky='W')

        self.user_name_box = Entry(self.frame, font=('Times New Roman', 16), width=20)
        self.user_name_box.grid(row=0, column=1)

        self.ip_label = Label(self.frame, text='Server IP Address')
        self.ip_label.grid(row=1, column=0, sticky='W')

        self.ip_addr_box = Entry(self.frame, font=('Times New Roman', 16))
        self.ip_addr_box.grid(row=1, column=1)

        self.connect_btn = Button(self.frame, text='Connect', command=self.connectBtnClick)
        self.connect_btn.grid(row=2, column=0, sticky='W')

        self.chat_area = scrolledtext.ScrolledText(self.frame, height=10, width=42, undo=True,
                                                   font=('Times New Roman', 16))
        self.chat_area.grid(row=3, column=0, sticky='NEWS')
        self.chat_area.configure(state='disabled')

        self.client_list = Listbox(self.frame, font=('Times New Roman', 16), selectmode=tk.MULTIPLE)
        self.client_list.grid(row=3, column=1, sticky='NEWS')

        self.msg_box = scrolledtext.ScrolledText(self.frame, font=('Times New Roman', 16), height=1, width=42, undo=True)
        self.msg_box.grid(row=4, column=0, sticky='W')

        self.send_btn = Button(self.frame, text='Send', command=self.sendBtnClick)
        self.send_btn.grid(row=4, column=1, sticky='W')
        self.send_btn.state(['disabled'])

        self.frame.grid(row=0, column=0)
        return

    def connectBtnClick(self):
        self.client_name = self.user_name_box.get()
        if not (len(self.client_name) == 0):
            try:
                self.client_socket.sendto(('1111||'+ self.client_name).encode('ascii'), (self.ip_addr_box.get(), self.port))
                self.message, self.address = self.client_socket.recvfrom(2048)
                self.chat_area.config(state='normal')
                self.chat_area.insert(tk.END, self.message.decode('ascii') + '\n')
                self.chat_area.config(state='disabled')
                self.client_running = True
                threading.Thread(target=self.updateClientList).start()
                self.connect_btn.state(['disabled'])
                self.user_name_box.state(['disabled'])
                self.ip_addr_box.state(['disabled'])
                self.send_btn.state(['!disabled'])
                self.client_connected = True
            except socket.timeout as tout:
                messagebox.showerror("Message From Client", tout)
            except socket.gaierror as gerror:
                messagebox.showerror("Message From Client", gerror)
                pass
            pass
        else:
            messagebox.showerror('Message From Client', 'User Name cannot be empty')

    def sendBtnClick(self):
        recv_clients_list = self.client_list.curselection()
        msg_type = ''
        if len(recv_clients_list) > 1:
            msg_type = ' [MC]'
        recv_clients = ''
        for k in recv_clients_list:
            recv_clients = recv_clients + '||' + self.client_list.get(k)
        self.message_to_sent = self.client_name + '||' + self.msg_box.get(1.0, tk.END) + msg_type + recv_clients
        self.chat_area.config(state='normal')
        self.chat_area.insert(tk.END, 'You>>' + self.msg_box.get(1.0, tk.END) + '\n')
        self.chat_area.config(state='disabled')
        self.client_socket.sendto(self.message_to_sent.encode('ascii'), (self.ip_addr_box.get(), self.port))
        self.msg_box.delete(1.0, tk.END)
        pass

    def onShuttingDownClient(self):
        if self.client_connected:
            self.client_socket.sendto(('0000' + '||' + self.client_name).encode('ascii'), (self.ip_addr_box.get(), self.port))
        self.client_running = False
        self.root.destroy()
        pass

    def updateClientList(self):
        self.client_socket.setblocking(0)
        while self.client_running:
            try:
                self.message, self.address = self.client_socket.recvfrom(2048)
                self.message = self.message.decode('ascii')
                i = 0
                j = 0
                if self.isContain(self.message, '1111'):
                    temp_client_list = self.message.split('||')
                    self.client_list.delete(0, tk.END)
                    while i < len(temp_client_list):
                        if not (self.client_name == temp_client_list[i]) and not (temp_client_list[i] == '1111'):
                            self.client_list.insert(j, temp_client_list[i])
                            j = j + 1
                        i = i+1
                elif 'Empty List 0000' in self.message:
                    self.client_list.delete(0, tk.END)
                elif 'Server Offline 0101' in self.message:
                    self.client_connected = False
                    self.chat_area.config(state='normal')
                    self.chat_area.insert(tk.END, 'Server is Offline. Chat discontinues!!!\n')
                    self.chat_area.config(state='disabled')
                    self.send_btn.state(['disabled'])
                    self.connect_btn.state(['!disabled'])
                    self.ip_addr_box.state(['!disabled'])
                    self.user_name_box.state(['!disabled'])
                    self.client_socket.settimeout(1)
                    self.client_running = False
                else:
                    self.chat_area.config(state='normal')
                    self.chat_area.insert(tk.END, self.message + '\n')
                    self.chat_area.config(state='disabled')
            except socket.error:
                pass
        pass

    def isContain(self, str, sub_str):
        index = str.find(sub_str)
        if index == -1:
            return False
        else:
            return True
コード例 #8
0
class App(Tk):
    def __init__(self, **kwargs):
        super(App, self).__init__(**kwargs)
        tags = list(self.bindtags())
        tags.insert(2, 'App')
        self.bindtags(tags)

        themes = ThemeEngine()
        self.option_readfile(themes.options_file)
        themes.set_ttk_style()

        self.new_reader_icon = get_icon('ic_new_reader')
        self.new_writer_icon = get_icon('ic_new_writer')
        self.close_panel_icon = get_icon('ic_close')
        self.save_icon = get_icon('ic_save')
        self.edit_icon = get_icon('ic_mode_edit')
        self.link_icon = get_icon('ic_insert_link')
        self.clear_content_icon = get_icon('ic_delete_sweep')
        self.delete_icon = get_icon('ic_delete_forever')

        img = Image.open('.resources/wm_icon.png')
        app_icon = ImageTk.PhotoImage(image=img)

        if backup_enabled():
            check = check_backup()
            if check != 1:
                # print(check)
                pass

        self.title('Meta-Jurnl')
        self.iconphoto(True, app_icon)

        self.withdraw()

        menu_bar = Menu(master=self)
        self.config(menu=menu_bar)
        # menu_bar.pack(fill='x')

        file_menu = Menu(master=menu_bar,
                         tearoff=0,
                         relief='flat',
                         borderwidth=1)
        file_menu.add_command(label='New Entry', command=self.add_writer)
        file_menu.add_command(label='New Linked Entry')

        page_menu = Menu(master=file_menu, tearoff=0)
        page_menu.add_command(label='New Reader', command=self.add_reader)
        page_menu.add_command(label='New Writer', command=self.add_writer)

        file_menu.add_cascade(label='New', menu=page_menu, underline=0)
        file_menu.add_command(label='Save')
        file_menu.add_command(label='Quit', command=self.destroy)

        edit_menu = Menu(master=menu_bar, tearoff=0)
        edit_menu.add_command(label='Preferences')

        help_menu = Menu(master=menu_bar, tearoff=0)
        help_menu.add_command(label='Keyboard Shortcuts')
        help_menu.add_command(label='About')

        menu_bar.add_cascade(label='File', menu=file_menu, underline=0)
        menu_bar.add_cascade(label='Edit', menu=edit_menu, underline=0)
        menu_bar.add_cascade(label='Help', menu=help_menu, underline=0)

        toolbar = Frame(master=self, relief='flat', borderwidth=0, padding=5)
        toolbar.pack(fill='x')

        self.journal = Journal(master=self)
        self.journal.pack(fill='both', expand=True)

        self.autoimport()

        self.new_reader = Button(master=toolbar,
                                 image=self.new_reader_icon,
                                 command=self.add_reader)
        new_writer = Button(master=toolbar,
                            image=self.new_writer_icon,
                            command=self.add_writer)

        self.close_panel = Button(master=toolbar,
                                  text='Close Tab',
                                  image=self.close_panel_icon,
                                  command=self.journal.remove_page)
        self.clear_button = Button(master=toolbar,
                                   image=self.clear_content_icon,
                                   command=self.clear_fields)

        self.save_button = Button(master=toolbar,
                                  image=self.save_icon,
                                  command=self.save_entry)
        self.edit_button = Button(master=toolbar,
                                  image=self.edit_icon,
                                  command=self.edit_entry)
        self.link_button = Button(master=toolbar,
                                  image=self.link_icon,
                                  command=self.link_entry)
        self.delete_button = Button(master=toolbar, image=self.delete_icon)

        self.new_reader.pack(side='left', padx=(0, 5))
        new_writer.pack(side='left', padx=(0, 5))
        self.close_panel.pack(side='left', padx=(0, 5))
        self.clear_button.pack(side='left', padx=(0, 5))

        self.close_panel.state(['disabled'])
        self.clear_button.state(['disabled'])

        self.journal.update_idletasks()

        s_width = self.winfo_screenwidth() / 2
        s_height = self.winfo_screenheight() / 2
        dims = dimensions()
        try:
            w_width = self.winfo_reqwidth() if not dims[0] else dims[0]
            w_height = self.winfo_reqheight() if not dims[1] else dims[1]
        except IndexError:
            w_width = self.winfo_reqwidth()
            w_height = self.winfo_reqheight()
        pos = w_width, w_height, s_width - floor(
            w_width / 2), s_height - floor(w_height / 2)
        self.geometry('{}x{}+{}+{}'.format(int(pos[0]), int(pos[1]),
                                           int(pos[2]), int(pos[3])))

        self.bind('<Configure>', self.update_dimensions)
        self.bind_all('<<Check Save Button>>', self.check_writer_buttons)
        self.bind_all('<<Id Selected>>', self.check_reader_buttons)
        self.bind('<<NotebookTabChanged>>', self.change_buttons)

        self.change_buttons()

        self.after(1000, self.deiconify)

        self.mainloop()

    def update_dimensions(self, event):
        # s_width = self.winfo_screenwidth() / 2
        # s_height = self.winfo_screenheight() / 2
        w_width = max(self.winfo_width(), 1500)
        w_height = max(self.winfo_height(), 600)
        # pos = w_width, w_height, self.winfo_rootx(), self.winfo_rooty()
        # self.geometry('{}x{}+{}+{}'.format(
        #     int(pos[0]), int(pos[1]), int(pos[2]), int(pos[3])
        # ))
        dimensions((w_width, w_height))

    def add_reader(self):
        if not database_is_empty():
            self.journal.add_page('Reader')

    def add_writer(self):
        self.journal.add_page('Writer')

    # def add_tagged_writer(self):
    #     self.journal.add_page('Writer', tags=True)

    def change_buttons(self, event=None):
        self.new_reader.state(
            ['disabled' if database_is_empty() else '!disabled'])
        if self.journal.mode_ == 'Writer':
            self.close_panel.state(['!disabled'])
            self.clear_button.state(['!disabled'])
            self.save_button.pack_forget()
            self.edit_button.pack_forget()
            self.link_button.pack_forget()
            self.delete_button.pack_forget()
            self.save_button.pack(side='left', padx=(0, 5))
            self.link_button.pack(side='left', padx=(0, 5))
            self.check_writer_buttons()
        elif self.journal.mode_ == 'Reader':
            self.close_panel.state(['!disabled'])
            self.clear_button.state(['!disabled'])
            self.save_button.pack_forget()
            self.edit_button.pack_forget()
            self.link_button.pack_forget()
            self.delete_button.pack_forget()
            self.edit_button.pack(side='left', padx=(0, 5))
            self.link_button.pack(side='left', padx=(0, 5))
            self.delete_button.pack(side='left', padx=(0, 5))
            self.check_reader_buttons()
        else:
            self.close_panel.state(['disabled'])
            self.clear_button.state(['disabled'])
            self.save_button.pack_forget()
            self.edit_button.pack_forget()
            self.link_button.pack_forget()
            self.delete_button.pack_forget()

    def check_writer_buttons(self, event=None):
        saved = self.journal.check_saved(event)
        self.save_button.state(['disabled' if saved else '!disabled'])
        self.link_button.state(
            ['disabled' if not self.journal.id_ else '!disabled'])

    def check_reader_buttons(self, event=None):
        entry = True if self.journal.id_ else False
        self.edit_button.state(['disabled' if not entry else '!disabled'])
        self.link_button.state(['disabled' if not entry else '!disabled'])
        self.delete_button.state(['disabled' if not entry else '!disabled'])

    def save_entry(self):
        self.journal.save()
        self.change_buttons()

    def edit_entry(self):
        self.journal.add_page(mode='Writer', entry_id=self.journal.id_)

    def link_entry(self):
        self.journal.add_page(mode='Writer', parent=self.journal.id_)

    def delete_entry(self):
        # TODO Implement
        pass

    def clear_fields(self):
        if self.journal.mode_ == 'Writer':
            saved = self.journal.check_saved()
            if not saved:
                if askquestion(
                        'Clear Window Contents?',
                        'There are unsaved edits in this tab.\nSave before continuing?'
                ):
                    self.save_entry()
        self.journal.clear()

    def autoimport(self):
        import_entries()
        self.journal.refresh_readers()
        if autodelete_imports():
            delete_imports()
コード例 #9
0
ファイル: forth_exec.py プロジェクト: alflanagan/PyBadge
class Application(Frame):
    """The main Tk application, a simple dialog."""
    def __init__(self, master=None):
        super().__init__(master)
        self.badge = None
        self.grid()
        self.columnconfigure(0, minsize=200)
        self.columnconfigure(1, minsize=200)
        self.rowconfigure(0, minsize=300)
        self.rowconfigure(3, minsize=30)
        self.create_widgets()
        # self.output.insert("1.0", "This is a test\n")
        # self.sentto.insert("end", b"this is an error\n", "error")
        self.connect()
        self.after(SERIAL_POLL_INTERVAL, self.poll_serial)

    def create_widgets(self):
        """Sets up dialog elements."""
        da_row = 0
        "counter so I don't have to update every grid() call when I add/remove row"
        self.select = tix.FileSelectBox(self,
                                        browsecmd=self.on_file_selected,
                                        pattern="*.fs",
                                        directory="forth")
        # self.select["textVariable"] = self.forth_file
        self.select.grid(row=da_row, columnspan=2, sticky='nwes', pady=10)
        da_row += 1
        self.output_label = Label(self, text="Badge Output")
        self.output_label.grid(row=da_row,
                               column=1,
                               sticky='w',
                               padx=10,
                               pady=3)
        self.sentto_label = Label(self, text="Sent To Badge")
        self.sentto_label.grid(row=da_row,
                               column=0,
                               sticky='w',
                               padx=10,
                               pady=3)
        da_row += 1
        # height is in lines, width in characters
        self.output = Text(self, height=16, width=40)
        self.output.grid(row=da_row, column=1, padx=10)
        self.sentto = Text(self, height=16, width=40)
        self.sentto.tag_configure("error", background="red")
        self.sentto.grid(row=da_row, column=0, padx=10)
        da_row += 1
        self.connect_btn = Button(self,
                                  text="Connect",
                                  command=self.toggle_connect)
        self.connect_btn.grid(row=da_row, column=0, columnspan=2)
        da_row += 1
        self.exec_btn = Button(self, text="Execute", command=self.send_file)
        self.exec_btn.state(["disabled"])
        self.exec_btn.grid(row=da_row,
                           column=0,
                           sticky='w' + 'e',
                           padx=10,
                           pady=3)
        self.quit = Button(self, text="QUIT", command=self.master.destroy)
        self.quit.grid(row=da_row, column=1, sticky='w' + 'e', padx=10, pady=3)
        da_row += 1
        self.status_panel = Frame(self, relief="sunken", borderwidth=3)
        self.status_panel.grid(row=da_row, columnspan=2, sticky='nwse')
        self.connect_status = Label(self.status_panel, text="Not Connected")
        self.connect_status.grid(row=0, padx=10, pady=5, sticky="w")
        if self.badge is not None:
            self.connect_btn.state(["disabled"])
            self.connect_status.config(text="Connected: " +
                                       self.badge.os_device)

    def send_file(self, _retry=False):
        """Send the selected file to the badge."""
        if self.badge:
            try:
                # oddly, very first set LED seems to not set correct color
                self.badge.led(0, 0, 128)
                self.badge.led(0, 0, 128)
                with open(self.select.cget("value"), 'r') as forthin:
                    self.badge.forth_run(forthin.read())
                time.sleep(1)  # because forth_run() may be too fast
                self.badge.led(0, 128, 0)
            except IOError:
                if not _retry:
                    self.connect()
                    self.send_file(True)
                else:
                    raise

    def poll_serial(self):
        "Checks serial port for incoming bytes, reads and displays them."
        if self.badge is not None:
            bytes_in = self.badge.read_from()
            if bytes_in:
                self.output.insert("end", bytes_in + b'\n')
        self.after(SERIAL_POLL_INTERVAL, self.poll_serial)

    def toggle_connect(self):
        "If connected, disconnect, otherwise connect."
        if self.connect_btn.cget("text") == "Connect":
            self.connect()
        else:
            self.disconnect()

    def disconnect(self):
        "Disconnect from current badge."
        isinstance(self.badge, Badge)
        self.badge.close()
        self.badge = None
        self.connect_btn.config(text="Connect")
        self.connect_status.config(text="Not connected.")
        self.exec_btn.state(["disabled"])

    def connect(self):
        """Attempt to connect to a badge; toggle Connect button if successful."""
        try:
            self.badge = Badge(on_serial_write=self.on_bytes_sent,
                               min_write_dt=0.01)
            self.connect_status.config(text="Connected: " +
                                       self.badge.os_device)
            self.connect_btn.config(text="Disconnect")
            # enable "Execute" if file is selected
            self.on_file_selected(self.select.cget("value"))
        except BadgeSerialException:
            self.connect_status.config(text="Not connected")

    def on_file_selected(self, selected_file):
        """Respond to user selection of file by enabling the Execute button."""
        if Path(selected_file).is_file:
            self.exec_btn.state(["!disabled"])
        else:
            self.exec_btn.state(["disabled"])

    def on_bytes_sent(self, data, count):
        self.sentto.insert("end", data[:count])
        if count < len(data):
            self.sentto.insert("end", data[count:], "error")
        self.sentto.insert("end", "\n")
コード例 #10
0
class App(Tk):
    def __init__(self, master=None):
        super().__init__(master)
        self.style = ThemedStyle(self)
        self.style.set_theme('elegance')
        self.iconbitmap(r'data\app.ico')
        self.minsize(450, 300)

        self.title('WoT Battle Counter')
        self.menu_bar = Menu(self)
        self.content = Frame(self)
        self.entry = Entry(self.content)
        self.player_list = Listbox(self.content)
        self.count_button = Button(self)
        self.scrollbar = Scrollbar(self.content)
        self.buttons_frame = Frame(self)
        self.sort_button = Checkbutton(self.buttons_frame)
        self.progressbar = Progressbar(self.buttons_frame)
        self.sort_variable = IntVar(self)
        self.PlayerObjects = []
        self.replays = []
        self.player_names = []
        self.offset = 0
        self.skirmish_value = 1
        self.advance_value = 1
        self.clan_war_value = 3

    def create_app(self):
        # Config menu entries and attach them
        self.menu_bar.add_command(label='Config',
                                  command=self.open_config_window)
        self.menu_bar.add_command(label='Open replay files',
                                  command=self.open_skirmish_files)
        self.menu_bar.add_command(label='Open list', command=self.load_list)
        self.menu_bar.add_command(label='Save list', command=self.save_list)
        self.menu_bar.add_command(label='Export to file',
                                  command=self.export_to_file)
        self.menu_bar.add_command(label='About', command=about)
        self.config(menu=self.menu_bar)

        # Config main content window
        self.content.pack(fill='both', expand=1)

        # Config Text Entry + bind enter key
        self.entry.config(exportselection=0)
        self.entry.pack(fill='x')
        self.entry.bind('<Return>', self.add_player)

        # Config Listbox + bind delete key
        self.player_list.config(yscrollcommand=self.scrollbar.set)
        self.scrollbar.config(command=self.player_list.yview,
                              orient='vertical')
        self.scrollbar.pack(side='right', fill='y')
        self.player_list.pack(side='left', fill='both', expand=1)
        self.player_list.bind('<Delete>', self.remove_player)

        # Count button at the bottom
        self.count_button.config(
            text='Count!', command=Thread(target=self.decode_replays).start)
        self.count_button.pack(side='right', padx=10, pady=10)
        self.count_button.state(['disabled'])

        # Config button frame + button + progressbar
        self.buttons_frame.pack(side='left',
                                fill='both',
                                pady=5,
                                padx=5,
                                expand=1)
        self.sort_button.config(text="Sort the list",
                                variable=self.sort_variable)
        self.sort_button.pack(anchor='nw', pady=3, padx=3)
        self.progressbar.config(length=360,
                                mode='indeterminate',
                                orient=HORIZONTAL,
                                maximum=10)
        self.progressbar.pack(anchor='e', pady=3, padx=3)

        # Config the style
        Style().configure('TEntry', background='white')
        Style().configure('TButton', font=('Roboto', 12))
        Style().configure('OK.TButton', font=('Roboto', 12, 'bold'))

        # Loading configuration
        self.load_config()

        # Start the app
        self.mainloop()

    def add_player(self, event):
        name = self.entry.get()
        self.entry.delete(0, 'end')
        player_obj = Player(name)
        self.PlayerObjects.append(player_obj)
        if self.sort_variable.get() == 1:
            self.PlayerObjects.sort(key=lambda player: player.name.lower())
            self.player_list.delete(0, 'end')
            for player in self.PlayerObjects:
                self.player_list.insert('end',
                                        (self.PlayerObjects.index(player) +
                                         self.offset + 1, player.name))
        else:
            self.player_list.delete(0, 'end')
            for player in self.PlayerObjects:
                self.player_list.insert('end',
                                        (self.PlayerObjects.index(player) +
                                         self.offset + 1, player.name))

    def remove_player(self, event):
        select = self.player_list.curselection()
        name = self.player_list.get(select)
        self.player_list.delete(select)
        for player in self.PlayerObjects:
            if name.split()[1] == player.name:
                self.PlayerObjects.remove(player)

        self.player_list.delete(0, 'end')
        for player in self.PlayerObjects:
            self.player_list.insert('end', (self.PlayerObjects.index(player) +
                                            self.offset + 1, player.name))

    def open_skirmish_files(self):
        directory_path = filedialog.askdirectory()
        if not path.exists(directory_path):
            return
        self.replays = self.list_dir(directory_path)
        self.count_button.state(['!disabled'])

    def save_list(self):
        file_path = filedialog.asksaveasfilename(defaultextension='.json')
        if not path.exists(file_path):
            return
        players = list()
        for player in self.PlayerObjects:
            players.append(player.name)
        if path.isfile(file_path):
            f = open(file_path, 'w')
        else:
            f = open(file_path, 'x')
        f.seek(0)
        f.write(dumps(players))

    def load_list(self):
        file_path = filedialog.askopenfilename(
            filetypes=[('json-file', '*.json'), ('all files', '*.*')])
        if path.isfile(file_path):
            self.player_list.delete(0, 'end')
            f = open(file_path, 'r')
            players = loads(f.read())
            for name in players:
                player_obj = Player(name)
                self.PlayerObjects.append(player_obj)
            for player in self.PlayerObjects:
                self.player_list.insert('end',
                                        (self.PlayerObjects.index(player) +
                                         self.offset + 1, player.name))

    def export_to_file(self):
        file_path = filedialog.asksaveasfilename(defaultextension='.txt')
        if path.isfile(file_path):
            f = open(file_path, 'w')
        elif path.exists(file_path):
            f = open(file_path, 'x')
        else:
            return
        data = str()
        for player in self.PlayerObjects:
            if player.battles >= 100:
                data += f'{player.battles}  {player.name} \n'
            elif player.battles >= 10:
                data += f'{player.battles}   {player.name} \n'
            elif player.battles > 0:
                data += f'{player.battles}    {player.name} \n'
        f.seek(0)
        f.write(data)

    def list_dir(self, path):
        entries = listdir(path)
        re_replay = compile('\.wotreplay')
        re_file = compile('\.')
        replays = []
        # recursive function for searching in subdirectories for .wotreplay files and putting them into a list
        for entry in entries:
            if not search(re_file, entry):
                new_path = path + "/" + entry
                new_replays = self.list_dir(new_path)
                for replay in new_replays:
                    replays.append(replay)
            elif search(re_replay, entry):
                replays.append((path + '/' + entry))
            elif not search(re_replay, entry) and search(re_file, entry):
                continue
        return replays

    def decode_replays(self):
        self.progressbar.start()
        thread_queue = Queue()
        replay_list_1 = [
            self.replays[x] for x in range(0, round(len(self.replays) / 4))
        ]
        replay_list_2 = [
            self.replays[x] for x in range(round(len(self.replays) / 4),
                                           round(len(self.replays) / 4) * 2)
        ]
        replay_list_3 = [
            self.replays[x] for x in range(
                round(len(self.replays) / 4) * 2,
                round(len(self.replays) / 4) * 3)
        ]
        replay_list_4 = [
            self.replays[x] for x in range(
                round(len(self.replays) / 4) * 3, len(self.replays))
        ]

        thread_1 = Thread(target=self.convert_binary_data,
                          args=(replay_list_1, thread_queue))
        thread_2 = Thread(target=self.convert_binary_data,
                          args=(replay_list_2, thread_queue))
        thread_3 = Thread(target=self.convert_binary_data,
                          args=(replay_list_3, thread_queue))
        thread_4 = Thread(target=self.convert_binary_data,
                          args=(replay_list_4, thread_queue))

        threads = (thread_1, thread_2, thread_3, thread_4)

        for thread in threads:
            thread.start()

        sleep(1)
        if self.listen_for_result(threads):
            self.player_names = thread_queue.get()
            for name in thread_queue.get():
                self.player_names.append(name)
            for name in thread_queue.get():
                self.player_names.append(name)
            for name in thread_queue.get():
                self.player_names.append(name)

        # COUNTING TIME!
        for name in self.player_names:
            for player in self.PlayerObjects:
                if name[0] == player.name:
                    player.battles += name[1]

        # Insert names together with battle count back into the list
        self.player_list.delete(0, 'end')
        for player in self.PlayerObjects:
            if player.battles > 0:
                self.player_list.insert(
                    'end', (self.PlayerObjects.index(player) + self.offset + 1,
                            player.name, player.battles))
            else:
                continue
        self.progressbar.stop()

    def listen_for_result(self, threads):
        # Check if all replay results have come in
        alive_threads = 0
        for thread in threads:
            thread.join(0.1)
        for thread in threads:
            if thread.is_alive():
                print("thread not ded")
                alive_threads += 1
        if alive_threads > 0:
            if self.listen_for_result(threads):
                return True
        return True

    def convert_binary_data(self, replays, queue):
        player_names = list()
        for replay in range(len(replays)):
            filename_source = replays[replay]
            f = open(filename_source, 'rb')
            f.seek(8)
            size = f.read(4)
            data_block_size = unpack('I', size)[0]
            f.seek(12)
            my_block = f.read(int(data_block_size))

            # Convert binary data into a json and then into an iterable tuple
            json_replay = loads(my_block)
            players_dict = [(v, k)
                            for (k, v) in dict.items(json_replay['vehicles'])]

            # Extract names and append to a list
            for player_id in players_dict:
                player_name = player_id[0]['name']
                if json_replay['battleType'] == 20:
                    player_names.append((player_name, self.skirmish_value))
                elif json_replay['battleType'] == 13:
                    player_names.append((player_name, self.clan_war_value))
                else:
                    player_names.append((player_name, 1))
        queue.put(player_names)

    def open_config_window(self):
        config_window = Toplevel(self)
        config_window.iconbitmap(r'data\app.ico')
        config_window.minsize(500, 350)

        config_frame = Labelframe(config_window)
        config_frame.config(text="App Configuration",
                            relief='groove',
                            borderwidth=5)
        config_frame.pack(expand=1, fill='both', padx=5, pady=5)

        offset_title = Label(config_frame)
        offset_title.config(text='Numbering offset (Default 0)')
        offset_title.pack(anchor='nw', padx=5, pady=5)

        offset_entry = Entry(config_frame)
        offset_entry.config(
            width=10,
            exportselection=0,
            validate='key',
            validatecommand=(offset_entry.register(validate_config_entry),
                             '%P'))
        offset_entry.pack(anchor='nw', padx=5, pady=5)

        battle_value_frame = Labelframe(config_frame)
        battle_value_frame.config(text='Battle weighting',
                                  relief='groove',
                                  borderwidth=5)
        battle_value_frame.pack(anchor='nw',
                                fill='both',
                                expand=1,
                                padx=5,
                                pady=5)

        descriptor_frame = Frame(battle_value_frame)
        descriptor_frame.pack(side='left', fill='both', expand=1)

        entry_frame = Frame(battle_value_frame)
        entry_frame.pack(side='left', fill='both', expand=1)

        skirmish_title = Label(descriptor_frame)
        skirmish_title.config(text='Skirmish weighting (Default = 1):')
        skirmish_title.pack(anchor='nw', padx=5, pady=7)

        skirmish_entry = Entry(entry_frame)
        skirmish_entry.config(
            width=10,
            exportselection=0,
            validate='key',
            validatecommand=(skirmish_entry.register(validate_config_entry),
                             '%P'))
        skirmish_entry.pack(anchor='nw', padx=5, pady=5)

        advance_title = Label(descriptor_frame)
        advance_title.config(text='Advance weighting (Default = 1):')
        advance_title.pack(anchor='nw', padx=5, pady=10)

        advance_entry = Entry(entry_frame)
        advance_entry.config(
            width=10,
            exportselection=0,
            validate='key',
            validatecommand=(advance_entry.register(validate_config_entry),
                             '%P'))
        advance_entry.pack(anchor='nw', padx=5, pady=5)

        clan_war_title = Label(descriptor_frame)
        clan_war_title.config(text='Clan War weighting (Default = 3):')
        clan_war_title.pack(anchor='nw', padx=5, pady=6)

        clan_war_entry = Entry(entry_frame)
        clan_war_entry.config(
            width=10,
            exportselection=0,
            validate='key',
            validatecommand=(clan_war_entry.register(validate_config_entry),
                             '%P'))
        clan_war_entry.pack(anchor='nw', padx=5, pady=5)

        buttons_frame = Frame(config_frame)
        buttons_frame.pack(anchor='sw', fill='both', expand=0)

        apply_button = Button(buttons_frame)
        apply_button.config(text='Apply',
                            command=partial(self.config_apply, offset_entry,
                                            skirmish_entry, advance_entry,
                                            clan_war_entry))
        apply_button.pack(side='right', padx=5, pady=5)

        cancel_button = Button(buttons_frame)
        cancel_button.config(text='Cancel',
                             command=lambda: config_window.destroy())
        cancel_button.pack(side='right', padx=5, pady=5)

        ok_button = Button(buttons_frame)
        ok_button.config(text='OK',
                         style='OK.TButton',
                         command=partial(self.config_ok, offset_entry,
                                         skirmish_entry, advance_entry,
                                         clan_war_entry, config_window))
        ok_button.pack(side='right', padx=5, pady=5)

        offset_entry.insert('end', self.offset)
        skirmish_entry.insert('end', self.skirmish_value)
        advance_entry.insert('end', self.advance_value)
        clan_war_entry.insert('end', self.clan_war_value)

    def config_ok(self, offset, skirmish, advance, clan_war, window):
        self.offset = int(offset.get())
        self.skirmish_value = int(skirmish.get())
        self.advance_value = int(advance.get())
        self.clan_war_value = int(clan_war.get())
        data = {
            'offset': offset.get(),
            'skirmish_value': skirmish.get(),
            'advance_value': advance.get(),
            'clan_war_value': clan_war.get()
        }
        if path.isfile(r'config.json'):
            f = open(r'config.json', 'w')
        else:
            f = open(r'config.json', 'x')
        f.seek(0)
        f.write(dumps(data))
        window.destroy()

    def config_apply(self, offset, skirmish, advance, clan_war):
        self.offset = int(offset.get())
        self.skirmish_value = int(skirmish.get())
        self.advance_value = int(advance.get())
        self.clan_war_value = int(clan_war.get())
        data = {
            'offset': offset.get(),
            'skirmish_value': skirmish.get(),
            'advance_value': advance.get(),
            'clan_war_value': clan_war.get()
        }
        if path.isfile(r'config.json'):
            f = open(r'config.json', 'w')
        else:
            f = open(r'config.json', 'x')
        f.seek(0)
        f.write(dumps(data))

    def load_config(self):
        if path.isfile(r'config.json'):
            f = open(r'config.json', 'r')
            data = loads(f.read())
            print(data)
            self.offset = int(data['offset'])
            self.skirmish_value = int(data['skirmish_value'])
            self.advance_value = int(data['advance_value'])
            self.clan_war_value = int(data['clan_war_value'])
        else:
            pass
コード例 #11
0
ファイル: forth_exec.py プロジェクト: Morgan243/PyBadge
class Application(Frame):
    """The main Tk application, a simple dialog."""
    def __init__(self, master=None):
        super().__init__(master)
        self.badge = None
        self.grid()
        self.columnconfigure(0, minsize=200)
        self.columnconfigure(1, minsize=200)
        self.rowconfigure(0, minsize=300)
        self.rowconfigure(3, minsize=30)
        self.create_widgets()
        self.connect()

    def create_widgets(self):
        """Sets up dialog elements."""
        self.select = tix.FileSelectBox(self, browsecmd=self.on_file_selected,
                                        pattern="*.fs", directory="forth")
        # self.select["textVariable"] = self.forth_file
        self.select.grid(row=0, columnspan=2, sticky='n'+'w'+'e')
        self.connect_btn = Button(self, text="Connect",
                                  command=self.toggle_connect)
        self.connect_btn.grid(row=1, column=0, columnspan=2)
        self.exec_btn = Button(self, text="Execute", command=self.send_file)
        self.exec_btn.state(["disabled"])
        self.exec_btn.grid(row=2, column=0, sticky='w' + 'e', padx=5, pady=3)
        self.quit = Button(self, text="QUIT", command=self.master.destroy)
        self.quit.grid(row=2, column=1, sticky='w' + 'e', padx=5, pady=3)
        self.status_panel = Frame(self, relief="groove", borderwidth=3)
        self.status_panel.grid(row=3, columnspan=2, sticky='nwse')
        self.connect_status = Label(self.status_panel, text="Not Connected")
        self.connect_status.grid(row=0, padx=5, pady=5, sticky="w")
        if self.badge is not None:
            self.connect_btn.state(["disabled"])
            self.connect_status.config(text="Connected: " + self.badge.os_device)

    def send_file(self, _retry=False):
        """Send the selected file to the badge."""
        if self.badge:
            try:
                # oddly, very first set LED seems to not set correct color
                self.badge.led(0, 0, 128)
                self.badge.led(0, 0, 128)
                with open(self.select.cget("value"), 'r') as forthin:
                    self.badge.forth_run(forthin.read())
                time.sleep(1)  # because forth_run() may be too fast
                self.badge.led(0, 128, 0)
            except IOError:
                if not _retry:
                    self.connect()
                    self.send_file(True)
                else:
                    raise

    def toggle_connect(self):
        "If connected, disconnect, otherwise connect."
        if self.connect_btn.cget("text") == "Connect":
            self.connect()
        else:
            self.disconnect()

    def disconnect(self):
        "Disconnect from current badge."
        isinstance(self.badge, Badge)
        self.badge.close()
        self.badge = None
        self.connect_btn.config(text="Connect")
        self.connect_status.config(text="Not connected.")
        self.exec_btn.state(["disabled"])

    def connect(self):
        """Attempt to connect to a badge; toggle Connect button if successful."""
        try:
            self.badge = Badge()
            self.connect_status.config(text="Connected: " + self.badge.os_device)
            self.connect_btn.config(text="Disconnect")
            # enable "Execute" if file is selected
            self.on_file_selected(self.select.cget("value"))
        except BadgeSerialException:
            self.connect_status.config(text="Not connected")

    def on_file_selected(self, selected_file):
        """Respond to user selection of file by enabling the Execute button."""
        if Path(selected_file).is_file:
            self.exec_btn.state(["!disabled"])
        else:
            self.exec_btn.state(["disabled"])
コード例 #12
0
ファイル: forth_exec.py プロジェクト: Morgan243/PyBadge
class Application(Frame):
    """The main Tk application, a simple dialog."""
    def __init__(self, master=None):
        super().__init__(master)
        self.badge = None
        self.grid()
        self.columnconfigure(0, minsize=200)
        self.columnconfigure(1, minsize=200)
        self.rowconfigure(0, minsize=300)
        self.rowconfigure(3, minsize=30)
        self.create_widgets()
        self.connect()

    def create_widgets(self):
        """Sets up dialog elements."""
        self.select = tix.FileSelectBox(self,
                                        browsecmd=self.on_file_selected,
                                        pattern="*.fs",
                                        directory="forth")
        # self.select["textVariable"] = self.forth_file
        self.select.grid(row=0, columnspan=2, sticky='n' + 'w' + 'e')
        self.connect_btn = Button(self,
                                  text="Connect",
                                  command=self.toggle_connect)
        self.connect_btn.grid(row=1, column=0, columnspan=2)
        self.exec_btn = Button(self, text="Execute", command=self.send_file)
        self.exec_btn.state(["disabled"])
        self.exec_btn.grid(row=2, column=0, sticky='w' + 'e', padx=5, pady=3)
        self.quit = Button(self, text="QUIT", command=self.master.destroy)
        self.quit.grid(row=2, column=1, sticky='w' + 'e', padx=5, pady=3)
        self.status_panel = Frame(self, relief="groove", borderwidth=3)
        self.status_panel.grid(row=3, columnspan=2, sticky='nwse')
        self.connect_status = Label(self.status_panel, text="Not Connected")
        self.connect_status.grid(row=0, padx=5, pady=5, sticky="w")
        if self.badge is not None:
            self.connect_btn.state(["disabled"])
            self.connect_status.config(text="Connected: " +
                                       self.badge.os_device)

    def send_file(self, _retry=False):
        """Send the selected file to the badge."""
        if self.badge:
            try:
                # oddly, very first set LED seems to not set correct color
                self.badge.led(0, 0, 128)
                self.badge.led(0, 0, 128)
                with open(self.select.cget("value"), 'r') as forthin:
                    self.badge.forth_run(forthin.read())
                time.sleep(1)  # because forth_run() may be too fast
                self.badge.led(0, 128, 0)
            except IOError:
                if not _retry:
                    self.connect()
                    self.send_file(True)
                else:
                    raise

    def toggle_connect(self):
        "If connected, disconnect, otherwise connect."
        if self.connect_btn.cget("text") == "Connect":
            self.connect()
        else:
            self.disconnect()

    def disconnect(self):
        "Disconnect from current badge."
        isinstance(self.badge, Badge)
        self.badge.close()
        self.badge = None
        self.connect_btn.config(text="Connect")
        self.connect_status.config(text="Not connected.")
        self.exec_btn.state(["disabled"])

    def connect(self):
        """Attempt to connect to a badge; toggle Connect button if successful."""
        try:
            self.badge = Badge()
            self.connect_status.config(text="Connected: " +
                                       self.badge.os_device)
            self.connect_btn.config(text="Disconnect")
            # enable "Execute" if file is selected
            self.on_file_selected(self.select.cget("value"))
        except BadgeSerialException:
            self.connect_status.config(text="Not connected")

    def on_file_selected(self, selected_file):
        """Respond to user selection of file by enabling the Execute button."""
        if Path(selected_file).is_file:
            self.exec_btn.state(["!disabled"])
        else:
            self.exec_btn.state(["disabled"])
コード例 #13
0
ファイル: timer_widget.py プロジェクト: j4321/Scheduler
class Timer(BaseWidget):
    def __init__(self, master):
        BaseWidget.__init__(self, 'Timer', master)

    def create_content(self, **kw):
        self.minsize(50, 120)

        self._time = [0, 0, 0]
        self._on = False
        self._after_id = ''

        self.img_play = PhotoImage(master=self, file=IM_START)
        self.img_pause = PhotoImage(master=self, file=IM_PAUSE)
        self.img_stop = PhotoImage(master=self, file=IM_STOP)

        self.rowconfigure(2, weight=1)
        self.columnconfigure(0, weight=1)
        self.columnconfigure(1, weight=1)

        # --- GUI elements
        self.display = Label(self, text='%i:%.2i:%.2i' % tuple(self._time),
                             anchor='center',
                             style='timer.TLabel')
        self.intervals = Text(self, highlightthickness=0, relief='flat',
                              height=3, width=1,
                              inactiveselectbackground=self.style.lookup('TEntry', 'selectbackground'))
        self.intervals.tag_configure('center', justify='center')
        self.intervals.configure(state='disabled')
        self.b_interv = Button(self, text=_('Interval'), style='timer.TButton',
                               command=self.add_interval)
        self.b_interv.state(('disabled',))

        self.b_launch = Button(self, image=self.img_play, padding=2,
                               command=self.launch, style='timer.TButton')
        self.b_stop = Button(self, image=self.img_stop, padding=2,
                             command=self.stop, style='timer.TButton')

        # --- placement
        self.display.grid(row=0, columnspan=2, sticky='ew', padx=8, pady=(4, 0))
        Label(self, text=_('Intervals:'),
              style='timer.TLabel').grid(row=1, columnspan=2, sticky='w', padx=4)
        self.intervals.grid(row=2, columnspan=2, sticky='eswn')
        self.b_interv.grid(row=3, columnspan=2, sticky='ew')
        self.b_launch.grid(row=4, column=0, sticky='ew')
        self.b_stop.grid(row=4, column=1, sticky='ew')

        self._corner = Sizegrip(self, style="timer.TSizegrip")
        self._corner.place(relx=1, rely=1, anchor='se')

        # --- bindings
        self.intervals.bind("<1>", lambda event: self.intervals.focus_set())
        self.bind('<3>', lambda e: self.menu.tk_popup(e.x_root, e.y_root))
        self.display.bind('<ButtonPress-1>', self._start_move)
        self.display.bind('<ButtonRelease-1>', self._stop_move)
        self.display.bind('<B1-Motion>', self._move)
        self.b_stop.bind('<Enter>', self._on_enter)
        self.b_stop.bind('<Leave>', self._on_leave)

    def update_style(self):
        self.attributes('-alpha', CONFIG.get(self.name, 'alpha', fallback=0.85))
        bg = CONFIG.get('Timer', 'background')
        fg = CONFIG.get('Timer', 'foreground')
        active_bg = active_color(*self.winfo_rgb(bg))
        self.configure(bg=bg)
        self.menu.configure(bg=bg, fg=fg, selectcolor=fg, activeforeground=fg,
                            activebackground=active_bg)
        self.menu_pos.configure(bg=bg, fg=fg, selectcolor=fg, activeforeground=fg,
                                activebackground=active_bg)
        self.display.configure(font=CONFIG.get('Timer', 'font_time'))
        self.intervals.configure(bg=bg, fg=fg,
                                 font=CONFIG.get('Timer', 'font_intervals'))
        self.style.configure('timer.TButton', background=bg, relief='flat',
                             foreground=fg, borderwidth=0)
        self.style.configure('timer.TLabel', background=bg,
                             foreground=fg)
        self.style.configure('timer.TSizegrip', background=bg)
        self.style.map('timer.TSizegrip', background=[('active', active_bg)])
        self.style.map('timer.TButton', background=[('disabled', bg),
                                                    ('!disabled', 'active', active_bg)])

    def _on_enter(self, event=None):
        self._corner.state(('active',))

    def _on_leave(self, event=None):
        self._corner.state(('!active',))

    def show(self):
        self.deiconify()
        self.update_idletasks()
        self.withdraw()
        if self._position.get() == 'above':
            self.overrideredirect(True)
        else:
            self.overrideredirect(False)
        BaseWidget.show(self)
        self.update_idletasks()
        self.withdraw()
        self.deiconify()

    def _run(self):
        if self._on:
            self._time[2] += 1
            if self._time[2] == 60:
                self._time[2] = 0
                self._time[1] += 1
                if self._time[1] == 60:
                    self._time[1] = 0
                    self._time[0] += 1
            self.display.configure(text='%i:%.2i:%.2i' % tuple(self._time))
            self._after_id = self.after(1000, self._run)

    def launch(self):
        if self._on:
            self._on = False
            self.b_launch.configure(image=self.img_play)
            self.b_interv.state(('disabled',))
        else:
            self._on = True
            self.b_interv.state(('!disabled',))
            self.b_launch.configure(image=self.img_pause)
            self.after(1000, self._run)

    def add_interval(self):
        tps = '\n%i:%.2i:%.2i' % tuple(self._time)
        if self.intervals.get('1.0', 'end') == '\n':
            tps = tps[1:]
        self.intervals.configure(state='normal')
        self.intervals.insert('end', tps, 'center')
        self.intervals.configure(state='disabled')

    def stop(self):
        self._on = False
        self.b_interv.state(('disabled',))
        self.b_launch.configure(image=self.img_play)
        self._time = [0, 0, 0]
        self.intervals.configure(state='normal')
        self.intervals.delete('1.0', 'end')
        self.intervals.configure(state='disabled')
        self.display.configure(text='%i:%.2i:%.2i' % tuple(self._time))
コード例 #14
0
class Home(TkPage):
    Name = 'Home' #name of the class (attributes)
    Font = lambda Size: ('Courier', Size) #font of the page

    def __init__(self, Parent, *args, **kwargs):
        super().__init__(Parent, *args, **kwargs) #constructor of super class

        self.Songs = [Song.replace('.mid', '') for Song in DirList('Songs') if Song.endswith('.mid')] #mappable songs
        self.MappedSongs = [Song for Song in DirList('MappedSongs') if Song.endswith('.cmid')] #mapped and compiled song
        self.Playing = ThreadEvent() #pause state
        self.Stop = ThreadEvent() #playing state
        self.Stop.set()

        TopLabel = Label(self, text = 'Genshin Lyre Player', font= Home.Font(24), bd = 10) #top label with a title for the page
        TopLabel.place(anchor= 'n', relx= 0.5, rely = 0.015, relwidth = 1, relheight=0.15) #placing the label

        self.ItemList = ListBox(self) #item list of the song
        for Index,Item in enumerate(self.MappedSongs): #for loop for every compiled comiled song
            self.ItemList.insert(Index, Item) #indexing the song in the list
            self.ItemList.itemconfig(Index, {'bg' : '#C2C2C2'}) #background of itemlist
        self.ItemList.place(anchor= 'n', relx= 0.5, rely = 0.19, relwidth = 1, relheight = 0.46) #placing the item list

        #RefreshLogo = Photo(file = 'Res/Refresh.png') #logo of refresh button (not showing at the moment)
        self.RefreshButton = Button\
        (
            self,
            text = 'Refresh',
            command = lambda : self.Refresh()
        ) #button to refresh the song list
        #self.RefreshButton.image = RefreshLogo ########
        self.RefreshButton.place(anchor= 'nw', relx = 0.01, rely = 0.7, relwidth = 0.18, relheight = 0.2) #placing the button

        self.StopButton = Button\
        (
            self,
            text = 'Stop',
            command = lambda : self.StopSong()
        )
        self.StopButton.place(anchor= 'nw', relx= 0.21, rely = 0.7, relwidth = 0.18, relheight = 0.2)

        #PlayLogo = Photo(file = 'Res/Play.png') #logo of play button (not showing at the moment)
        self.PlayButton = Button\
        (
            self,
            text = 'Play',
        )#button to play the song selected
        self.PlayButton.config\
        (
            command =\
            lambda:
            [
                Thread(target = self.PlayTrack, args = (Track,), name = f'{self.ItemList.get("active")}[{i}]', daemon = True).start()
                for i, Track in enumerate(self.LoadSong())
            ]#lambda: self.Play()
        )
        #self.PlayButton.image = PlayLogo ##########
        self.PlayButton.place(anchor= 'nw', relx= 0.41, rely = 0.7, relwidth = 0.18, relheight = 0.2) #placing the button

        #PauseLogo = Photo(file = '') #logo of the pause button
        self.PauseButton = Button\
        (
            self,
            text = 'Pause',
            command = lambda : self.PauseSong()
        ) #button to pause a song
        self.PauseButton.place(anchor= 'nw', relx= 0.61, rely = 0.7, relwidth = 0.18, relheight = 0.2) #placing the button

        self.CompileButton = Button\
        (
            self,
            text = 'Compilation\n     Screen',
            command = lambda : self.Compile(),
        )
        self.CompileButton.place(anchor = 'nw', relx= 0.81, rely = 0.7, relwidth = 0.18, relheight = 0.2) #placing the button

    def Refresh(self): #this function refresh the song list
        self.MappedSongs = [Song for Song in DirList('MappedSongs') if Song.endswith('.cmid')] #check the folder for the songs
        self.ItemList.delete('0','end') #delete every item of the list
        for Index, Item in enumerate(self.MappedSongs): #loop for every song in the folder
            self.ItemList.insert(Index, Item) #index the song in the item list
            self.ItemList.itemconfig(Index, {'bg' : '#C2C2C2'}) #background of the itemlist

    def Countdown(self): #this function create an initial countdown
        for i in range(3): #3...2...1
            print(3-i)
            self.after(1000)

    def LoadSong(self):
        self.PlayButton.state(['disabled']) #disable the play button (might cause some unexpected behaviours)
        Song = self.ItemList.get('active') #getting the selected song from the list
        self.Stop.clear() #reset the stop state
        self.Playing.set()

        with open('MappedSongs/' + Song, 'rb') as InputFile: #opening the compiled midi file
            Music = Load(InputFile) #load the searialized object

        self.Countdown() #initial countdown to give user time to switch to genshin

        return Music

    def PlayTrack(self, Part = None): #this (THREADED) function play a single part (track) of the selected song
        if Part == None:
            raise ValueError('Part cannot be None')
        else:
            print(f'{Identity()} ready')

        global Notes
        global DXCodes
        global NotesFlags
        Elements = len(Part) #keystrokes to execute
        Actual = 0 #element counter

        def PlayNote(Sound, Duration): #this function play a single note of the part
            NotesFlags[Sound] = False #make the resource unavailable for other threads (to avoid deadlock)
            PressKey(DXCodes[Notes[Sound]]) #press note-corresponding key
            Sleep(abs(Duration)) #wait the duration of the note
            ReleaseKey(DXCodes[Notes[Sound]]) #release note-corresponding key
            NotesFlags[Sound] = True #make the resource available for other threads

        def PlayChord(Sounds, Duration): # function play a single chord of the part
            #print(Duration)
            for Sound in Sounds: #for loop  to make every note of the chord unavailable for other threads (to avoid deadlock)
                NotesFlags[Sound] = False #lock single resource

            for Sound in Sounds: #for loop to press chord-corresponding keys
                PressKey(DXCodes[Notes[Sound]]) #press the note-corresponding key of the chord

            Sleep(abs(Duration)) #wait the duration of the notes

            for Sound in Sounds: #for loop to release chord-corresponding keys
                ReleaseKey(DXCodes[Notes[Sound]]) #release the note-corresponding key of the chord

            for Sound in Sounds:#for loop to make every note of the chord available for other threads
                NotesFlags[Sound] = True #unlock single resource

        while not self.Stop.is_set() and Actual < Elements:
            if IsPressed('ctrl+space'):
                if not PauseFunction.locked():
                    PauseFunction.acquire()
                    self.StopSong()
                    PauseFunction.release()
                break
            if IsPressed('shift'):
                print('resume trigger')
                Sleep(1)
                self.Playing.set()

            while self.Playing.is_set() and Actual < Elements:
                if IsPressed('ctrl+space'):
                    if not PauseFunction.locked():
                        PauseFunction.acquire()
                        self.StopSong()
                        PauseFunction.release()
                    break
                if IsPressed('shift'):
                    print('pause trigger')
                    Sleep(1)
                    self.Playing.clear()
                    break

                Counter = 0

                if Part[Actual]['Type'] == 'Note': #check if the element is a note
                    Duration = float(Part[Actual]['Duration'])
                    PartialDuration = Duration / 10 #duration splitted to check multiple times
                    Note = f'{Part[Actual]["Sound"]}{Part[Actual]["Octave"]}' #extract the note

                    if NotesFlags[Note] and IsMainAlive(): #check if the resource is available
                        PlayNote(Note, Duration) #if the reseourse plays the note at full duration
                    else: #if the resource is not available at the moment
                        while not NotesFlags[Note] or Counter < 10: #check if the note is still playable
                            Sleep(PartialDuration) #waiting the partial duration to check availability
                            Counter += 1 #increasing wastd times

                        if NotesFlags[Note] and Counter < 10: #check if the resource are available and the note is still playable
                            RemainingDuration = Duration - (PartialDuration * Counter) #calculate remaining duration
                            PlayNote(Note, RemainingDuration) #play the note at partial duration
                elif Part[Actual]['Type'] == 'Chord': # check if the element is a chord (multiple notes together)
                    NotesList = Part[Actual]['Sound'] #extract notes of the chord
                    Octaves = Part[Actual]['Octave'] #extract respective octaves of the notes
                    Chord = [f'{Note}{Octave}' for Note, Octave in zip(NotesList, Octaves)] # combine notes and octaves together
                    Duration = float(Part[Actual]['Duration'])
                    PartialDuration = Duration / 10 #duration splitted to check multiple times

                    if all([NotesFlags[Note] for Note in Chord]) and IsMainAlive(): #check if all the notes in the chord are available (otherwise the cord wouldn't make sense)
                        PlayChord(Chord, Duration) #play the chord at full duration
                    else:
                        while not all([NotesFlags[Note] for Note in Chord]): #check if the chord is stil playable
                            Sleep(PartialDuration) #waiting the partial duration to check availability
                            Counter += 1 #increasing wasted times

                        if all([NotesFlags[Note] for Note in Chord]) and IsMainAlive(): #check if the resources are available and the chors is still playable
                            RemainingDuration = Duration - (PartialDuration * Counter) #calculate remaining duration
                            PlayChord(Chord, RemainingDuration) #play the chord at partial duration
                elif Part[Actual]['Type'] == 'Rest': #check if the element is a rest
                    Duration = float(Part[Actual]['Duration'])
                    Sleep(abs(Duration)) #wait the rest

                Actual += 1 #increase the played notes

        Sleep(5)
        print(f'{Identity()} ended')

    def PauseSong(self): #this fucntion pause the song playing
        Sleep(2) #delay to get rid of function call besides the first
        if self.Playing.is_set(): #check if the song is playing
            print(f'{Identity()} is pausing')
            self.Playing.clear() #clear the playing state
        if not self.Playing.is_set(): #check if the song is not playing
            print(f'{Identity()} is resuming')
            self.Playing.set() #set the song as playing

    def StopSong(self): #this fucntion stop the song
        if not StopFunction.locked(): #check if the fucntion has called by multiple thread
            print('Stop called')
            Sleep(1)
            self.Stop.set() #set the stop state
            self.Playing.clear() #clear the playing state
            self.PlayButton.state(['!disabled']) #enable the play button
            self.PlayButton.state(['!pressed']) #reset the play button to the default state
            ReleaseResources() #release possible hanging resources

    def Compile(self): #this function switch to compilation screen
        GenshinLyrePlayer.Raise(CompilationScreen.Name)