Ejemplo n.º 1
0
def _find_item(menu: tkinter.Menu, label: str) -> Optional[int]:
    last_index = menu.index('end')
    if last_index is not None:  # menu not empty
        for index in range(last_index + 1):
            if menu.type(
                    index) in _MENU_ITEM_TYPES_WITH_LABEL and menu.entrycget(
                        index, 'label') == label:
                return index
    return None
Ejemplo n.º 2
0
def _find_item(menu: tkinter.Menu, label: str) -> Optional[int]:
    last_index = menu.index("end")  # type: ignore[no-untyped-call]
    if last_index is not None:  # menu not empty
        for index in range(last_index + 1):
            if (menu.type(index) in
                    _MENU_ITEM_TYPES_WITH_LABEL  # type: ignore[no-untyped-call]
                    and menu.entrycget(index, "label") ==
                    label  # type: ignore[no-untyped-call]
                ):
                return index
    return None
Ejemplo n.º 3
0
class App(Tk):
    """
    Main app.

    Put an icon in the system tray with a right click menu to
    create notes.
    """
    def __init__(self):
        Tk.__init__(self)
        self.withdraw()
        self.notes = {}
        self.img = PhotoImage(file=cst.IM_ICON)
        self.icon = PhotoImage(master=self, file=cst.IM_ICON_48)
        self.iconphoto(True, self.icon)

        self.ewmh = ewmh.EWMH()

        style = Style(self)
        style.theme_use("clam")

        self.close1 = PhotoImage("img_close", file=cst.IM_CLOSE)
        self.close2 = PhotoImage("img_closeactive", file=cst.IM_CLOSE_ACTIVE)
        self.roll1 = PhotoImage("img_roll", file=cst.IM_ROLL)
        self.roll2 = PhotoImage("img_rollactive", file=cst.IM_ROLL_ACTIVE)

        self.protocol("WM_DELETE_WINDOW", self.quit)
        self.icon = tktray.Icon(self, docked=True)

        # --- Menu
        self.menu_notes = Menu(self.icon.menu, tearoff=False)
        self.hidden_notes = {cat: {} for cat in CONFIG.options("Categories")}
        self.menu_show_cat = Menu(self.icon.menu, tearoff=False)
        self.menu_hide_cat = Menu(self.icon.menu, tearoff=False)
        self.icon.configure(image=self.img)
        self.icon.menu.add_command(label=_("New Note"), command=self.new)
        self.icon.menu.add_separator()
        self.icon.menu.add_command(label=_('Show All'), command=self.show_all)
        self.icon.menu.add_cascade(label=_('Show Category'),
                                   menu=self.menu_show_cat)
        self.icon.menu.add_cascade(label=_('Show Note'),
                                   menu=self.menu_notes,
                                   state="disabled")
        self.icon.menu.add_separator()
        self.icon.menu.add_command(label=_('Hide All'), command=self.hide_all)
        self.icon.menu.add_cascade(label=_('Hide Category'),
                                   menu=self.menu_hide_cat)
        self.icon.menu.add_separator()
        self.icon.menu.add_command(label=_("Preferences"), command=self.config)
        self.icon.menu.add_command(label=_("Note Manager"),
                                   command=self.manage)
        self.icon.menu.add_separator()
        self.icon.menu.add_command(label=_("Backup Notes"),
                                   command=self.backup)
        self.icon.menu.add_command(label=_("Restore Backup"),
                                   command=self.restore)
        self.icon.menu.add_separator()
        self.icon.menu.add_command(label=_("Export"),
                                   command=self.export_notes)
        self.icon.menu.add_command(label=_("Import"),
                                   command=self.import_notes)
        self.icon.menu.add_separator()
        self.icon.menu.add_command(label=_('Check for Updates'),
                                   command=lambda: UpdateChecker(self))
        self.icon.menu.add_command(label=_('About'),
                                   command=lambda: About(self))
        self.icon.menu.add_command(label=_('Quit'), command=self.quit)

        # --- Restore notes
        self.note_data = {}
        if os.path.exists(PATH_DATA):
            with open(PATH_DATA, "rb") as fich:
                dp = pickle.Unpickler(fich)
                note_data = dp.load()
                for i, key in enumerate(note_data):
                    self.note_data["%i" % i] = note_data[key]
            backup()
            for key in self.note_data:
                data = self.note_data[key]
                cat = data["category"]
                if not CONFIG.has_option("Categories", cat):
                    CONFIG.set("Categories", cat, data["color"])
                if data["visible"]:
                    self.notes[key] = Sticky(self, key, **data)
                else:
                    self.add_note_to_menu(key, data["title"], cat)
        self.nb = len(self.note_data)
        self.update_menu()
        self.update_notes()
        self.make_notes_sticky()

        # --- class bindings
        # newline depending on mode
        self.bind_class("Text", "<Return>", self.insert_newline)
        # char deletion taking into account list type
        self.bind_class("Text", "<BackSpace>", self.delete_char)
        # change Ctrl+A to select all instead of go to the beginning of the line
        self.bind_class('Text', '<Control-a>', self.select_all_text)
        self.bind_class('TEntry', '<Control-a>', self.select_all_entry)
        # bind Ctrl+Y to redo
        self.bind_class('Text', '<Control-y>', self.redo_event)
        # unbind Ctrl+I and Ctrl+B
        self.bind_class('Text', '<Control-i>', lambda e: None)
        self.bind_class('Text', '<Control-b>', lambda e: None)
        # highlight checkboxes when inside text selection
        self.bind_class("Text", "<ButtonPress-1>", self.highlight_checkboxes,
                        True)
        self.bind_class("Text", "<ButtonRelease-1>", self.highlight_checkboxes,
                        True)
        self.bind_class("Text", "<B1-Motion>", self.highlight_checkboxes, True)
        evs = [
            '<<SelectAll>>', '<<SelectLineEnd>>', '<<SelectLineStart>>',
            '<<SelectNextChar>>', '<<SelectNextLine>>', '<<SelectNextPara>>',
            '<<SelectNextWord>>', '<<SelectNone>>', '<<SelectPrevChar>>',
            '<<SelectPrevLine>>', '<<SelectPrevPara>>', '<<SelectPrevWord>>'
        ]
        for ev in evs:
            self.bind_class("Text", ev, self.highlight_checkboxes, True)

        # check for updates
        if CONFIG.getboolean("General", "check_update"):
            UpdateChecker(self)

    # --- class bindings methods
    def highlight_checkboxes(self, event):
        txt = event.widget
        try:
            deb = cst.sorting(txt.index("sel.first"))
            fin = cst.sorting(txt.index("sel.last"))
            for ch in txt.children.values():
                try:
                    i = cst.sorting(txt.index(ch))
                    if i >= deb and i <= fin:
                        ch.configure(style="sel.TCheckbutton")
                    else:
                        ch.configure(style=txt.master.id + ".TCheckbutton")
                except TclError:
                    pass
        except TclError:
            for ch in txt.children.values():
                try:
                    i = cst.sorting(txt.index(ch))
                    ch.configure(style=txt.master.id + ".TCheckbutton")
                except TclError:
                    pass

    def redo_event(self, event):
        try:
            event.widget.edit_redo()
        except TclError:
            # nothing to redo
            pass

    def select_all_entry(self, event):
        event.widget.selection_range(0, "end")

    def select_all_text(self, event):
        event.widget.tag_add("sel", "1.0", "end-1c")
        self.highlight_checkboxes(event)

    def delete_char(self, event):
        txt = event.widget
        deb_line = txt.get("insert linestart", "insert")
        tags = txt.tag_names("insert")
        if txt.tag_ranges("sel"):
            if txt.tag_nextrange("enum", "sel.first", "sel.last"):
                update = True
            else:
                update = False
            txt.delete("sel.first", "sel.last")
            if update:
                txt.master.update_enum()
        elif txt.index("insert") != "1.0":
            if re.match('^\t[0-9]+\.\t$', deb_line) and 'enum' in tags:
                txt.delete("insert linestart", "insert")
                txt.insert("insert", "\t\t")
                txt.master.update_enum()
            elif deb_line == "\t•\t" and 'list' in tags:
                txt.delete("insert linestart", "insert")
                txt.insert("insert", "\t\t")
            elif deb_line == "\t\t":
                txt.delete("insert linestart", "insert")
            elif "todolist" in tags and txt.index("insert") == txt.index(
                    "insert linestart+1c"):
                try:
                    ch = txt.window_cget("insert-1c", "window")
                    txt.delete("insert-1c")
                    txt.children[ch.split('.')[-1]].destroy()
                    txt.insert("insert", "\t\t")
                except TclError:
                    txt.delete("insert-1c")
            else:
                txt.delete("insert-1c")

    def insert_newline(self, event):
        mode = event.widget.master.mode.get()
        if mode == "list":
            event.widget.insert("insert", "\n\t•\t")
            event.widget.tag_add("list", "1.0", "end")
        elif mode == "todolist":
            event.widget.insert("insert", "\n")
            ch = Checkbutton(event.widget,
                             takefocus=False,
                             style=event.widget.master.id + ".TCheckbutton")
            event.widget.window_create("insert", window=ch)
            event.widget.tag_add("todolist", "1.0", "end")
        elif mode == "enum":
            event.widget.configure(autoseparators=False)
            event.widget.edit_separator()
            event.widget.insert("insert", "\n\t0.\t")
            event.widget.master.update_enum()
            event.widget.edit_separator()
            event.widget.configure(autoseparators=True)
        else:
            event.widget.insert("insert", "\n")

    def make_notes_sticky(self):
        for w in self.ewmh.getClientList():
            if w.get_wm_name()[:7] == 'mynotes':
                self.ewmh.setWmState(w, 1, '_NET_WM_STATE_STICKY')
        self.ewmh.display.flush()

    def add_note_to_menu(self, nb, note_title, category):
        """add note to 'show notes' menu. """

        try:
            name = self.menu_notes.entrycget(category.capitalize(), 'menu')
            if not isinstance(name, str):
                name = str(name)
            menu = self.menu_notes.children[name.split('.')[-1]]
            end = menu.index("end")
            if end is not None:
                # le menu n'est pas vide
                titles = self.hidden_notes[category].values()
                titles = [t for t in titles if t.split(" ~#")[0] == note_title]
                if titles:
                    title = "%s ~#%i" % (note_title, len(titles) + 1)
                else:
                    title = note_title
            else:
                title = note_title
        except TclError:
            # cat is not in the menu
            menu = Menu(self.menu_notes, tearoff=False)
            self.menu_notes.add_cascade(label=category.capitalize(), menu=menu)
            title = note_title
        menu.add_command(label=title, command=lambda: self.show_note(nb))
        self.icon.menu.entryconfigure(4, state="normal")
        self.hidden_notes[category][nb] = title

    def backup(self):
        """Create a backup at the location indicated by user."""
        initialdir, initialfile = os.path.split(PATH_DATA_BACKUP % 0)
        fichier = asksaveasfilename(defaultextension=".backup",
                                    filetypes=[],
                                    initialdir=initialdir,
                                    initialfile="notes.backup0",
                                    title=_('Backup Notes'))
        if fichier:
            try:
                with open(fichier, "wb") as fich:
                    dp = pickle.Pickler(fich)
                    dp.dump(self.note_data)
            except Exception as e:
                report_msg = e.strerror != 'Permission denied'
                showerror(_("Error"), _("Backup failed."),
                          traceback.format_exc(), report_msg)

    def restore(self, fichier=None, confirmation=True):
        """Restore notes from backup."""
        if confirmation:
            rep = askokcancel(
                _("Warning"),
                _("Restoring a backup will erase the current notes."),
                icon="warning")
        else:
            rep = True
        if rep:
            if fichier is None:
                fichier = askopenfilename(defaultextension=".backup",
                                          filetypes=[],
                                          initialdir=LOCAL_PATH,
                                          initialfile="",
                                          title=_('Restore Backup'))
            if fichier:
                try:
                    keys = list(self.note_data.keys())
                    for key in keys:
                        self.delete_note(key)
                    if not os.path.samefile(fichier, PATH_DATA):
                        copy(fichier, PATH_DATA)
                    with open(PATH_DATA, "rb") as myfich:
                        dp = pickle.Unpickler(myfich)
                        note_data = dp.load()
                    for i, key in enumerate(note_data):
                        data = note_data[key]
                        note_id = "%i" % i
                        self.note_data[note_id] = data
                        cat = data["category"]
                        if not CONFIG.has_option("Categories", cat):
                            CONFIG.set("Categories", cat, data["color"])
                        if data["visible"]:
                            self.notes[note_id] = Sticky(self, note_id, **data)
                    self.nb = len(self.note_data)
                    self.update_menu()
                    self.update_notes()
                except FileNotFoundError:
                    showerror(
                        _("Error"),
                        _("The file {filename} does not exists.").format(
                            filename=fichier))
                except Exception as e:
                    showerror(_("Error"), str(e), traceback.format_exc(), True)

    def show_all(self):
        """Show all notes."""
        for cat in self.hidden_notes.keys():
            keys = list(self.hidden_notes[cat].keys())
            for key in keys:
                self.show_note(key)

    def show_cat(self, category):
        """Show all notes belonging to category."""
        keys = list(self.hidden_notes[category].keys())
        for key in keys:
            self.show_note(key)

    def hide_all(self):
        """Hide all notes."""
        keys = list(self.notes.keys())
        for key in keys:
            self.notes[key].hide()

    def hide_cat(self, category):
        """Hide all notes belonging to category."""
        keys = list(self.notes.keys())
        for key in keys:
            if self.note_data[key]["category"] == category:
                self.notes[key].hide()

    def manage(self):
        """Launch note manager."""
        Manager(self)

    def config(self):
        """Launch the setting manager."""
        conf = Config(self)
        self.wait_window(conf)
        col_changes, name_changes = conf.get_changes()
        if col_changes or name_changes:
            self.update_notes(col_changes, name_changes)
            self.update_menu()
            alpha = CONFIG.getint("General", "opacity") / 100
            for note in self.notes.values():
                note.attributes("-alpha", alpha)
                note.update_title_font()
                note.update_text_font()
                note.update_titlebar()

    def delete_cat(self, category):
        """Delete all notes belonging to category."""
        keys = list(self.notes.keys())
        for key in keys:
            if self.note_data[key]["category"] == category:
                self.notes[key].delete(confirmation=False)

    def delete_note(self, nb):
        if self.note_data[nb]["visible"]:
            self.notes[nb].delete(confirmation=False)
        else:
            cat = self.note_data[nb]["category"]
            name = self.menu_notes.entrycget(cat.capitalize(), 'menu')
            if not isinstance(name, str):
                name = str(name)
            menu = self.menu_notes.children[name.split('.')[-1]]
            index = menu.index(self.hidden_notes[cat][nb])
            menu.delete(index)
            if menu.index("end") is None:
                # the menu is empty
                self.menu_notes.delete(cat.capitalize())
                if self.menu_notes.index('end') is None:
                    self.icon.menu.entryconfigure(4, state="disabled")
            del (self.hidden_notes[cat][nb])
            del (self.note_data[nb])
            self.save()

    def show_note(self, nb):
        """Display the note corresponding to the 'nb' key in self.note_data."""
        self.note_data[nb]["visible"] = True
        cat = self.note_data[nb]["category"]
        name = self.menu_notes.entrycget(cat.capitalize(), 'menu')
        if not isinstance(name, str):
            name = str(name)
        menu = self.menu_notes.children[name.split('.')[-1]]
        index = menu.index(self.hidden_notes[cat][nb])
        del (self.hidden_notes[cat][nb])
        self.notes[nb] = Sticky(self, nb, **self.note_data[nb])
        menu.delete(index)
        if menu.index("end") is None:
            # the menu is empty
            self.menu_notes.delete(cat.capitalize())
            if self.menu_notes.index('end') is None:
                self.icon.menu.entryconfigure(4, state="disabled")
        self.make_notes_sticky()

    def update_notes(self, col_changes={}, name_changes={}):
        """Update the notes after changes in the categories."""
        categories = CONFIG.options("Categories")
        categories.sort()
        self.menu_notes.delete(0, "end")
        self.hidden_notes = {cat: {} for cat in categories}
        for key in self.note_data:
            cat = self.note_data[key]["category"]
            if cat in name_changes:
                cat = name_changes[cat]
                self.note_data[key]["category"] = cat
                if self.note_data[key]["visible"]:
                    self.notes[key].change_category(cat)
            elif cat not in categories:
                default = CONFIG.get("General", "default_category")
                default_color = CONFIG.get("Categories", default)
                if self.note_data[key]["visible"]:
                    self.notes[key].change_category(default)
                self.note_data[key]["category"] = default
                self.note_data[key]["color"] = default_color
                cat = default
            if cat in col_changes:
                old_color, new_color = col_changes[cat]
                if self.note_data[key]["color"] == old_color:
                    self.note_data[key]["color"] = new_color
                    if self.note_data[key]["visible"]:
                        self.notes[key].change_color(cst.INV_COLORS[new_color])
            if not self.note_data[key]['visible']:
                self.add_note_to_menu(key, self.note_data[key]["title"],
                                      self.note_data[key]['category'])
            else:
                self.notes[key].update_menu_cat(categories)
        self.save()
        if self.menu_notes.index("end") is not None:
            self.icon.menu.entryconfigure(4, state="normal")
        else:
            self.icon.menu.entryconfigure(4, state="disabled")

    def update_menu(self):
        """Populate self.menu_show_cat and self.menu_hide_cat with the categories."""
        self.menu_hide_cat.delete(0, "end")
        self.menu_show_cat.delete(0, "end")
        categories = CONFIG.options("Categories")
        categories.sort()
        for cat in categories:
            self.menu_show_cat.add_command(
                label=cat.capitalize(), command=lambda c=cat: self.show_cat(c))
            self.menu_hide_cat.add_command(
                label=cat.capitalize(), command=lambda c=cat: self.hide_cat(c))

    def save(self):
        """Save the data."""
        with open(PATH_DATA, "wb") as fich:
            dp = pickle.Pickler(fich)
            dp.dump(self.note_data)

    def new(self):
        """Create a new note."""
        key = "%i" % self.nb
        self.notes[key] = Sticky(self, key)
        data = self.notes[key].save_info()
        data["visible"] = True
        self.note_data[key] = data
        self.nb += 1
        self.make_notes_sticky()

    def export_notes(self):
        export = Export(self)
        self.wait_window(export)
        categories_to_export, only_visible = export.get_export()
        if categories_to_export:
            initialdir, initialfile = os.path.split(PATH_DATA_BACKUP % 0)
            fichier = asksaveasfilename(defaultextension=".html",
                                        filetypes=[
                                            (_("HTML file (.html)"), "*.html"),
                                            (_("Text file (.txt)"), "*.txt"),
                                            (_("All files"), "*")
                                        ],
                                        initialdir=initialdir,
                                        initialfile="",
                                        title=_('Export Notes As'))
            if fichier:
                try:
                    if os.path.splitext(fichier)[-1] == ".html":
                        # --- html export
                        cats = {cat: [] for cat in categories_to_export}
                        for key in self.note_data:
                            cat = self.note_data[key]["category"]
                            if cat in cats and (
                                (not only_visible)
                                    or self.note_data[key]["visible"]):
                                cats[cat].append(
                                    (self.note_data[key]["title"],
                                     cst.note_to_html(self.note_data[key],
                                                      self)))
                        text = ""
                        for cat in cats:
                            cat_txt = "<h1 style='text-align:center'>" + _(
                                "Category: {category}").format(
                                    category=cat) + "<h1/>\n"
                            text += cat_txt
                            text += "<br>"
                            for title, txt in cats[cat]:
                                text += "<h2 style='text-align:center'>%s</h2>\n" % title
                                text += txt
                                text += "<br>\n"
                                text += "<hr />"
                                text += "<br>\n"
                            text += '<hr style="height: 8px;background-color:grey" />'
                            text += "<br>\n"
                        with open(fichier, "w") as fich:
                            fich.write('<body style="max-width:30em">\n')
                            fich.write(
                                text.encode(
                                    'ascii',
                                    'xmlcharrefreplace').decode("utf-8"))
                            fich.write("\n</body>")
#                if os.path.splitext(fichier)[-1] == ".txt":
                    else:
                        # --- txt export
                        # export notes to .txt: all formatting is lost
                        cats = {cat: [] for cat in categories_to_export}
                        for key in self.note_data:
                            cat = self.note_data[key]["category"]
                            if cat in cats and (
                                (not only_visible)
                                    or self.note_data[key]["visible"]):
                                cats[cat].append(
                                    (self.note_data[key]["title"],
                                     cst.note_to_txt(self.note_data[key])))
                        text = ""
                        for cat in cats:
                            cat_txt = _("Category: {category}").format(
                                category=cat) + "\n"
                            text += cat_txt
                            text += "=" * len(cat_txt)
                            text += "\n\n"
                            for title, txt in cats[cat]:
                                text += title
                                text += "\n"
                                text += "-" * len(title)
                                text += "\n\n"
                                text += txt
                                text += "\n\n"
                                text += "-" * 30
                                text += "\n\n"
                            text += "#" * 30
                            text += "\n\n"
                        with open(fichier, "w") as fich:
                            fich.write(text)
#                    else:
#        # --- pickle export
#                        note_data = {}
#                        for key in self.note_data:
#                            if self.note_data[key]["category"] in categories_to_export:
#                                if (not only_visible) or self.note_data[key]["visible"]:
#                                    note_data[key] = self.note_data[key]
#
#                        with open(fichier, "wb") as fich:
#                            dp = pickle.Pickler(fich)
#                            dp.dump(note_data)
                except Exception as e:
                    report_msg = e.strerror != 'Permission denied'
                    showerror(_("Error"), str(e), traceback.format_exc(),
                              report_msg)

    def import_notes(self):
        fichier = askopenfilename(defaultextension=".backup",
                                  filetypes=[(_("Notes (.notes)"), "*.notes"),
                                             (_("All files"), "*")],
                                  initialdir=LOCAL_PATH,
                                  initialfile="",
                                  title=_('Import'))
        if fichier:
            try:
                with open(fichier, "rb") as fich:
                    dp = pickle.Unpickler(fich)
                    note_data = dp.load()
                for i, key in enumerate(note_data):
                    data = note_data[key]
                    note_id = "%i" % (i + self.nb)
                    self.note_data[note_id] = data
                    cat = data["category"]
                    if not CONFIG.has_option("Categories", cat):
                        CONFIG.set("Categories", cat, data["color"])
                        self.hidden_notes[cat] = {}
                    if data["visible"]:
                        self.notes[note_id] = Sticky(self, note_id, **data)
                self.nb = int(max(self.note_data.keys(),
                                  key=lambda x: int(x))) + 1
                self.update_menu()
                self.update_notes()
            except Exception:
                message = _("The file {file} is not a valid .notes file."
                            ).format(file=fichier)
                showerror(_("Error"), message, traceback.format_exc())

    def cleanup(self):
        """Remove unused latex images."""
        img_stored = os.listdir(cst.PATH_LATEX)
        img_used = []
        for data in self.note_data.values():
            img_used.extend(list(data.get("latex", {}).keys()))
        for img in img_stored:
            if img not in img_used:
                os.remove(os.path.join(cst.PATH_LATEX, img))

    def quit(self):
        self.destroy()
Ejemplo n.º 4
0
class Fenetre:
    def __init__(self, root, job):

        # Récupération de l'objet Jeu
        self.jeu = job
        self.saved = True

        # Création de la fenêtre principale
        self.root = root
        self.root.title('Rêve de Dragon')
        self.root.resizable(True, True)

        # Création des menus
        # On a 4 menu principaux : filemenu, cmdmenu, viewmenu et helpmenu
        self.menubar = Menu(root)
        self.root.config(menu=self.menubar)

        # filemenu: menu de manipulation des fichiers contenant les personnages
        self.filemenu = Menu(self.menubar, tearoff=0)
        self.menubar.add_cascade(label="Fichier", menu=self.filemenu)
        self.filemenu.add_command(label="Nouveau", command=self.nouveau)
        self.filemenu.add_command(label="Ouvrir", command=self.ouvrir)
        self.filemenu.add_separator()
        self.filemenu.add_command(label="Enregistrer",
                                  command=self.jeu.enregistrer)
        self.filemenu.add_command(label="Enregistrer sous...",
                                  command=self.jeu.enregistrer_sous)
        self.filemenu.add_separator()
        self.filemenu.add_command(label="Fermer", command=self.fermer)
        self.filemenu.add_separator()
        self.filemenu.add_command(label="Imprimer",
                                  command=self.void,
                                  state='disabled')
        self.filemenu.add_separator()
        self.filemenu.add_command(label="Quitter", command=self.quitter)

        # cmdmenu: menu des commandes sur les personnages
        self.cmdmenu = Menu(self.menubar, tearoff=0)
        self.menubar.add_cascade(label="Commande", menu=self.cmdmenu)
        self.cmdmenu.add_command(label="Nouvelle Partie", command=self.partie)
        self.cmdmenu.add_separator()
        self.cmdmenu.add_command(label="Nouveau Personnage",
                                 command=self.creer)
        self.cmdmenu.add_separator()
        self.cmdmenu.add_command(label="Valider le Personnage",
                                 command=self.valider)

        # viewmenu: menu de sélection du personnage à l'affichage
        # Ce menu est vide en l'absence de personnage
        # Il est rempli au chargement ou à la création d'un personnage
        self.viewmenu = Menu(self.menubar, tearoff=0)
        self.menubar.add_cascade(label="Personnage", menu=self.viewmenu)

        # helpmenu: menu d'aide
        self.helpmenu = Menu(self.menubar, tearoff=0)
        self.menubar.add_cascade(label="Aide", menu=self.helpmenu)
        self.helpmenu.add_command(label="Règles du Jeu", command=self.regles)
        self.helpmenu.add_command(label="Utilisation du Programme",
                                  command=self.utilise)
        self.helpmenu.add_command(label="A Propos...", command=self.a_propos)

        # frame1 : Fiche du personnage
        frame1 = Frame(root,
                       borderwidth=0,
                       relief='flat',
                       height=200,
                       width=600)
        frame1.grid(row=0, column=0, sticky='NW', padx="10", pady="5")

        # Nom
        self.Entry_Nom = StringVar()
        self.Old_Nom = ""
        Label(frame1, text='Nom:').grid(row=0,
                                        column=0,
                                        columnspan=2,
                                        sticky='E')
        Entry(frame1, textvariable=self.Entry_Nom, justify='left', width=34)\
            .grid(row=0, column=2, columnspan=4, sticky='W', padx="5")

        # Age
        self.Entry_Age = IntVar()
        Label(frame1, text='Age:').grid(row=1,
                                        column=0,
                                        columnspan=2,
                                        sticky='E')
        Entry(frame1, textvariable=self.Entry_Age, justify='right', width=3)\
            .grid(row=1, column=2, sticky='W', padx="5")

        # Heure de naissance (pour hauts-rêvants)
        self.Entry_Heure = IntVar()
        Label(frame1, text='Heure de Naissance:').grid(row=1,
                                                       column=3,
                                                       sticky='E')
        Entry(frame1, textvariable=self.Entry_Heure, justify='right', width=3) \
            .grid(row=1, column=4, sticky='W', padx="5")

        # Taille
        self.Entry_Taille = IntVar()
        Label(frame1, text='Taille:').grid(row=1, column=5, sticky='E')
        Entry(frame1, textvariable=self.Entry_Taille, justify='right', width=3)\
            .grid(row=1, column=6, sticky='W', padx="5")

        # Poids
        self.Entry_Poids = IntVar()
        Label(frame1, text='Poids:').grid(row=1, column=7, sticky='E')
        Entry(frame1, textvariable=self.Entry_Poids, justify='right', width=3)\
            .grid(row=1, column=8, sticky='W', padx="5")

        # Beauté
        self.Entry_Beaute = IntVar()
        Label(frame1, text='Beauté:').grid(row=2,
                                           column=0,
                                           columnspan=2,
                                           sticky='E')
        Entry(frame1, textvariable=self.Entry_Beaute, justify='right', width=3) \
            .grid(row=2, column=2, sticky='W', padx="5")

        # Cheveux
        self.Entry_Cheveux = StringVar()
        Label(frame1, text='Cheveux:').grid(row=2, column=3, sticky='E')
        Entry(frame1, textvariable=self.Entry_Cheveux, justify='left', width=8)\
            .grid(row=2, column=4, sticky='W', padx="5")

        # Yeux
        self.Entry_Yeux = StringVar()
        Label(frame1, text='Yeux:').grid(row=2, column=5, sticky='E')
        Entry(frame1, textvariable=self.Entry_Yeux, justify='left', width=8)\
            .grid(row=2, column=6, sticky='W', padx="5")

        # Haut rêvant
        self.Entry_HRevant = IntVar()
        Checkbutton(frame1, text="Haut-Rêvant", variable=self.Entry_HRevant, command=self.sel_revant) \
            .grid(row=2, column=7, columnspan=2, sticky='W', padx="5")

        # Sexe
        self.Entry_Sexe = StringVar()
        Label(frame1, text='Sexe:').grid(row=3,
                                         column=0,
                                         columnspan=2,
                                         sticky='E')
        Entry(frame1, textvariable=self.Entry_Sexe, justify='left', width=2)\
            .grid(row=3, column=2, sticky='W', padx="5")

        # Ambidextre
        self.Entry_Ambidextre = IntVar()
        Label(frame1, text='Ambidextre:').grid(row=3, column=3, sticky='E')
        Entry(frame1, textvariable=self.Entry_Ambidextre, justify='right', width=3)\
            .grid(row=3, column=4, sticky='W', padx="5")

        # Signes Particuliers
        self.Entry_SignesP = StringVar()
        Label(frame1, text='Signes Particuliers:').grid(row=3,
                                                        column=5,
                                                        sticky='E')
        Entry(frame1, textvariable=self.Entry_SignesP, justify='left', width=37)\
            .grid(row=3, column=6, columnspan=3, sticky='W', padx="5")

        # Frame 2 : Caractéristiques
        frame2 = LabelFrame(root,
                            text=" Caractéristiques ",
                            borderwidth=2,
                            relief='ridge',
                            height=200,
                            width=600)
        frame2.grid(row=1, column=0, sticky='NW', padx="10", pady="5")
        frame20 = LabelFrame(frame2,
                             text=' Physiques ',
                             borderwidth=2,
                             relief='ridge',
                             height=200,
                             width=200)
        frame20.grid(row=0, column=0, sticky='NW', padx="5", pady="5")
        frame21 = LabelFrame(frame2,
                             text=' Mentales ',
                             borderwidth=2,
                             relief='ridge',
                             height=200,
                             width=200)
        frame21.grid(row=0, column=1, sticky='NW', padx="5", pady="5")
        frame22 = LabelFrame(frame2,
                             text=' Pouvoirs ',
                             borderwidth=2,
                             relief='ridge',
                             height=200,
                             width=200)
        frame22.grid(row=0, column=2, sticky='NW', padx="5", pady="5")
        frame23 = LabelFrame(frame2,
                             text=' Dérivées ',
                             borderwidth=2,
                             relief='ridge',
                             height=200,
                             width=200)
        frame23.grid(row=0, column=3, sticky='NW', padx="5", pady="5")
        self.Entry_C = []

        # Colonne 0 de taille à Dextérité
        for i in range(0, 6):
            self.Entry_C.append(IntVar())
            Label(frame20, text="           "+personnage.caracteristique(i, 1)+':')\
                .grid(row=i, column=0, sticky='E')
            Entry(frame20, textvariable=self.Entry_C[i], justify='right', width=3)\
                .grid(row=i, column=1, sticky='W', padx="5")
        Label(frame20, text=' ').grid(row=6, column=0, sticky='E')

        # Colonne 1 de Vue à Empathie
        for i in range(6, 12):
            self.Entry_C.append(IntVar())
            Label(frame21, text="           "+personnage.caracteristique(i, 1) + ':')\
                .grid(row=i-6, column=0, sticky='E')
            Entry(frame21, textvariable=self.Entry_C[i], justify='right', width=3)\
                .grid(row=i-6, column=1, sticky='W', padx="5")
        Label(frame21, text=' ').grid(row=6, column=0, sticky='E')

        # Colonne 2 de Rêve à Chance
        for i in range(12, 14):
            self.Entry_C.append(IntVar())
            Label(frame22, text="               "+personnage.caracteristique(i, 1) + ':')\
                .grid(row=i-12, column=0, sticky='E')
            Entry(frame22, textvariable=self.Entry_C[i], justify='right', width=3)\
                .grid(row=i-12, column=1, sticky='W', padx="5")
        for i in range(2, 7):
            Label(frame22, text=' ').grid(row=i, column=0, sticky='E')

        # Colonne 3 de Tir à Dérobée (ne peuvent être saisies)
        for i in range(14, 18):
            self.Entry_C.append(IntVar())
            Label(frame23, text="             "+personnage.caracteristique(i, 1) + ':')\
                .grid(row=i-14, column=0, sticky='E')
            Entry(frame23, textvariable=self.Entry_C[i], justify='right', width=3, state='disabled')\
                .grid(row=i-14, column=1, sticky='W', padx="5")
        for i in range(4, 7):
            Label(frame23, text=' ').grid(row=i, column=0, sticky='E')

        # frame 3 : Points et Seuils (ne peuvent être saisis)
        frame3 = Frame(root,
                       borderwidth=0,
                       relief='flat',
                       height=200,
                       width=600,
                       padx="5",
                       pady="5")
        frame3.grid(row=2, column=0, sticky='NW', padx="10")
        self.Entry_P = []

        # Vie - Endurance - Encombrement
        for i in range(0, 3):
            self.Entry_P.append(IntVar())
            Label(frame3, text=personnage.point(i, 1) + ':').grid(row=0,
                                                                  column=2 * i,
                                                                  sticky='E')
            Entry(frame3, textvariable=self.Entry_P[i], justify='right', width=3, state='disabled')\
                .grid(row=0, column=2*i+1, sticky='W', padx="5")

        # Bonus aux Dommages - Malus Armure - Seuil de Constitution - Seuil de Sustentation
        for i in range(3, 7):
            self.Entry_P.append(IntVar())
            Label(frame3,
                  text=personnage.point(i, 1) + ':').grid(row=1,
                                                          column=2 * i - 6,
                                                          sticky='E')
            Entry(frame3, textvariable=self.Entry_P[i], justify='right', width=3, state='disabled')\
                .grid(row=1, column=2*i-5, sticky='W', padx="5")

        # frame 4 : Compétences
        frame4 = LabelFrame(root,
                            text=" Compétences ",
                            borderwidth=2,
                            relief='ridge',
                            height=200,
                            width=800)
        frame4.grid(row=3,
                    column=0,
                    columnspan=2,
                    sticky='NW',
                    padx="10",
                    pady="5")
        frame40 = LabelFrame(frame4,
                             text=' Générales ',
                             borderwidth=2,
                             relief='ridge',
                             height=200,
                             width=300)
        frame40.grid(row=0,
                     column=0,
                     rowspan=2,
                     sticky='NW',
                     padx="5",
                     pady="5")
        frame41 = LabelFrame(frame4,
                             text=' Particulières ',
                             borderwidth=2,
                             relief='ridge',
                             height=200,
                             width=300)
        frame41.grid(row=0,
                     column=1,
                     rowspan=2,
                     sticky='NW',
                     padx="5",
                     pady="5")
        frame42 = LabelFrame(frame4,
                             text=' Spécialisées ',
                             borderwidth=2,
                             relief='ridge',
                             height=200,
                             width=300)
        frame42.grid(row=0,
                     column=2,
                     rowspan=2,
                     sticky='NW',
                     padx="5",
                     pady="5")
        frame43 = LabelFrame(frame4,
                             text=' Connaissances ',
                             borderwidth=2,
                             relief='ridge',
                             height=200,
                             width=300)
        frame43.grid(row=0, column=3, sticky='NW', padx="5", pady="5")
        frame44 = LabelFrame(frame4,
                             text=' Draconic ',
                             borderwidth=2,
                             relief='ridge',
                             height=200,
                             width=300)
        frame44.grid(row=1, column=3, sticky='SW', padx="5", pady="5")
        frame45 = LabelFrame(frame4,
                             text=' Combat Mélée ',
                             borderwidth=2,
                             relief='ridge',
                             height=200,
                             width=300)
        frame45.grid(row=0,
                     column=4,
                     rowspan=2,
                     sticky='NW',
                     padx="5",
                     pady="5")
        frame46 = LabelFrame(frame4,
                             text=' Combat Tir-Lancer ',
                             borderwidth=2,
                             relief='ridge',
                             height=200,
                             width=300)
        frame46.grid(row=0,
                     column=5,
                     rowspan=2,
                     sticky='NW',
                     padx="5",
                     pady="5")
        self.Entry_A = []

        # Colonne 0 : Générales
        for i in range(0, 11):
            self.Entry_A.append(IntVar())
            Label(frame40,
                  text="           " + personnage.competence(i, 2) + ':').grid(
                      row=i, column=0, sticky='E')
            Entry(frame40, textvariable=self.Entry_A[i], justify='right', width=3)\
                .grid(row=i, column=1, sticky='W', padx="5")
        for i in range(11, 15):
            Label(frame40, text=' ').grid(row=i, column=0, sticky='E')

        # Colonne 1 : Particulières
        for i in range(11, 26):
            self.Entry_A.append(IntVar())
            Label(frame41, text="      " + personnage.competence(i, 2) +
                  ':').grid(row=i - 11, column=0, sticky='E')
            Entry(frame41, textvariable=self.Entry_A[i], justify='right', width=3)\
                .grid(row=i-11, column=1, sticky='W', padx="5")

        # Colonne 2 : Spécialisées
        for i in range(26, 36):
            self.Entry_A.append(IntVar())
            Label(frame42, text="       "+personnage.competence(i, 2)+':')\
                .grid(row=i-25, column=0, sticky='E')
            Entry(frame42, textvariable=self.Entry_A[i], justify='right', width=3)\
                .grid(row=i-25, column=1, sticky='W', padx="5")
        for i in range(10, 15):
            Label(frame42, text=' ').grid(row=i + 1, column=0, sticky='E')

        # Colonne 3: Connaissances
        for i in range(36, 43):
            self.Entry_A.append(IntVar())
            Label(frame43, text="         "+personnage.competence(i, 2)+':')\
                .grid(row=i-35, column=0, sticky='E')
            Entry(frame43, textvariable=self.Entry_A[i], justify='right', width=3)\
                .grid(row=i-35, column=1, sticky='W', padx="5")
        Label(frame43, text=' ').grid(row=8, column=0, sticky='E')

        # Colonne 3 : Draconic
        self.Draconic = []
        for i in range(0, 4):
            self.Entry_A.append(IntVar())
            Label(frame44, text="             "+personnage.competence(i+43, 2)+':')\
                .grid(row=i, column=0, sticky='E')
            self.Draconic.append(
                Entry(frame44,
                      textvariable=self.Entry_A[i + 43],
                      justify='right',
                      width=3))
            self.Draconic[i].grid(row=i, column=1, sticky='W', padx="5")
        Label(frame44, text=' ').grid(row=4, column=0, sticky='E')

        # Colonne 4 : Combat Mélée
        for i in range(47, 60):
            self.Entry_A.append(IntVar())
            Label(frame45, text=personnage.competence(i, 2) + ':') \
                .grid(row=i - 46, column=0, sticky='E')
            Entry(frame45, textvariable=self.Entry_A[i], justify='right', width=3) \
                .grid(row=i - 46, column=1, sticky='W', padx="5")
        for i in range(13, 15):
            Label(frame45, text=' ').grid(row=i + 1, column=0, sticky='E')

        # Colonne 5 : Combat Tir
        for i in range(60, 66):
            self.Entry_A.append(IntVar())
            Label(frame46, text="         " + personnage.competence(i, 2) + ':') \
                .grid(row=i - 59, column=0, sticky='E')
            Entry(frame46, textvariable=self.Entry_A[i], justify='right', width=3) \
                .grid(row=i - 59, column=1, sticky='W', padx="5")
        for i in range(6, 15):
            Label(frame46, text=' ').grid(row=i + 1, column=0, sticky='E')

        # frame5 : table de résolution et lancer de dé
        frame5 = LabelFrame(root,
                            text=" Résolution et Lancer de Dés ",
                            borderwidth=2,
                            relief='ridge',
                            height=200,
                            width=600)
        frame5.grid(row=0,
                    column=1,
                    rowspan=3,
                    columnspan=2,
                    sticky='NW',
                    padx="10",
                    pady="5")

        # Listbox caractéristiques
        Label(frame5, text='         Caractéristique:').grid(row=0,
                                                             column=0,
                                                             columnspan=2,
                                                             padx="10",
                                                             sticky='NW')
        self.liste1 = Listbox(frame5, height=13, width=18, relief='sunken')
        self.liste1.grid(row=1, column=1, sticky='NW', pady="5")
        for i in range(0, 18):
            self.liste1.insert(i, personnage.caracteristique(i, 1))
        self.liste1.bind('<<ListboxSelect>>', self.sel_liste1)

        # Listbox compétences
        Label(frame5, text='Compétence:').grid(row=0,
                                               column=2,
                                               columnspan=2,
                                               padx="10",
                                               sticky='NW')
        self.liste2 = Listbox(frame5, height=13, width=18, relief='sunken')
        self.liste2.grid(row=1, column=3, sticky='NW', pady="5")
        for i in range(0, 66):
            self.liste2.insert(i, personnage.competence(i, 2))
        self.liste2.bind('<<ListboxSelect>>', self.sel_liste2)

        # Zone de résulats
        self.Entry_R_C_Val = IntVar()
        Entry(frame5, textvariable=self.Entry_R_C_Val, justify='right', width=3,) \
            .grid(row=16, column=0, sticky='E', padx="10")
        self.Entry_R_C_Name = StringVar()
        Entry(frame5, textvariable=self.Entry_R_C_Name, justify='left', width=18, state='disabled') \
            .grid(row=16, column=1, sticky='W')
        self.Entry_R_A_Val = IntVar()
        Entry(frame5, textvariable=self.Entry_R_A_Val, justify='right', width=3,) \
            .grid(row=16, column=2, sticky='E', padx="10")
        self.Entry_R_A_Name = StringVar()
        Entry(frame5, textvariable=self.Entry_R_A_Name, justify='left', width=18, state='disabled') \
            .grid(row=16, column=3, sticky='W')
        Label(frame5, text='   Seuil de Réussite:').grid(row=17,
                                                         column=0,
                                                         sticky='NE')
        self.Entry_R_Seuil = IntVar()
        Entry(frame5, textvariable=self.Entry_R_Seuil, justify='right', width=3, state='disabled')\
            .grid(row=17, column=1, sticky='W', padx="10")
        Label(frame5, text='Tirage:').grid(row=17, column=2, sticky='NE')
        self.Entry_R_Tirage = IntVar()
        Entry(frame5, textvariable=self.Entry_R_Tirage, justify='right', width=3, state='disabled') \
            .grid(row=17, column=3, sticky='W', padx="10")
        Label(frame5, text='Résultat Spécial:').grid(row=18,
                                                     column=0,
                                                     sticky='NE')
        self.Entry_R_Special = StringVar()
        Entry(frame5, textvariable=self.Entry_R_Special, justify='left', width=30, state='disabled') \
            .grid(row=18, column=1, columnspan=2, sticky='W', padx="10")
        Label(frame5, text=' ').grid(row=19, column=4, sticky='NE')

        # Bouton pour le lancer de Dés
        Button(frame5, text="Lancer les Dés", command=self.lancer) \
            .grid(row=18, column=3, columnspan=3, sticky='W', padx="10")

        # La mascote
        # On la fait déborder sur le frame4 pour gagner en largeur totale
        self.dragon = PhotoImage(file='./dragon3.gif')
        logo = Canvas(root, width=200, height=181, bd=1, relief='ridge')
        logo.grid(row=3,
                  column=1,
                  columnspan=2,
                  sticky='SE',
                  padx="10",
                  pady="3")
        logo.create_image(0, 0, image=self.dragon, anchor='nw')

        # L'ecran étant initialisé, on peut créér un premier personnage par défaut
        self.creer()
        return

    # Fonction de recopie de la sélection depuis la Listbox des caractéristiques
    # Met à jour les 2 champs points et nom de caractéristique pour le calcul de résolution
    def sel_liste1(self, event):

        if self.liste1.curselection() != ():
            index = self.liste1.curselection()[0]
            self.Entry_R_C_Name.set(self.liste1.get(index))
            self.Entry_R_C_Val.set(self.Entry_C[index].get())
        return

    # Fonction de recopie de la sélection depuis la Listbox des compétences
    # Met à jour les 2 champs points et nom de compétence pour le calcul de résolution
    def sel_liste2(self, event):

        if self.liste2.curselection() != ():
            index = self.liste2.curselection()[0]
            self.Entry_R_A_Name.set(self.liste2.get(index))
            self.Entry_R_A_Val.set(self.Entry_A[index].get())
        return

    # Fonction de changement d'etat haut-rêvant
    def sel_revant(self):

        if self.Entry_HRevant.get() != 1:
            for i in range(0, 4):
                self.Entry_A[i + 42].set(-11)
                self.Draconic[i].configure(state='disabled')
        else:
            for i in range(0, 4):
                self.Draconic[i].configure(state='normal')
        return

    # Nouveau jeu
    # Il faut préalablement fermer le jeu en cours
    def nouveau(self):

        if self.fermer():
            self.jeu.nouveau()
        return

    # Ouvrir jeu
    # Il faut préalablement fermer le jeu en cours
    # On reçoit le nom du jeu suivi d'une liste de personnages ou None si rien d'ouvert par le jeu
    def ouvrir(self):

        if self.fermer():
            names = self.jeu.ouvrir()
            if names != None:
                numero = -1
                for person in names:

                    # index 0 : nom du fichier jeu
                    if numero < 0:
                        self.root.title('Rêve de Dragon - ' + person)

                    # autres index : personnages
                    # index vaudra le nombre de personnages reçus
                    else:
                        self.viewmenu.add_command(label=person,
                                                  command=lambda index=numero:
                                                  self.selectionner(index))
                    numero += 1

                # On affiche le premier personnage
                if numero > 0:
                    self.selectionner(0)

        return

    # Fermer le jeu en cours
    # On efface tous les personnages
    # on cree un nouveau personnage vide pour obtenir un affichage vierge
    def fermer(self):

        if not self.saved:
            self.saved = askyesno(
                'Fermer',
                'Voulez-vous vraiment fermer ce Jeu ?\nLes données non enregistrées seront perdues'
            )
        if self.saved:
            last = self.viewmenu.index("end")
            if last is not None:
                for i in range(last + 1):
                    self.viewmenu.delete(0)
            self.root.title('Rêve de Dragon')
            self.jeu.fermer()
            self.creer()
        return self.saved

    # Quitter le programme
    # onh détruit la fenêtre et on quitte
    def quitter(self):

        if askyesno('Quitter', 'Voulez-vous vraiment quitter le programme ?'):
            self.root.destroy()
            self.root.quit()
        return

    # Fonction interne d'affichage des données d'un personnage
    # Copie toutes les données du dictionnaire local dans les variables associées aux champs de saisie
    def affiche(self):

        self.Entry_Nom.set(self.pod["Fiche"]["Nom"])
        self.Entry_Age.set(self.pod["Fiche"]["Age"])
        self.Entry_Heure.set(self.pod["Fiche"]["Heure_Naissance"])
        self.Entry_Taille.set(self.pod["Fiche"]["Taille"])
        self.Entry_Poids.set(self.pod["Fiche"]["Poids"])
        self.Entry_Sexe.set(self.pod["Fiche"]["Sexe"])
        self.Entry_Cheveux.set(self.pod["Fiche"]["Cheveux"])
        self.Entry_Yeux.set(self.pod["Fiche"]["Yeux"])
        self.Entry_Beaute.set(self.pod["Fiche"]["Beaute"])
        self.Entry_Ambidextre.set(self.pod["Fiche"]["Ambidextre"])
        self.Entry_HRevant.set(self.pod["Fiche"]["Haut_Revant"])
        self.Entry_SignesP.set(self.pod["Fiche"]["Signes_Particulier"])
        for i in range(0, 18):
            self.Entry_C[i].set(
                self.pod["Caracteristique"][personnage.caracteristique(i, 0)])
        for i in range(0, 7):
            self.Entry_P[i].set(self.pod["Point"][personnage.point(i, 0)])
        for i in range(0, 66):
            self.Entry_A[i].set(self.pod["Competence"][personnage.competence(
                i, 0)][personnage.competence(i, 1)])
        if self.Entry_HRevant.get() != 1:
            for i in range(0, 4):
                self.Draconic[i].configure(state='disabled')
        return

    # Création d'un nouveau personnage
    # On demande au jeu de créer un nouveau personnage dans la liste
    # On initialise toutes les variables de saisie aux valeur reçues
    def creer(self):

        self.pod = self.jeu.creer()
        self.affiche()
        return

    # Validation des données du personnage
    # On reconstitue le dictionnaire qui est envoyé au jeu pour vérification
    # Le jeu répond avec un dictionnaire contenant
    # - l'index du personnage
    # - Le nom de personnage
    # - Un message d'erreur ou d'acceptation
    def valider(self):

        if len(self.Entry_Nom.get()) < 1:
            return

        self.pod["Fiche"]["Nom"] = self.Entry_Nom.get()
        self.pod["Fiche"]["Age"] = self.Entry_Age.get()
        self.pod["Fiche"]["Heure_Naissance"] = self.Entry_Heure.get()
        self.pod["Fiche"]["Taille"] = self.Entry_Taille.get()
        self.pod["Fiche"]["Poids"] = self.Entry_Poids.get()
        self.pod["Fiche"]["Sexe"] = self.Entry_Sexe.get()
        self.pod["Fiche"]["Cheveux"] = self.Entry_Cheveux.get()
        self.pod["Fiche"]["Yeux"] = self.Entry_Yeux.get()
        self.pod["Fiche"]["Beaute"] = self.Entry_Beaute.get()
        self.pod["Fiche"]["Ambidextre"] = self.Entry_Ambidextre.get()
        self.pod["Fiche"]["Haut_Revant"] = self.Entry_HRevant.get()
        self.pod["Fiche"]["Signes_Particulier"] = self.Entry_SignesP.get()
        for i in range(0, 18):
            self.pod["Caracteristique"][personnage.caracteristique(
                i, 0)] = self.Entry_C[i].get()
        for i in range(0, 7):
            self.pod["Point"][personnage.point(i, 0)] = self.Entry_P[i].get()
        for i in range(0, 65):
            self.pod["Competence"][personnage.competence(
                i, 0)][personnage.competence(i, 1)] = self.Entry_A[i].get()
        retour = self.jeu.valider(self.pod)
        index = retour["index"]

        # On a bien un index valide : alors on met à jour le menu et on va chercher les données du personnage
        if index is not None:
            if self.viewmenu.entrycget(index, 'label') != self.Old_Nom:
                self.viewmenu.entryconfigure(index, label=retour["nom"])
            elif self.viewmenu.entrycget(index, 'label') != retour["nom"]:
                self.viewmenu.add_command(
                    label=retour["nom"],
                    command=lambda index=index: self.selectionner(index))
            self.pod = self.jeu.selectionner(index)
            self.affiche()

        # En cas d'erreur ou retour ok, il y a un message du jeu
        if len(retour["message"]):
            showerror("Validation", retour["message"])

        self.saved = False
        return

    # Sélection d'un personnage depuis le menu
    # On envoie au jeu l'index du menu qui correspond à l'index de la liste du jeu
    # Les données du personnage reçu sont ensuite affichées
    def selectionner(self, code):

        self.pod = self.jeu.selectionner(code)
        self.affiche()
        return

    # Nouvelle partie
    # On demande au Jeu de changer de partie
    # Le jeu renvoie le contenu du personnage courant
    def partie(self):

        if askyesno(
                'Nouvelle Partie',
                'Voulez-vous vraiment terminer cette partie ?\nLes données non enregistrées seront perdues'
        ):
            self.pod = self.jeu.partie()
            self.affiche()
        return

    # Lancer de dé
    # Calcul de résolution puis lancer des dés
    # On passe au jeu les valeurs de caractéristiques et compétences sélectionnées
    # Le résultat sera récupéré en retour et affiché
    def lancer(self):

        resultat = self.jeu.lancer(self.Entry_R_C_Val.get(),
                                   self.Entry_R_A_Val.get())
        self.Entry_R_Seuil.set(resultat["seuil"])
        self.Entry_R_Tirage.set(resultat["tirage"])
        self.Entry_R_Special.set(resultat["special"])
        self.saved = False
        return

    # Affichage de la boite de dialogue A propos
    # Le texte est dans le fichier A_PROPOS.TXT
    def a_propos(self):

        fp = open("A_PROPOS.TXT", "r")
        texte_a_propos = fp.read()
        fp.close()
        showinfo("A Propos de...", texte_a_propos)
        return

    # Dialogue d'aide pour connaitre les règles du jeu
    # Le texte est dans le fichier REGLES.TXT
    def regles(self):

        file = "REGLE.TXT"
        titre = "Règles du Jeu Rêve de Dragon."
        self.aide(file, titre)
        return

    # Le texte est dans le fichier AIDE.TXT
    def utilise(self):

        file = "AIDE.TXT"
        titre = "Utilisation de Rêve de Dragon."
        self.aide(file, titre)
        return

    # Aide du jeu
    # Affiche une boite de dialogue avec un widget texte contenant l'aide
    def aide(self, file, titre):

        # On ouvre une fenêtre fille de celle du jeu
        self.wdw = Toplevel()
        self.wdw.geometry('+400+100')
        self.wdw.title(titre)

        # Le texte de l'aide est stocké dans un fichier Atexte
        fp = open(file, "r")
        texte_aide = fp.read()
        fp.close()

        # On l'affiche dans un widget Text avec une barre de défilement
        # Ne fonctionne que si on utilise grid pour placer les Widgets
        # Il faut mettre le widget en état disabled pour éviter que l'on y entre du texte
        self.S = Scrollbar(self.wdw, orient='vertical')
        self.T = Text(self.wdw,
                      height=50,
                      width=100,
                      font=('TkDefaultFont', 10))
        self.T.grid(row=0, column=0, sticky='NW')
        self.S.configure(command=self.T.yview)
        self.T.configure(yscrollcommand=self.S.set)
        self.S.grid(row=0, column=1, sticky='SN')
        self.T.configure(state='normal')
        self.T.insert('end', texte_aide)
        self.T.configure(state='disabled')

        # La nouvelle fenêtre est ouverte en modal
        # Il faudra la fermer pour reprendre le contrôle de la fenêtre principale
        self.wdw.transient(self.root)
        self.wdw.grab_set()
        self.root.wait_window(self.wdw)
        return

    # fonction qui ne fait rien (pour les tests)
    def void(self):
        return
Ejemplo n.º 5
0
class Example(Frame):
    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):

        self.conn = sqlite3.connect('tatardict.db')  # Подключаемся к базе
        self.curdict = self.conn.cursor()

        self.master.title('Татарский язык')
        self.menubar = Menu(self.master)
        self.master.config(menu=self.menubar)
        self.fileMenu = Menu(self.menubar)
        self.nfm = Menu(self.menubar)
        self.tfm = Menu(self.menubar)
        self.fileMenu.add_cascade(label="Выбор темы", menu=self.tfm)
        self.curdict.execute('SELECT sub_them, in_them FROM themes')
        th = self.curdict.fetchall()

        self.choice_them = StringVar()
        for text, mode in th:
            self.tfm.add_radiobutton(label=text,
                                     value=mode,
                                     variable=self.choice_them,
                                     command=self.choise_theme)
        self.choice_them.set(2)

        self.fileMenu.add_command(label="Выход", command=self.onExit)
        self.menubar.add_cascade(label="≡", menu=self.fileMenu)

        self.them = Label(self.master,
                          text='Тема: ' +
                          self.tfm.entrycget(self.choice_them.get(), 'label'),
                          bd=1,
                          relief='flat',
                          anchor='w')
        self.them.grid(row=1, column=0, columnspan=2)
        self.question = Label(self.master,
                              text=' ',
                              font=('BOLD', 12),
                              fg='red')
        self.question.grid(row=2, column=0, columnspan=4)

        ttk.Separator(self.master, orient='horizontal').grid(row=3,
                                                             column=0,
                                                             columnspan=2,
                                                             sticky='ew')
        answer_width = 15  # Ширина кнопок ответа

        self.v = StringVar()  # Выбранный ответ радиокнопкой
        self.var = StringVar()  # Строка состояния 'предыдущий ответ
        self.var1 = StringVar()  # Подсчет количества выполненных ответов
        self.var1.set('0/0')
        self.rightanswer = 0
        self.allanswer = 0

        self.b1 = Radiobutton(self.master,
                              text='',
                              variable=self.v,
                              value='',
                              indicatoron=0,
                              width=answer_width)
        self.b2 = Radiobutton(self.master,
                              text='',
                              variable=self.v,
                              value='',
                              indicatoron=0,
                              width=answer_width)
        self.b3 = Radiobutton(self.master,
                              text='',
                              variable=self.v,
                              value='',
                              indicatoron=0,
                              width=answer_width)
        self.b4 = Radiobutton(self.master,
                              text='',
                              variable=self.v,
                              value='',
                              indicatoron=0,
                              width=answer_width)
        self.b5 = Radiobutton(self.master,
                              text='',
                              variable=self.v,
                              value='',
                              indicatoron=0,
                              width=answer_width)
        self.b1.grid(row=4, column=0, columnspan=4)
        self.b2.grid(row=5, column=0, columnspan=4)
        self.b3.grid(row=6, column=0, columnspan=4)
        self.b4.grid(row=7, column=0, columnspan=4)
        self.b5.grid(row=8, column=0, columnspan=4)

        self.NextButton = Button(self.master,
                                 text='Дальше',
                                 command=self.next_question).grid(row=9,
                                                                  column=1,
                                                                  columnspan=3,
                                                                  sticky='e')

        self.status = Label(self.master,
                            text='Предыдущий ответ: ',
                            bd=1,
                            relief='sunken',
                            width=18,
                            anchor='w').grid(row=10, column=0)
        self.status1 = Label(self.master,
                             text='Не было!',
                             textvariable=self.var,
                             bd=1,
                             relief='sunken',
                             width=10,
                             anchor='w').grid(row=10, column=1)
        self.status2 = Label(self.master,
                             text=' Количество вопросов: ',
                             bd=1,
                             relief='sunken',
                             width=18,
                             anchor='e').grid(row=11, column=0, sticky='w')
        self.status3 = Label(self.master,
                             textvariable=self.var1,
                             bd=1,
                             relief='sunken',
                             width=10,
                             anchor='w').grid(row=11, column=1)
        self.choise_theme()

    # Определение правильности ответа, переход на следующий вопрос
    def next_question(self):
        if self.question['text'] == self.v.get():
            self.var.set('правильный')
            self.rightanswer += 1
        else:
            self.var.set('не верный')
        self.allanswer += 1
        self.var1.set(str(self.allanswer) + '/' + str(self.rightanswer))
        self.create_question()

    # Выбор темы
    def choise_theme(self):
        choices_them = self.tfm.entrycget(self.choice_them.get(), 'label')
        self.them["text"] = 'Тема: ' + choices_them
        dbselect = 'SELECT tatar, orys FROM dictionary where in_themes = '
        self.curdict.execute(dbselect + self.choice_them.get())
        self.f = self.curdict.fetchall()
        self.create_question()

    # Создаем вопрос
    def create_question(self):
        self.ff = random.choice(self.f)
        # Предотвращаем повторяемость вопросов
        if 'oldword' in locals():
            while self.ff == oldword:
                self.ff = random.choice(self.f)
        self.f.remove(self.ff)
        self.fff = random.sample(self.f, 4)
        self.fff.append(self.ff)
        random.shuffle(self.fff)
        self.question['text'] = self.ff[1]
        self.result = self.ff[0]
        self.b1["text"] = self.fff[0][0]
        self.b1["value"] = self.fff[0][1]
        self.b2["text"] = self.fff[1][0]
        self.b2["value"] = self.fff[1][1]
        self.b3["text"] = self.fff[2][0]
        self.b3["value"] = self.fff[2][1]
        self.b4["text"] = self.fff[3][0]
        self.b4["value"] = self.fff[3][1]
        self.b5["text"] = self.fff[4][0]
        self.b5["value"] = self.fff[4][1]
        self.f.append(self.ff)
        oldword = self.ff  # Сохраняем предыдущий ответ

    def onExit(self):
        self.master.destroy()