Exemple #1
0
class _List(WidgetBase):
    def __init__(self, master, **options):
        super().__init__(master)
        from tkinter.ttk import Treeview
        self.widget = Treeview(self.master, show="tree", **options)
        self.widget.bind("<<TreeviewSelect>>", self.callback)
        self.value = ()

    def set_header(self, column, text):
        self.widget.heading("#" + str(column), text=text)

    def add_item(self,
                 parent="",
                 index=0,
                 id=None,
                 label="",
                 values=None,
                 **options):
        if not id is None:
            options.update(iid=id)
        if not values is None:
            options.update(values=values)
        self.widget.insert(parent=parent, index=index, text=label, **options)

    def callback(self, event):
        self.value = self.widget.selection()

    def set_selection(self, items):
        self.value = items
        self.widget.selection_set(items)

    def exist_item(self, id):
        return self.widget.exists(id)
Exemple #2
0
class DialogOpenArchive(Toplevel):
    def __init__(self, mainWin, openType, filesource, filenames, title, colHeader, showAltViewButton=False):
        parent = mainWin.parent
        super(DialogOpenArchive, self).__init__(parent)
        self.parent = parent
        self.showAltViewButton = showAltViewButton
        parentGeometry = re.match("(\d+)x(\d+)[+]?([-]?\d+)[+]?([-]?\d+)", parent.geometry())
        dialogX = int(parentGeometry.group(3))
        dialogY = int(parentGeometry.group(4))
        self.accepted = False

        self.transient(self.parent)
        
        frame = Frame(self)

        treeFrame = Frame(frame, width=500)
        vScrollbar = Scrollbar(treeFrame, orient=VERTICAL)
        hScrollbar = Scrollbar(treeFrame, orient=HORIZONTAL)
        self.treeView = Treeview(treeFrame, xscrollcommand=hScrollbar.set, yscrollcommand=vScrollbar.set)
        self.treeView.grid(row=0, column=0, sticky=(N, S, E, W))
        hScrollbar["command"] = self.treeView.xview
        hScrollbar.grid(row=1, column=0, sticky=(E,W))
        vScrollbar["command"] = self.treeView.yview
        vScrollbar.grid(row=0, column=1, sticky=(N,S))
        treeFrame.columnconfigure(0, weight=1)
        treeFrame.rowconfigure(0, weight=1)
        treeFrame.grid(row=0, column=0, columnspan=4, sticky=(N, S, E, W), padx=3, pady=3)
        self.treeView.focus_set()
        
        mainWin.showStatus(_("loading archive {0}").format(filesource.url))
        self.filesource = filesource
        self.filenames = filenames
        self.selection = filesource.selection
        self.hasToolTip = False
        selectedNode = None

        if openType == ENTRY_POINTS:
            try:
                metadataFiles = filesource.taxonomyPackageMetadataFiles
                if len(metadataFiles) > 1:
                    raise IOError(_("Taxonomy package contained more than one metadata file: {0}.")
                                  .format(', '.join(metadataFiles)))
                metadataFile = metadataFiles[0]
                metadata = filesource.file(filesource.url + os.sep + metadataFile)[0]
                self.metadataFilePrefix = os.sep.join(os.path.split(metadataFile)[:-1])
                if self.metadataFilePrefix:
                    self.metadataFilePrefix += os.sep
        
                self.nameToUrls, self.remappings = parseTxmyPkg(mainWin, metadata)
            except Exception as e:
                self.close()
                err = _("Failed to parse metadata; the underlying error was: {0}").format(e)
                messagebox.showerror(_("Malformed taxonomy package"), err)
                mainWin.addToLog(err)
                return
    
        mainWin.showStatus(None)
        
        if openType == DISCLOSURE_SYSTEM:
            y = 3
        else:
            y = 1

        okButton = Button(frame, text=_("OK"), command=self.ok)
        cancelButton = Button(frame, text=_("Cancel"), command=self.close)
        okButton.grid(row=y, column=2, sticky=(S,E,W), pady=3)
        cancelButton.grid(row=y, column=3, sticky=(S,E,W), pady=3, padx=3)
        
        if showAltViewButton:
            self.altViewButton = Button(frame, command=self.showAltView)
            self.altViewButton.grid(row=y, column=0, sticky=(S,W), pady=3, padx=3)
        
        self.loadTreeView(openType, colHeader, title)

        frame.grid(row=0, column=0, sticky=(N,S,E,W))
        frame.columnconfigure(0, weight=1)
        window = self.winfo_toplevel()
        window.columnconfigure(0, weight=1)
        self.geometry("+{0}+{1}".format(dialogX+50,dialogY+100))
        
        self.bind("<Return>", self.ok)
        self.bind("<Escape>", self.close)
        
        self.toolTipText = StringVar()
        if self.hasToolTip:
            self.treeView.bind("<Motion>", self.motion, '+')
            self.treeView.bind("<Leave>", self.leave, '+')
            self.toolTipText = StringVar()
            self.toolTip = ToolTip(self.treeView, 
                                   textvariable=self.toolTipText, 
                                   wraplength=640, 
                                   follow_mouse=True,
                                   state="disabled")
            self.toolTipRowId = None

        self.protocol("WM_DELETE_WINDOW", self.close)
        self.grab_set()
        self.wait_window(self)
        
    def loadTreeView(self, openType, title, colHeader):
        self.title(title)
        self.openType = openType
        selectedNode = None

        # clear previous treeview entries
        for previousNode in self.treeView.get_children(""): 
            self.treeView.delete(previousNode)

        # set up treeView widget and tabbed pane
        if openType in (ARCHIVE, DISCLOSURE_SYSTEM):
            self.treeView.column("#0", width=500, anchor="w")
            self.treeView.heading("#0", text=colHeader)
            try:
                self.isRss = self.filesource.isRss
                if self.isRss:
                    self.treeView.column("#0", width=350, anchor="w")
                    self.treeView["columns"] = ("descr", "date", "instDoc")
                    self.treeView.column("descr", width=50, anchor="center", stretch=False)
                    self.treeView.heading("descr", text="Form")
                    self.treeView.column("date", width=170, anchor="w", stretch=False)
                    self.treeView.heading("date", text="Pub Date")
                    self.treeView.column("instDoc", width=200, anchor="w", stretch=False)
                    self.treeView.heading("instDoc", text="Instance Document")
            except AttributeError:
                self.isRss = False
                self.treeView["columns"] = tuple()
        
            loadedPaths = []
            for i, filename in enumerate(self.filenames):
                if isinstance(filename,tuple):
                    if self.isRss:
                        form, date, instDoc = filename[2:5]
                    filename = filename[0] # ignore tooltip
                    self.hasToolTip = True
                if filename.endswith("/"):
                    filename = filename[:-1]
                path = filename.split("/")
                if not self.isRss and len(path) > 1 and path[:-1] in loadedPaths:
                    parent = "file{0}".format(loadedPaths.index(path[:-1]))
                else:
                    parent = "" 
                node = self.treeView.insert(parent, "end", "file{0}".format(i), text=path[-1])
                if self.isRss:
                    self.treeView.set(node, "descr", form)
                    self.treeView.set(node, "date", date)
                    self.treeView.set(node, "instDoc", os.path.basename(instDoc))
                if self.selection == filename:
                    selectedNode = node
                loadedPaths.append(path)

        elif openType == ENTRY_POINTS:
            self.treeView.column("#0", width=150, anchor="w")
            self.treeView.heading("#0", text="Name")
    
            self.treeView["columns"] = ("url",)
            self.treeView.column("url", width=350, anchor="w")
            self.treeView.heading("url", text="URL")
            
            for name, urls in self.nameToUrls.items():
                displayUrl = urls[1] # display the canonical URL
                self.treeView.insert("", "end", name, values=[displayUrl], text=name)
                
            self.hasToolTip = True
        else: # unknown openType
            return None
        if selectedNode:
            self.treeView.see(selectedNode)
            self.treeView.selection_set(selectedNode)

        if self.showAltViewButton:
            self.altViewButton.config(text=_("Show Files") if openType == ENTRY_POINTS else _("Show Entries"))

        
    def ok(self, event=None):
        selection = self.treeView.selection()
        if len(selection) > 0:
            if self.openType in (ARCHIVE, DISCLOSURE_SYSTEM):
                filename = self.filenames[int(selection[0][4:])]
                if isinstance(filename,tuple):
                    if self.isRss:
                        filename = filename[4]
                    else:
                        filename = filename[0]
                if not filename.endswith("/"):
                    self.filesource.select(filename)
                    self.accepted = True
                    self.close()
            elif self.openType == ENTRY_POINTS:
                epName = selection[0]
                #index 0 is the remapped Url, as opposed to the canonical one used for display
                urlOrFile = self.nameToUrls[epName][0]
                
                # load file source remappings
                self.filesource.mappedPaths = \
                    dict((prefix, 
                          remapping if isHttpUrl(remapping)
                          else (self.filesource.baseurl + os.sep + self.metadataFilePrefix +remapping.replace("/", os.sep)))
                          for prefix, remapping in self.remappings.items())
    
                if not urlOrFile.endswith("/"):
                    # check if it's an absolute URL rather than a path into the archive
                    if isHttpUrl(urlOrFile):
                        self.filesource.select(urlOrFile)  # absolute path selection
                    else:
                        # assume it's a path inside the archive:
                        self.filesource.select(self.metadataFilePrefix + urlOrFile)
                    self.accepted = True
                    self.close()
        
    def close(self, event=None):
        self.parent.focus_set()
        self.destroy()
        
    def showAltView(self, event=None):
        if self.openType == ENTRY_POINTS:
            self.loadTreeView(ARCHIVE, _("Select Entry Point"), _("File"))
        else:
            self.loadTreeView(ENTRY_POINTS, _("Select Archive File"), _("File"))
        
    def leave(self, *args):
        self.toolTipRowId = None

    def motion(self, *args):
        tvRowId = self.treeView.identify_row(args[0].y)
        if tvRowId != self.toolTipRowId:
            text = None
            if self.openType in (ARCHIVE, DISCLOSURE_SYSTEM):
                self.toolTipRowId = tvRowId
                if tvRowId and len(tvRowId) > 4:
                    try:
                        text = self.filenames[ int(tvRowId[4:]) ]
                        if isinstance(text, tuple):
                            text = text[1].replace("\\n","\n")
                    except (KeyError, ValueError):
                        pass
            elif self.openType == ENTRY_POINTS:
                try:
                    epUrl = self.nameToUrls[tvRowId][1]
                    text = "{0}\n{1}".format(tvRowId, epUrl)
                except KeyError:
                    pass
            self.setToolTip(text)
                
    def setToolTip(self, text):
        self.toolTip._hide()
        if text:
            self.toolTipText.set(text)
            self.toolTip.configure(state="normal")
            self.toolTip._schedule()
        else:
            self.toolTipText.set("")
            self.toolTip.configure(state="disabled")
Exemple #3
0
class Configurator(tk.Tk):
    """
    The main Tk window representing the main app.
    
    Attributes
    ---------- 
    treeview :  :py:class:`~tkinter.Treeview`
        The treeview widget.
    treeview_popup_target_id : `int`
        The pop target id relating to the id of the selected element.
    treeview_popup : :py:class:`~tkinter.Widget`
        The treeview popup widget.
    cfg_file_name : `str`
        The file name of the current configuration.
    element_dict : `dict`
        The dictionary of elements. Keys are the element ids.
    root_element : :py:class:`~enrich2.base.storemanager.StoreManager`
        An instance inheriting from storemanager that acts as a root object.
    force_recalculate :py:class:`tkinter.BooleanVar`
        The tkinter boolean variable for this option.
    component_outliers :py:class:`tkinter.BooleanVar`
        The tkinter boolean variable for this option.
    tsv_requested : :py:class:`tkinter.BooleanVar`
        The tkinter boolean variable for this option.
    treeview_buttons : `list`
        The ``new``, ``edit`` and ``delete`` buttons.
    go_button : :py:class`~tkinter.ttk.Button`
        The button that begins the analysis
    scorer_widget : :py:class:`~enrich2.gui.options_frame.ScorerScriptsDropDown`
        The ScorerScriptsDropDown instance associated with this app.
    scorer : :py:class:`~enrich2.plugins.scoring.BaseScorerPlugin`
        The scorer class loaded from a plugin
    scorer_attrs : `dict`
        The scoring attributes for the plugin.
    scorer_path : `str`
        The path to the currently selected scoring plugin.
    analysis_thread : :py:class:`~threading.Thread`
        The thread object that runs the computation method to prevent 
        GUI blocking.
    
    Methods
    -------
    create_main_frame
    create_menubar
    create_treeview_context_menu
    create_new_element
    
    menu_open
    menu_save
    menu_saveas
    menu_selectall
    
    refresh_treeview
    treeview_context_menu
    set_treeview_properties
    populate_tree
    go_button_press
    new_button_press
    edit_button_press
    delete_button_press
    delete_element
    apply_seqlib_fastq
    
    get_element
    get_focused_element
    get_selected_elements
    get_selected_scorer_class
    get_selected_scorer_attrs
    get_selected_scorer_path
    
    run_analysis
    set_gui_state
    configure_analysis
    
    refresh_plugins
    show_plugin_source_window

    See Also
    --------
    :py:class:`~tkinter.Tk`
    
    """
    def __init__(self):
        tk.Tk.__init__(self)
        self.title("Enrich 2")

        # Main app variables
        self.cfg_file_name = tk.StringVar()
        self.element_dict = dict()
        self.root_element = None
        self.analysis_thread = None
        self.plugin_source_window = None
        self.queue = queue.Queue()

        # Treeview variables
        self.treeview = None
        self.treeview_popup_target_id = None
        self.treeview_popup = None

        # analysis options
        self.force_recalculate = tk.BooleanVar()
        self.component_outliers = tk.BooleanVar()
        self.tsv_requested = tk.BooleanVar()

        # allow resizing
        self.rowconfigure(0, weight=1)
        self.columnconfigure(0, weight=1)

        # create UI elements
        self.treeview_buttons = []
        self.go_button = None
        self.scorer_widget = None
        self.scorer = None
        self.scorer_attrs = None
        self.scorer_path = None
        self.create_main_frame()
        self.create_menubar()
        self.create_treeview_context_menu()
        self.after(10, self.poll_logging_queue)

        self.plugin_source_window = SourceWindow(master=self)
        self.plugin_source_window.hide()
        self.refresh_plugins()

    # ---------------------------------------------------------------------- #
    #                           Creation Methods
    # ---------------------------------------------------------------------- #
    def create_treeview_context_menu(self):
        """
        This creates the tree-like view rendering the experiment heirachy.
        """
        self.treeview_popup = tk.Menu(self, tearoff=0)
        self.treeview_popup.add_command(label="Apply FASTQ...",
                                        command=self.apply_seqlib_fastq)

    def create_main_frame(self):
        """
        Large function creating all the basic elements of the main app frame.
        Creates the treeview and associated buttons, the scoring plugin frame
        and the go button.
        """
        # Frame for the Treeview and New/Edit/Delete buttons
        main = Frame(self, padding=(3, 3, 12, 12))
        main.rowconfigure(0, weight=1)
        main.columnconfigure(0, weight=1)
        main.columnconfigure(1, weight=0)
        main.grid(row=0, column=0, sticky="nsew")

        # ------------------------------------------------------- #
        # Frame for the Treeview and its scrollbars
        tree_frame = Frame(main, padding=(3, 3, 12, 12))
        tree_frame.rowconfigure(0, weight=1)
        tree_frame.rowconfigure(1, weight=0)
        tree_frame.columnconfigure(0, weight=1)
        tree_frame.columnconfigure(1, weight=0)
        tree_frame.grid(row=0, column=0, sticky="nsew")

        # ------------------------------------------------------- #
        # Treeview with column headings
        self.treeview = Treeview(tree_frame)
        self.treeview["columns"] = ("class", "barcodes", "variants")
        self.treeview.column("class", width=120)
        self.treeview.heading("class", text="Type")
        self.treeview.column("barcodes",
                             width=25,
                             stretch=tk.NO,
                             anchor=tk.CENTER)
        self.treeview.heading("barcodes", text="BC")
        self.treeview.column("variants",
                             width=25,
                             stretch=tk.NO,
                             anchor=tk.CENTER)
        self.treeview.heading("variants", text="V")
        self.treeview.grid(row=0, column=0, sticky="nsew")

        # Treeview context menu bindings
        self.treeview.bind("<Button-2>", self.treeview_context_menu)

        # Treeview scrollbars
        tree_ysb = tk.Scrollbar(tree_frame,
                                orient="vertical",
                                command=self.treeview.yview)
        tree_xsb = tk.Scrollbar(tree_frame,
                                orient="horizontal",
                                command=self.treeview.xview)
        tree_ysb.grid(row=0, column=1, sticky="nsw")
        tree_xsb.grid(row=1, column=0, sticky="ewn")
        self.treeview.config(yscroll=tree_ysb.set, xscroll=tree_xsb.set)

        # ------------------------------------------------------- #
        # Frame for New/Edit/Delete buttons
        button_frame = Frame(main, padding=(3, 3, 12, 12))
        button_frame.grid(row=1, column=0)
        new_button = Button(button_frame,
                            text="New...",
                            command=self.new_button_press)
        new_button.grid(row=0, column=0)
        edit_button = Button(button_frame,
                             text="Edit...",
                             command=self.edit_button_press)
        edit_button.grid(row=0, column=1)
        delete_button = Button(button_frame,
                               text="Delete",
                               command=self.delete_button_press)
        delete_button.grid(row=0, column=2)

        self.treeview_buttons = [new_button, delete_button, edit_button]

        # ------------------------------------------------------- #
        # Frame for Plugin and Analysis Options
        right_frame = Frame(main, padding=(3, 3, 12, 12))
        right_frame.rowconfigure(0, weight=1)
        right_frame.rowconfigure(1, weight=0)
        right_frame.columnconfigure(0, weight=1)
        right_frame.columnconfigure(1, weight=0)
        right_frame.grid(row=0, column=1, sticky="new")

        # ------------------------------------------------------- #
        # LabelFrame for plugin and options
        scoring_plugin = ScorerScriptsDropDown(right_frame,
                                               text="Scoring Options",
                                               padding=(3, 3, 12, 12))
        scoring_plugin.grid(row=0, column=0, sticky="new")
        self.scorer_widget = scoring_plugin

        # ------------------------------------------------------- #
        # LabelFrame for Analysis Options
        row = 0
        options_frame = LabelFrame(right_frame,
                                   text="Analysis Options",
                                   padding=(3, 3, 12, 12))
        options_frame.grid(row=1, column=0, sticky="new", pady=4)

        # force recalculate
        force_recalculate = Checkbutton(options_frame,
                                        text="Force Recalculation",
                                        variable=self.force_recalculate)
        force_recalculate.grid(column=0, row=row, sticky="w")
        row += 1

        # component outliers
        component_outliers = Checkbutton(
            options_frame,
            text="Component Outlier Statistics",
            variable=self.component_outliers,
        )
        component_outliers.grid(column=0, row=row, sticky="w")
        row += 1

        # write tsv
        tsv_requested = Checkbutton(options_frame,
                                    text="Write TSV Files",
                                    variable=self.tsv_requested)
        tsv_requested.grid(column=0, row=row, sticky="w")
        tsv_requested.invoke()
        row += 1

        # ------------------------------------------------------- #
        # Run Analysis button frame
        go_button_frame = Frame(main, padding=(3, 3, 12, 12))
        go_button_frame.grid(row=1, column=1)
        go_button = Button(go_button_frame,
                           text="Run Analysis",
                           command=self.go_button_press)
        go_button.grid(column=0, row=0)
        self.go_button = go_button

    def create_new_element(self):
        """
        Create and return a new element based on the current selection.

        This element is not added to the treeview. 
        """
        element = None
        parent_element = self.get_focused_element()
        if isinstance(parent_element, Experiment):
            element = Condition()
            element.parent = parent_element
        elif isinstance(parent_element, Condition):
            element = Selection()
            element.parent = parent_element
        elif isinstance(parent_element, Selection):
            element = CreateSeqLibDialog(self).element_type()
            element.parent = parent_element
        elif isinstance(parent_element, SeqLib):
            # special case: creates a copy of the selected SeqLib as a sibling
            element = type(parent_element)()
            element.configure(parent_element.serialize())
            element.parent = parent_element.parent
            # clear out the seqlib-specific values
            element.name = None
            element.timepoint = None
            element.counts_file = None
            element.reads = None
        else:
            raise ValueError("Unrecognized parent object "
                             "type '{}'".format(type(parent_element)))
        return element

    def create_menubar(self):
        """
        Creates the menubar for the main app, with associated drop down menus.
        """
        # make platform-specific keybinds
        if platform.system() == "Darwin":
            accel_string = "Command+"
            accel_bind = "Command-"
        else:
            accel_string = "Ctrl+"
            accel_bind = "Control-"

        # create the menubar
        menubar = tk.Menu(self)

        # file menu
        filemenu = tk.Menu(menubar, tearoff=0)
        filemenu.add_command(
            label="Open...",
            accelerator="{}O".format(accel_string),
            command=self.menu_open,
        )
        filemenu.add_command(label="Save",
                             accelerator="{}S".format(accel_string),
                             command=self.menu_save)
        filemenu.add_command(
            label="Save As...",
            accelerator="{}Shift+S".format(accel_string),
            command=self.menu_saveas,
        )
        menubar.add_cascade(label="File", menu=filemenu)

        # edit menu
        filemenu = tk.Menu(menubar, tearoff=0)
        filemenu.add_command(
            label="Select All",
            accelerator="{}A".format(accel_string),
            command=self.menu_selectall,
        )
        menubar.add_cascade(label="Edit", menu=filemenu)

        # tools menu
        filemenu = tk.Menu(menubar, tearoff=0)
        filemenu.add_command(
            label="Show Log",
            accelerator="{}L".format(accel_string),
            command=show_log_window,
        )
        filemenu.add_command(
            label="Plugin Sources",
            accelerator="{}P".format(accel_string),
            command=self.show_plugin_source_window,
        )
        filemenu.add_command(
            label="Refresh Plugins",
            accelerator="{}R".format(accel_string),
            command=self.refresh_plugins,
        )
        menubar.add_cascade(label="Tools", menu=filemenu)

        # add the menubar
        self.config(menu=menubar)

        # add file menu keybinds
        self.bind("<{}o>".format(accel_bind), lambda event: self.menu_open())
        self.bind("<{}s>".format(accel_bind), lambda event: self.menu_save())
        self.bind("<{}Shift-s>".format(accel_bind),
                  lambda event: self.menu_saveas())

        # add edit menu keybinds
        self.bind("<{}a>".format(accel_bind),
                  lambda event: self.menu_selectall())

        # add show log menu keybinds
        # add edit menu keybinds
        self.bind("<{}l>".format(accel_bind), lambda event: show_log_window())
        self.bind("<{}p>".format(accel_bind),
                  lambda event: self.show_plugin_source_window())
        self.bind("<{}r>".format(accel_bind),
                  lambda event: self.refresh_plugins())

    # ---------------------------------------------------------------------- #
    #                          Treeview Methods
    # ---------------------------------------------------------------------- #
    def treeview_context_menu(self, click):
        """
        Sets the currently selected treeview object id in the variable
        ``treeview_popup_target_id``.
        
        Parameters
        ----------
        click : 
            tkinter click event
        """
        target = self.treeview.identify_row(click.y)
        if target != "":
            self.treeview_popup_target_id = target
            self.treeview_popup.post(click.x_root, click.y_root)
        self.treeview_popup_target_id = None

    def apply_seqlib_fastq(self):
        """
        Applies settings to the seqlib object by running the configuration
        method.
        """
        SeqLibApplyDialog(self, self, self.treeview_popup_target_id)

    def new_button_press(self):
        """
        Spawns a dialog box depending on the currently selected treeview item
        to create a new element.
        """
        if self.treeview.focus() == "" and self.root_element is not None:
            tkinter.messagebox.showwarning(None, "No parent element selected.")
        else:
            if self.treeview.focus() == "" and self.root_element is None:
                element = CreateRootDialog(self).element
                if isinstance(element, SeqLib):
                    EditDialog(self, self, element)
                self.root_element = element
            else:
                element = self.create_new_element()
                EditDialog(self, self, element)

            # refresh the treeview and re-assign treeview id's
            self.refresh_treeview()

            # select the newly added element if it was successfully added
            if element.treeview_id in list(self.element_dict.keys()):
                self.treeview.focus(element.treeview_id)
                self.treeview.selection_set(element.treeview_id)
            else:
                if element.parent is not None:
                    self.treeview.focus(element.parent.treeview_id)
                    self.treeview.selection_set(element.parent.treeview_id)
                del element

    def edit_button_press(self):
        """
        Spawns a dialog box depending on the currently selected treeview item
        to edit the selected element.
        """
        if self.treeview.focus() == "":
            tkinter.messagebox.showwarning(None, "No element selected.")
        else:
            EditDialog(self, self, self.get_focused_element())

    def delete_button_press(self):
        """
        Deletes the selected treeview element and it's children.
        """
        if self.treeview.focus() == "":
            tkinter.messagebox.showwarning(None, "No element selected.")
        else:
            DeleteDialog(self, self)

    def delete_element(self, tree_id):
        """
        Delete element with Treeview id *tree_id* from the tree, from the 
        element dictionary, and from the associated data structure. Recursively 
        deletes all children of *tree_id*.

        The tree should be refreshed using :py:meth:`refresh_tree` after 
        each deletion. This is the responsibility of the caller.

        Parameters
        ----------
        tree_id : `int`
            The id of the currently selected treeview element.

        """
        if tree_id in self.element_dict:
            # recursively delete children
            if self.element_dict[tree_id].children is not None:
                for child in self.element_dict[tree_id].children:
                    self.delete_element(child.treeview_id)

            # check if deleting the root element
            if self.root_element.treeview_id == tree_id:
                # clear the root element
                print("None {}".format(tree_id))
                self.root_element = None
            else:
                try:
                    # remove the element from its parent's list of children
                    self.element_dict[tree_id].parent.remove_child_id(tree_id)
                except AttributeError:
                    raise AttributeError(
                        "Non-root element lacks proper parent")

            # delete the element from the dictionary
            del self.element_dict[tree_id]

    def refresh_treeview(self):
        """
        Clears the Treeview and repopulates it with the 
        current contents of the tree.
        """
        # clear the entries in the Treeview
        for x in self.treeview.get_children():
            self.treeview.delete(x)

        # clear the id-element dictionary
        # elements may be given new id's after repopulation
        self.element_dict.clear()

        # repopulate
        if self.root_element is not None:
            self.populate_tree(self.root_element)

    def set_treeview_properties(self, element):
        """
        Set the information text for the Treeview *element*.

        Parameters
        ----------
        element : :py:class:`~enrich2.base.storemanager.StoreManager`
            The storemanager object to configure.
        """
        # set class property
        self.treeview.set(element.treeview_id, "class",
                          element.treeview_class_name)

        # add the check marks for barcodes/variants
        if "variants" in element.labels:
            self.treeview.set(element.treeview_id, "variants", u"\u2713")
        else:
            self.treeview.set(element.treeview_id, "variants", "")
        if "barcodes" in element.labels:
            self.treeview.set(element.treeview_id, "barcodes", u"\u2713")
        else:
            self.treeview.set(element.treeview_id, "barcodes", "")

        self.treeview.set(element.treeview_id, "class",
                          element.treeview_class_name)

    def populate_tree(self, element, parent_id=""):
        """
        Recursively populate the Treeview.

        Also populates the *id_cfgstrings*.

        Parameters
        ----------
        element : :py:class:`~enrich2.base.storemanager.StoreManager`
            The storemanager object to configure.
        parent_id : `int`
            ``treeview_id`` of element's parent.
        """
        # insert into the Treeview
        element.treeview_id = self.treeview.insert(parent_id,
                                                   "end",
                                                   text=element.name,
                                                   open=True)
        # add id-element pair to dictionary
        self.element_dict[element.treeview_id] = element
        # set information fields
        self.set_treeview_properties(element)

        # populate for children
        if element.children is not None:
            for child in element.children:
                self.populate_tree(child, parent_id=element.treeview_id)

    # ---------------------------------------------------------------------- #
    #                          Getter Methods
    # ---------------------------------------------------------------------- #
    def get_selected_scorer_class(self):
        """
        Returns the currently selected scoring class object.
        """
        return self.scorer

    def get_selected_scorer_attrs(self):
        """
        Returns the currently selected scoring class attribute `dict`.
        """
        return self.scorer_attrs

    def get_selected_scorer_path(self):
        """
        Returns the currently selected scoring path.
        """
        return self.scorer_path

    def get_element(self, treeview_id):
        """
        Returns the element with *treeview_id*.

        Parameters
        ----------
        treeview_id : `int`
            ``treeview_id`` attribute of element to get.

        Returns
        -------
        :py:class:`~enrich2.base.storemanager.StoreManager`
            The instance with matching ``treeview_id``

        """
        return self.element_dict[treeview_id]

    def get_focused_element(self):
        """
        Gets the focused element in the treeview.

        Returns
        -------
        :py:class:`~enrich2.base.storemanager.StoreManager`
            Returns the element that is currently being focused in the 
            Treeview. ``None`` if nothing is focused.
        """
        if self.treeview.focus() != "":
            return self.get_element(self.treeview.focus())
        else:
            return None

    def get_selected_elements(self):
        """
        Returns a list of currently selected elements in the treeview.

        Returns
        -------
        `list`
            Returns a list of elements that are currently selected in the 
            Treeview. If no elements are selected, it returns an empty list.

        """
        return [self.get_element(x) for x in self.treeview.selection()]

    # ---------------------------------------------------------------------- #
    #                          Menubar Methods
    # ---------------------------------------------------------------------- #
    def menu_open(self):
        """
        Spawns an `askopenfilename` dialog to open a configuration file.
        """
        message_title = "Open Configuration"
        fname = tkinter.filedialog.askopenfilename()
        if len(fname) > 0:  # file was selected
            try:
                with open(fname, "rU") as handle:
                    cfg = json.load(handle)
            except ValueError:
                tkinter.messagebox.showerror(message_title,
                                             "Failed to parse config file.")
            except IOError:
                tkinter.messagebox.showerror(message_title,
                                             "Could not read config file.")
            else:
                if is_experiment(cfg):
                    obj = Experiment()
                elif is_selection(cfg):
                    obj = Selection()
                elif is_seqlib(cfg):
                    sltype = seqlib_type(cfg)
                    obj = globals()[sltype]()
                else:
                    tkinter.messagebox.showerror(
                        message_title, "Unrecognized config format.")
                    return
                obj.output_dir_override = False
                try:
                    if isinstance(obj, Experiment) or isinstance(
                            obj, Selection):
                        obj.configure(cfg, init_from_gui=True)
                    else:
                        obj.configure(cfg)

                    # Try load the scorer into the GUI
                    scorer_path = cfg.get(SCORER, {}).get(SCORER_PATH, "")
                    scorer_attrs = cfg.get(SCORER, {}).get(SCORER_OPTIONS, {})
                    if scorer_path:
                        self.scorer_widget.load_from_cfg_file(
                            scorer_path, scorer_attrs)
                    else:
                        log_message(
                            logging_callback=logging.warning,
                            msg="No plugin could be loaded from configuration.",
                            extra={"oname": self.__class__.__name__},
                        )

                except Exception as e:
                    tkinter.messagebox.showerror(
                        message_title,
                        "Failed to load config file:\n\n{}".format(e))
                else:
                    self.root_element = obj
                    self.cfg_file_name.set(fname)
                    self.refresh_treeview()

    def menu_save(self):
        """
        Asks the user where to save the current configuration.
        """
        if len(self.cfg_file_name.get()) == 0:
            self.menu_saveas()
        elif self.root_element is None:
            tkinter.messagebox.showwarning("Save Configuration",
                                           "Cannot save empty configuration.")
        else:
            save = askyesno("Save Configuration",
                            "Overwrite existing configuration?")
            if not save:
                return
            try:
                with open(self.cfg_file_name.get(), "w") as handle:
                    cfg = self.root_element.serialize()

                    # Get the currently selected scorer
                    if not isinstance(self.root_element,
                                      SeqLib) and not isinstance(
                                          self.root_element, Condition):
                        (
                            _,
                            attrs,
                            scorer_path,
                        ) = self.scorer_widget.get_scorer_class_attrs_path()
                        cfg[SCORER] = {
                            SCORER_PATH: scorer_path,
                            SCORER_OPTIONS: attrs
                        }
                    write_json(cfg, handle)
            except IOError:
                tkinter.messagebox.showerror("Save Configuration",
                                             "Failed to save config file.")
            else:
                tkinter.messagebox.showinfo(
                    "Save Configuration",
                    "Saved file at location:\n\n{}".format(
                        self.cfg_file_name.get()),
                )

    def menu_saveas(self):
        """
        Asks the user where to save the current configuration.
        """
        if self.root_element is None:
            tkinter.messagebox.showwarning("Save Configuration",
                                           "Cannot save empty configuration.")
        else:
            fname = tkinter.filedialog.asksaveasfilename()
            if len(fname) > 0:  # file was selected
                try:
                    with open(fname, "w") as handle:
                        cfg = self.root_element.serialize()

                        # Get the currently selected scorer
                        if not isinstance(self.root_element,
                                          SeqLib) and not isinstance(
                                              self.root_element, Condition):
                            (
                                _,
                                attrs,
                                scorer_path,
                            ) = self.scorer_widget.get_scorer_class_attrs_path(
                            )
                            cfg[SCORER] = {
                                SCORER_PATH: scorer_path,
                                SCORER_OPTIONS: attrs,
                            }
                        write_json(cfg, handle)
                except IOError:
                    tkinter.messagebox.showerror(
                        "Save Configuration", "Failed to save config file.")
                else:
                    self.cfg_file_name.set(fname)
                    tkinter.messagebox.showinfo(
                        "Save Configuration",
                        "Saved file at location:\n\n{}".format(
                            self.cfg_file_name.get()),
                    )

    def menu_selectall(self):
        """
        Add all elements in the Treeview to the selection.
        """
        for k in self.element_dict.keys():
            self.treeview.selection_add(k)

    def show_plugin_source_window(self):
        """
        Show the pop-up window to modify plugin sources
        """
        if not self.plugin_source_window:
            self.plugin_source_window = SourceWindow(master=self)
        else:
            self.plugin_source_window.toggle_show()

    # ---------------------------------------------------------------------- #
    #                         Run Analysis Methods
    # ---------------------------------------------------------------------- #
    def go_button_press(self):
        """
        Starts the analysis if all elements have been properly configured.
        This will run the analysis in a new thread and block out GUI editing 
        to prevent the analysis breaking.
        """
        (
            self.scorer,
            self.scorer_attrs,
            self.scorer_path,
        ) = self.scorer_widget.get_scorer_class_attrs_path()

        if self.scorer is None or self.scorer_attrs is None:
            tkinter.messagebox.showwarning("Incomplete Configuration",
                                           "No scoring plugin selected.")
        elif self.root_element is None:
            tkinter.messagebox.showwarning(
                "Incomplete Configuration",
                "No experimental design specified.")
        else:
            plugin, *_ = self.scorer_widget.get_selected_plugin()
            if plugin.md5_has_changed():
                proceed = askokcancel(
                    "Selected plugin has been modified.",
                    "The selected plugin has been modified on disk. Do you "
                    "want to proceed with the current version? To see changes "
                    "click 'Cancel' and refresh plugins before proceeding.",
                )
                if not proceed:
                    return
            if askyesno(
                    "Save Configuration?",
                    "Would you like to save the confiugration "
                    "file before proceeding?",
            ):
                self.menu_save()
            run = askyesno(
                "Begin Analysis?",
                "Click Yes when you are ready to start.\n\nThis could "
                "take some time so grab a cup of tea, or a beer if that's "
                "your thing, and enjoy the show.",
            )
            if run:
                self.configure_analysis()
                self.set_gui_state(tk.DISABLED)
                thread = threading.Thread(target=self.run_analysis)
                thread.setDaemon(True)
                self.analysis_thread = thread
                self.analysis_thread.start()
                self.after(100, self.poll_analysis_thread)

    def poll_logging_queue(self):
        """
        Polls the logging queue for messages to log.
        """
        try:
            log = get_logging_queue(init=True).get(0)
            log[CALLBACK](log[MESSAGE], **log[KWARGS])
            self.after(10, self.poll_logging_queue)
        except queue.Empty:
            self.after(10, self.poll_logging_queue)

    def poll_analysis_thread(self):
        """
        Polls the thread to check it's state. When it is finished, all stores
        are closed.
        """
        try:
            analysis_result = self.queue.get(0)
            self.handle_analysis_result(analysis_result)
        except queue.Empty:
            self.after(100, self.poll_analysis_thread)

    def handle_analysis_result(self, success):
        """
        Shows the appropriate messagebox and logs exceptions upon analysis
        completing.
        
        Parameters
        ----------
        success : `bool`
            Exception object if an error occured during analysis, otherwise
            None to indicate successful computation.
        """
        log_message(
            logging_callback=logging.info,
            msg="Closing stores...",
            extra={"oname": self.root_element.name},
        )
        self.root_element.store_close(children=True)
        log_message(
            logging_callback=logging.info,
            msg="Stores closed.",
            extra={"oname": self.root_element.name},
        )

        if success:
            showinfo("Analysis completed.",
                     "Analysis has completed successfully!")
            log_message(
                logging_callback=logging.info,
                msg="Completed successfully!",
                extra={"oname": self.root_element.name},
            )
        else:
            showwarning(
                "Error during analysis.",
                "An error occurred during the analysis. See log for details",
            )
            log_message(
                logging_callback=logging.info,
                msg="Completed, but with errors!",
                extra={"oname": self.root_element.name},
            )
        self.set_gui_state(tk.NORMAL)

    def run_analysis(self):
        """
        Runs the storemanager compute method.
        """
        try:
            self.root_element.validate()
            self.root_element.store_open(children=True)
            self.root_element.calculate()
            if self.root_element.tsv_requested:
                self.root_element.write_tsv()
            self.queue.put(True, block=False)
        except Exception as exception:
            log_message(
                logging_callback=logging.exception,
                msg=exception,
                extra={"oname": self.root_element.name},
            )
            self.queue.put(False, block=False)
        finally:
            return

    def configure_analysis(self):
        """
        Configures the attributes of the root_element by querying the GUI
        options.
        """
        try:
            self.root_element.force_recalculate = self.force_recalculate.get()
            self.root_element.component_outliers = self.component_outliers.get(
            )
            self.root_element.tsv_requested = self.tsv_requested.get()

            scorer_class = self.get_selected_scorer_class()
            scorer_class_attrs = self.get_selected_scorer_attrs()
            scorer_path = self.get_selected_scorer_path()
            self.root_element.scorer_class = scorer_class
            self.root_element.scorer_class_attrs = scorer_class_attrs
            self.root_element.scorer_path = scorer_path
        except Exception as e:
            log_message(
                logging_callback=logging.info,
                msg="An error occurred when trying to configure the "
                "root element.",
                extra={"oname": self.root_element.name},
            )
            log_message(
                logging_callback=logging.exception,
                msg=e,
                extra={"oname": self.root_element.name},
            )

    # ---------------------------------------------------------------------- #
    #                         GUI Modifications
    # ---------------------------------------------------------------------- #
    def set_gui_state(self, state):
        """
        Sets the state of the `go_button`, `treeview` and `treeview_buttons`.

        Parameters
        ----------
        state : `str`
            State to set, usually ``'normal'`` or ``'disabled'``

        """
        for btn in self.treeview_buttons:
            btn.config(state=state)
        self.go_button.config(state=state)
        if state == "normal":
            self.treeview.bind("<Button-2>", self.treeview_context_menu)
        else:
            self.treeview.bind("<Button-2>", lambda event: event)

    def refresh_plugins(self):
        """
        Refresh the plugins by re-checking the sources file.
        """
        if self.plugin_source_window:
            sources = self.plugin_source_window.sources
            self.scorer_widget.refresh_sources(sources)
Exemple #4
0
class DialogOpenArchive(Toplevel):
    def __init__(self, mainWin, openType, filesource, filenames, title, colHeader, showAltViewButton=False):
        parent = mainWin.parent
        super(DialogOpenArchive, self).__init__(parent)
        self.parent = parent
        self.showAltViewButton = showAltViewButton
        parentGeometry = re.match("(\d+)x(\d+)[+]?([-]?\d+)[+]?([-]?\d+)", parent.geometry())
        dialogX = int(parentGeometry.group(3))
        dialogY = int(parentGeometry.group(4))
        self.accepted = False

        self.transient(self.parent)
        
        frame = Frame(self)

        treeFrame = Frame(frame, width=500)
        vScrollbar = Scrollbar(treeFrame, orient=VERTICAL)
        hScrollbar = Scrollbar(treeFrame, orient=HORIZONTAL)
        self.treeView = Treeview(treeFrame, xscrollcommand=hScrollbar.set, yscrollcommand=vScrollbar.set)
        self.treeView.grid(row=0, column=0, sticky=(N, S, E, W))
        hScrollbar["command"] = self.treeView.xview
        hScrollbar.grid(row=1, column=0, sticky=(E,W))
        vScrollbar["command"] = self.treeView.yview
        vScrollbar.grid(row=0, column=1, sticky=(N,S))
        treeFrame.columnconfigure(0, weight=1)
        treeFrame.rowconfigure(0, weight=1)
        treeFrame.grid(row=0, column=0, columnspan=4, sticky=(N, S, E, W), padx=3, pady=3)
        self.treeView.focus_set()
        
        mainWin.showStatus(_("loading archive {0}").format(filesource.url))
        self.filesource = filesource
        self.filenames = filenames
        self.selection = filesource.selection
        self.hasToolTip = False
        selectedNode = None

        if openType == ENTRY_POINTS:
            try:
                metadataFiles = filesource.taxonomyPackageMetadataFiles
                ''' take first for now
                if len(metadataFiles) != 1:
                    raise IOError(_("Taxonomy package contained more than one metadata file: {0}.")
                                  .format(', '.join(metadataFiles)))
                '''
                metadataFile = metadataFiles[0]
                metadata = filesource.url + os.sep + metadataFile
                self.metadataFilePrefix = os.sep.join(os.path.split(metadataFile)[:-1])
                if self.metadataFilePrefix:
                    self.metadataFilePrefix += "/"  # zip contents have /, never \ file seps
                self.taxonomyPkgMetaInf = '{}/META-INF/'.format(
                            os.path.splitext(os.path.basename(filesource.url))[0])

        
                self.taxonomyPackage = parsePackage(mainWin, filesource, metadata,
                                                    os.sep.join(os.path.split(metadata)[:-1]) + os.sep)
                
                # may be a catalog file with no entry oint names
                if not self.taxonomyPackage["nameToUrls"]:
                    openType = ARCHIVE  # no entry points to show, just archive
                    self.showAltViewButton = False
            except Exception as e:
                self.close()
                err = _("Failed to parse metadata; the underlying error was: {0}").format(e)
                messagebox.showerror(_("Malformed taxonomy package"), err)
                mainWin.addToLog(err)
                return
    
        mainWin.showStatus(None)
        
        if openType == DISCLOSURE_SYSTEM:
            y = 3
        else:
            y = 1

        okButton = Button(frame, text=_("OK"), command=self.ok)
        cancelButton = Button(frame, text=_("Cancel"), command=self.close)
        okButton.grid(row=y, column=2, sticky=(S,E,W), pady=3)
        cancelButton.grid(row=y, column=3, sticky=(S,E,W), pady=3, padx=3)
        
        if self.showAltViewButton:
            self.altViewButton = Button(frame, command=self.showAltView)
            self.altViewButton.grid(row=y, column=0, sticky=(S,W), pady=3, padx=3)
        
        self.loadTreeView(openType, colHeader, title)

        self.geometry("+{0}+{1}".format(dialogX+50,dialogY+100))
        frame.grid(row=0, column=0, sticky=(N,S,E,W))
        frame.columnconfigure(0, weight=1)
        frame.rowconfigure(0, weight=1)
        window = self.winfo_toplevel()
        window.columnconfigure(0, weight=1)
        window.rowconfigure(0, weight=1)
        
        self.bind("<Return>", self.ok)
        self.bind("<Escape>", self.close)
        
        self.toolTipText = StringVar()
        if self.hasToolTip:
            self.treeView.bind("<Motion>", self.motion, '+')
            self.treeView.bind("<Leave>", self.leave, '+')
            self.toolTipText = StringVar()
            self.toolTip = ToolTip(self.treeView, 
                                   textvariable=self.toolTipText, 
                                   wraplength=640, 
                                   follow_mouse=True,
                                   state="disabled")
            self.toolTipRowId = None

        self.protocol("WM_DELETE_WINDOW", self.close)
        self.grab_set()
        self.wait_window(self)
        
    def loadTreeView(self, openType, title, colHeader):
        self.title(title)
        self.openType = openType
        selectedNode = None

        # clear previous treeview entries
        for previousNode in self.treeView.get_children(""): 
            self.treeView.delete(previousNode)

        # set up treeView widget and tabbed pane
        if openType in (ARCHIVE, DISCLOSURE_SYSTEM):
            self.treeView.column("#0", width=500, anchor="w")
            self.treeView.heading("#0", text=colHeader)
            try:
                self.isRss = self.filesource.isRss
                if self.isRss:
                    self.treeView.column("#0", width=350, anchor="w")
                    self.treeView["columns"] = ("descr", "date", "instDoc")
                    self.treeView.column("descr", width=50, anchor="center", stretch=False)
                    self.treeView.heading("descr", text="Form")
                    self.treeView.column("date", width=170, anchor="w", stretch=False)
                    self.treeView.heading("date", text="Pub Date")
                    self.treeView.column("instDoc", width=200, anchor="w", stretch=False)
                    self.treeView.heading("instDoc", text="Instance Document")
            except AttributeError:
                self.isRss = False
                self.treeView["columns"] = tuple()
        
            loadedPaths = []
            for i, filename in enumerate(self.filenames):
                if isinstance(filename,tuple):
                    if self.isRss:
                        form, date, instDoc = filename[2:5]
                    filename = filename[0] # ignore tooltip
                    self.hasToolTip = True
                if filename.endswith("/"):
                    filename = filename[:-1]
                path = filename.split("/")
                if not self.isRss and len(path) > 1 and path[:-1] in loadedPaths:
                    parent = "file{0}".format(loadedPaths.index(path[:-1]))
                else:
                    parent = "" 
                node = self.treeView.insert(parent, "end", "file{0}".format(i), text=path[-1])
                if self.isRss:
                    self.treeView.set(node, "descr", form)
                    self.treeView.set(node, "date", date)
                    self.treeView.set(node, "instDoc", os.path.basename(instDoc))
                if self.selection == filename:
                    selectedNode = node
                loadedPaths.append(path)

        elif openType == ENTRY_POINTS:
            self.treeView.column("#0", width=150, anchor="w")
            self.treeView.heading("#0", text="Name")
    
            self.treeView["columns"] = ("url",)
            self.treeView.column("url", width=350, anchor="w")
            self.treeView.heading("url", text="URL")
            
            for name, urls in self.taxonomyPackage["nameToUrls"].items():
                displayUrl = urls[1] # display the canonical URL
                self.treeView.insert("", "end", name, values=[displayUrl], text=name)
                
            self.hasToolTip = True
        else: # unknown openType
            return None
        if selectedNode:
            self.treeView.see(selectedNode)
            self.treeView.selection_set(selectedNode)

        if self.showAltViewButton:
            self.altViewButton.config(text=_("Show Files") if openType == ENTRY_POINTS else _("Show Entries"))

        
    def ok(self, event=None):
        selection = self.treeView.selection()
        if len(selection) > 0:
            if hasattr(self, "taxonomyPackage"):
                # load file source remappings
                self.filesource.mappedPaths = self.taxonomyPackage["remappings"]
            filename = None
            if self.openType in (ARCHIVE, DISCLOSURE_SYSTEM):
                filename = self.filenames[int(selection[0][4:])]
                if isinstance(filename,tuple):
                    if self.isRss:
                        filename = filename[4]
                    else:
                        filename = filename[0]
            elif self.openType == ENTRY_POINTS:
                epName = selection[0]
                #index 0 is the remapped Url, as opposed to the canonical one used for display
                filename = self.taxonomyPackage["nameToUrls"][epName][0]
                if not filename.endswith("/"):
                    # check if it's an absolute URL rather than a path into the archive
                    if not isHttpUrl(filename) and self.metadataFilePrefix != self.taxonomyPkgMetaInf:
                        # assume it's a path inside the archive:
                        filename = self.metadataFilePrefix + filename
            if filename is not None and not filename.endswith("/"):
                if hasattr(self, "taxonomyPackage"):
                    # attempt to unmap the filename to original file
                    # will be mapped again in loading, but this allows schemaLocation to be unmapped
                    for prefix, remapping in self.taxonomyPackage["remappings"].items():
                        if isHttpUrl(remapping):
                            remapStart = remapping
                        else:
                            remapStart = self.metadataFilePrefix + remapping
                        if filename.startswith(remapStart):
                            # set unmmapped file
                            filename = prefix + filename[len(remapStart):]
                            break
                self.filesource.select(filename)
                self.accepted = True
                self.close()
                        
        
    def close(self, event=None):
        self.parent.focus_set()
        self.destroy()
        
    def showAltView(self, event=None):
        if self.openType == ENTRY_POINTS:
            self.loadTreeView(ARCHIVE, _("Select Entry Point"), _("File"))
        else:
            self.loadTreeView(ENTRY_POINTS, _("Select Archive File"), _("File"))
        
    def leave(self, *args):
        self.toolTipRowId = None

    def motion(self, *args):
        tvRowId = self.treeView.identify_row(args[0].y)
        if tvRowId != self.toolTipRowId:
            text = None
            if self.openType in (ARCHIVE, DISCLOSURE_SYSTEM):
                self.toolTipRowId = tvRowId
                if tvRowId and len(tvRowId) > 4:
                    try:
                        text = self.filenames[ int(tvRowId[4:]) ]
                        if isinstance(text, tuple):
                            text = text[1].replace("\\n","\n")
                    except (KeyError, ValueError):
                        pass
            elif self.openType == ENTRY_POINTS:
                try:
                    epUrl = self.taxonomyPackage["nameToUrls"][tvRowId][1]
                    text = "{0}\n{1}".format(tvRowId, epUrl)
                except KeyError:
                    pass
            self.setToolTip(text)
                
    def setToolTip(self, text):
        self.toolTip._hide()
        if text:
            self.toolTipText.set(text)
            self.toolTip.configure(state="normal")
            self.toolTip._schedule()
        else:
            self.toolTipText.set("")
            self.toolTip.configure(state="disabled")
Exemple #5
0
class NameView(object):
    """Shows a treeview of unique names."""
    def __init__(self, master, names):
        self.widget = Frame(master)
        self._tree = Treeview(self.widget, columns='name')
        self._tree.grid(row=0, column=0, sticky=(N, S, W, E))
        self._tree.view = self
        self.widget.columnconfigure(0, weight=1)
        self.widget.rowconfigure(0, weight=1)
        self._tree.column('name', width=50)
        self._tree['show'] = 'tree'
        actions = {
            'edit': lambda e: self.edit(),
            'search': lambda e: self.search(),
            'focus_next': lambda e: self.focus_next(),
            'focus_prev': lambda e: self.focus_prev(),
            'select':
            lambda e: self._tree.selection_toggle(self._tree.focus()),
            'clear_selection': lambda e: self._tree.selection_set([])
        }
        kb.make_bindings(kb.tagview, actions, self._tree.bind)
        self._iids = dict()
        self._names = dict()
        logger.debug('Names: %s', names)
        self.widget.focus_set = self._tree.focus_set
        for name in sorted(names):
            iid = self._tree.insert('', 'end', text=name)
            self._names[iid] = name
            self._iids[name] = iid
        self._scroll = Scrollbar(self.widget, command=self._tree.yview)
        self._tree['yscrollcommand'] = self._scroll.set
        self._scroll.grid(row=0, column=1, sticky=(N, S))
        self.widget.columnconfigure(1, weight=0)

    def selection(self):
        logger.debug('Selection: %s', self._tree.selection())
        return [self._names[iid] for iid in self._tree.selection()]

    def edit(self):
        self._tree.event_generate('<<NameViewEdit>>')

    def search(self):
        if len(self._tree.selection()) == 0:
            self._tree.selection_add(self._tree.focus())
        self._tree.event_generate('<<NameViewSearch>>')

    def append(self, names):
        logger.debug('Append names: %s', names)
        for name in names:
            if name not in self._names.values():
                iid = self._tree.insert('', 'end', text=name)
                self._names[iid] = name
                self._iids[name] = iid

    def delete(self, name):
        self._tree.delete(self._iids[name])
        del self._names[self._iids[name]]
        del self._iids[name]

    def _focus(self, iid):
        self._tree.focus(iid)
        self._tree.see(iid)

    def focus_next(self):
        cur_iid = self._tree.focus()
        next_iid = self._tree.next(cur_iid)
        if next_iid == '':
            iids = self._tree.get_children()
            next_iid = iids[0]
        self._focus(next_iid)

    def focus_prev(self):
        cur_iid = self._tree.focus()
        prev_iid = self._tree.prev(cur_iid)
        if prev_iid == '':
            iids = self._tree.get_children()
            prev_iid = iids[-1]
        self._focus(prev_iid)

    def jump_to(self, name):
        try:
            iid = self._iids[name]
            self._focus(iid)
        except KeyError:
            pass

    def get_names(self):
        return tuple(self._names.values())

    def set(self, names):
        self._tree.delete(*self._iids.values())
        self._iids.clear()
        self._names.clear()
        for name in sorted(names):
            iid = self._tree.insert('', 'end', text=name)
            self._names[iid] = name
            self._iids[name] = iid
Exemple #6
0
class Demo(Tk):
    icon_res = []
    file_name = None

    def __init__(self):
        super().__init__()
        self._set_windows_()
        self._create_menu_bar_()
        self._create_shortcut_bar_()
        self._create_body_()
        self.DATAS = pd.DataFrame(columns=INFOS)

    def _set_windows_(self):
        self.title('学生成绩管理系统')
        scn_width, scn_height = self.maxsize()
        wm_val = '1200x450+%d+%d' % ((scn_width - 800) / 2,
                                     (scn_height - 450) / 2)
        self.geometry(wm_val)
        # self.iconbitmap("img/editor.ico")

    def _create_menu_bar_(self):
        menu_bar = Menu(self)

        file_menu = Menu(menu_bar, tearoff=0)
        file_menu.add_command(label='add',
                              accelerator='Ctrl+N',
                              command=self.add)
        file_menu.add_command(label='save_to_file', command=self.save_to_file)
        file_menu.add_command(label='Exit', command=self.edit)
        file_menu.add_command(label='Search', command=self.search)
        file_menu.add_command(label='Total', command=self.total)
        file_menu.add_command(label='save_to_DATAS',
                              command=self.save_to_DATAS)
        file_menu.add_command(label='Average', command=self.average)
        file_menu.add_command(label='open_file', command=self.open_file)
        file_menu.add_command(label='add', command=self.add)
        file_menu.add_command(label='delete_selected_item',
                              command=self.delete_item)
        file_menu.add_command(label='clear_all', command=self.clear_all)
        file_menu.add_command(label='delete_item', command=self.delete_item)
        file_menu.add_command(label='init_demo', command=self.init_demo)
        file_menu.add_command(label='sort_as_total',
                              command=self.sort_as_total)
        file_menu.add_command(label='average_stu', command=self.average_stu)

        menu_bar.add_cascade(label='程序中所有函数(测试)', menu=file_menu)

        help_menu = Menu(menu_bar, tearoff=0)
        help_menu.add_command(label='帮助',
                              accelerator='Ctrl+H',
                              command=self.about)
        help_menu.add_command(label='关于',
                              accelerator='Ctrl+H',
                              command=self.about)

        menu_bar.add_cascade(label='---> 请先阅读帮助中的说明!!!<---', menu=help_menu)

        self["menu"] = menu_bar

    def _create_shortcut_bar_(self):
        shortcut_bar = Frame(self, height=25, background='#00CED1')
        shortcut_bar.pack(fill=X)

        right_bar = Frame(self, width=25, background='#FF8C00')
        right_bar.pack(side=RIGHT, fill=Y)

        for i, icon in enumerate(ICONS):
            icon_img = PhotoImage(file='img/%s.gif' % icon)
            btn = Button(shortcut_bar,
                         image=icon_img,
                         command=lambda x=icon: self._shortcut_action_(x))
            btn.pack(side=LEFT)
            self.icon_res.append(icon_img)

    def _create_body_(self):

        scrollBar = Scrollbar(self)
        scrollBar.pack(side=RIGHT, fill=Y)

        self.tree = Treeview(self,
                             show='headings',
                             yscrollcommand=scrollBar.set)  # 表格
        index = tuple([str(i) for i in range(len(INFOS))])
        self.tree["columns"] = index

        for i, info in zip(index, INFOS):
            self.tree.column(i, width=len(i) * 10, anchor='center')
            self.tree.heading(i, text=info)  #显示表头

        self.tree.pack(fill=BOTH, ipady=500)

        # 将滚动条绑定至Treeview
        scrollBar.config(command=self.tree.yview)

        # 快捷键相关设置
        # 函数参数需要有event=None
        self.tree.bind('<Double-Button-1>', self.edit)

    # 响应快捷菜单
    def _shortcut_action_(self, type):

        if type == "open_file":
            self.open_file()
        elif type == "add":
            self.add()
        elif type == "edit":
            self.edit()
        elif type == "save_file":
            self.save_to_file()
        elif type == "save_DATAS":
            self.save_to_DATAS()
        elif type == "delete":
            self.delete_item()
        elif type == "clear_all":
            self.clear_all()
        elif type == "search":
            self.search()
        elif type == "total":
            try:
                self.total()
            except:
                messagebox.showinfo(title='警告', message='请检查输入信息')
        elif type == "average":
            try:
                self.average()
            except:
                messagebox.showinfo(title='警告', message='请检查输入信息')
        elif type == "sort":
            try:
                self.sort_as_total()
            except:
                messagebox.showinfo(title='警告', message='请检查输入信息')
        elif type == "sort_no":
            try:
                self.sort_as_no()
            except:
                messagebox.showinfo(title='警告', message='请检查输入信息')
        elif type == "exit":
            self.exit()
        elif type == "average_stu":
            try:
                self.average_stu()
            except:
                messagebox.showinfo(title='警告', message='请检查输入信息')
        elif type == "about":
            self.about()

    def showline(self, event=None):
        print(self.tree.selection())
        for item in self.tree.selection():
            item_text = self.tree.item(item, 'values')
            print(item_text)

    def clear_all(self):
        print('-------------follow item will be cleared:BEG-------------')
        print(self.tree.get_children())
        print('\n')
        for item in self.tree.get_children():
            self.tree.delete(item)
            print(item + ' : ', end=' ')
            print('deleted', end=' ')
        print('\n-------------items had been cleared:END-------------')

    def init_demo(self):
        self.clear_all()
        self.DATAS = pd.DataFrame(columns=INFOS)
        print('---------------demo inited----------------')

    def delete_item(self, event=None):
        print('-------------Your selection:BEG-------------')
        print(self.tree.selection())
        for item in self.tree.selection():
            self.tree.delete(item)
        print('-------------Your selection cleared:END-------------')

    def add(self, event=None):
        add_windows = Toplevel(self)
        scn_width, scn_height = self.maxsize()
        wm_val = '320x400+%d+%d' % ((scn_width - 320) / 2,
                                    (scn_height - 400) / 2)
        add_windows.geometry(wm_val)
        add_windows.resizable(0, 0)
        add_windows.title('添加新的学生')

        frame = Frame(add_windows)
        frame.pack(fill=Y)

        self.entryList = locals()

        for i, info in enumerate(INFOS[:-2]):  # -2 不添加总分 和 平均分
            Label(frame, text=info + ' : ').grid(row=i, column=0, pady=5)
            self.entryList[info] = Entry(frame)
            self.entryList[info].grid(row=i, column=1, pady=5)

        frame_btn = Frame(add_windows)
        frame_btn.pack(fill=Y)
        Button(frame_btn, text='添加', command=lambda: update()).grid(row=0,
                                                                    column=0,
                                                                    pady=5)
        Button(frame_btn, text='清空', command=lambda: clear()).grid(row=0,
                                                                   column=1,
                                                                   pady=5)
        Button(frame_btn, text='取消', command=lambda: exit()).grid(row=0,
                                                                  column=2,
                                                                  pady=5)

        def update():
            DATAS = []
            for info in INFOS:
                data = self.entryList[info].get()
                DATAS.append(data)

            for i in DATAS[:-1]:
                if i == '':
                    messagebox.showwarning(title='警告', message='输入空白')
                    clear()
                    return

            self.tree.insert("", END, values=DATAS)

        def clear():
            for info in INFOS:
                self.entryList[info].delete(0, END)

        def exit():
            add_windows.destroy()

    def open_file(self, options=None):
        if options:
            input_file = options
        else:
            input_file = filedialog.askopenfilename(
                filetypes=[("所有文件", "*.*"), ("Excel文档", "*.xlsx")])

            if input_file:
                print('-----------------成功导入文件:', input_file, type(input_file),
                      '---------------')

        def read_excel(file):
            df = pd.read_excel(file)
            print('---------------reading excel!-----------------')
            print(df)
            print('---------------read excel done!-----------------')
            return df

        df = read_excel(input_file)

        # input data to Treeview
        print('---------------inputing to TreeView!-----------------')
        for i in df.iloc:
            data = i.tolist()

            # 若导入的文件中缺少数据,则缺少的数据用空补上
            for i in range(len(INFOS) - len(data)):
                data.append('')
            # 打印测试
            print(data)

            # 测试用 把 平均分 和 总分 置空
            data[-1] = ''
            data[-2] = ''
            self.tree.insert("", END, values=data)
        print('---------------input to TreeView done!-----------------')

    def save_to_DATAS(self):
        print('---------------saving to DATAS!-----------------')
        self.DATAS = pd.DataFrame(columns=INFOS)
        indexs = self.tree.get_children()
        for i in indexs:
            value = self.tree.item(i, 'values')
            print(value)
            self.DATAS.loc[len(self.DATAS)] = list(value)
        print('---------------save to DATAS done!-----------------')

    def save_to_file(self):
        outputfile = filedialog.asksaveasfilename(
            filetypes=[("所有文件", "*.*"), ("Excel文档", "*.xlsx")])
        self.save_to_DATAS()
        # outputfile = 'new.xlsx'
        self.DATAS.to_excel(outputfile, index=FALSE)
        print(
            '---------------------------save to file done!---------------------------------'
        )

    def edit(self, event=None):

        item = self.tree.selection()

        # Layout
        edit_windows = Toplevel(self)
        scn_width, scn_height = self.maxsize()
        wm_val = '320x450+%d+%d' % ((scn_width - 320) / 2,
                                    (scn_height - 450) / 2)
        edit_windows.geometry(wm_val)
        edit_windows.resizable(0, 0)
        edit_windows.title('编辑学生信息')

        # 创建标签和输入框
        frame = Frame(edit_windows)
        frame.pack(fill=Y)
        self.entryList = locals()
        for i, info in enumerate(INFOS):
            Label(frame, text=info + ' : ').grid(row=i, column=0, pady=5)
            self.entryList[info] = Entry(frame)
            self.entryList[info].grid(row=i, column=1, pady=5)

        # 创建按钮布局
        frame_btn = Frame(edit_windows)
        frame_btn.pack(fill=Y)
        Button(frame_btn, text='确定', command=lambda: update()).grid(row=0,
                                                                    column=0,
                                                                    pady=5)
        Button(frame_btn, text='清空', command=lambda: clear()).grid(row=0,
                                                                   column=1,
                                                                   pady=5)
        Button(frame_btn, text='取消', command=lambda: exit()).grid(row=0,
                                                                  column=2,
                                                                  pady=5)

        # 将待修改的数据导入输入框(方便修改,不用全部重新输入)
        values = self.tree.item(item, 'values')
        for value, info in zip(values, INFOS):
            self.entryList[info].insert(END, value)
            print(values)
            print('--------------input to entry done!------------')

        def update():
            temp = []
            for i, info in enumerate(INFOS):
                data = self.entryList[info].get()
                temp.append(data)
                self.tree.set(item, i, value=data)

            print('----------------editing data!-----------------')
            print(temp)
            print('---------------edit data done!-----------------')
            exit()

        def clear():
            for info in INFOS:
                self.entryList[info].delete(0, END)

        def exit():
            edit_windows.destroy()

        # item = self.tree.selection()
        # self.tree.set(item, 0, value='helloworld')

    def search(self):
        search_windows = Toplevel(self)
        search_windows.title('搜索全部')
        search_windows.transient(self)
        search_windows.resizable(0, 0)
        scn_width, scn_height = self.maxsize()
        wm_val = '380x70+%d+%d' % ((scn_width - 380) / 2,
                                   (scn_height - 70) / 2)
        search_windows.geometry(wm_val)
        Label(search_windows, text='查找全部:').grid(row=0, column=0, sticky=E)
        search_entry = Entry(search_windows, width=25)
        search_entry.grid(row=0, column=1, padx=2, pady=20, sticky='we')
        search_entry.focus_set()

        Button(search_windows, text='查找',
               command=lambda: search_result()).grid(row=0,
                                                     column=2,
                                                     sticky=E + W,
                                                     padx=2,
                                                     pady=2)

        def search_result():
            result = []
            indexs = self.tree.get_children()
            search_data = search_entry.get()
            print(search_data, type(search_data))

            for i, index in enumerate(indexs):
                values = self.tree.item(index, 'values')
                print(values, end=' ')
                for value in list(values)[:2]:
                    if search_data in value:
                        result.append(search_data)
                        print(result)
                        self.tree.selection_set(index)
                        self.tree.yview_moveto(i / len(indexs))
                        break
                    else:
                        print('No Found')

            if result:
                return
            else:
                messagebox.showinfo(title='提示', message='未找到匹配项')

        # 选择
        # self.tree.selection_set('I001')
        # self.tree.yview_moveto(1)

        # 取消选择
        # self.tree.selection_remove('I001')

    def update_to_tree(self):
        # 删除Treeview中所有元素
        for index in self.tree.get_children():
            self.tree.delete(index)
        # 插入DATAS中的元素到TreeView中
        for i in self.DATAS.iloc:
            data = i.tolist()
            self.tree.insert("", END, values=data)

    def total(self):
        self.save_to_DATAS()
        temp = self.DATAS[['成绩A', '成绩B', '成绩C']].astype('int')
        self.DATAS['总分'] = temp.sum(axis=1)
        print(
            '---------------------------Total calculate done!-----------------------------'
        )
        print(self.DATAS)
        print(
            '---------------------------Total calculate done!-----------------------------'
        )

        # # 删除Treeview中所有元素
        # for index in self.tree.get_children():
        #     self.tree.delete(index)
        # # 插入DATAS中的元素到TreeView中
        # for i in self.DATAS.iloc:
        #     data = i.tolist()
        #     self.tree.insert("", END, values=data)

        self.update_to_tree()

    def average_stu(self):
        self.save_to_DATAS()
        temp = self.DATAS[['成绩A', '成绩B', '成绩C']].astype('int')

        # round 计算并保留两位小数 round( *, 2 )
        self.DATAS['平均分'] = round(temp.mean(axis=1), 2)
        print(
            '---------------------------per average calculate done!-----------------------------'
        )
        print(self.DATAS)
        print(
            '---------------------------per average done!-----------------------------'
        )

        self.update_to_tree()

    def average(self):
        self.total()
        temp = self.DATAS[['总分']].astype('float64')
        av = temp.mean(axis=0)
        print('---------------------------AV-----------------------------')
        print('平均分:', float(av))
        print('---------------------------AV-----------------------------')

        class_grade = '\t总平均分: %.2f' % float(av)

        messagebox.showinfo(title='平均分', message=class_grade)

    def sort_as_total(self):
        self.total()
        self.DATAS.sort_values(by='总分', ascending=False, inplace=True)
        print('--------------------sorting values!----------------')
        print(self.DATAS)
        print('--------------------sort values done!--------------')

        # drop 清洗列表 去掉NaN的行
        self.DATAS.reset_index(drop=True, inplace=True)
        print('--------------------cleaning datas!----------------')
        print(self.DATAS)
        print('--------------------clean datas done!--------------')
        self.update_to_tree()
        print('--------------------update to tree done!--------------')

        # Text
        self.DATAS.to_excel('text.xlsx', index=False)

    def sort_as_no(self):
        self.save_to_DATAS()
        self.DATAS['学号'] = self.DATAS[['学号']].astype('int')
        self.DATAS.sort_values(by='学号', inplace=True)
        print(self.DATAS)
        print('-------------------sort as no done!-----------------')

        self.DATAS.reset_index(drop=True, inplace=True)
        print(self.DATAS)
        print('--------------------clean data done!-----------------')
        self.update_to_tree()
        print('--------------------update to tree done!--------------')

        # Text
        self.DATAS.to_excel('text_sort_no.xlsx', index=False)

    def about(self):
        # info = 'Github Page: %s \nWeb: %s\n' % (ADRS[0], ADRS[1])
        # messagebox.showinfo(title='About', message=info)
        help = [
            '打开Excel文件 其中所有学生信息将导入', '添加一个学生信息 单个添加',
            '编辑学生信息 选中一个数据后点击 也可直接双击数据编辑', '保存学生信息至Excel文件 文件可用于导入',
            '刷新数据列表 将窗口内所有学生信息更新至DATAS 用于计算',
            '删除学生 选中一个或多个(Shift)数据后单击 即可删除该数据 ', '清空所有信息',
            '查找学生信息 输入姓名 或 学号即可自动遍历查找', '求各个学生的总分', '求所有学生的平均分',
            '将所有学生按总分排序 会先自动计算总分', '求各个学生的平均分 会先自动线计算总分', '将所有学生按学号排序', '关于帮助',
            '退出程序'
        ]
        about_windows = Toplevel(self)
        about_windows.title('程序使用说明')

        # 容器框 (LabelFrame)
        group = LabelFrame(about_windows, text="Help", padx=5, pady=5)
        group.grid(padx=10, pady=10)
        # w = Label(group, text='本学习项目由  http://pegasu.cn  出品 \n\nGithub: https://github.com/pegasuswiki')
        # w.pack()
        for i, icon in enumerate(ICONS):
            icon_img = PhotoImage(file='img/%s.gif' % icon)
            Label(group, image=icon_img).grid(row=i,
                                              column=0,
                                              stick=NW,
                                              padx=5,
                                              pady=5)
            Label(group, text=help[i]).grid(row=i,
                                            column=1,
                                            stick=W,
                                            padx=5,
                                            pady=5)

            self.icon_res.append(icon_img)  # 必须有 作用: 保存图片

        w = Label(
            about_windows,
            text=
            '本学习项目由  http://pegasu.cn  出品 \n\nGithub: https://github.com/pegasuswiki'
        )
        w.grid(pady=10)

    def exit(self):
        if messagebox.askokcancel("退出?", "确定退出吗?"):
            self.quit()
class DialogOpenArchive(Toplevel):
    def __init__(self,
                 parent,
                 openType,
                 filesource,
                 filenames,
                 title,
                 colHeader,
                 showAltViewButton=False):
        if isinstance(parent, Cntlr):
            cntlr = parent
            parent = parent.parent  # parent is cntlrWinMain
        else:  # parent is a Toplevel dialog
            cntlr = parent.cntlr
        super(DialogOpenArchive, self).__init__(parent)
        self.parent = parent
        self.showAltViewButton = showAltViewButton
        parentGeometry = re.match("(\d+)x(\d+)[+]?([-]?\d+)[+]?([-]?\d+)",
                                  parent.geometry())
        dialogX = int(parentGeometry.group(3))
        dialogY = int(parentGeometry.group(4))
        self.accepted = False

        self.transient(self.parent)

        frame = Frame(self)

        treeFrame = Frame(frame, width=500)
        vScrollbar = Scrollbar(treeFrame, orient=VERTICAL)
        hScrollbar = Scrollbar(treeFrame, orient=HORIZONTAL)
        self.treeView = Treeview(treeFrame,
                                 xscrollcommand=hScrollbar.set,
                                 yscrollcommand=vScrollbar.set)
        self.treeView.grid(row=0, column=0, sticky=(N, S, E, W))
        hScrollbar["command"] = self.treeView.xview
        hScrollbar.grid(row=1, column=0, sticky=(E, W))
        vScrollbar["command"] = self.treeView.yview
        vScrollbar.grid(row=0, column=1, sticky=(N, S))
        treeFrame.columnconfigure(0, weight=1)
        treeFrame.rowconfigure(0, weight=1)
        treeFrame.grid(row=0,
                       column=0,
                       columnspan=4,
                       sticky=(N, S, E, W),
                       padx=3,
                       pady=3)
        self.treeView.focus_set()

        if openType not in (PLUGIN, PACKAGE):
            cntlr.showStatus(_("loading archive {0}").format(filesource.url))
        self.filesource = filesource
        self.filenames = filenames
        self.selection = filesource.selection
        self.hasToolTip = False
        selectedNode = None

        if openType == ENTRY_POINTS:
            try:
                metadataFiles = filesource.taxonomyPackageMetadataFiles
                ''' take first for now
                if len(metadataFiles) != 1:
                    raise IOError(_("Taxonomy package contained more than one metadata file: {0}.")
                                  .format(', '.join(metadataFiles)))
                '''
                metadataFile = metadataFiles[0]
                metadata = filesource.url + os.sep + metadataFile
                self.metadataFilePrefix = os.sep.join(
                    os.path.split(metadataFile)[:-1])
                if self.metadataFilePrefix:
                    self.metadataFilePrefix += "/"  # zip contents have /, never \ file seps
                self.taxonomyPkgMetaInf = '{}/META-INF/'.format(
                    os.path.splitext(os.path.basename(filesource.url))[0])

                self.taxonomyPackage = parsePackage(
                    cntlr, filesource, metadata,
                    os.sep.join(os.path.split(metadata)[:-1]) + os.sep)

                if self.taxonomyPackage["entryPoints"]:
                    # may have instance documents too
                    self.packageContainedInstances = []
                    packageContentTypeCounts = {}
                    for suffix in (".xhtml", ".htm", ".html"):
                        for potentialInstance in filesource.dir:
                            if potentialInstance.endswith(".xhtml"):
                                _type = "Inline Instance"
                                self.packageContainedInstances.append(
                                    [potentialInstance, _type])
                                packageContentTypeCounts[
                                    potentialInstance] = packageContentTypeCounts.get(
                                        potentialInstance, 0) + 1
                        if self.packageContainedInstances:
                            break
                    if self.packageContainedInstances:  # add sequences to any duplicated entry types
                        for _type, count in packageContentTypeCounts.items():
                            if count > 1:
                                _dupNo = 0
                                for i in range(
                                        len(self.packageContainedInstances)):
                                    if self.packageContainedInstances[i][
                                            0] == _type:
                                        _dupNo += 1
                                        self.packageContainedInstances[i][
                                            0] = "{} {}".format(_type, _dupNo)

                else:
                    # may be a catalog file with no entry oint names
                    openType = ARCHIVE  # no entry points to show, just archive
                    self.showAltViewButton = False
            except Exception as e:
                self.close()
                err = _(
                    "Failed to parse metadata; the underlying error was: {0}"
                ).format(e)
                messagebox.showerror(_("Malformed taxonomy package"), err)
                cntlr.addToLog(err)
                return

        if openType not in (PLUGIN, PACKAGE):
            cntlr.showStatus(None)

        if openType in (DISCLOSURE_SYSTEM, PLUGIN, PACKAGE):
            y = 3
        else:
            y = 1

        okButton = Button(frame, text=_("OK"), command=self.ok)
        cancelButton = Button(frame, text=_("Cancel"), command=self.close)
        okButton.grid(row=y, column=2, sticky=(S, E, W), pady=3)
        cancelButton.grid(row=y, column=3, sticky=(S, E, W), pady=3, padx=3)

        if self.showAltViewButton:
            self.altViewButton = Button(frame, command=self.showAltView)
            self.altViewButton.grid(row=y,
                                    column=0,
                                    sticky=(S, W),
                                    pady=3,
                                    padx=3)

        self.loadTreeView(openType, colHeader, title)

        self.geometry("+{0}+{1}".format(dialogX + 50, dialogY + 100))
        frame.grid(row=0, column=0, sticky=(N, S, E, W))
        frame.columnconfigure(0, weight=1)
        frame.rowconfigure(0, weight=1)
        window = self.winfo_toplevel()
        window.columnconfigure(0, weight=1)
        window.rowconfigure(0, weight=1)

        self.bind("<Return>", self.ok)
        self.bind("<Escape>", self.close)

        self.toolTipText = StringVar()
        if self.hasToolTip:
            self.treeView.bind("<Motion>", self.motion, '+')
            self.treeView.bind("<Leave>", self.leave, '+')
            self.toolTipText = StringVar()
            self.toolTip = ToolTip(self.treeView,
                                   textvariable=self.toolTipText,
                                   wraplength=640,
                                   follow_mouse=True,
                                   state="disabled")
            self.toolTipRowId = None

        self.protocol("WM_DELETE_WINDOW", self.close)
        self.grab_set()

        self.wait_window(self)

    def loadTreeView(self, openType, title, colHeader):
        self.title(title)
        self.openType = openType
        selectedNode = None

        # clear previous treeview entries
        for previousNode in self.treeView.get_children(""):
            self.treeView.delete(previousNode)

        # set up treeView widget and tabbed pane
        if openType in (ARCHIVE, DISCLOSURE_SYSTEM, PLUGIN, PACKAGE):
            if openType in (PLUGIN, PACKAGE): width = 770
            else: width = 500
            self.treeView.column("#0", width=width, anchor="w")
            self.treeView.heading("#0", text=colHeader)
            self.isRss = getattr(self.filesource, "isRss", False)
            if self.isRss:
                self.treeView.column("#0", width=350, anchor="w")
                self.treeView["columns"] = ("descr", "date", "instDoc")
                self.treeView.column("descr",
                                     width=50,
                                     anchor="center",
                                     stretch=False)
                self.treeView.heading("descr", text="Form")
                self.treeView.column("date",
                                     width=170,
                                     anchor="w",
                                     stretch=False)
                self.treeView.heading("date", text="Pub Date")
                self.treeView.column("instDoc",
                                     width=200,
                                     anchor="w",
                                     stretch=False)
                self.treeView.heading("instDoc", text="Instance Document")
            elif openType == PLUGIN:
                self.treeView.column("#0", width=150, anchor="w")
                self.treeView["columns"] = ("name", "vers", "descr", "license")
                self.treeView.column("name",
                                     width=150,
                                     anchor="w",
                                     stretch=False)
                self.treeView.heading("name", text="Name")
                self.treeView.column("vers",
                                     width=60,
                                     anchor="w",
                                     stretch=False)
                self.treeView.heading("vers", text="Version")
                self.treeView.column("descr",
                                     width=300,
                                     anchor="w",
                                     stretch=False)
                self.treeView.heading("descr", text="Description")
                self.treeView.column("license",
                                     width=60,
                                     anchor="w",
                                     stretch=False)
                self.treeView.heading("license", text="License")
            elif openType == PACKAGE:
                self.treeView.column("#0", width=200, anchor="w")
                self.treeView["columns"] = ("vers", "descr", "license")
                self.treeView.column("vers",
                                     width=100,
                                     anchor="w",
                                     stretch=False)
                self.treeView.heading("vers", text="Version")
                self.treeView.column("descr",
                                     width=400,
                                     anchor="w",
                                     stretch=False)
                self.treeView.heading("descr", text="Description")
                self.treeView.column("license",
                                     width=70,
                                     anchor="w",
                                     stretch=False)
                self.treeView.heading("license", text="License")
            else:
                self.treeView["columns"] = tuple()

            loadedPaths = []
            for i, filename in enumerate(self.filenames):
                if isinstance(filename, tuple):
                    if self.isRss:
                        form, date, instDoc = filename[2:5]
                    elif openType == PLUGIN:
                        name, vers, descr, license = filename[3:7]
                    elif openType == PACKAGE:
                        vers, descr, license = filename[3:6]
                    filename = filename[0]  # ignore tooltip
                    self.hasToolTip = True
                if filename.endswith("/"):
                    filename = filename[:-1]
                path = filename.split("/")
                if not self.isRss and len(
                        path) > 1 and path[:-1] in loadedPaths:
                    parent = "file{0}".format(loadedPaths.index(path[:-1]))
                else:
                    parent = ""
                node = self.treeView.insert(parent,
                                            "end",
                                            "file{0}".format(i),
                                            text=path[-1])
                if self.isRss:
                    self.treeView.set(node, "descr", form)
                    self.treeView.set(node, "date", date)
                    self.treeView.set(node, "instDoc",
                                      os.path.basename(instDoc))
                elif openType == PLUGIN:
                    self.treeView.set(node, "name", name)
                    self.treeView.set(node, "vers", vers)
                    self.treeView.set(node, "descr", descr)
                    self.treeView.set(node, "license", license)
                elif openType == PACKAGE:
                    self.treeView.set(node, "vers", vers)
                    self.treeView.set(node, "descr", descr)
                    self.treeView.set(node, "license", license)
                if self.selection == filename:
                    selectedNode = node
                loadedPaths.append(path)

        elif openType == ENTRY_POINTS:
            self.treeView.column("#0", width=200, anchor="w")
            self.treeView.heading("#0", text="Name")

            self.treeView["columns"] = ("url", )
            self.treeView.column("url", width=300, anchor="w")
            self.treeView.heading("url", text="URL")

            for fileType, fileUrl in getattr(self, "packageContainedInstances",
                                             ()):
                self.treeView.insert("",
                                     "end",
                                     fileUrl,
                                     values=fileType,
                                     text=fileUrl or urls[0][2])
            for name, urls in sorted(
                    self.taxonomyPackage["entryPoints"].items(),
                    key=lambda i: i[0][2]):
                self.treeView.insert("",
                                     "end",
                                     name,
                                     values="\n".join(url[1] for url in urls),
                                     text=name or urls[0][2])

            self.hasToolTip = True
        else:  # unknown openType
            return None
        if selectedNode:
            self.treeView.see(selectedNode)
            self.treeView.selection_set(selectedNode)

        if self.showAltViewButton:
            self.altViewButton.config(text=_("Show Files") if openType ==
                                      ENTRY_POINTS else _("Show Entries"))

    def ok(self, event=None):
        selection = self.treeView.selection()
        if len(selection) > 0:
            if hasattr(self, "taxonomyPackage"):
                # load file source remappings
                self.filesource.mappedPaths = self.taxonomyPackage[
                    "remappings"]
            filename = None
            if self.openType in (ARCHIVE, DISCLOSURE_SYSTEM):
                filename = self.filenames[int(selection[0][4:])]
                if isinstance(filename, tuple):
                    if self.isRss:
                        filename = filename[4]
                    else:
                        filename = filename[0]
            elif self.openType == ENTRY_POINTS:
                epName = selection[0]
                #index 0 is the remapped Url, as opposed to the canonical one used for display
                # Greg Acsone reports [0] does not work for Corep 1.6 pkgs, need [1], old style packages
                filenames = []
                for _url, _type in self.packageContainedInstances:  # check if selection was an inline instance
                    if _type == epName:
                        filenames.append(_url)
                if not filenames:  # else if it's a named taxonomy entry point
                    for url in self.taxonomyPackage["entryPoints"][epName]:
                        filename = url[0]
                        if not filename.endswith("/"):
                            # check if it's an absolute URL rather than a path into the archive
                            if not isHttpUrl(
                                    filename
                            ) and self.metadataFilePrefix != self.taxonomyPkgMetaInf:
                                # assume it's a path inside the archive:
                                filename = self.metadataFilePrefix + filename
                        filenames.append(filename)
                if filenames:
                    self.filesource.select(filenames)
                    self.accepted = True
                    self.close()
                return
            elif self.openType in (PLUGIN, PACKAGE):
                filename = self.filenames[int(selection[0][4:])][2]
            if filename is not None and not filename.endswith("/"):
                if hasattr(self, "taxonomyPackage"):
                    # attempt to unmap the filename to original file
                    # will be mapped again in loading, but this allows schemaLocation to be unmapped
                    for prefix, remapping in self.taxonomyPackage[
                            "remappings"].items():
                        if isHttpUrl(remapping):
                            remapStart = remapping
                        else:
                            remapStart = self.metadataFilePrefix + remapping
                        if filename.startswith(remapStart):
                            # set unmmapped file
                            filename = prefix + filename[len(remapStart):]
                            break
                if self.openType in (PLUGIN, PACKAGE):
                    self.filesource.selection = filename
                else:
                    self.filesource.select(filename)
                self.accepted = True
                self.close()

    def close(self, event=None):
        self.parent.focus_set()
        self.destroy()

    def showAltView(self, event=None):
        if self.openType == ENTRY_POINTS:
            self.loadTreeView(ARCHIVE, _("Select Entry Point"), _("File"))
        else:
            self.loadTreeView(ENTRY_POINTS, _("Select Archive File"),
                              _("File"))

    def leave(self, *args):
        self.toolTipRowId = None

    def motion(self, *args):
        tvRowId = self.treeView.identify_row(args[0].y)
        if tvRowId != self.toolTipRowId:
            text = None
            if self.openType in (ARCHIVE, DISCLOSURE_SYSTEM, PLUGIN, PACKAGE):
                self.toolTipRowId = tvRowId
                if tvRowId and len(tvRowId) > 4:
                    try:
                        text = self.filenames[int(tvRowId[4:])]
                        if isinstance(text, tuple):
                            text = (text[1] or "").replace("\\n", "\n")
                    except (KeyError, ValueError):
                        pass
            elif self.openType == ENTRY_POINTS:
                try:
                    text = "{0}\n{1}".format(
                        tvRowId, "\n".join(
                            url[1] for url in
                            self.taxonomyPackage["entryPoints"][tvRowId]))
                except KeyError:
                    pass
            self.setToolTip(text)

    def setToolTip(self, text):
        self.toolTip._hide()
        if text:
            self.toolTipText.set(text)
            self.toolTip.configure(state="normal")
            self.toolTip._schedule()
        else:
            self.toolTipText.set("")
            self.toolTip.configure(state="disabled")
Exemple #8
0
class FormChildAED:
    def __init__(self, frm_parent, title, connection):
        self.connection = connection
        self.directive = Message()
        self.title = title
        self.decide = True
        self.id_selected = 0
        self.frm_child_list = LabelFrame(frm_parent)
        self.frm_child_crud = LabelFrame(frm_parent)
        self.frm_child_crud.config(fg=TEXT_COLOR, font=SUBTITLE_FONT)
        self.initialize_components()

    def initialize_components(self):
        """
        Method that initialize the visual components for each form associated with the local administration
        """
        # Resources for the Forms
        self.new_icon = PhotoImage(file=r"./Resources/create.png")
        self.modify_icon = PhotoImage(file=r"./Resources/modify.png")
        self.remove_icon = PhotoImage(file=r"./Resources/delete.png")
        self.save_icon = PhotoImage(file=r"./Resources/save.png")
        self.cancel_icon = PhotoImage(file=r"./Resources/cancel.png")

        # Components for List Form
        lbl_sep1 = Label(self.frm_child_list)
        lbl_sep1.grid(row=0, column=0, padx=10, pady=25)
        self.trv_available = Treeview(self.frm_child_list,
                                      height=15,
                                      columns=('N', 'Name', 'Surname',
                                               'E-mail'))
        self.trv_available.heading('#0', text='ID', anchor=CENTER)
        self.trv_available.heading('#1', text='N', anchor=CENTER)
        self.trv_available.heading('#2', text='Name', anchor=CENTER)
        self.trv_available.heading('#3', text='Surname', anchor=CENTER)
        self.trv_available.heading('#4', text='E-mail', anchor=CENTER)
        self.trv_available.column('#0', width=0, minwidth=50, stretch=NO)
        self.trv_available.column('#1', width=20, minwidth=20, stretch=NO)
        self.trv_available.column('#2', width=200, minwidth=200, stretch=NO)
        self.trv_available.column('#3', width=200, minwidth=200, stretch=NO)
        self.trv_available.column('#4', width=400, minwidth=400, stretch=NO)
        self.trv_available.grid(row=0, column=1, sticky=W, pady=25)
        vsb_trv_av = Scrollbar(self.frm_child_list,
                               orient="vertical",
                               command=self.trv_available.yview)
        vsb_trv_av.grid(row=0, column=2, pady=25, sticky=NS)
        self.trv_available.configure(yscrollcommand=vsb_trv_av.set)
        frm_aux4 = Frame(self.frm_child_list)
        btn_new = Button(frm_aux4, image=self.new_icon, command=self.click_new)
        btn_new.grid(row=0, column=0, pady=5, padx=5, sticky=E)
        btn_new_ttp = CreateToolTip(btn_new, 'New ' + self.title.lower())
        btn_edit = Button(frm_aux4,
                          image=self.modify_icon,
                          command=self.click_update)
        btn_edit.grid(row=1, column=0, pady=5, padx=5, sticky=E)
        btn_edit_ttp = CreateToolTip(btn_edit, 'Edit ' + self.title.lower())
        btn_delete = Button(frm_aux4,
                            image=self.remove_icon,
                            command=self.click_delete)
        btn_delete.grid(row=2, column=0, pady=5, padx=5, sticky=E)
        btn_delete_ttp = CreateToolTip(btn_delete,
                                       'Delete ' + self.title.lower())
        frm_aux4.grid(row=0, column=3, pady=25, padx=25, sticky=NW)

        # Components for CRUD FRM
        lbl_name = Label(self.frm_child_crud, text='Name*')
        lbl_name.config(fg=TEXT_COLOR, font=LABEL_FONT)
        lbl_name.grid(row=0, column=0, pady=10, padx=20, sticky=W)
        lbl_surname = Label(self.frm_child_crud, text='Surname*')
        lbl_surname.config(fg=TEXT_COLOR, font=LABEL_FONT)
        lbl_surname.grid(row=1, column=0, pady=10, padx=20, sticky=W)
        lbl_email = Label(self.frm_child_crud, text='E-mail*')
        lbl_email.config(fg=TEXT_COLOR, font=LABEL_FONT)
        lbl_email.grid(row=2, column=0, pady=10, padx=20, sticky=W)
        self.lbl_old_passwd = Label(self.frm_child_crud, text='Old password*')
        self.lbl_old_passwd.config(fg=TEXT_COLOR, font=LABEL_FONT)
        self.lbl_passwd = Label(self.frm_child_crud, text='New password*')
        self.lbl_passwd.config(fg=TEXT_COLOR, font=LABEL_FONT)
        self.lbl_passwd_conf = Label(self.frm_child_crud,
                                     text='Confirm new password*')
        self.lbl_passwd_conf.config(fg=TEXT_COLOR, font=LABEL_FONT)
        self.txt_name = Entry(self.frm_child_crud)
        self.txt_name.grid(row=0, column=1, pady=10, padx=20, sticky=W)
        self.txt_surname = Entry(self.frm_child_crud)
        self.txt_surname.grid(row=1, column=1, pady=10, padx=20, sticky=W)
        self.txt_email = Entry(self.frm_child_crud)
        self.txt_email.grid(row=2, column=1, pady=10, padx=20, sticky=W)
        self.txt_old_passwd = Entry(self.frm_child_crud, show="*")
        self.txt_passwd = Entry(self.frm_child_crud, show="*")
        self.txt_passwd_conf = Entry(self.frm_child_crud, show="*")
        sep_aux2 = Separator(self.frm_child_crud, orient=VERTICAL)
        sep_aux2.grid(row=0, column=2, sticky=NS, rowspan=6)
        frm_aux = Frame(self.frm_child_crud)
        btn_save = Button(frm_aux,
                          image=self.save_icon,
                          command=self.click_save)
        btn_save.grid(row=0, column=0, padx=5, pady=5, sticky=E)
        btn_save_ttp = CreateToolTip(btn_save, 'Save ' + self.title.lower())
        btn_cancel = Button(frm_aux,
                            image=self.cancel_icon,
                            command=self.click_cancel)
        btn_cancel.grid(row=1, column=0, padx=5, pady=5, sticky=E)
        btn_cancel_ttp = CreateToolTip(btn_cancel, 'Cancel')
        frm_aux.grid(row=0, column=3, pady=10, padx=25, sticky=N, rowspan=6)

    def retrieve_list(self):
        """
        Method that retrieve users information from the server and displays them in the TreeView from
        the List Form
        """
        # Remove existing elements in the list
        for item in self.trv_available.get_children():
            self.trv_available.delete(item)
        # Retrieve information from the server
        if self.title == 'Experimenter':
            self.directive = Message(action=17)
        elif self.title == 'Designer':
            self.directive = Message(action=22)
        elif self.title == 'Administrator':
            self.directive = Message(action=12)
        else:
            raise Exception('Error en recuperacion: tipo de usuario')
        self.connection = self.directive.send_directive(self.connection)
        # Adding elements in the list
        for index, item in enumerate(self.connection.message.information):
            elements = item.split('¥')
            self.trv_available.insert('',
                                      'end',
                                      text=elements[0],
                                      values=(index + 1, elements[1],
                                              elements[2], elements[3]))
        # Mark first element of the treeview if exist
        if len(self.trv_available.get_children()) != 0:
            self.trv_available.selection_set(
                self.trv_available.get_children()[0])

    def show_frm(self):
        """
        Show the List form when the User administration is called
        """
        self.retrieve_list()
        self.frm_child_list.grid(row=1,
                                 column=0,
                                 columnspan=9,
                                 rowspan=8,
                                 pady=10,
                                 padx=10)

    def hide_frm(self):
        """
        Hide the User administration Forms
        """
        self.clear_fields()
        self.frm_child_list.grid_forget()
        self.frm_child_crud.grid_forget()

    def click_new(self):
        """
        Initialize CRUD Form for creating a new user.
        """
        self.user = Designer()
        self.frm_child_list.grid_forget()
        self.txt_name.focus_set()
        self.frm_child_crud['text'] = 'New ' + self.title.lower()
        self.lbl_passwd.grid(row=3, column=0, pady=10, padx=20, sticky=W)
        self.lbl_passwd_conf.grid(row=4, column=0, pady=10, padx=20, sticky=W)
        self.txt_passwd.grid(row=3, column=1, pady=10, padx=20, sticky=W)
        self.txt_passwd_conf.grid(row=4, column=1, pady=10, padx=20, sticky=W)
        self.frm_child_crud.grid(row=1,
                                 column=0,
                                 columnspan=9,
                                 rowspan=8,
                                 pady=10,
                                 padx=10)

    def click_update(self):
        """
        Initialize CRUD Form for updating a user. It loads information of selected User into visual components
        """
        if len(self.trv_available.selection()) == 1:
            id_selected = int(
                self.trv_available.item(
                    self.trv_available.selection())['text'])
            if self.title == 'Experimenter':
                self.directive = Message(action=20, information=[id_selected])
            elif self.title == 'Designer':
                self.directive = Message(action=25, information=[id_selected])
            else:
                self.directive = Message(action=15, information=[id_selected])
            self.connection = self.directive.send_directive(self.connection)
            if self.connection.message.action == 5:  # An error ocurred while trying to update the item
                messagebox.showerror(
                    parent=self.frm_child_list,
                    title='Can not update the item',
                    message=self.connection.message.information[0])
            else:
                self.user = Designer(
                    id=id_selected,
                    name=self.connection.message.information[0],
                    surname=self.connection.message.information[1],
                    user=self.connection.message.information[2],
                    password=self.connection.message.information[3])
                self.txt_name.insert(0, self.user.name)
                self.txt_surname.insert(0, self.user.surname)
                self.txt_email.insert(0, self.user.user)
                self.frm_child_list.grid_forget()
                self.txt_name.focus_set()
                self.frm_child_crud['text'] = 'Update ' + self.title.lower()
                self.lbl_old_passwd.grid(row=3,
                                         column=0,
                                         pady=10,
                                         padx=20,
                                         sticky=W)
                self.lbl_passwd.grid(row=4,
                                     column=0,
                                     pady=10,
                                     padx=20,
                                     sticky=W)
                self.lbl_passwd_conf.grid(row=5,
                                          column=0,
                                          pady=10,
                                          padx=20,
                                          sticky=W)
                self.txt_old_passwd.grid(row=3,
                                         column=1,
                                         pady=10,
                                         padx=20,
                                         sticky=W)
                self.txt_passwd.grid(row=4,
                                     column=1,
                                     pady=10,
                                     padx=20,
                                     sticky=W)
                self.txt_passwd_conf.grid(row=5,
                                          column=1,
                                          pady=10,
                                          padx=20,
                                          sticky=W)
                self.frm_child_crud.grid(row=1,
                                         column=0,
                                         columnspan=9,
                                         rowspan=8,
                                         pady=10,
                                         padx=10)
        else:
            messagebox.showwarning(parent=self.frm_child_list,
                                   title='No selection',
                                   message='You must select one item')

    def click_delete(self):
        """
        Method that removes a selected user from the initial list (changes are updated in DB)
        """
        if len(self.trv_available.selection()) == 1:
            decision = messagebox.askyesno(
                parent=self.frm_child_list,
                title='Confirmation',
                message='Are you sure you want to delete the item?')
            if decision:
                id_selected = int(
                    self.trv_available.item(
                        self.trv_available.selection())['text'])
                if self.title == 'Experimenter':
                    self.directive = Message(action=19,
                                             information=[id_selected])
                elif self.title == 'Designer':
                    self.directive = Message(action=24,
                                             information=[id_selected])
                else:
                    self.directive = Message(action=14,
                                             information=[id_selected])
                self.connection = self.directive.send_directive(
                    self.connection)
                if self.connection.message.action == 5:  # An error ocurred while deleting the item
                    messagebox.showerror(
                        parent=self.frm_child_list,
                        title='Can not delete the item',
                        message=self.connection.message.information[0])
                else:
                    self.retrieve_list()
        else:
            messagebox.showwarning(parent=self.frm_child_list,
                                   title='No selection',
                                   message='You must select one item')

    def click_save(self):
        """
        Saves information of the user inserted into the visual components and sends to the server
        """
        if self.validate_fields():
            self.user.name = self.txt_name.get()
            self.user.surname = self.txt_surname.get()
            self.user.user = self.txt_email.get()
            self.user.password = self.txt_passwd.get()
            if self.user.id == 0:  # Creating an user
                if self.title == 'Experimenter':
                    self.directive = Message(
                        action=16,
                        information=[
                            self.user.name, self.user.surname, self.user.user,
                            hashlib.sha1(
                                self.user.password.encode()).hexdigest()
                        ])
                elif self.title == 'Designer':
                    self.directive = Message(
                        action=21,
                        information=[
                            self.user.name, self.user.surname, self.user.user,
                            hashlib.sha1(
                                self.user.password.encode()).hexdigest()
                        ])
                else:
                    self.directive = Message(
                        action=11,
                        information=[
                            self.user.name, self.user.surname, self.user.user,
                            hashlib.sha1(
                                self.user.password.encode()).hexdigest()
                        ])
            else:  # Updating an user
                if self.title == 'Experimenter':
                    self.directive = Message(
                        action=18,
                        information=[
                            self.user.id, self.user.name, self.user.surname,
                            self.user.user,
                            hashlib.sha1(
                                self.user.password.encode()).hexdigest()
                        ])
                elif self.title == 'Designer':
                    self.directive = Message(
                        action=23,
                        information=[
                            self.user.id, self.user.name, self.user.surname,
                            self.user.user,
                            hashlib.sha1(
                                self.user.password.encode()).hexdigest()
                        ])
                else:
                    self.directive = Message(action=13,
                                             information=[
                                                 self.user.id, self.user.name,
                                                 self.user.surname,
                                                 self.user.user,
                                                 self.user.password
                                             ])
            self.connection = self.directive.send_directive(self.connection)
            if self.connection.message.action == 5:
                messagebox.showwarning(parent=self.frm_child_crud,
                                       title='Repeated e-mail',
                                       message=self.connection.message.comment)
            else:
                self.clear_fields()
                self.frm_child_crud.grid_forget()
                self.show_frm()

    def click_cancel(self):
        """
        Function activated when 'Cancel' button is pressed in frm_child_crud
        """
        decision = True
        if self.txt_name.get() != self.user.name or \
                self.txt_surname.get() != self.user.surname or \
                self.txt_email.get() != self.user.user or len(self.txt_passwd.get()) != 0 or \
                len(self.txt_passwd_conf.get()) != 0:
            if self.user.id != 0 and len(
                    self.txt_passwd_conf.get()) != 0 or self.user.id == 0:
                decision = messagebox.askyesno(
                    parent=self.frm_child_crud,
                    title='Cancel',
                    message='Are you sure you want to cancel?')
        if decision:
            self.clear_fields()
            self.frm_child_crud.grid_forget()
            self.show_frm()

    def validate_fields(self):
        if len(self.txt_name.get()) == 0:
            messagebox.showwarning(
                parent=self.frm_child_crud,
                title='Missing information',
                message='You must insert a name for the {}'.format(
                    self.title.lower()))
            return False
        if len(self.txt_surname.get()) == 0:
            messagebox.showwarning(
                parent=self.frm_child_crud,
                title='Missing information',
                message='You must insert a surname for the {}'.format(
                    self.title.lower()))
            return False
        if len(self.txt_email.get()) == 0:
            messagebox.showwarning(
                parent=self.frm_child_crud,
                title='Missing information',
                message='You must insert an e-mail for the {}'.format(
                    self.title.lower()))
            return False
        # If updating an user
        if self.user.id != 0 and len(self.txt_old_passwd.get()) == 0:
            messagebox.showwarning(
                parent=self.frm_child_crud,
                title='Missing information',
                message='You must insert the old password for the {}'.format(
                    self.title.lower()))
            return False
        if len(self.txt_passwd.get()) == 0:
            messagebox.showwarning(
                parent=self.frm_child_crud,
                title='Missing information',
                message='You must insert a new password for the {}'.format(
                    self.title.lower()))
            return False
        if len(self.txt_passwd_conf.get()) == 0:
            messagebox.showwarning(
                parent=self.frm_child_crud,
                title='Missing information',
                message='You must confirm the new password for the {}'.format(
                    self.title.lower()))
            return False
        if self.txt_passwd.get() != self.txt_passwd_conf.get():
            messagebox.showwarning(
                parent=self.frm_child_crud,
                title='Password field',
                message=
                'The new password you provided does not match the confirmation'
            )
            return False
        # If updating an user
        if self.user.id != 0 and self.user.password != hashlib.sha1(
                self.txt_old_passwd.get().encode()).hexdigest():
            messagebox.showwarning(parent=self.frm_child_crud,
                                   title='Old password field',
                                   message='The old password is incorrect')
            return False
        return True

    def clear_fields(self):
        self.txt_name.delete(0, END)
        self.txt_surname.delete(0, END)
        self.txt_email.delete(0, END)
        self.txt_old_passwd.delete(0, END)
        self.txt_passwd.delete(0, END)
        self.txt_passwd_conf.delete(0, END)
        self.lbl_old_passwd.grid_forget()
        self.lbl_passwd.grid_forget()
        self.lbl_passwd_conf.grid_forget()
        self.txt_old_passwd.grid_forget()
        self.txt_passwd.grid_forget()
        self.txt_passwd_conf.grid_forget()
Exemple #9
0
    def listchans(self,
                  index=None,
                  tagsearch="",
                  archived=0,
                  ob=Channel.displayname,
                  so=asc):
        self.center = Frame(root,
                            bg=config.bgcolor,
                            width=50,
                            height=40,
                            padx=3,
                            pady=3)

        # layout all of the main containers
        root.grid_rowconfigure(1, weight=1)
        root.grid_columnconfigure(0, weight=1)
        self.center.grid(row=1, sticky="nsew")

        channels = database.get_channels(archived, ob, so)
        if so == desc:
            so = asc
        else:
            so = desc

        tree = Treeview(self.center)

        sstring = Entry(self.center, width=config.textBoxWidth)
        sstring.bind(
            "<KeyRelease-Return>",
            lambda e: self.listchans(None, sstring.get(), archived, ob, so))
        sstring.grid(column=0, row=0)
        if len(tagsearch) >= 1:
            sstring.focus()
            sstring.insert(0, tagsearch)
        searchbutton = Button(self.center,
                              text="Search",
                              command=lambda: self.listchans(
                                  None, sstring.get(), archived, ob, so))
        searchbutton.grid(column=1, row=0)

        clearbutton = Button(self.center, text="Clear Search")
        clearbutton.configure(command=lambda: self.listchans(archived))
        clearbutton.grid(column=3, row=0)

        tree["columns"] = ("one", "two")

        tree.column("#0", width=210, minwidth=10, stretch=YES)
        tree.column("one", width=350, minwidth=250, stretch=YES)
        tree.column("two", width=210, minwidth=10, stretch=YES)

        tree.heading("#0",
                     text="Last Check",
                     anchor=W,
                     command=lambda: self.listchans(None, sstring.get(
                     ), archived, Channel.lastcheck, so))

        tree.heading("one",
                     text="Channel Name",
                     anchor=E,
                     command=lambda: self.listchans(None, sstring.get(
                     ), archived, Channel.displayname, so))
        tree.heading("two",
                     text="Last Published",
                     command=lambda: self.listchans(None, sstring.get(
                     ), archived, Channel.lastpub, so))
        i = 0
        tree.tag_configure('oddrow', background='#88DD88')
        tree.tag_configure('evenrow', background='#FFFFFF')
        tree.tag_configure('archivedodd',
                           background="#88DD88",
                           foreground="#aaaaaa")
        tree.tag_configure('archivedeven',
                           background='#FFFFFF',
                           foreground="#cccccc")

        for item in channels:
            foldername = "folder" + str(i)

            if i % 2 == 0:
                color = "evenrow"
            else:
                color = "oddrow"

            if item.archive == True:
                if i % 2 == 0:
                    color = "archivedeven"
                else:
                    color = "archivedodd"

            if tagsearch.lower() in str(
                    item.displayname).lower() or tagsearch.lower() in str(
                        item.dldir).lower() or tagsearch.lower() in str(
                            item.yt_channelid).lower():
                if item.lastpub == None:
                    lastpub = "N/A"
                else:
                    lastpub = time.ctime(item.lastpub)

                foldername = tree.insert("",
                                         "end",
                                         text=time.ctime(item.lastcheck),
                                         values=(item.displayname, lastpub),
                                         tags=(color, item.displayname,
                                               item.dldir, item.yt_channelid))

                tree.insert(foldername,
                            "end",
                            text="Directory",
                            values=(item.dldir, ),
                            tags=(color))

                tree.insert(foldername,
                            "end",
                            text="Last Published",
                            values=(lastpub, ),
                            tags=(color))

                i = i + 1

        vertscroll = Scrollbar(self.center)
        vertscroll.config(command=tree.yview)
        tree.config(yscrollcommand=vertscroll.set, height=20)
        vertscroll.grid(column=4, row=1, sticky='NSE')
        tree.bind("<Double-1>", self.item_selected)

        tree.grid(row=1, columnspan=4, sticky="NSEW")
        tree.focus(index)
        tree.selection_set(index)
        tree.see(index)
class FormParentDesigner:
    def __init__(self, window, connection, current_designer):
        self.connection = connection
        self.current_designer = current_designer
        self.frm_parent = LabelFrame(window)
        self.tlevel_auth_scenario = Toplevel(window)
        self.tlevel_auth_scenario.protocol("WM_DELETE_WINDOW", self.click_authenticate_cancel)
        self.tlevel_auth_scenario.withdraw()
        self.frm_general = LabelFrame(window)
        self.tlevel_image_exp = Toplevel(window)
        self.tlevel_image_exp.title('Diagram')
        self.tlevel_image_exp.protocol("WM_DELETE_WINDOW", self.close_tlevel_image)
        self.tlevel_image_exp.withdraw()
        self.initialize_components()

    def initialize_components(self):
        # Resources for the Forms
        self.new_icon = PhotoImage(file=r"./Resources/create.png")
        self.modify_icon = PhotoImage(file=r"./Resources/modify.png")
        self.remove_icon = PhotoImage(file=r"./Resources/delete.png")
        self.save_icon = PhotoImage(file=r"./Resources/save.png")
        self.cancel_icon = PhotoImage(file=r"./Resources/cancel.png")
        self.add_icon = PhotoImage(file=r"./Resources/down_arrow.png")
        self.delete_icon = PhotoImage(file=r"./Resources/up_arrow.png")
        self.next_icon = PhotoImage(file=r"./Resources/next.png")
        self.back_icon = PhotoImage(file=r"./Resources/back.png")
        self.copy_icon = PhotoImage(file=r"./Resources/copy.png")
        self.open_icon = PhotoImage(file=r"./Resources/open.png")
        self.complete_icon = PhotoImage(file=r"./Resources/complete.png")
        self.incomplete_icon = PhotoImage(file=r"./Resources/incomplete.png")
        self.optional_icon = PhotoImage(file=r"./Resources/optional.png")
        self.refresh_icon = PhotoImage(file=r"./Resources/refresh.png")

        # Initialize visual components for displaying available experiment scenarios
        lbl_title = Label(self.frm_parent, text='Experiments')
        lbl_title.config(fg=TEXT_COLOR, font=TITLE_FONT)
        lbl_title.grid(row=0, column=0, columnspan=9, pady=20, sticky=EW)
        sep_aux5 = Separator(self.frm_parent, orient=HORIZONTAL)
        sep_aux5.grid(row=1, column=0, sticky=EW, columnspan=9)
        lbl_sep1 = Label(self.frm_parent)
        lbl_sep1.grid(row=2, column=0, padx=10, pady=20, rowspan=2)
        lbl_experimental_trv = Label(self.frm_parent, text='Select an experiment')
        lbl_experimental_trv.config(fg=TEXT_COLOR, font=SUBTITLE2_FONT)
        lbl_experimental_trv.grid(row=2, column=1, pady=20, sticky=W)
        lbl_sep2 = Label(self.frm_parent)
        lbl_sep2.grid(row=2, column=3, padx=10, pady=20, rowspan=2)
        lbl_problem_desc = Label(self.frm_parent, text='Information')
        lbl_problem_desc.config(fg=TEXT_COLOR, font=SUBTITLE2_FONT)
        lbl_problem_desc.grid(row=2, column=4, pady=20, sticky=W)
        lbl_sep3 = Label(self.frm_parent)
        lbl_sep3.grid(row=2, column=6, padx=10, pady=20, rowspan=2)
        self.trv_available = Treeview(self.frm_parent, height=11, columns='Title')
        self.trv_available.heading('#0', text='ID', anchor=CENTER)
        self.trv_available.heading('#1', text='Title', anchor=CENTER)
        self.trv_available.column('#0', width=0, minwidth=50, stretch=NO)
        self.trv_available.column('#1', width=400, minwidth=400, stretch=NO)
        self.trv_available.bind("<ButtonRelease-1>", self.select_experimental_scenario)
        self.trv_available.grid(row=3, column=1, sticky=W, pady=20)
        vsb_trv_av = Scrollbar(self.frm_parent, orient="vertical", command=self.trv_available.yview)
        vsb_trv_av.grid(row=3, column=2, pady=20, sticky=NS)
        self.trv_available.configure(yscrollcommand=vsb_trv_av.set)
        self.txt_scenario_desc = Text(self.frm_parent, height=15, width=40)
        self.txt_scenario_desc.config(font=TEXT_FONT, bg=DISABLED_COLOR)
        self.txt_scenario_desc.grid(row=3, column=4, pady=20, sticky=W)
        vsb_txt_sc = Scrollbar(self.frm_parent, orient="vertical", command=self.txt_scenario_desc.yview)
        vsb_txt_sc.grid(row=3, column=5, pady=20, sticky=NS)
        self.txt_scenario_desc.configure(yscrollcommand=vsb_txt_sc.set)
        sep_aux1 = Separator(self.frm_parent, orient=VERTICAL)
        sep_aux1.grid(row=2, column=7, sticky=NS, rowspan=2)
        btn_access = Button(self.frm_parent, image=self.next_icon, command=self.click_enter_scenario)
        btn_access.grid(row=2, column=8, padx=30, pady=5)
        btn_access_ttp = CreateToolTip(btn_access, 'Go')
        btn_refresh = Button(self.frm_parent, image=self.refresh_icon, command=self.retrieve_list)
        btn_refresh.grid(row=3, column=8, padx=30, pady=5, sticky=N)
        btn_refresh_ttp = CreateToolTip(btn_refresh, 'Refresh list')

        # Window dialog to authenticate the selected experimental scenario
        lbl_access_auth = Label(self.tlevel_auth_scenario, text='Insert access code: ')
        lbl_access_auth.config(fg=TEXT_COLOR, font=SUBTITLE2_FONT)
        lbl_access_auth.grid(pady=10, padx=20, sticky=W)
        self.txt_auth_scenario = Entry(self.tlevel_auth_scenario, width=50)
        self.txt_auth_scenario.config(font=SUBTITLE2_FONT, show="*")
        self.txt_auth_scenario.grid(row=0, column=1, padx=20, pady=10, sticky=W)
        btn_access_auth = Button(self.tlevel_auth_scenario, image=self.next_icon, command=self.click_authenticate_scenario)
        btn_access_auth.grid(row=0, column=2, padx=20, pady=10, sticky=W)
        btn_access_auth_ttp = CreateToolTip(btn_access_auth, 'Go')
        btn_cancel_auth = Button(self.tlevel_auth_scenario, image=self.cancel_icon, command=self.click_authenticate_cancel)
        btn_cancel_auth.grid(row=1, column=2, padx=20, pady=10, sticky=W)
        btn_cancel_auth_ttp = CreateToolTip(btn_cancel_auth, 'Cancel')

        # Experiment form
        lbl_sep4 = Label(self.frm_general)
        lbl_sep4.grid(row=0, column=0, padx=10, pady=5, rowspan=2)
        self.lbl_exp_title = Label(self.frm_general, text='Experiment: {}')
        self.lbl_exp_title.config(fg=TEXT_COLOR, font=TITLE_FONT)
        self.lbl_exp_title.grid(row=0, column=1, columnspan=5, pady=5, sticky=EW)
        self.txt_exp_desc = Text(self.frm_general, height=4, width=157)
        self.txt_exp_desc.config(font=TEXT_FONT, bg=DISABLED_COLOR)
        self.txt_exp_desc.grid(row=1, column=1, pady=5, sticky=W)
        vsb_txt_expd = Scrollbar(self.frm_general, orient="vertical", command=self.txt_exp_desc.yview)
        vsb_txt_expd.grid(row=1, column=2, pady=5, sticky=NS)
        self.txt_exp_desc.configure(yscrollcommand=vsb_txt_expd.set)
        lbl_sep11 = Label(self.frm_general)
        lbl_sep11.grid(row=1, column=3, padx=10, pady=5)
        self.btn_view_dd = Button(self.frm_general, text='View >>\ndiagram', command=self.click_expand_dd)
        btn_view_dd_ttp = CreateToolTip(self.btn_view_dd, 'Experiment description diagram')
        sep_aux3 = Separator(self.frm_general, orient=HORIZONTAL)
        sep_aux3.grid(row=2, column=0, sticky=EW, columnspan=6)

        lbl_sep5 = Label(self.frm_general)
        lbl_sep5.grid(row=3, column=0, padx=10, pady=5, rowspan=2)
        self.lbl_prob_title = Label(self.frm_general, text='Problem {} of {}: {}')
        self.lbl_prob_title.config(fg=TEXT_COLOR, font=SUBTITLE_FONT)
        self.lbl_prob_title.grid(row=3, column=1, columnspan=2, pady=5, sticky=W)
        self.txt_prob_desc = Text(self.frm_general, height=7, width=157)
        self.txt_prob_desc.config(font=TEXT_FONT, bg=DISABLED_COLOR)
        self.txt_prob_desc.grid(row=4, column=1, pady=5, sticky=W)
        vsb_txt_probd = Scrollbar(self.frm_general, orient="vertical", command=self.txt_prob_desc.yview)
        vsb_txt_probd.grid(row=4, column=2, pady=5, sticky=NS)
        self.txt_prob_desc.configure(yscrollcommand=vsb_txt_probd.set)
        lbl_sep12 = Label(self.frm_general)
        lbl_sep12.grid(row=3, column=3, padx=10, pady=5, rowspan=2)
        sep_aux2 = Separator(self.frm_general, orient=HORIZONTAL)
        sep_aux2.grid(row=5, column=0, sticky=EW, columnspan=4)
        lbl_sep6 = Label(self.frm_general)
        lbl_sep6.grid(row=6, column=0, padx=10, pady=5, rowspan=2)
        lbl_solution_title = Label(self.frm_general, text='Your solution')
        lbl_solution_title.config(fg=TEXT_COLOR, font=SUBTITLE_FONT)
        lbl_solution_title.grid(row=6, column=1, pady=5, columnspan=2, sticky=W)
        self.btn_next_scenario = Button(self.frm_general, image=self.next_icon, command=self.click_next_scenario)
        self.btn_next_scenario.grid(row=3, column=5, padx=30, pady=5, sticky=W)
        btn_next_scenario_ttp = CreateToolTip(self.btn_next_scenario, 'Next component')
        sep_aux4 = Separator(self.frm_general, orient=VERTICAL)
        sep_aux4.grid(row=3, column=4, sticky=NS, rowspan=5)

        self.tab_control = Notebook(self.frm_general)
        self.tab_patterns = Frame(self.tab_control)
        self.tab_control.add(self.tab_patterns, text="Design patterns", padding=10, image=self.incomplete_icon, compound=RIGHT)
        lbl_av_patterns = Label(self.tab_patterns, text='Patterns browser')
        lbl_av_patterns.config(fg=TEXT_COLOR, font=SUBTITLE2_FONT)
        lbl_av_patterns.grid(row=0, column=1, pady=10, columnspan=6, sticky=W)
        lbl_content = Label(self.tab_patterns, text='Pattern content')
        lbl_content.config(fg=TEXT_COLOR, font=SUBTITLE2_FONT)
        lbl_content.grid(row=0, column=8, pady=10, sticky=W)
        lbl_sel_patterns = Label(self.tab_patterns, text='Selected patterns')
        lbl_sel_patterns.config(fg=TEXT_COLOR, font=SUBTITLE2_FONT)
        lbl_sel_patterns.grid(row=3, column=1, pady=10, columnspan=6, sticky=W)
        lbl_sep7 = Label(self.tab_patterns)
        lbl_sep7.grid(row=0, column=0, padx=10, pady=10, rowspan=5)
        self.lbx_av_patterns = Listbox(self.tab_patterns, height=6, width=60, exportselection=0)
        self.lbx_av_patterns.grid(row=1, column=1, sticky=W, columnspan=5)
        self.lbx_av_patterns.bind('<<ListboxSelect>>', self.select_available_pattern)
        vsb_trv_avpat = Scrollbar(self.tab_patterns, orient="vertical", command=self.lbx_av_patterns.yview)
        vsb_trv_avpat.grid(row=1, column=6, pady=10, sticky=NS)
        self.lbx_av_patterns.configure(yscrollcommand=vsb_trv_avpat.set)
        lbl_sep8 = Label(self.tab_patterns)
        lbl_sep8.grid(row=0, column=7, padx=10, pady=10, rowspan=5)
        self.btn_view_pd = Button(self.tab_patterns, text='View diagram >>', command=self.click_expand_pd)
        btn_view_pd_ttp = CreateToolTip(self.btn_view_pd, 'Pattern section diagram')
        self.txt_pattern_content = Text(self.tab_patterns, height=18, width=85)
        self.txt_pattern_content.config(font=TEXT_FONT, bg=DISABLED_COLOR)
        self.txt_pattern_content.tag_configure("center", justify='center')
        self.txt_pattern_content.tag_add("center", 1.0, "end")
        self.txt_pattern_content.grid(row=1, column=8, sticky=W, columnspan=2, rowspan=4)
        vsb_txt_content = Scrollbar(self.tab_patterns, orient="vertical", command=self.txt_pattern_content.yview)
        vsb_txt_content.grid(row=1, column=10, rowspan=4, pady=10, sticky=NS)
        self.txt_pattern_content.configure(yscrollcommand=vsb_txt_content.set)
        btn_add = Button(self.tab_patterns, image=self.add_icon, command=self.click_add_patt)
        btn_add.grid(row=2, column=3, pady=10, sticky=E)
        btn_add_ttp = CreateToolTip(btn_add, 'Add pattern')
        btn_remove = Button(self.tab_patterns, image=self.delete_icon, command=self.click_remove_patt)
        btn_remove.grid(row=2, column=4, pady=10)
        btn_remove_ttp = CreateToolTip(btn_remove, 'Remove pattern')
        self.lbx_sel_patterns = Listbox(self.tab_patterns, height=6, width=60, exportselection=0)
        self.lbx_sel_patterns.grid(row=4, column=1, sticky=W, columnspan=5)
        vsb_trv_selpat = Scrollbar(self.tab_patterns, orient="vertical", command=self.lbx_sel_patterns.yview)
        vsb_trv_selpat.grid(row=4, column=6, pady=10, sticky=NS)
        self.lbx_sel_patterns.configure(yscrollcommand=vsb_trv_selpat.set)
        lbl_sep9 = Label(self.tab_patterns)
        lbl_sep9.grid(row=0, column=11, padx=10, pady=10, rowspan=5)

        self.tab_desc = Frame(self.tab_control)
        self.tab_control.add(self.tab_desc, text="Notes", padding=10, image=self.incomplete_icon, compound=RIGHT)
        lbl_sep10 = Label(self.tab_desc)
        lbl_sep10.grid(row=0, column=0, padx=10, pady=20)
        self.txt_solution_desc = Text(self.tab_desc, height=20, width=145)
        self.txt_solution_desc.config(font=TEXT_FONT)
        self.txt_solution_desc.bind("<Key>", self.txt_notes_modified)
        self.txt_solution_desc.grid(row=0, column=1, pady=20, sticky=W)
        vsb_txt_solution_desc = Scrollbar(self.tab_desc, orient="vertical", command=self.txt_solution_desc.yview)
        vsb_txt_solution_desc.grid(row=0, column=2, pady=20, sticky=NS)
        self.txt_solution_desc.configure(yscrollcommand=vsb_txt_solution_desc.set)
        lbl_sep14 = Label(self.tab_desc)
        lbl_sep14.grid(row=0, column=3, padx=10, pady=20)

        self.tab_file = Frame(self.tab_control)
        self.tab_control.add(self.tab_file, text="File", padding=1, image=self.optional_icon, compound=RIGHT)
        lbl_upload = Label(self.tab_file, text='Attach a file to the solution: ')
        lbl_upload.config(fg=TEXT_COLOR, font=SUBTITLE2_FONT)
        lbl_upload.grid(row=0, column=0, padx=20, pady=20, sticky=W)
        btn_open = Button(self.tab_file, image=self.open_icon, command=self.click_attach_file)
        btn_open.grid(row=1, column=0, padx=20, pady=10, sticky=E)
        btn_open_ttp = CreateToolTip(btn_open, 'Attach file')
        btn_quit = Button(self.tab_file, image=self.remove_icon, command=self.click_remove_file)
        btn_quit.grid(row=2, column=0, padx=20, pady=10, sticky=E)
        btn_quit_ttp = CreateToolTip(btn_quit, 'Remove file')
        self.canvas_solution = Canvas(self.tab_file, width=350, height=350)
        self.canvas_solution.config(background='white', borderwidth=1)
        self.canvas_solution.grid(row=0, column=1, padx=10, pady=10, rowspan=10, sticky=NS)
        self.tab_control.grid(row=7, column=1, pady=10, sticky=W, columnspan=2)

        self.canvas_expanded = Canvas(self.tlevel_image_exp, width=500, height=500)
        self.canvas_expanded.config(background='white', borderwidth=1)
        self.canvas_expanded.grid()

    def show_frm(self):
        self.retrieve_list()
        self.frm_parent.grid(row=0, column=0, columnspan=9, rowspan=9, pady=10, padx=10)

    def hide_frm(self):
        self.frm_parent.grid_forget()
        self.frm_general.grid_forget()

    def retrieve_list(self):
        """
        This function shows the existing 'Experimental scenarios for the specified designer' in the home TreeView, if
        not 'Experimental scenario' then an information box is showed
        """
        # Remove existing elements in the list
        for item in self.trv_available.get_children():
            self.trv_available.delete(item)
        # Retrieve scenario for current designer
        self.directive = Message(action=82, information=['my scenarios', self.current_designer.id])
        self.connection = self.directive.send_directive(self.connection)
        # Set visual components with retrieved scenarios
        for item in self.connection.message.information:
            elements = item.split('¥')
            self.trv_available.insert('', 'end', text=elements[0], values=(summarize_text(elements[1], 400),))
        if len(self.trv_available.get_children()) != 0:
            self.trv_available.selection_set(self.trv_available.get_children()[0])
            self.select_experimental_scenario()
        else:
            self.txt_scenario_desc['state'] = NORMAL
            self.txt_scenario_desc.delete('1.0', 'end-1c')
            self.txt_scenario_desc.insert('1.0', 'No experiments available for you')
            self.txt_scenario_desc['state'] = DISABLED

    def select_experimental_scenario(self, event=None):
        """
        Retrieves information of the selected experimental scenario form the server and saves in a local variable for
        future usage, also shows the description of the selected experimental scenario in a textbox
        """
        if self.trv_available.item(self.trv_available.selection())['text'] != '':
            id_selected_ex_scenario = int(self.trv_available.item(self.trv_available.selection())['text'])  # Retrieve id of selected item from TreeView
            self.directive = Message(action=85, information=[id_selected_ex_scenario])
            self.connection = self.directive.send_directive(self.connection)
            self.experimental_scenario = ExperimentalSC(id=id_selected_ex_scenario,
                                                        title=self.connection.message.information[0],
                                                        description=self.connection.message.information[1],
                                                        access_code=self.connection.message.information[2],
                                                        state=self.connection.message.information[3],
                                                        availability=self.connection.message.information[4],
                                                        id_experiment=self.connection.message.information[6],
                                                        id_description_diagram=self.connection.message.information[5],
                                                        connection=self.connection)
            if self.experimental_scenario.description_diagram is not None:
                self.file_dd = self.experimental_scenario.description_diagram
            else:
                self.file_dd = None
            # Insert description of the current scenario into visual component
            self.txt_scenario_desc['state'] = NORMAL
            self.txt_scenario_desc.delete('1.0', 'end-1c')
            self.txt_scenario_desc.insert('1.0', self.experimental_scenario.description)
            self.txt_scenario_desc['state'] = DISABLED

    def click_enter_scenario(self):
        """
        Shows pop-up window that allows validation of access code for the experimental scenario (necessary before showing
        the problems of the experimental scenario)
        """
        if self.trv_available.item(self.trv_available.selection())['text'] != '':
            self.tlevel_auth_scenario.title('Need to authenticate')
            self.tlevel_auth_scenario.deiconify()
            self.tlevel_auth_scenario.grab_set()
            self.txt_auth_scenario.focus_set()

    def click_authenticate_scenario(self):
        """
        If validation process is correct the the experimental scenario and it's problems are loaded into the visual
        components
        """
        if self.validate_access_code():
            self.load_experiment()
            self.txt_auth_scenario.delete(0, END)
            self.tlevel_auth_scenario.grab_release()
            self.tlevel_auth_scenario.withdraw()
            self.frm_parent.grid_forget()
            self.frm_general.grid(row=0, column=0, columnspan=9, rowspan=9, pady=10, padx=10)
        else:
            messagebox.showerror(parent=self.tlevel_auth_scenario, title='Wrong access code',
                                 message='The access code you provided is wrong, retry')

    def initialize_component_variables(self):
        """
        Initialize to zero or empty state all variables associated with the resolution of a problem for the designers
        (solutions and measurements)
        """
        self.attached_file = None
        self.av_patterns_seen = []
        self.solution_time = 0
        self.selection_time = []    # Time for selecting solution patterns (may be more than one solution pattern)
        self.selected_pattern_sol = []  # Indicates that any pattern solution has been selected yet

    def click_authenticate_cancel(self):
        """
        Cancel validation of access code for an experimental scenario, it hides the validation pop-up window
        """
        self.txt_auth_scenario.delete(0, END)
        self.tlevel_auth_scenario.grab_release()
        self.tlevel_auth_scenario.withdraw()

    def validate_access_code(self):
        """
        Verifies that the provided code matches the stored access code for the selected experimental scenario
        """
        if len(self.txt_auth_scenario.get()) != 0:
            if self.txt_auth_scenario.get() == self.experimental_scenario.access_code:
                return True
        return False

    def click_next_scenario(self):
        """
        After clicking next scenario component. This method prompts the user to confirm his decision. After confirming
        the system saves the important information (solutions and measurements) and then continue to the next scenario
        component if available, otherwise the experiment will be closed
        """
        decision = messagebox.askyesno(parent=self.frm_general, title='Confirmation',
                                       message="Are you sure you want to continue? Yo won't be able to make any change "
                                               "later")
        if decision: # Confirmation of action
            if self.save_changes():
                self.problems_counter += 1
                if self.problems_counter == len(self.experimental_scenario.problems): # If no more problems available
                    messagebox.showinfo(parent=self.frm_general, title='Experiment',
                                        message="This concludes the execution of the experiment. Thank you!")
                    self.finish_experiment()
                    self.clear_visual_components()
                    self.hide_frm()
                    self.show_frm()
                else: # If another scenario component available
                    messagebox.showinfo(parent=self.frm_general, title='Next problem',
                                        message="You are about to start a new problem, press Ok when you are ready.")
                    self.clear_visual_components()
                    self.initialize_component_variables()
                    self.load_problem()

    def save_changes(self):
        # Validate any problem with info inserted into visual components
        if self.validate_component_frm():  # No problem, proceed to save info
            self.solution_time += self.time_thread.seconds
            self.time_thread.stop() # Stop thread timer
            acquisition_end_date = datetime.now()
            # Get measurements of important metrics for the current component
            problem_id = self.experimental_scenario.problems[self.problems_counter].id
            current_measurements = []
            # Solution time
            measurement_1 = Measurement(value=float(self.solution_time), acquisition_start_date=self.acquisition_start_date,
                                        acquisition_end_date=acquisition_end_date, id_metric=1,
                                        id_designer=self.current_designer.id, id_problem=problem_id)
            current_measurements.append(measurement_1)
            # Save measurements associated with patterns only when these are available for designer
            if self.pattern_decision:
                # Selection time
                # Getting average if more than one value for this metric
                selection_time = 0 if len(self.selection_time) == 0 else sum(self.selection_time) / len(self.selection_time)
                measurement_2 = Measurement(value=float(selection_time), acquisition_start_date=self.acquisition_start_date,
                                            acquisition_end_date=acquisition_end_date, id_metric=2,
                                            id_designer=self.current_designer.id, id_problem=problem_id)
                current_measurements.append(measurement_2)
                # Viewed patterns
                measurement_3 = Measurement(value=float(len(self.av_patterns_seen)),
                                            acquisition_start_date=self.acquisition_start_date,
                                            acquisition_end_date=acquisition_end_date, id_metric=3,
                                            id_designer=self.current_designer.id, id_problem=problem_id)
                current_measurements.append(measurement_3)
                # Chosen patterns
                measurement_4 = Measurement(value=float(self.lbx_sel_patterns.size()),
                                            acquisition_start_date=self.acquisition_start_date,
                                            acquisition_end_date=acquisition_end_date, id_metric=4,
                                            id_designer=self.current_designer.id, id_problem=problem_id)
                current_measurements.append(measurement_4)
            for item in current_measurements:
                self.directive = Message(action=96, information=[item.value, item.acquisition_start_date,
                                                                 item.acquisition_end_date, item.id_metric,
                                                                 item.id_designer, item.id_problem])
                self.connection = self.directive.send_directive(self.connection)
            # Get info and build the solution to send to the server
            # Create diagram in DB
            id_diagram = None
            if self.attached_file is not None:
                self.directive = Message(action=61, information=[self.attached_file.file_bytes, self.attached_file.name,
                                                                 'sent sol'])
                self.connection = self.directive.send_directive(self.connection)
                id_diagram = self.connection.message.information[0]
            # Create object for the solution
            solution_aux = Solution(annotations=self.txt_solution_desc.get('1.0', 'end-1c'), diagram_id=id_diagram,
                                    patterns_id=self.sel_patterns_ids)
            # Create the solution in DB
            self.directive = Message(action=101, information=[solution_aux.annotations, solution_aux.diagram_id,
                                                              self.current_designer.id, problem_id,
                                                              solution_aux.patterns_id])
            self.connection = self.directive.send_directive(self.connection)
            return True
        return False

    def finish_experiment(self):
        """
        This section checks in the server if current designer is the last of the current experimental scenario, and if
        True changes it's state to finished, otherwise the state remains in executed
        """
        self.directive = Message(action=83, information=['finished', self.experimental_scenario.id])
        self.connection = self.directive.send_directive(self.connection)

    def validate_component_frm(self):
        if self.pattern_decision:
            if self.lbx_sel_patterns.size() == 0:
                messagebox.showwarning(parent=self.frm_general, title='Missing information',
                                       message="You haven't selected any pattern")
                return False
        if len(self.txt_solution_desc.get('1.0', 'end-1c')) == 0:
            messagebox.showwarning(parent=self.frm_general, title='Missing information',
                                   message='You must add notes to your solution')
            return False
        return True

    def click_attach_file(self):
        """
        Create a File object that is uploaded by the user, validating that there is not a file uploaded already.
        """
        if self.attached_file is None:
            filename = filedialog.askopenfilename(initialdir=os.getcwd(), title="Select image file",
                                                  filetypes=[("Diagrams", ".jpg .png .tiff")])
            if not filename:
                return  # user cancelled; stop this method
            self.attached_file = File()
            self.attached_file.read_file(filename)
            # Display image into canvas
            load = Image.open(self.attached_file.filename)
            load = load.resize((350, 350), Image.ANTIALIAS)
            self.render = ImageTk.PhotoImage(load)
            if self.attached_file.image is not None:  # if an image was already loaded
                self.canvas_solution.delete(self.attached_file.image)  # remove the previous image
            self.attached_file.image = self.canvas_solution.create_image(0, 0, anchor='nw', image=self.render)

    def click_remove_file(self):
        """
        Remove an uploaded file from the system validating it is already uploaded. This method also delete
        any image in the canvas that may be fulfilled with an image.
        """
        if self.attached_file is not None:  # if an image was already loaded
            self.canvas_solution.delete(self.attached_file.image)
            self.attached_file = None

    def click_add_patt(self):
        """
        Adds a pattern to the selected pattern listbox (when available to the designer).
        """
        element = self.lbx_av_patterns.curselection()
        if element is not None:   # Check if listbox is selected
            index = element[0]
            id_selected = self.av_patterns_ids[index]
            if not id_selected in self.sel_patterns_ids:    # Check if current pattern_id is not in the 'selected patterns list'
                if id_selected in self.current_ideal_patterns and not id_selected in self.selected_pattern_sol:  # Check if selected pattern matches ideal patterns and for the first time
                    self.selected_pattern_sol.append(id_selected)
                    self.selection_time.append(self.time_thread.seconds)
                for item in self.available_patterns:
                    if item.id == id_selected:  # Find selected pattern in available patterns list
                        self.selected_pattern = item
                        break
                self.sel_patterns_ids.append(id_selected)   # Append pattern_id to selected patterns ids
                self.lbx_sel_patterns.insert(END, self.selected_pattern.get_joined_main_s())  # Insert pattern name into selected listbox patters
                self.check_selected_patterns()

    def click_remove_patt(self):
        """
        Removes a pattern from the selected pattern listbox (when available to the designer).
        """
        element = self.lbx_sel_patterns.curselection()
        if element is not None:  # Check if listbox is selected
            if element:
                index = element[0]
                id_selected = self.sel_patterns_ids[index]
                self.lbx_sel_patterns.delete(element)  # Remove from listbox
                if id_selected in self.current_ideal_patterns and id_selected in self.selected_pattern_sol:  # Check if selected pattern matches ideal patterns and if the the selected pattern correspond to the one selected previously as the pattern solution
                    del self.selection_time[self.selected_pattern_sol.index(id_selected)]
                    self.selected_pattern_sol.remove(id_selected)
                for item in reversed(self.sel_patterns_ids):
                    if item == id_selected:
                        self.sel_patterns_ids.remove(item)
                        break
                self.check_selected_patterns()

    def click_expand_pd(self):
        # Fill canvas with retrieved image
        load = Image.open(self.file_pd.filename)
        load = load.resize((500, 500), Image.ANTIALIAS)
        self.render_pd = ImageTk.PhotoImage(load)
        self.canvas_expanded.delete()
        self.file_pd.image = self.canvas_expanded.create_image(0, 0, anchor='nw', image=self.render_pd)  # and display new image
        self.tlevel_image_exp.deiconify()
        self.tlevel_image_exp.grab_set()

    def click_expand_dd(self):
        # Fill canvas with retrieved image
        load = Image.open(self.file_dd.filename)
        load = load.resize((500, 500), Image.ANTIALIAS)
        self.render_dd = ImageTk.PhotoImage(load)
        self.canvas_expanded.delete()
        self.file_dd.image = self.canvas_expanded.create_image(0, 0, anchor='nw', image=self.render_dd)  # and display new image
        self.tlevel_image_exp.deiconify()
        self.tlevel_image_exp.grab_set()

    def close_tlevel_image(self):
        self.tlevel_image_exp.grab_release()
        self.tlevel_image_exp.withdraw()

    def clear_visual_components(self):
        self.txt_prob_desc['state'] = NORMAL
        self.txt_prob_desc.delete('1.0', 'end-1c')
        self.lbx_av_patterns.delete(0, END)
        self.lbx_sel_patterns.delete(0, END)
        self.txt_pattern_content['state'] = NORMAL
        self.txt_pattern_content.delete('1.0', 'end-1c')
        self.txt_pattern_content['state'] = DISABLED
        self.txt_solution_desc.delete('1.0', 'end-1c')
        if self.attached_file is not None:  # if an image was already loaded
            self.canvas_solution.delete(self.attached_file.image)
            self.attached_file = None
        self.tab_control.tab(0, image=self.incomplete_icon)
        self.tab_control.tab(1, image=self.incomplete_icon)

    def load_experiment(self):
        # Retrieve all information of problems of current scenario
        self.experimental_scenario.retrieve_problems(Pattern.get_available_patterns(self.connection))
        self.initialize_component_variables()
        self.problems_counter = 0
        self.current_designer.get_current_role(
            self.experimental_scenario.id)  # Get role for current experimental scenario
        # Retrieve patterns for designer in current experimental scenario
        self.directive = Message(action=42, information=[self.experimental_scenario.id,
                                                         1 if self.current_designer.current_group == 'experimental' else 2])
        self.connection = self.directive.send_directive(self.connection)
        self.available_patterns = Pattern.get_patterns(self.connection, self.connection.message.information)
        # Make patterns visible if the patterns are available for current designer in current experimental scenario
        if self.available_patterns:
            self.tab_control.tab(0, state='normal')
            self.tab_control.select(0)
            self.pattern_decision = True
        else:
            self.tab_control.tab(0, state='hidden')
            self.tab_control.select(1)
            self.pattern_decision = False
        self.btn_view_dd.grid_forget()
        if self.experimental_scenario.description_diagram is not None:
            self.btn_view_dd.grid(row=1, column=5, padx=10, pady=10, sticky=N)
        self.lbl_exp_title['text'] = 'Experiment: {}'.format(self.experimental_scenario.title)
        self.txt_exp_desc['state'] = NORMAL
        self.txt_exp_desc.delete('1.0', 'end-1c')
        self.txt_exp_desc.insert('1.0', self.experimental_scenario.description)
        self.txt_exp_desc['state'] = DISABLED
        self.load_problem()

    def load_problem(self):
        # Ask for available patterns in current problem for the current designer, depending of the role
        self.lbl_prob_title['text'] = 'Problem {} of {}: {}'.format(self.problems_counter + 1,
                                                                    len(self.experimental_scenario.problems),
                                                                    self.experimental_scenario.problems[self.problems_counter].brief_description)
        self.txt_prob_desc['state'] = NORMAL
        self.txt_prob_desc.delete('1.0', 'end-1c')
        self.txt_prob_desc.insert('1.0', self.experimental_scenario.problems[self.problems_counter].description)
        self.txt_prob_desc['state'] = DISABLED
        self.av_patterns_ids = []
        self.sel_patterns_ids = []
        for item in self.available_patterns:
            self.av_patterns_ids.append(item.id)
            self.lbx_av_patterns.insert(END, item.get_joined_main_s())
        self.current_ideal_patterns = self.experimental_scenario.problems[self.problems_counter].solution.patterns_id  # Get the patterns of the ideal solution for current problem
        # Make patterns visible if the patterns are available for current designer in current experimental scenario
        if self.pattern_decision:
            self.tab_control.select(0)
        else:
            self.tab_control.select(1)
        self.btn_view_pd.grid_forget()
        self.time_thread = TimerClass()
        self.time_thread.begin()
        self.acquisition_start_date = datetime.now()

    def select_available_pattern(self, event):
        id_selected = self.av_patterns_ids[self.lbx_av_patterns.curselection()[0]]
        # Add selected pattern to the seen pattern (metric)
        if not id_selected in self.av_patterns_seen:
            self.av_patterns_seen.append(id_selected)
        # Retrieve info of the selected pattern (in available patterns list)
        for item in self.available_patterns:
            if item.id == id_selected:
                self.selected_pattern = item
                break
        # Set visual components depending on the selected pattern and its info
        self.btn_view_pd.grid_forget()
        self.txt_pattern_content['state'] = NORMAL
        self.txt_pattern_content.delete('1.0', 'end-1c')
        for item in self.selected_pattern.sections:
            self.txt_pattern_content.insert('end-1c', item.name + ": ")
            if item.data_type == 'File': # The section content is a file
                if item.diagram_id != 0:
                    self.directive = Message(action=65, information=[item.diagram_id])
                    self.connection = self.directive.send_directive(self.connection)
                    self.file_pd = File()
                    self.file_pd.write_file(self.connection.message.information[0], self.connection.message.information[1])
                    self.btn_view_pd.grid(row=0, column=9, sticky=E)
                    self.txt_pattern_content.insert('end-1c', "\nClick up button to see diagram ^\n\n")

                else:
                    self.file_pd = None
                    self.txt_pattern_content.insert('end-1c', "\nNo diagram loaded for this section\n\n")

            else:
                self.txt_pattern_content.insert('end-1c', "\n" + item.content + "\n\n")
        self.txt_pattern_content['state'] = DISABLED

    def txt_notes_modified(self, event):
        """
        Method that checks if text box of additional notes (solution) is filled or not, so tab image is selected
        """
        if self.txt_solution_desc.get('1.0', 'end-1c') != '':
            self.tab_control.tab(1, image=self.complete_icon)
        else:
            self.tab_control.tab(1, image=self.incomplete_icon)

    def check_selected_patterns(self):
        """
        Method that checks if list box of selected patterns (solution) is filled or not, so tab image is selected
        """
        if self.lbx_sel_patterns.size() != 0:
            self.tab_control.tab(0, image=self.complete_icon)
        else:
            self.tab_control.tab(0, image=self.incomplete_icon)
Exemple #11
0
class DialogOpenArchive(Toplevel):
    def __init__(self, parent, openType, filesource, filenames, title, colHeader, showAltViewButton=False):
        if isinstance(parent, Cntlr):
            cntlr = parent
            parent = parent.parent # parent is cntlrWinMain
        else: # parent is a Toplevel dialog
            cntlr = parent.cntlr
        super(DialogOpenArchive, self).__init__(parent)
        self.parent = parent
        self.showAltViewButton = showAltViewButton
        parentGeometry = re.match("(\d+)x(\d+)[+]?([-]?\d+)[+]?([-]?\d+)", parent.geometry())
        dialogX = int(parentGeometry.group(3))
        dialogY = int(parentGeometry.group(4))
        self.accepted = False

        self.transient(self.parent)
        
        frame = Frame(self)

        treeFrame = Frame(frame, width=500)
        vScrollbar = Scrollbar(treeFrame, orient=VERTICAL)
        hScrollbar = Scrollbar(treeFrame, orient=HORIZONTAL)
        self.treeView = Treeview(treeFrame, xscrollcommand=hScrollbar.set, yscrollcommand=vScrollbar.set)
        self.treeView.grid(row=0, column=0, sticky=(N, S, E, W))
        hScrollbar["command"] = self.treeView.xview
        hScrollbar.grid(row=1, column=0, sticky=(E,W))
        vScrollbar["command"] = self.treeView.yview
        vScrollbar.grid(row=0, column=1, sticky=(N,S))
        treeFrame.columnconfigure(0, weight=1)
        treeFrame.rowconfigure(0, weight=1)
        treeFrame.grid(row=0, column=0, columnspan=4, sticky=(N, S, E, W), padx=3, pady=3)
        self.treeView.focus_set()
        
        if openType not in (PLUGIN, PACKAGE):
            cntlr.showStatus(_("loading archive {0}").format(filesource.url))
        self.filesource = filesource
        self.filenames = filenames
        self.selection = filesource.selection
        self.hasToolTip = False
        selectedNode = None

        if openType == ENTRY_POINTS:
            try:
                metadataFiles = filesource.taxonomyPackageMetadataFiles
                ''' take first for now
                if len(metadataFiles) != 1:
                    raise IOError(_("Taxonomy package contained more than one metadata file: {0}.")
                                  .format(', '.join(metadataFiles)))
                '''
                metadataFile = metadataFiles[0]
                metadata = filesource.url + os.sep + metadataFile
                self.metadataFilePrefix = os.sep.join(os.path.split(metadataFile)[:-1])
                if self.metadataFilePrefix:
                    self.metadataFilePrefix += "/"  # zip contents have /, never \ file seps
                self.taxonomyPkgMetaInf = '{}/META-INF/'.format(
                            os.path.splitext(os.path.basename(filesource.url))[0])

        
                self.taxonomyPackage = parsePackage(cntlr, filesource, metadata,
                                                    os.sep.join(os.path.split(metadata)[:-1]) + os.sep)
                
                
                if self.taxonomyPackage["entryPoints"]:
                    # may have instance documents too
                    self.packageContainedInstances = []
                    packageContentTypeCounts = {}
                    for suffix in (".xhtml", ".htm", ".html"):
                        for potentialInstance in filesource.dir:
                            if potentialInstance.endswith(".xhtml"):
                                _type = "Inline Instance"
                                self.packageContainedInstances.append([potentialInstance, _type])
                                packageContentTypeCounts[potentialInstance] = packageContentTypeCounts.get(potentialInstance, 0) + 1
                        if self.packageContainedInstances:
                            break 
                    if self.packageContainedInstances: # add sequences to any duplicated entry types
                        for _type, count in packageContentTypeCounts.items():
                            if count > 1:
                                _dupNo = 0
                                for i in range(len(self.packageContainedInstances)):
                                    if self.packageContainedInstances[i][0] == _type:
                                        _dupNo += 1
                                        self.packageContainedInstances[i][0] = "{} {}".format(_type, _dupNo)
                                    
                else:
                    # may be a catalog file with no entry oint names
                    openType = ARCHIVE  # no entry points to show, just archive
                    self.showAltViewButton = False
            except Exception as e:
                self.close()
                err = _("Failed to parse metadata; the underlying error was: {0}").format(e)
                messagebox.showerror(_("Malformed taxonomy package"), err)
                cntlr.addToLog(err)
                return
    
        if openType not in (PLUGIN, PACKAGE):
            cntlr.showStatus(None)
        
        if openType in (DISCLOSURE_SYSTEM, PLUGIN, PACKAGE):
            y = 3
        else:
            y = 1

        okButton = Button(frame, text=_("OK"), command=self.ok)
        cancelButton = Button(frame, text=_("Cancel"), command=self.close)
        okButton.grid(row=y, column=2, sticky=(S,E,W), pady=3)
        cancelButton.grid(row=y, column=3, sticky=(S,E,W), pady=3, padx=3)
        
        if self.showAltViewButton:
            self.altViewButton = Button(frame, command=self.showAltView)
            self.altViewButton.grid(row=y, column=0, sticky=(S,W), pady=3, padx=3)
        
        self.loadTreeView(openType, colHeader, title)

        self.geometry("+{0}+{1}".format(dialogX+50,dialogY+100))
        frame.grid(row=0, column=0, sticky=(N,S,E,W))
        frame.columnconfigure(0, weight=1)
        frame.rowconfigure(0, weight=1)
        window = self.winfo_toplevel()
        window.columnconfigure(0, weight=1)
        window.rowconfigure(0, weight=1)
        
        self.bind("<Return>", self.ok)
        self.bind("<Escape>", self.close)
        
        self.toolTipText = StringVar()
        if self.hasToolTip:
            self.treeView.bind("<Motion>", self.motion, '+')
            self.treeView.bind("<Leave>", self.leave, '+')
            self.toolTipText = StringVar()
            self.toolTip = ToolTip(self.treeView, 
                                   textvariable=self.toolTipText, 
                                   wraplength=640, 
                                   follow_mouse=True,
                                   state="disabled")
            self.toolTipRowId = None

        self.protocol("WM_DELETE_WINDOW", self.close)
        self.grab_set()
        
        self.wait_window(self)

    
        
        
    def loadTreeView(self, openType, title, colHeader):
        self.title(title)
        self.openType = openType
        selectedNode = None

        # clear previous treeview entries
        for previousNode in self.treeView.get_children(""): 
            self.treeView.delete(previousNode)

        # set up treeView widget and tabbed pane
        if openType in (ARCHIVE, DISCLOSURE_SYSTEM, PLUGIN, PACKAGE):
            if openType in (PLUGIN, PACKAGE): width = 770
            else: width = 500
            self.treeView.column("#0", width=width, anchor="w")
            self.treeView.heading("#0", text=colHeader)
            self.isRss = getattr(self.filesource, "isRss", False)
            if self.isRss:
                self.treeView.column("#0", width=350, anchor="w")
                self.treeView["columns"] = ("descr", "date", "instDoc")
                self.treeView.column("descr", width=50, anchor="center", stretch=False)
                self.treeView.heading("descr", text="Form")
                self.treeView.column("date", width=170, anchor="w", stretch=False)
                self.treeView.heading("date", text="Pub Date")
                self.treeView.column("instDoc", width=200, anchor="w", stretch=False)
                self.treeView.heading("instDoc", text="Instance Document")
            elif openType == PLUGIN:
                self.treeView.column("#0", width=150, anchor="w")
                self.treeView["columns"] = ("name", "vers", "descr", "license")
                self.treeView.column("name", width=150, anchor="w", stretch=False)
                self.treeView.heading("name", text="Name")
                self.treeView.column("vers", width=60, anchor="w", stretch=False)
                self.treeView.heading("vers", text="Version")
                self.treeView.column("descr", width=300, anchor="w", stretch=False)
                self.treeView.heading("descr", text="Description")
                self.treeView.column("license", width=60, anchor="w", stretch=False)
                self.treeView.heading("license", text="License")
            elif openType == PACKAGE:
                self.treeView.column("#0", width=200, anchor="w")
                self.treeView["columns"] = ("vers", "descr", "license")
                self.treeView.column("vers", width=100, anchor="w", stretch=False)
                self.treeView.heading("vers", text="Version")
                self.treeView.column("descr", width=400, anchor="w", stretch=False)
                self.treeView.heading("descr", text="Description")
                self.treeView.column("license", width=70, anchor="w", stretch=False)
                self.treeView.heading("license", text="License")
            else:
                self.treeView["columns"] = tuple()
        
            loadedPaths = []
            for i, filename in enumerate(self.filenames):
                if isinstance(filename,tuple):
                    if self.isRss:
                        form, date, instDoc = filename[2:5]
                    elif openType == PLUGIN:
                        name, vers, descr, license = filename[3:7]
                    elif openType == PACKAGE:
                        vers, descr, license = filename[3:6]
                    filename = filename[0] # ignore tooltip
                    self.hasToolTip = True
                if filename.endswith("/"):
                    filename = filename[:-1]
                path = filename.split("/")
                if not self.isRss and len(path) > 1 and path[:-1] in loadedPaths:
                    parent = "file{0}".format(loadedPaths.index(path[:-1]))
                else:
                    parent = "" 
                node = self.treeView.insert(parent, "end", "file{0}".format(i), text=path[-1])
                if self.isRss:
                    self.treeView.set(node, "descr", form)
                    self.treeView.set(node, "date", date)
                    self.treeView.set(node, "instDoc", os.path.basename(instDoc))
                elif openType == PLUGIN:
                    self.treeView.set(node, "name", name)
                    self.treeView.set(node, "vers", vers)
                    self.treeView.set(node, "descr", descr)
                    self.treeView.set(node, "license", license)
                elif openType == PACKAGE:
                    self.treeView.set(node, "vers", vers)
                    self.treeView.set(node, "descr", descr)
                    self.treeView.set(node, "license", license)
                if self.selection == filename:
                    selectedNode = node
                loadedPaths.append(path)

        elif openType == ENTRY_POINTS:
            self.treeView.column("#0", width=200, anchor="w")
            self.treeView.heading("#0", text="Name")
    
            self.treeView["columns"] = ("url",)
            self.treeView.column("url", width=300, anchor="w")
            self.treeView.heading("url", text="URL")
            
            for fileType, fileUrl in getattr(self, "packageContainedInstances", ()):
                self.treeView.insert("", "end", fileUrl, 
                                     values=fileType, 
                                     text=fileUrl or urls[0][2])
            for name, urls in sorted(self.taxonomyPackage["entryPoints"].items(), key=lambda i:i[0][2]):
                self.treeView.insert("", "end", name, 
                                     values="\n".join(url[1] for url in urls), 
                                     text=name or urls[0][2])
                
            self.hasToolTip = True
        else: # unknown openType
            return None
        if selectedNode:
            self.treeView.see(selectedNode)
            self.treeView.selection_set(selectedNode)

        if self.showAltViewButton:
            self.altViewButton.config(text=_("Show Files") if openType == ENTRY_POINTS else _("Show Entries"))

        
    def ok(self, event=None):
        selection = self.treeView.selection()
        if len(selection) > 0:
            if hasattr(self, "taxonomyPackage"):
                # load file source remappings
                self.filesource.mappedPaths = self.taxonomyPackage["remappings"]
            filename = None
            if self.openType in (ARCHIVE, DISCLOSURE_SYSTEM):
                filename = self.filenames[int(selection[0][4:])]
                if isinstance(filename,tuple):
                    if self.isRss:
                        filename = filename[4]
                    else:
                        filename = filename[0]
            elif self.openType == ENTRY_POINTS:
                epName = selection[0]
                #index 0 is the remapped Url, as opposed to the canonical one used for display
                # Greg Acsone reports [0] does not work for Corep 1.6 pkgs, need [1], old style packages
                filenames = []
                for _url, _type in self.packageContainedInstances: # check if selection was an inline instance
                    if _type == epName:
                        filenames.append(_url)
                if not filenames: # else if it's a named taxonomy entry point
                    for url in self.taxonomyPackage["entryPoints"][epName]:
                        filename = url[0]
                        if not filename.endswith("/"):
                            # check if it's an absolute URL rather than a path into the archive
                            if not isHttpUrl(filename) and self.metadataFilePrefix != self.taxonomyPkgMetaInf:
                                # assume it's a path inside the archive:
                                filename = self.metadataFilePrefix + filename
                        filenames.append(filename)
                if filenames:
                    self.filesource.select(filenames)
                    self.accepted = True
                    self.close()
                return
            elif self.openType in (PLUGIN, PACKAGE):
                filename = self.filenames[int(selection[0][4:])][2]
            if filename is not None and not filename.endswith("/"):
                if hasattr(self, "taxonomyPackage"):
                    # attempt to unmap the filename to original file
                    # will be mapped again in loading, but this allows schemaLocation to be unmapped
                    for prefix, remapping in self.taxonomyPackage["remappings"].items():
                        if isHttpUrl(remapping):
                            remapStart = remapping
                        else:
                            remapStart = self.metadataFilePrefix + remapping
                        if filename.startswith(remapStart):
                            # set unmmapped file
                            filename = prefix + filename[len(remapStart):]
                            break
                if self.openType in (PLUGIN, PACKAGE):
                    self.filesource.selection = filename
                else:
                    self.filesource.select(filename)
                self.accepted = True
                self.close()
                        
        
    def close(self, event=None):
        self.parent.focus_set()
        self.destroy()
        
    def showAltView(self, event=None):
        if self.openType == ENTRY_POINTS:
            self.loadTreeView(ARCHIVE, _("Select Entry Point"), _("File"))
        else:
            self.loadTreeView(ENTRY_POINTS, _("Select Archive File"), _("File"))
        
    def leave(self, *args):
        self.toolTipRowId = None

    def motion(self, *args):
        tvRowId = self.treeView.identify_row(args[0].y)
        if tvRowId != self.toolTipRowId:
            text = None
            if self.openType in (ARCHIVE, DISCLOSURE_SYSTEM, PLUGIN, PACKAGE):
                self.toolTipRowId = tvRowId
                if tvRowId and len(tvRowId) > 4:
                    try:
                        text = self.filenames[ int(tvRowId[4:]) ]
                        if isinstance(text, tuple):
                            text = (text[1] or "").replace("\\n","\n")
                    except (KeyError, ValueError):
                        pass
            elif self.openType == ENTRY_POINTS:
                try:
                    text = "{0}\n{1}".format(tvRowId, 
                             "\n".join(url[1] for url in self.taxonomyPackage["entryPoints"][tvRowId]))
                except KeyError:
                    pass
            self.setToolTip(text)
                
    def setToolTip(self, text):
        self.toolTip._hide()
        if text:
            self.toolTipText.set(text)
            self.toolTip.configure(state="normal")
            self.toolTip._schedule()
        else:
            self.toolTipText.set("")
            self.toolTip.configure(state="disabled")
Exemple #12
0
class FormChildSection:
    def __init__(self, frm_parent, connection):
        self.connection = connection
        self.directive = Message()
        self.decide = True
        self.id_selected = 0
        self.frm_child_list = LabelFrame(frm_parent)
        self.frm_child_crud = LabelFrame(frm_parent)
        self.frm_child_crud.config(fg=TEXT_COLOR, font=SUBTITLE_FONT)
        self.initialize_components()

    def initialize_components(self):
        """
        Method that initialize the visual components for each form associated with the local administration
        """
        # Resources for the Forms
        self.new_icon = PhotoImage(file=r"./Resources/create.png")
        self.view_icon = PhotoImage(file=r"./Resources/view.png")
        self.modify_icon = PhotoImage(file=r"./Resources/modify.png")
        self.remove_icon = PhotoImage(file=r"./Resources/delete.png")
        self.save_icon = PhotoImage(file=r"./Resources/save.png")
        self.cancel_icon = PhotoImage(file=r"./Resources/cancel.png")
        self.back_icon = PhotoImage(file=r"./Resources/back.png")

        # Components for List FRM
        lbl_sep1 = Label(self.frm_child_list)
        lbl_sep1.grid(row=0, column=0, padx=10, pady=25)
        self.trv_available = Treeview(self.frm_child_list,
                                      height=15,
                                      columns=('N', 'Name', 'Description',
                                               'Data Type'))
        self.trv_available.heading('#0', text='ID', anchor=CENTER)
        self.trv_available.heading('#1', text='N', anchor=CENTER)
        self.trv_available.heading('#2', text='Name', anchor=CENTER)
        self.trv_available.heading('#3', text='Description', anchor=CENTER)
        self.trv_available.heading('#4', text='Data Type', anchor=CENTER)
        self.trv_available.column('#0', width=0, minwidth=50, stretch=NO)
        self.trv_available.column('#1', width=20, minwidth=20, stretch=NO)
        self.trv_available.column('#2', width=200, minwidth=200, stretch=NO)
        self.trv_available.column('#3', width=400, minwidth=400, stretch=NO)
        self.trv_available.column('#4', width=200, minwidth=200, stretch=NO)
        self.trv_available.grid(row=0, column=1, sticky=W, pady=25)
        vsb_trv_av = Scrollbar(self.frm_child_list,
                               orient="vertical",
                               command=self.trv_available.yview)
        vsb_trv_av.grid(row=0, column=2, pady=25, sticky=NS)
        self.trv_available.configure(yscrollcommand=vsb_trv_av.set)
        frm_aux4 = Frame(self.frm_child_list)
        btn_new = Button(frm_aux4, image=self.new_icon, command=self.click_new)
        btn_new.grid(row=0, column=0, pady=5, padx=5, sticky=E)
        btn_new_ttp = CreateToolTip(btn_new, 'New section')
        btn_view = Button(frm_aux4,
                          image=self.view_icon,
                          command=self.click_view)
        btn_view.grid(row=1, column=0, pady=5, padx=5, sticky=E)
        btn_view_ttp = CreateToolTip(btn_view, 'View section')
        btn_edit = Button(frm_aux4,
                          image=self.modify_icon,
                          command=self.click_update)
        btn_edit.grid(row=2, column=0, pady=5, padx=5, sticky=E)
        btn_edit_ttp = CreateToolTip(btn_edit, 'Edit section')
        btn_delete = Button(frm_aux4,
                            image=self.remove_icon,
                            command=self.click_delete)
        btn_delete.grid(row=3, column=0, pady=5, padx=5, sticky=E)
        btn_delete_ttp = CreateToolTip(btn_delete, 'Delete section')
        frm_aux4.grid(row=0, column=4, pady=25, padx=25, sticky=NW)

        # Components for CRUD FRM
        self.frm_aux1 = Frame(self.frm_child_crud)
        lbl_type = Label(self.frm_aux1, text='Data type*')
        lbl_type.config(fg=TEXT_COLOR, font=LABEL_FONT)
        lbl_type.grid(row=0, column=0, pady=10, padx=20, sticky=W)
        lbl_name = Label(self.frm_aux1, text='Name*')
        lbl_name.config(fg=TEXT_COLOR, font=LABEL_FONT)
        lbl_name.grid(row=1, column=0, pady=10, padx=20, sticky=W)
        lbl_description = Label(self.frm_aux1, text='Description*\t')
        lbl_description.config(fg=TEXT_COLOR, font=LABEL_FONT)
        lbl_description.grid(row=2, column=0, pady=10, padx=20, sticky=NW)
        self.cbx_data = Combobox(self.frm_aux1, state="readonly")
        self.cbx_data['values'] = ['Text', 'File', 'Classification']
        self.cbx_data.grid(row=0, column=2, pady=10, sticky=W)
        self.cbx_data.bind("<<ComboboxSelected>>", self.cbx_data_selected)
        self.txt_name = Entry(self.frm_aux1, width=50, font=TEXT_FONT)
        self.txt_name.grid(row=1, column=2, pady=10, sticky=W)
        lbl_sep2 = Label(self.frm_aux1)
        lbl_sep2.grid(row=0, column=1, rowspan=3, padx=10, pady=10)
        self.txt_description = Text(self.frm_aux1,
                                    height=6,
                                    width=50,
                                    font=TEXT_FONT)
        self.txt_description.grid(row=2, column=2, pady=10, sticky=W)
        vsb_txt_desc = Scrollbar(self.frm_aux1,
                                 orient="vertical",
                                 command=self.txt_description.yview)
        vsb_txt_desc.grid(row=2, column=3, pady=10, sticky=NS)
        self.txt_description.configure(yscrollcommand=vsb_txt_desc.set)
        sep_aux1 = Separator(self.frm_aux1, orient=VERTICAL)
        sep_aux1.grid(row=0, column=4, sticky=NS, rowspan=4, padx=20)
        self.btn_save = Button(self.frm_aux1,
                               image=self.save_icon,
                               command=self.click_save)
        btn_save_ttp = CreateToolTip(self.btn_save, 'Save section')
        self.btn_back = Button(self.frm_aux1,
                               image=self.back_icon,
                               command=self.click_back)
        btn_back_ttp = CreateToolTip(self.btn_back, 'Go back')
        self.btn_cancel = Button(self.frm_aux1,
                                 image=self.cancel_icon,
                                 command=self.click_cancel)
        btn_cancel_ttp = CreateToolTip(self.btn_cancel, 'Cancel')
        self.frm_aux1.grid()

        # Frame for showing available classifications
        self.frm_aux2 = Frame(self.frm_aux1)
        lbl_class = Label(self.frm_aux2, text='Classification\t')
        lbl_class.config(fg=TEXT_COLOR, font=LABEL_FONT)
        lbl_class.grid(row=0, column=0, pady=10, padx=20, sticky=W)
        lbl_category = Label(self.frm_aux2, text='Categories')
        lbl_category.config(fg=TEXT_COLOR, font=LABEL_FONT)
        lbl_category.grid(row=1, column=0, pady=10, padx=20, sticky=NW)
        lbl_sep3 = Label(self.frm_aux2)
        lbl_sep3.grid(row=0, column=1, rowspan=2, padx=10, pady=10)
        self.cbx_classification = Combobox(self.frm_aux2, state="readonly")
        self.cbx_classification.bind("<<ComboboxSelected>>",
                                     self.cbx_class_selected)
        self.cbx_classification.grid(row=0, column=2, pady=10, sticky=NW)
        self.lbx_category = Listbox(self.frm_aux2,
                                    font=TEXT_FONT,
                                    height=10,
                                    width=50,
                                    selectmode='none')
        self.lbx_category.config(bg=DISABLED_COLOR)
        self.lbx_category.grid(row=1, column=2, pady=10, sticky=W)
        vsb_lbx_cat = Scrollbar(self.frm_aux2,
                                orient="vertical",
                                command=self.lbx_category.yview)
        vsb_lbx_cat.grid(row=1, column=3, pady=10, sticky=NS)
        self.lbx_category.configure(yscrollcommand=vsb_lbx_cat.set)

    def retrieve_list(self):
        # Remove existing elements in the list
        for item in self.trv_available.get_children():
            self.trv_available.delete(item)
        self.directive = Message(action=32)
        self.connection = self.directive.send_directive(self.connection)
        # Adding elements into the list
        for index, item in enumerate(self.connection.message.information):
            elements = item.split('¥')
            self.trv_available.insert('',
                                      'end',
                                      text=elements[0],
                                      values=(index + 1,
                                              summarize_text(elements[1], 200),
                                              summarize_text(elements[2], 400),
                                              summarize_text(elements[3],
                                                             200)))
        if len(self.trv_available.get_children()) != 0:
            self.trv_available.selection_set(
                self.trv_available.get_children()[0])

    def show_frm(self):
        self.retrieve_list()
        self.frm_child_list.grid(row=1,
                                 column=0,
                                 columnspan=9,
                                 rowspan=8,
                                 pady=10,
                                 padx=10)

    def hide_frm(self):
        self.clear_fields()
        self.frm_child_list.grid_forget()
        self.frm_child_crud.grid_forget()

    def click_new(self):
        self.section = Section()
        self.txt_name.focus_set()
        self.frm_child_crud['text'] = 'New section'
        self.btn_save.grid(row=0, column=5, padx=20)
        self.btn_cancel.grid(row=1, column=5, padx=20)
        self.frm_child_list.grid_forget()
        self.frm_child_crud.grid(row=1,
                                 column=0,
                                 columnspan=9,
                                 rowspan=8,
                                 pady=10,
                                 padx=10)

    def click_view(self):
        if len(self.trv_available.selection()) == 1:
            id_selected = int(
                self.trv_available.item(
                    self.trv_available.selection())['text'])
            self.directive = Message(action=35, information=[id_selected])
            self.connection = self.directive.send_directive(self.connection)
            self.section = Section(
                section_id=id_selected,
                name=self.connection.message.information[0],
                description=self.connection.message.information[1],
                data_type=self.connection.message.information[2])
            self.txt_name.insert(0, self.section.name)
            self.txt_description.insert('1.0', self.section.description)
            self.cbx_data.set(self.section.data_type)
            if self.section.data_type == 'Classification':
                self.section.classification_id = self.connection.message.information[
                    3]
                self.retrieve_classifications()
                self.directive = Message(
                    action=70, information=[self.section.classification_id])
                self.connection = self.directive.send_directive(
                    self.connection)
                self.cbx_classification.set(
                    self.connection.message.information[0])
                self.cbx_class_selected()
                self.frm_aux2.grid(row=3, column=0, columnspan=4, sticky=W)
            self.txt_name['bg'] = DISABLED_COLOR
            self.txt_description['bg'] = DISABLED_COLOR
            self.lbx_category['bg'] = DISABLED_COLOR
            self.txt_name['state'] = DISABLED
            self.txt_description['state'] = DISABLED
            self.cbx_data['state'] = DISABLED
            self.cbx_classification['state'] = DISABLED
            self.lbx_category['state'] = DISABLED
            self.frm_child_crud['text'] = 'View section'
            self.btn_back.grid(row=0, column=5, padx=20)
            self.frm_child_list.grid_forget()
            self.frm_child_crud.grid(row=1,
                                     column=0,
                                     columnspan=9,
                                     rowspan=8,
                                     pady=10,
                                     padx=10)
        else:
            messagebox.showwarning(parent=self.frm_child_list,
                                   title='No selection',
                                   message='You must select one item')

    def click_update(self):
        if len(self.trv_available.selection()) == 1:
            id_selected = int(
                self.trv_available.item(
                    self.trv_available.selection())['text'])
            self.directive = Message(action=35,
                                     information=[id_selected, 'validate'])
            self.connection = self.directive.send_directive(self.connection)
            if self.connection.message.action == 5:  # An error ocurred while trying to update the item
                messagebox.showerror(
                    parent=self.frm_child_list,
                    title='Can not update the item',
                    message=self.connection.message.information[0])
            else:
                self.section = Section(
                    section_id=id_selected,
                    name=self.connection.message.information[0],
                    description=self.connection.message.information[1],
                    data_type=self.connection.message.information[2])
                self.txt_name.insert(0, self.section.name)
                self.txt_description.insert('1.0', self.section.description)
                self.cbx_data.set(self.section.data_type)
                if self.section.data_type == 'Classification':
                    self.section.classification_id = self.connection.message.information[
                        3]
                    self.retrieve_classifications()
                    self.directive = Message(
                        action=70,
                        information=[self.section.classification_id])
                    self.connection = self.directive.send_directive(
                        self.connection)
                    self.cbx_classification.set(
                        self.connection.message.information[0])
                    self.cbx_class_selected()
                    self.frm_aux2.grid(row=3, column=0, columnspan=4, sticky=W)
                self.frm_child_crud['text'] = 'Update section'
                self.btn_save.grid(row=0, column=5, padx=20)
                self.btn_cancel.grid(row=1, column=5, padx=20)
                self.frm_child_list.grid_forget()
                self.frm_child_crud.grid(row=1,
                                         column=0,
                                         columnspan=9,
                                         rowspan=8,
                                         pady=10,
                                         padx=10)
        else:
            messagebox.showwarning(parent=self.frm_child_list,
                                   title='No selection',
                                   message='You must select one item')

    def click_delete(self):
        if len(self.trv_available.selection()) == 1:
            decision = messagebox.askyesno(
                parent=self.frm_child_list,
                title='Confirmation',
                message='Are you sure you want to delete the item?')
            if decision:
                id_selected = int(
                    self.trv_available.item(
                        self.trv_available.selection())['text'])
                self.directive = Message(action=34, information=[id_selected])
                self.connection = self.directive.send_directive(
                    self.connection)
                if self.connection.message.action == 5:  # An error ocurred while deleting the item
                    messagebox.showerror(
                        parent=self.frm_child_list,
                        title='Can not delete the item',
                        message=self.connection.message.information[0])
                else:
                    self.retrieve_list()
        else:
            messagebox.showwarning(parent=self.frm_child_list,
                                   title='No selection',
                                   message='You must select one item')

    def click_save(self):
        if self.validate_fields():
            self.section.name = self.txt_name.get()
            self.section.description = self.txt_description.get(
                '1.0', 'end-1c')
            self.section.data_type = self.cbx_data.get()
            if self.section.section_id == 0:  # If creating a section
                if self.section.data_type == 'Classification':
                    id_class = self.classifications[
                        self.cbx_classification.current()]
                    self.directive = Message(action=31,
                                             information=[
                                                 self.section.name,
                                                 self.section.description,
                                                 self.section.data_type,
                                                 id_class
                                             ])
                else:
                    self.directive = Message(action=31,
                                             information=[
                                                 self.section.name,
                                                 self.section.description,
                                                 self.section.data_type
                                             ])
            else:  # If updating a section
                if self.section.data_type == 'Classification':
                    id_class = self.classifications[
                        self.cbx_classification.current()]
                    self.directive = Message(action=33,
                                             information=[
                                                 self.section.section_id,
                                                 self.section.name,
                                                 self.section.description,
                                                 self.section.data_type,
                                                 id_class
                                             ])
                else:
                    self.directive = Message(action=33,
                                             information=[
                                                 self.section.section_id,
                                                 self.section.name,
                                                 self.section.description,
                                                 self.section.data_type
                                             ])
            self.connection = self.directive.send_directive(self.connection)
            self.click_back()

    def click_back(self):
        self.clear_fields()
        self.frm_child_crud.grid_forget()
        self.show_frm()

    def click_cancel(self):
        decision = True
        if self.txt_name.get() != self.section.name or \
                self.txt_description.get('1.0', 'end-1c') != self.section.description or \
                self.cbx_data.get() != self.section.data_type:
            decision = messagebox.askyesno(
                parent=self.frm_child_crud,
                title='Cancel',
                message='Are you sure you want to cancel?')
        if decision:
            self.click_back()

    def cbx_data_selected(self, event):
        if self.cbx_data.get() == 'Classification':
            self.retrieve_classifications()
            self.frm_aux2.grid(row=3, column=0, columnspan=4, sticky=W)
        else:
            self.frm_aux2.grid_forget()
        self.txt_name.focus_set()

    def cbx_class_selected(self, event=None):
        id_class = self.classifications[self.cbx_classification.current()]
        self.directive = Message(action=72, information=[id_class])
        self.connection = self.directive.send_directive(self.connection)
        self.lbx_category.delete(0, END)
        for index, item in enumerate(self.connection.message.information):
            item = item.split('¥')
            self.lbx_category.insert(END, '{}) {}'.format(index + 1, item[1]))

    def retrieve_classifications(self):
        self.classifications = []
        self.lbx_category.delete(0, END)
        self.directive = Message(action=67)
        self.connection = self.directive.send_directive(self.connection)
        classifications = []
        for item in self.connection.message.information:
            elements = item.split('¥')
            classifications.append(elements[1])
            self.classifications.append(int(elements[0]))
        self.cbx_classification['values'] = []
        self.cbx_classification['values'] = classifications

    def validate_fields(self):
        if len(self.txt_name.get()) == 0:
            messagebox.showwarning(
                parent=self.frm_child_crud,
                title='Missing information',
                message='You must insert a name for the section')
            return False
        if len(self.txt_description.get('1.0', 'end-1c')) == 0:
            messagebox.showwarning(
                parent=self.frm_child_crud,
                title='Missing information',
                message='You must insert a description for the section')
            return False
        if len(self.cbx_data.get()) == 0:
            messagebox.showwarning(
                parent=self.frm_child_crud,
                title='Missing information',
                message='You must select data type for the section')
            return False
        if self.cbx_data.get() == 'Classification' and len(
                self.cbx_classification.get()) == 0:
            messagebox.showwarning(
                parent=self.frm_child_crud,
                title='Missing information',
                message='You must select a classification for this section')
            return False
        return True

    def clear_fields(self):
        self.btn_save.grid_forget()
        self.btn_cancel.grid_forget()
        self.btn_back.grid_forget()
        self.txt_name['state'] = NORMAL
        self.txt_description['state'] = NORMAL
        self.cbx_data['state'] = NORMAL
        self.cbx_classification['state'] = NORMAL
        self.lbx_category['state'] = NORMAL
        self.txt_name['bg'] = ENABLED_COLOR
        self.txt_description['bg'] = ENABLED_COLOR
        self.lbx_category['bg'] = ENABLED_COLOR
        self.txt_name.delete(0, END)
        self.txt_description.delete('1.0', 'end-1c')
        self.cbx_data.set('')
        self.cbx_classification.set('')
        self.lbx_category.delete(0, END)
        self.frm_aux2.grid_forget()
Exemple #13
0
class FormChildTemplate:
    def __init__(self, frm_parent, connection):
        self.connection = connection
        self.directive = Message()
        self.decide_template = True
        self.id_selected = 0
        self.frm_child_list = LabelFrame(frm_parent)
        self.frm_child_crud = LabelFrame(frm_parent)
        self.frm_child_crud.config(fg=TEXT_COLOR, font=SUBTITLE_FONT)
        self.initialize_components()

    def initialize_components(self):
        """
        Method that initialize the visual components for each form associated with the local administration
        """
        # Resources for the Forms
        self.new_icon = PhotoImage(file=r"./Resources/create.png")
        self.modify_icon = PhotoImage(file=r"./Resources/modify.png")
        self.remove_icon = PhotoImage(file=r"./Resources/delete.png")
        self.save_icon = PhotoImage(file=r"./Resources/save.png")
        self.cancel_icon = PhotoImage(file=r"./Resources/cancel.png")
        self.add_icon = PhotoImage(file=r"./Resources/right.png")
        self.delete_icon = PhotoImage(file=r"./Resources/left.png")
        self.up_arrow = PhotoImage(file=r"./Resources/up_arrow.png")
        self.down_arrow = PhotoImage(file=r"./Resources/down_arrow.png")
        self.star_icon = PhotoImage(file=r"./Resources/star.png")
        self.back_icon = PhotoImage(file=r"./Resources/back.png")
        self.view_icon = PhotoImage(file=r"./Resources/view.png")

        # Components for List FRM
        lbl_sep1 = Label(self.frm_child_list)
        lbl_sep1.grid(row=0, column=0, padx=10, pady=25)
        self.trv_available = Treeview(self.frm_child_list,
                                      height=20,
                                      columns=('N', 'Name'))
        self.trv_available.heading('#0', text='ID', anchor=CENTER)
        self.trv_available.heading('#1', text='N', anchor=CENTER)
        self.trv_available.heading('#2', text='Name', anchor=CENTER)
        self.trv_available.column('#0', width=0, minwidth=50, stretch=NO)
        self.trv_available.column('#1', width=20, minwidth=20, stretch=NO)
        self.trv_available.column('#2', width=375, minwidth=375, stretch=NO)
        self.trv_available.bind("<ButtonRelease-1>",
                                self.select_template_summary)
        self.trv_available.grid(row=0, column=1, sticky=W, pady=25)
        vsb_trv_av = Scrollbar(self.frm_child_list,
                               orient="vertical",
                               command=self.trv_available.yview)
        vsb_trv_av.grid(row=0, column=2, pady=25, sticky=NS)
        self.trv_available.configure(yscrollcommand=vsb_trv_av.set)
        frm_aux4 = Frame(self.frm_child_list)
        btn_new = Button(frm_aux4, image=self.new_icon, command=self.click_new)
        btn_new.grid(row=0, column=0, pady=5, padx=5, sticky=E)
        btn_new_ttp = CreateToolTip(btn_new, 'New template')
        btn_view = Button(frm_aux4,
                          image=self.view_icon,
                          command=self.click_view)
        btn_view.grid(row=1, column=0, pady=5, padx=5, sticky=E)
        btn_view_ttp = CreateToolTip(btn_new, 'View template')
        btn_edit = Button(frm_aux4,
                          image=self.modify_icon,
                          command=self.click_update)
        btn_edit.grid(row=2, column=0, pady=5, padx=5, sticky=E)
        btn_edit_ttp = CreateToolTip(btn_edit, 'Edit template')
        btn_delete = Button(frm_aux4,
                            image=self.remove_icon,
                            command=self.click_delete)
        btn_delete.grid(row=3, column=0, pady=5, padx=5, sticky=E)
        btn_delete_ttp = CreateToolTip(btn_delete, 'Delete template')
        frm_aux4.grid(row=0, column=3, pady=25, padx=25, sticky=NW)
        sep_template = Separator(self.frm_child_list, orient=VERTICAL)
        sep_template.grid(row=0, column=4, sticky=NS, padx=25)
        frm_aux3 = Frame(self.frm_child_list)
        lbl_sep3 = Label(frm_aux3)
        lbl_sep3.grid(row=0, column=0, padx=10, pady=25, rowspan=3)
        lbl_details = Label(frm_aux3, text='Details')
        lbl_details.config(fg=TEXT_COLOR, font=SUBTITLE_FONT)
        lbl_details.grid(row=0, column=1, sticky=W, pady=25, columnspan=2)
        self.txt_summary = Text(frm_aux3, height=22, width=50)
        self.txt_summary.config(font=TEXT_FONT, bg=DISABLED_COLOR)
        self.txt_summary.grid(row=1, column=1)
        vsb_txt_sum = Scrollbar(frm_aux3,
                                orient="vertical",
                                command=self.txt_summary.yview)
        vsb_txt_sum.grid(row=1, column=2, sticky=NS)
        self.txt_summary.configure(yscrollcommand=vsb_txt_sum.set)
        lbl_sep4 = Label(frm_aux3)
        lbl_sep4.grid(row=0, column=3, padx=10, pady=25, rowspan=3)
        lbl_sep5 = Label(frm_aux3)
        lbl_sep5.grid(row=2, column=1, pady=5, columnspan=2)
        frm_aux3.grid(row=0, column=5)

        # Components for CRUD FRM
        lbl_sep6 = Label(self.frm_child_crud)
        lbl_sep6.grid(row=0, column=0, padx=10, pady=25, rowspan=10)
        lbl_name = Label(self.frm_child_crud, text='Name*')
        lbl_name.config(fg=TEXT_COLOR, font=LABEL_FONT)
        lbl_name.grid(row=0, column=1, pady=25, sticky=NW)
        lbl_description = Label(self.frm_child_crud, text='Description*')
        lbl_description.config(fg=TEXT_COLOR, font=LABEL_FONT)
        lbl_description.grid(row=0, column=6, pady=25, sticky=NW)
        lbl_sep3 = Label(self.frm_child_crud)
        lbl_sep3.grid(row=0, column=2, padx=10, pady=25)
        self.txt_name = Entry(self.frm_child_crud, width=30, font=TEXT_FONT)
        self.txt_name.grid(row=0, column=3, pady=25, sticky=NW)
        lbl_sep4 = Label(self.frm_child_crud)
        lbl_sep4.grid(row=0, column=7, padx=10, pady=25)
        self.txt_description = Text(self.frm_child_crud, height=5, width=49)
        self.txt_description.config(font=TEXT_FONT)
        self.txt_description.grid(row=0, column=8, pady=25, sticky=W)
        vsb_txt_desc = Scrollbar(self.frm_child_crud,
                                 orient="vertical",
                                 command=self.txt_description.yview)
        vsb_txt_desc.grid(row=0, column=9, pady=25, sticky=NS)
        self.txt_description.configure(yscrollcommand=vsb_txt_desc.set)
        lbl_sep7 = Label(self.frm_child_crud)
        lbl_sep7.grid(row=0, column=5, padx=10, pady=25, rowspan=3)
        lbl_sep8 = Label(self.frm_child_crud)
        lbl_sep8.grid(row=0, column=10, padx=10, pady=25, rowspan=2)
        lbl_available_d = Label(self.frm_child_crud, text='Available sections')
        lbl_available_d.config(fg=TEXT_COLOR, font=LABEL_FONT)
        lbl_available_d.grid(row=1, column=1, pady=10, sticky=W, columnspan=4)
        lbl_selected_d = Label(self.frm_child_crud, text='Selected sections*')
        lbl_selected_d.config(fg=TEXT_COLOR, font=LABEL_FONT)
        lbl_selected_d.grid(row=1, column=6, pady=10, sticky=W, columnspan=4)
        self.trv_available_sections = Treeview(self.frm_child_crud,
                                               height=10,
                                               columns=('N', 'Name',
                                                        'Data Type'))
        self.trv_available_sections.heading('#0', text='ID', anchor=CENTER)
        self.trv_available_sections.heading('#1', text='N', anchor=CENTER)
        self.trv_available_sections.heading('#2', text='Name', anchor=CENTER)
        self.trv_available_sections.heading('#3',
                                            text='Data Type',
                                            anchor=CENTER)
        self.trv_available_sections.column('#0',
                                           width=0,
                                           minwidth=20,
                                           stretch=NO)
        self.trv_available_sections.column('#1',
                                           width=20,
                                           minwidth=20,
                                           stretch=NO)
        self.trv_available_sections.column('#2',
                                           width=150,
                                           minwidth=150,
                                           stretch=NO)
        self.trv_available_sections.column('#3',
                                           width=120,
                                           minwidth=120,
                                           stretch=NO)
        self.trv_available_sections.bind("<Button-1>",
                                         self.click_trv_asections)
        self.trv_available_sections.grid(row=2,
                                         column=1,
                                         rowspan=7,
                                         columnspan=3,
                                         sticky=W,
                                         pady=10)
        vsb_trv_avs = Scrollbar(self.frm_child_crud,
                                orient="vertical",
                                command=self.trv_available_sections.yview)
        vsb_trv_avs.grid(row=2, column=4, rowspan=7, pady=10, sticky=NS)
        self.trv_available_sections.configure(yscrollcommand=vsb_trv_avs.set)
        self.trv_selected_sections = Treeview(self.frm_child_crud,
                                              height=10,
                                              columns=('N', 'Name',
                                                       'Data type',
                                                       'Mandatory', 'Main'))
        self.trv_selected_sections.heading('#0', text='ID', anchor=CENTER)
        self.trv_selected_sections.heading('#1', text='N', anchor=CENTER)
        self.trv_selected_sections.heading('#2', text='Name', anchor=CENTER)
        self.trv_selected_sections.heading('#3',
                                           text='Data type',
                                           anchor=CENTER)
        self.trv_selected_sections.heading('#4',
                                           text='Mandatory',
                                           anchor=CENTER)
        self.trv_selected_sections.heading('#5', text='Main', anchor=CENTER)
        self.trv_selected_sections.column('#0',
                                          width=0,
                                          minwidth=20,
                                          stretch=NO)
        self.trv_selected_sections.column('#1',
                                          width=20,
                                          minwidth=20,
                                          stretch=NO)
        self.trv_selected_sections.column('#2',
                                          width=150,
                                          minwidth=150,
                                          stretch=NO)
        self.trv_selected_sections.column('#3',
                                          width=120,
                                          minwidth=120,
                                          stretch=NO)
        self.trv_selected_sections.column('#4',
                                          width=80,
                                          minwidth=80,
                                          stretch=NO)
        self.trv_selected_sections.column('#5',
                                          width=80,
                                          minwidth=80,
                                          stretch=NO)
        self.trv_selected_sections.bind("<Button-1>", self.click_trv_ssections)
        self.trv_selected_sections.bind("<Double-1>",
                                        self.click_switch_mandatory)
        self.trv_selected_sections.grid(row=2,
                                        column=6,
                                        rowspan=7,
                                        columnspan=3,
                                        sticky=W,
                                        pady=10)
        vsb_trv_ses = Scrollbar(self.frm_child_crud,
                                orient="vertical",
                                command=self.trv_selected_sections.yview)
        vsb_trv_ses.grid(row=2, column=9, rowspan=7, pady=10, sticky=NS)
        self.trv_selected_sections.configure(yscrollcommand=vsb_trv_ses.set)
        self.lbl_note_optional = Label(
            self.frm_child_crud,
            text=
            'NOTES:\tTo switch between optional and mandatory, double click '
            'on selected section.\n\tChoose one or up to three main sections '
            'by first selecting the target sections\n\tand then clicking the '
            'star button.\n')
        self.lbl_note_optional.config(fg=TEXT_COLOR,
                                      font=NOTE_FONT,
                                      justify=LEFT)
        self.btn_add = Button(self.frm_child_crud,
                              image=self.add_icon,
                              command=self.click_add)
        btn_add_ttp = CreateToolTip(self.btn_add, 'Add section')
        self.btn_remove = Button(self.frm_child_crud,
                                 image=self.delete_icon,
                                 command=self.click_remove)
        btn_remove_ttp = CreateToolTip(self.btn_remove, 'Remove section')
        self.btn_main_section = Button(self.frm_child_crud,
                                       image=self.star_icon,
                                       command=self.click_main_section)
        btn_main_section_ttp = CreateToolTip(self.btn_main_section,
                                             'Main section(s)')
        self.btn_up = Button(self.frm_child_crud,
                             image=self.up_arrow,
                             command=self.click_up)
        btn_up_ttp = CreateToolTip(self.btn_up, 'Move up')
        self.btn_down = Button(self.frm_child_crud,
                               image=self.down_arrow,
                               command=self.click_down)
        btn_down_ttp = CreateToolTip(self.btn_down, 'Move down')
        sep_aux1 = Separator(self.frm_child_crud, orient=VERTICAL)
        sep_aux1.grid(row=0, column=11, sticky=NS, rowspan=10)
        frm_aux1 = Frame(self.frm_child_crud)
        self.btn_save = Button(frm_aux1,
                               image=self.save_icon,
                               command=self.click_save)
        btn_save_ttp = CreateToolTip(self.btn_save, 'Save template')
        self.btn_back = Button(frm_aux1,
                               image=self.back_icon,
                               command=self.click_back)
        btn_back_ttp = CreateToolTip(self.btn_back, 'Go back')
        self.btn_cancel = Button(frm_aux1,
                                 image=self.cancel_icon,
                                 command=self.click_cancel)
        btn_cancel_ttp = CreateToolTip(self.btn_cancel, 'Cancel')
        frm_aux1.grid(row=0,
                      column=12,
                      pady=10,
                      padx=25,
                      sticky=NW,
                      rowspan=10)

    def retrieve_list(self):
        # Remove existing elements in the list
        for item in self.trv_available.get_children():
            self.trv_available.delete(item)
        self.directive = Message(action=37)
        self.connection = self.directive.send_directive(self.connection)
        # Adding elements into the list
        for index, item in enumerate(self.connection.message.information):
            elements = item.split('¥')
            self.trv_available.insert('',
                                      'end',
                                      text=elements[0],
                                      values=(index + 1,
                                              summarize_text(elements[1],
                                                             375)))
        if len(self.trv_available.get_children()) != 0:
            self.trv_available.selection_set(
                self.trv_available.get_children()[0])
            self.select_template_summary()

    def select_template_summary(self, event=None):
        """
        Function activated when the event of selecting an item in the available templates TV is generated. It fills the
        summary text box with information of the selected template
        :param event:
        """
        if self.trv_available.item(
                self.trv_available.selection())['text'] != '':
            # Clear summary txt box
            self.txt_summary['state'] = NORMAL
            self.txt_summary.delete('1.0', 'end-1c')
            self.id_selected = int(
                self.trv_available.item(self.trv_available.selection())
                ['text'])  # Retrieve id of selected item from TreeView
            self.directive = Message(action=40,
                                     information=[self.id_selected
                                                  ])  # ask for the template
            self.connection = self.directive.send_directive(self.connection)
            # Insert template's name and description
            self.txt_summary.insert(
                'end-1c',
                "Name:\n{}\n\n".format(self.connection.message.information[0]))
            self.txt_summary.insert(
                'end-1c', "Description:\n{}\n\nSections:\n".format(
                    self.connection.message.information[1]))
            self.directive = Message(action=77, information=[
                self.id_selected
            ])  # ask for the sections of the selected template
            self.connection = self.directive.send_directive(self.connection)
            # Adding elements in the summary text box
            index = 0
            for item in self.connection.message.information:
                elements = item.split('¥')
                self.txt_summary.insert('end-1c', "{}) {}\t\t{}\t\t{}\n".format(index + 1, elements[3], 'optional' \
                    if elements[7] == '' else 'mandatory', '' if elements[8] == '' else '(MAIN)'))
                index += 1
            self.txt_summary['state'] = DISABLED

    def show_frm(self):
        self.retrieve_list()
        self.frm_child_list.grid(row=1,
                                 column=0,
                                 columnspan=9,
                                 rowspan=8,
                                 pady=10,
                                 padx=10)

    def hide_frm(self):
        self.clear_fields()
        self.frm_child_list.grid_forget()
        self.frm_child_crud.grid_forget()

    def click_new(self):
        self.view_decision = False  # Decision when viewing a template
        self.template = Template()
        self.retrieve_sections()
        self.txt_name.focus_set()
        self.show_cu_buttons()
        self.frm_child_crud['text'] = 'New template'
        self.frm_child_list.grid_forget()
        self.frm_child_crud.grid(row=1,
                                 column=0,
                                 columnspan=9,
                                 rowspan=8,
                                 pady=10,
                                 padx=10)

    def click_view(self):
        if len(self.trv_available.selection()) == 1:
            self.view_decision = True  # Decision when viewing a template
            self.directive = Message(action=40, information=[self.id_selected])
            self.connection = self.directive.send_directive(self.connection)
            self.template = Template(
                id=self.id_selected,
                name=self.connection.message.information[0],
                description=self.connection.message.information[1],
                sections=self.connection.message.information[2])
            self.txt_name.insert(0, self.template.name)
            self.txt_description.insert('1.0', self.template.description)
            self.retrieve_sections(self.template.sections)
            self.txt_name.focus_set()
            self.disable_visual_components()
            self.btn_back.grid(row=0, column=0, padx=5, pady=5, sticky=W)
            self.frm_child_list.grid_forget()
            self.frm_child_crud['text'] = 'View template'
            self.frm_child_crud.grid(row=1,
                                     column=0,
                                     columnspan=9,
                                     rowspan=8,
                                     pady=10,
                                     padx=10)
        else:
            messagebox.showwarning(parent=self.frm_child_list,
                                   title='No selection',
                                   message='You must select one item')

    def click_update(self):
        if len(self.trv_available.selection()) == 1:
            self.view_decision = False  # Decision when viewing a template
            self.directive = Message(
                action=40, information=[self.id_selected, 'validate'])
            self.connection = self.directive.send_directive(self.connection)
            if self.connection.message.action == 5:  # An error ocurred while trying to update the item
                messagebox.showerror(
                    parent=self.frm_child_list,
                    title='Can not edit the template',
                    message=self.connection.message.information[0])
            else:
                self.template = Template(
                    id=self.id_selected,
                    name=self.connection.message.information[0],
                    description=self.connection.message.information[1],
                    sections=self.connection.message.information[2])
                self.txt_name.insert(0, self.template.name)
                self.txt_description.insert('1.0', self.template.description)
                self.retrieve_sections(self.template.sections)
                self.txt_name.focus_set()
                self.show_cu_buttons()
                self.frm_child_crud['text'] = 'Update template'
                self.frm_child_list.grid_forget()
                self.frm_child_crud.grid(row=1,
                                         column=0,
                                         columnspan=9,
                                         rowspan=8,
                                         pady=10,
                                         padx=10)
        else:
            messagebox.showwarning(parent=self.frm_child_list,
                                   title='No selection',
                                   message='You must select one item')

    def click_delete(self):
        if len(self.trv_available.selection()) == 1:
            decision = messagebox.askyesno(
                parent=self.frm_child_list,
                title='Confirmation',
                message='Are you sure you want to delete the item?')
            if decision:
                self.directive = Message(action=39,
                                         information=[self.id_selected])
                self.connection = self.directive.send_directive(
                    self.connection)
                if self.connection.message.action == 5:  # An error ocurred while deleting the item
                    messagebox.showerror(
                        parent=self.frm_child_list,
                        title='Can not delete the item',
                        message=self.connection.message.information[0])
                else:
                    self.retrieve_list()
        else:
            messagebox.showwarning(parent=self.frm_child_list,
                                   title='No selection',
                                   message='You must select one item')

    def show_cu_buttons(self):
        self.btn_add.grid(row=5, column=5, padx=25)
        self.btn_remove.grid(row=6, column=5, padx=25)
        self.btn_main_section.grid(row=2, column=10, padx=25)
        self.btn_down.grid(row=6, column=10, padx=25)
        self.btn_up.grid(row=5, column=10, padx=25)
        self.btn_save.grid(row=0, column=0, padx=5, pady=5, sticky=W)
        self.btn_cancel.grid(row=1, column=0, padx=5, pady=5, sticky=W)
        self.lbl_note_optional.grid(row=9, column=6, columnspan=4, sticky=W)

    def disable_visual_components(self):
        self.txt_name['bg'] = DISABLED_COLOR
        self.txt_description['bg'] = DISABLED_COLOR
        self.txt_name['state'] = DISABLED
        self.txt_description['state'] = DISABLED
        self.btn_add.grid_forget()
        self.btn_remove.grid_forget()
        self.btn_main_section.grid_forget()
        self.btn_down.grid_forget()
        self.btn_up.grid_forget()

    def retrieve_sections(self, s_sections=None):
        if s_sections is None:
            s_sections = []
        self.directive = Message(action=32)
        self.connection = self.directive.send_directive(self.connection)
        a_sections = self.connection.message.information
        for item in self.trv_available_sections.get_children():
            self.trv_available_sections.delete(item)
        for item in self.trv_selected_sections.get_children():
            self.trv_selected_sections.delete(item)
        for item in s_sections:
            item_aux1 = item.split('¥')[2:6]
            item_aux2 = [item.split('¥')[-1]]
            item = item_aux1 + item_aux2
            item = '¥'.join(item)
            if item in a_sections:
                a_sections.remove(item)
        for index, item in enumerate(a_sections):
            elements = item.split('¥')
            self.trv_available_sections.insert(
                '',
                'end',
                text=elements[0],
                values=(index + 1, summarize_text(elements[1], 150),
                        summarize_text(elements[3], 120)))
        for index, item in enumerate(s_sections):
            elements = item.split('¥')
            self.trv_selected_sections.insert(
                '',
                'end',
                text=elements[2],
                values=(index + 1, summarize_text(elements[3], 150),
                        summarize_text(elements[5],
                                       120), elements[7], elements[8]))

    def click_add(self):
        """
        Function that moves a 'Section' from available tree view to selected tree view (in frm_child_crud)
        """
        if len(self.trv_available_sections.selection()) != 0 and len(
                self.trv_selected_sections.selection()) == 0:
            if len(self.trv_selected_sections.get_children()) != 0:
                index = self.trv_selected_sections.item(
                    self.trv_selected_sections.get_children()[-1])['values'][0]
            else:
                index = 0
            for row in self.trv_available_sections.selection():
                index += 1
                values = self.trv_available_sections.item(row)['values']
                self.trv_selected_sections.insert(
                    '',
                    'end',
                    text=self.trv_available_sections.item(row)['text'],
                    values=(index, values[1], values[2], '✓', ''))
                self.trv_available_sections.delete(row)

    def click_remove(self):
        """
        Function that moves a 'Section' from selected tree view to available tree view (in frm_child_crud)
        """
        if len(self.trv_selected_sections.selection()) != 0 and len(
                self.trv_available_sections.selection()) == 0:
            if len(self.trv_available_sections.get_children()) != 0:
                index = self.trv_available_sections.item(
                    self.trv_available_sections.get_children()
                    [-1])['values'][0]
            else:
                index = 0
            for row in self.trv_selected_sections.selection():
                index += 1
                values = self.trv_selected_sections.item(row)['values']
                self.trv_available_sections.insert(
                    '',
                    'end',
                    text=self.trv_selected_sections.item(row)['text'],
                    values=(index, values[1], values[2]))
                self.trv_selected_sections.delete(row)

    def click_up(self):
        # Make sure only one item in 'selected sections' is selected
        if len(self.trv_selected_sections.selection()) == 1 and len(
                self.trv_available_sections.selection()) == 0:
            item = self.trv_selected_sections.selection()
            index = self.trv_selected_sections.index(item)
            self.trv_selected_sections.move(item, '', index - 1)

    def click_down(self):
        # Make sure only one item in 'selected sections' is selected
        if len(self.trv_selected_sections.selection()) == 1 and len(
                self.trv_available_sections.selection()) == 0:
            item = self.trv_selected_sections.selection()
            index = self.trv_selected_sections.index(item)
            self.trv_selected_sections.move(item, '', index + 1)

    def click_main_section(self):
        # Make sure a max of three items of 'selected sections' are selected
        if 4 > len(self.trv_selected_sections.selection()) > 0 and len(
                self.trv_available_sections.selection()) == 0:
            # First change all sections as normal (not main)
            for item in self.trv_selected_sections.get_children():
                if self.trv_selected_sections.item(item)['values'][3] != '':
                    values = self.trv_selected_sections.item(item)['values']
                    self.trv_selected_sections.item(
                        item,
                        values=(values[0], values[1], values[2], values[3],
                                ''))
            # Set new main sections
            cont_error = False
            for row in self.trv_selected_sections.selection():
                values = self.trv_selected_sections.item(row)['values']
                if values[2] == 'Classification' or values[2] == 'Text':
                    self.trv_selected_sections.item(
                        row,
                        values=(values[0], values[1], values[2], values[3],
                                '✓'))
                else:  # File sections can not be main
                    cont_error = True
            if cont_error:
                messagebox.showwarning(
                    parent=self.frm_child_crud,
                    title='Main section(s)',
                    message=
                    "Main section(s) must be of 'Text' or 'Classification' data type"
                )
        else:
            messagebox.showwarning(
                parent=self.frm_child_crud,
                title='Main section(s)',
                message=
                'You must select a minimum of one and a maximum of three sections'
            )

    def click_trv_asections(self, event):
        """
        Function that removes selection from 'available' tree view when 'selected' tree view is selected (in frm_child_crud)
        """
        self.trv_selected_sections.selection_remove(
            self.trv_selected_sections.selection())

    def click_trv_ssections(self, event):
        """
        Function that removes selection from 'selected' tree view when 'available' tree view is selected (in frm_child_crud)
        """
        self.trv_available_sections.selection_remove(
            self.trv_available_sections.selection())

    def click_save(self):
        if self.validate_fields():
            self.template.name = self.txt_name.get()
            self.template.description = self.txt_description.get(
                '1.0', 'end-1c')
            if self.template.id == 0:  # Creating a template
                self.directive = Message(action=36,
                                         information=[
                                             self.template.name,
                                             self.template.description, [], [],
                                             []
                                         ])
                for item in self.trv_selected_sections.get_children():
                    values = self.trv_selected_sections.item(item)['values']
                    self.directive.information[2].append(
                        int(self.trv_selected_sections.item(item)['text']))
                    self.directive.information[3].append(values[4])
                    if values[4] != '':
                        self.directive.information[4].append('✓')
                    else:
                        self.directive.information[4].append(values[3])
            else:  # Updating a template
                self.directive = Message(action=38,
                                         information=[
                                             self.template.id,
                                             self.template.name,
                                             self.template.description, [], [],
                                             []
                                         ])
                for item in self.trv_selected_sections.get_children():
                    values = self.trv_selected_sections.item(item)['values']
                    self.directive.information[3].append(
                        int(self.trv_selected_sections.item(item)['text']))
                    self.directive.information[4].append(values[4])
                    if values[4] != '':
                        self.directive.information[5].append('✓')
                    else:
                        self.directive.information[5].append(values[3])
            self.connection = self.directive.send_directive(self.connection)
            self.clear_fields()
            self.frm_child_crud.grid_forget()
            self.show_frm()

    def click_cancel(self):
        decision = True
        if self.txt_name.get() != self.template.name or \
                self.txt_description.get('1.0', 'end-1c') != self.template.description or \
                len(self.trv_selected_sections.get_children()) != len(self.template.sections):
            decision = messagebox.askyesno(
                parent=self.frm_child_crud,
                title='Cancel',
                message='Are you sure you want to cancel?')
        if decision:
            self.click_back()

    def click_back(self):
        self.clear_fields()
        self.frm_child_crud.grid_forget()
        self.show_frm()

    def validate_fields(self):
        text_section = False
        if len(self.txt_name.get()) == 0:
            messagebox.showwarning(parent=self.frm_child_crud,
                                   title='Missing information',
                                   message='You must provide a name')
            return False
        if len(self.txt_description.get('1.0', 'end-1c')) == 0:
            messagebox.showwarning(parent=self.frm_child_crud,
                                   title='Missing information',
                                   message='You must provide a description')
            return False
        if len(self.trv_selected_sections.get_children()) == 0:
            messagebox.showwarning(
                parent=self.frm_child_crud,
                title='Missing information',
                message='You must select at least one section')
            return False
        for item in self.trv_selected_sections.get_children():
            values = self.trv_selected_sections.item(item)['values']
            if values[2] == 'Text' or values[2] == 'Classification':
                text_section = True
                break
        if not text_section:
            messagebox.showwarning(
                parent=self.frm_child_crud,
                title='Missing information',
                message='At least one section has to be of text type')
            return False
        for item in self.trv_selected_sections.get_children():
            values = self.trv_selected_sections.item(item)['values']
            if values[4] == '✓':
                if values[2] == 'Text' or values[2] == 'Classification':
                    return True
                else:
                    messagebox.showwarning(
                        parent=self.frm_child_crud,
                        title='Main section',
                        message=
                        'The main section has to be of text or classification type'
                    )
                    return False
        messagebox.showwarning(
            parent=self.frm_child_crud,
            title='Main section',
            message='You must set one of the selected section as main')
        return False

    def clear_fields(self):
        self.txt_name['state'] = NORMAL
        self.txt_description['state'] = NORMAL
        self.txt_name['bg'] = ENABLED_COLOR
        self.txt_description['bg'] = ENABLED_COLOR
        self.txt_name.delete(0, END)
        self.txt_description.delete('1.0', 'end-1c')
        self.btn_save.grid_forget()
        self.btn_cancel.grid_forget()
        self.btn_back.grid_forget()
        self.lbl_note_optional.grid_forget()

    def click_switch_mandatory(self, event):
        if not self.view_decision:  # Only if not viewing a template
            # Make sure only one item in 'selected sections' is selected
            if len(self.trv_selected_sections.selection()) == 1 and len(
                    self.trv_available_sections.selection()) == 0:
                values = self.trv_selected_sections.item(
                    self.trv_selected_sections.focus())['values']
                if values[3] == '':
                    self.trv_selected_sections.item(
                        self.trv_selected_sections.focus(),
                        values=(values[0], values[1], values[2], '✓',
                                values[4]))
                else:
                    self.trv_selected_sections.item(
                        self.trv_selected_sections.focus(),
                        values=(values[0], values[1], values[2], '',
                                values[4]))
Exemple #14
0
class NameView(object):
    """Shows a treeview of unique names."""

    def __init__(self, master, names):
        self.widget = Frame(master)
        self._tree = Treeview(self.widget, columns='name')
        self._tree.grid(row=0,column=0, sticky=(N,S,W,E))
        self._tree.view = self
        self.widget.columnconfigure(0, weight=1)
        self.widget.rowconfigure(0,weight=1)
        self._tree.column('name', width=50)
        self._tree['show'] = 'tree'
        actions = {'edit': lambda e: self.edit(),
                'search': lambda e: self.search(),
                'focus_next': lambda e: self.focus_next(),
                'focus_prev': lambda e: self.focus_prev(),
                'select': lambda e: self._tree.selection_toggle(self._tree.focus()),
                'clear_selection': lambda e: self._tree.selection_set([])
                }
        kb.make_bindings(kb.tagview, actions, self._tree.bind)
        self._iids = dict()
        self._names = dict()
        logger.debug('Names: %s', names)
        self.widget.focus_set = self._tree.focus_set
        for name in sorted(names):
            iid = self._tree.insert('', 'end', text=name)
            self._names[iid] = name
            self._iids[name] = iid
        self._scroll = Scrollbar(self.widget, command=self._tree.yview)
        self._tree['yscrollcommand'] = self._scroll.set
        self._scroll.grid(row=0, column=1, sticky=(N, S))
        self.widget.columnconfigure(1, weight=0)


    def selection(self):
        logger.debug('Selection: %s', self._tree.selection())
        return [self._names[iid] for iid in self._tree.selection()]

    def edit(self):
        self._tree.event_generate('<<NameViewEdit>>')

    def search(self):
        if len(self._tree.selection()) == 0:
            self._tree.selection_add(self._tree.focus())
        self._tree.event_generate('<<NameViewSearch>>')

    def append(self, names):
        logger.debug('Append names: %s', names)
        for name in names:
            if name not in self._names.values():
                iid = self._tree.insert('', 'end', text=name)
                self._names[iid] = name
                self._iids[name] = iid

    def delete(self, name):
        self._tree.delete(self._iids[name])
        del self._names[self._iids[name]]
        del self._iids[name]

    def _focus(self, iid):
        self._tree.focus(iid)
        self._tree.see(iid)

    def focus_next(self):
        cur_iid = self._tree.focus()
        next_iid = self._tree.next(cur_iid)
        if next_iid == '':
            iids = self._tree.get_children()
            next_iid = iids[0]
        self._focus(next_iid)

    def focus_prev(self):
        cur_iid = self._tree.focus()
        prev_iid = self._tree.prev(cur_iid)
        if prev_iid == '':
            iids = self._tree.get_children()
            prev_iid = iids[-1]
        self._focus(prev_iid)

    def jump_to(self, name):
        try:
            iid = self._iids[name]
            self._focus(iid)
        except KeyError:
            pass

    def get_names(self):
        return tuple(self._names.values())

    def set(self, names):
        self._tree.delete(*self._iids.values())
        self._iids.clear()
        self._names.clear()
        for name in sorted(names):
            iid = self._tree.insert('', 'end', text=name)
            self._names[iid] = name
            self._iids[name] = iid
Exemple #15
0
class ManagerWindow:
    """
    主管理界面入口类,直接无参数创建对象即可。
    """

    # 窗口宽高
    WIN_WIDTH = 800
    WIN_HEIGHT = 600

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        # 刷新显示
        self.__refresh()

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

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

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

        # 显示
        self.root.mainloop()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        :return: 无
        """

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

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

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

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

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

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

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

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

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

        :return: 无
        """

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

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

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

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

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

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

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

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

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

        :return: 无
        """

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

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

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

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

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

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

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

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

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

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

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

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

        :return: 无
        """

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

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

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

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

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

        :return: 无
        """

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        else:
            # 如果要断开连接

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

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

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

            # 释放session
            Conn.release_session_key()

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

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

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

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

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

        # 杀掉root
        self.root.destroy()

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

        :return: 无
        """

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

            :return: 无
            """

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

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

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

            :return: 无
            """

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

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

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

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

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

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

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

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

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

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

        # 调用刷新op列表
        refresh_op_list()

        # 刷新config显示
        refresh_config()

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

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

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

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

        # 刷新显示
        self.__refresh()

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

        :return: 无
        """

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

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

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

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

            :return: 无
            """

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

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

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

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

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

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

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

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

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

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

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

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

        :return: 无
        """

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

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

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

        :return: 无
        """

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

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



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

        :return: 无
        """

        content = self.entry_add_op.get()

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

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

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

        # 刷新显示
        self.__refresh()

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

        :return: 无
        """

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

            :return: 无
            """

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

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

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

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

        :return: 无
        """

        content = self.entry_command_head.get()

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

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

        # 刷新
        self.__refresh()

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

    def __auto_connect(self):
        if self.config_dict["autoConnect"]:
            self.__on_click_connect_event()
Exemple #16
0
class Multicolumn_Listbox(Frame):
    _style_index = 0

    class List_Of_Rows(object):
        def __init__(self, multicolumn_listbox):
            self._multicolumn_listbox = multicolumn_listbox

        def data(self, index):
            return self._multicolumn_listbox.row_data(index)

        def get(self, index):
            return Row(self._multicolumn_listbox, index)

        def insert(self, data, index=None):
            self._multicolumn_listbox.insert_row(data, index)

        def delete(self, index):
            self._multicolumn_listbox.delete_row(index)

        def update(self, index, data):
            self._multicolumn_listbox.update_row(index, data)

        def select(self, index):
            self._multicolumn_listbox.select_row(index)

        def deselect(self, index):
            self._multicolumn_listbox.deselect_row(index)

        def set_selection(self, indices):
            self._multicolumn_listbox.set_selection(indices)

        def __getitem__(self, index):
            return self.get(index)

        def __setitem__(self, index, value):
            return self._multicolumn_listbox.update_row(index, value)

        def __delitem__(self, index):
            self._multicolumn_listbox.delete_row(index)

        def __len__(self):
            return self._multicolumn_listbox.number_of_rows

    class List_Of_Columns(object):
        def __init__(self, multicolumn_listbox):
            self._multicolumn_listbox = multicolumn_listbox

        def data(self, index):
            return self._multicolumn_listbox.get_column(index)

        def get(self, index):
            return Column(self._multicolumn_listbox, index)

        def delete(self, index):
            self._multicolumn_listbox.delete_column(index)

        def update(self, index, data):
            self._multicolumn_listbox.update_column(index, data)

        def __getitem__(self, index):
            return self.get(index)

        def __setitem__(self, index, value):
            return self._multicolumn_listbox.update_column(index, value)

        def __delitem__(self, index):
            self._multicolumn_listbox.delete_column(index)

        def __len__(self):
            return self._multicolumn_listbox.number_of_columns

    def __init__(self,
                 master,
                 columns,
                 data=None,
                 command=None,
                 sort=True,
                 select_mode=None,
                 heading_anchor=CENTER,
                 cell_anchor=W,
                 style=None,
                 height=None,
                 padding=None,
                 adjust_heading_to_content=False,
                 stripped_rows=None,
                 selection_background=None,
                 selection_foreground=None,
                 field_background=None,
                 heading_font=None,
                 heading_background=None,
                 heading_foreground=None,
                 cell_pady=2,
                 cell_background=None,
                 cell_foreground=None,
                 cell_font=None,
                 headers=True):

        self._stripped_rows = stripped_rows

        self._columns = columns

        self._number_of_rows = 0
        self._number_of_columns = len(columns)

        self.row = self.List_Of_Rows(self)
        self.column = self.List_Of_Columns(self)

        s = Style()

        if style is None:
            style_name = "Multicolumn_Listbox%s.Treeview" % self._style_index
            self._style_index += 1
        else:
            style_name = style

        style_map = {}
        if selection_background is not None:
            style_map["background"] = [('selected', selection_background)]

        if selection_foreground is not None:
            style_map["foeground"] = [('selected', selection_foreground)]

        if style_map:
            s.map(style_name, **style_map)

        style_config = {}
        if cell_background is not None:
            style_config["background"] = cell_background

        if cell_foreground is not None:
            style_config["foreground"] = cell_foreground

        if cell_font is None:
            font_name = s.lookup(style_name, "font")
            cell_font = nametofont(font_name)
        else:
            if not isinstance(cell_font, Font):
                if isinstance(cell_font, basestring):
                    cell_font = nametofont(cell_font)
                else:
                    if len(Font) == 1:
                        cell_font = Font(family=cell_font[0])
                    elif len(Font) == 2:
                        cell_font = Font(family=cell_font[0],
                                         size=cell_font[1])

                    elif len(Font) == 3:
                        cell_font = Font(family=cell_font[0],
                                         size=cell_font[1],
                                         weight=cell_font[2])
                    else:
                        raise ValueError(
                            "Not possible more than 3 values for font")

            style_config["font"] = cell_font

        self._cell_font = cell_font

        self._rowheight = cell_font.metrics("linespace") + cell_pady
        style_config["rowheight"] = self._rowheight

        if field_background is not None:
            style_config["fieldbackground"] = field_background

        s.configure(style_name, **style_config)

        heading_style_config = {}
        if heading_font is not None:
            heading_style_config["font"] = heading_font
        if heading_background is not None:
            heading_style_config["background"] = heading_background
        if heading_foreground is not None:
            heading_style_config["foreground"] = heading_foreground

        heading_style_name = style_name + ".Heading"
        s.configure(heading_style_name, **heading_style_config)

        treeview_kwargs = {"style": style_name}

        if height is not None:
            treeview_kwargs["height"] = height

        if padding is not None:
            treeview_kwargs["padding"] = padding

        if headers:
            treeview_kwargs["show"] = "headings"
        else:
            treeview_kwargs["show"] = ""

        if select_mode is not None:
            treeview_kwargs["selectmode"] = select_mode

        self.interior = Treeview(master, columns=columns, **treeview_kwargs)

        if command is not None:
            self._command = command
            self.interior.bind("<<TreeviewSelect>>", self._on_select)

        for i in range(0, self._number_of_columns):

            if sort:
                self.interior.heading(
                    i,
                    text=columns[i],
                    anchor=heading_anchor,
                    command=lambda col=i: self.sort_by(col, descending=False))
            else:
                self.interior.heading(i,
                                      text=columns[i],
                                      anchor=heading_anchor)

            if adjust_heading_to_content:
                self.interior.column(i, width=Font().measure(columns[i]))

            self.interior.column(i, anchor=cell_anchor)

        if data is not None:
            for row in data:
                self.insert_row(row)

    @property
    def row_height(self):
        return self._rowheight

    @property
    def font(self):
        return self._cell_font

    def configure_column(self,
                         index,
                         width=None,
                         minwidth=None,
                         anchor=None,
                         stretch=None):
        kwargs = {}
        for config_name in ("width", "anchor", "stretch", "minwidth"):
            config_value = locals()[config_name]
            if config_value is not None:
                kwargs[config_name] = config_value

        self.interior.column('#%s' % (index + 1), **kwargs)

    def row_data(self, index):
        try:
            item_ID = self.interior.get_children()[index]
        except IndexError:
            raise ValueError("Row index out of range: %d" % index)

        return self.item_ID_to_row_data(item_ID)

    def update_row(self, index, data):
        try:
            item_ID = self.interior.get_children()[index]
        except IndexError:
            raise ValueError("Row index out of range: %d" % index)

        if len(data) == len(self._columns):
            self.interior.item(item_ID, values=data)
        else:
            raise ValueError("The multicolumn listbox has only %d columns" %
                             self._number_of_columns)

    def delete_row(self, index):
        list_of_items = self.interior.get_children()

        try:
            item_ID = list_of_items[index]
        except IndexError:
            raise ValueError("Row index out of range: %d" % index)

        self.interior.delete(item_ID)
        self._number_of_rows -= 1

        if self._stripped_rows:
            for i in range(index, self._number_of_rows):
                self.interior.tag_configure(list_of_items[i + 1],
                                            background=self._stripped_rows[i %
                                                                           2])

    def insert_row(self, data, index=None):
        if len(data) != self._number_of_columns:
            raise ValueError("The multicolumn listbox has only %d columns" %
                             self._number_of_columns)

        if index is None:
            index = self._number_of_rows - 1

        item_ID = self.interior.insert('', index, values=data)
        self.interior.item(item_ID, tags=item_ID)

        self._number_of_rows += 1

        if self._stripped_rows:
            list_of_items = self.interior.get_children()

            self.interior.tag_configure(item_ID,
                                        background=self._stripped_rows[index %
                                                                       2])

            for i in range(index + 1, self._number_of_rows):
                self.interior.tag_configure(list_of_items[i],
                                            background=self._stripped_rows[i %
                                                                           2])

    def column_data(self, index):
        return [
            self.interior.set(child_ID, index)
            for child_ID in self.interior.get_children('')
        ]

    def update_column(self, index, data):
        for i, item_ID in enumerate(self.interior.get_children()):
            data_row = self.item_ID_to_row_data(item_ID)
            data_row[index] = data[i]

            self.interior.item(item_ID, values=data_row)

        return data

    def clear(self):
        # Another possibility:
        #  self.interior.delete(*self.interior.get_children())

        for row in self.interior.get_children():
            self.interior.delete(row)

        self._number_of_rows = 0

    def update(self, data):
        self.clear()

        for row in data:
            self.insert_row(row)

    def focus(self, index=None):
        if index is None:
            return self.interior.item(self.interior.focus())
        else:
            item = self.interior.get_children()[index]
            self.interior.focus(item)

    def state(self, state=None):
        if state is None:
            return self.interior.state()
        else:
            self.interior.state(state)

    @property
    def number_of_rows(self):
        return self._number_of_rows

    @property
    def number_of_columns(self):
        return self._number_of_columns

    def toogle_selection(self, index):
        list_of_items = self.interior.get_children()

        try:
            item_ID = list_of_items[index]
        except IndexError:
            raise ValueError("Row index out of range: %d" % index)

        self.interior.selection_toggle(item_ID)

    def select_row(self, index):
        list_of_items = self.interior.get_children()

        try:
            item_ID = list_of_items[index]
        except IndexError:
            raise ValueError("Row index out of range: %d" % index)

        self.interior.selection_add(item_ID)

    def deselect_row(self, index):
        list_of_items = self.interior.get_children()

        try:
            item_ID = list_of_items[index]
        except IndexError:
            raise ValueError("Row index out of range: %d" % index)

        self.interior.selection_remove(item_ID)

    def deselect_all(self):
        self.interior.selection_remove(self.interior.selection())

    def set_selection(self, indices):
        list_of_items = self.interior.get_children()

        self.interior.selection_set(" ".join(list_of_items[row_index]
                                             for row_index in indices))

    @property
    def selected_rows(self):
        data = []
        for item_ID in self.interior.selection():
            data_row = self.item_ID_to_row_data(item_ID)
            data.append(data_row)

        return data

    @property
    def indices_of_selected_rows(self):
        list_of_indices = []
        for index, item_ID in enumerate(self.interior.get_children()):
            if item_ID in self.interior.selection():
                list_of_indices.append(index)

        return list_of_indices

    def delete_all_selected_rows(self):
        selected_items = self.interior.selection()
        for item_ID in selected_items:
            self.interior.delete(item_ID)

        number_of_deleted_rows = len(selected_items)
        self._number_of_rows -= number_of_deleted_rows

        return number_of_deleted_rows

    def _on_select(self, event):
        for item_ID in event.widget.selection():
            data_row = self.item_ID_to_row_data(item_ID)
            self._command(data_row)

    def item_ID_to_row_data(self, item_ID):
        item = self.interior.item(item_ID)
        return item["values"]

    @property
    def table_data(self):
        data = []

        for item_ID in self.interior.get_children():
            data_row = self.item_ID_to_row_data(item_ID)
            data.append(data_row)

        return data

    @table_data.setter
    def table_data(self, data):
        self.update(data)

    def cell_data(self, row, column):
        """Get the value of a table cell"""
        try:
            item = self.interior.get_children()[row]
        except IndexError:
            raise ValueError("Row index out of range: %d" % row)

        return self.interior.set(item, column)

    def update_cell(self, row, column, value):
        """Set the value of a table cell"""

        item_ID = self.interior.get_children()[row]

        data = self.item_ID_to_row_data(item_ID)

        data[column] = value
        self.interior.item(item_ID, values=data)

    def __getitem__(self, index):
        if isinstance(index, tuple):
            row, column = index
            return self.cell_data(row, column)
        else:
            raise Exception("Row and column indices are required")

    def __setitem__(self, index, value):
        if isinstance(index, tuple):
            row, column = index
            self.update_cell(row, column, value)
        else:
            raise Exception("Row and column indices are required")

    def bind(self, event, handler):
        self.interior.bind(event, handler)

    def sort_by(self, col, descending):
        """
        sort tree contents when a column header is clicked
        """
        # grab values to sort
        data = [(self.interior.set(child_ID, col), child_ID)
                for child_ID in self.interior.get_children('')]

        # if the data to be sorted is numeric change to float
        try:
            data = [(float(number), child_ID) for number, child_ID in data]
        except ValueError:
            pass

        # now sort the data in place
        data.sort(reverse=descending)
        for idx, item in enumerate(data):
            self.interior.move(item[1], '', idx)

        # switch the heading so that it will sort in the opposite direction
        self.interior.heading(
            col, command=lambda col=col: self.sort_by(col, not descending))

        if self._stripped_rows:
            list_of_items = self.interior.get_children('')
            for i in range(len(list_of_items)):
                self.interior.tag_configure(list_of_items[i],
                                            background=self._stripped_rows[i %
                                                                           2])

    def destroy(self):
        self.interior.destroy()

    def item_ID(self, index):
        return self.interior.get_children()[index]
Exemple #17
0
class Cerberus:
    def __init__(self, master, root):
        self.exportToCSV = False
        self.versionApp, self.key, self.salt = self.initApp()

        self.key = cerberusCryptography.getMasterKey()
        self.cipher_suite = Fernet(self.key)

        self.master = master
        self.master.title('Cerberus')
        self.windowWidth = 1060
        self.windowHeight = 450
        self.screenWidth = self.master.winfo_screenwidth()
        self.screenHeight = self.master.winfo_screenheight()
        self.positionRight = int(self.screenWidth / 2 - self.windowWidth / 2)
        self.positionDown = int(self.screenHeight / 3 - self.windowHeight / 2)
        self.master.geometry("{}x{}+{}+{}".format(self.windowWidth,
                                                  self.windowHeight,
                                                  self.positionRight,
                                                  self.positionDown))

        self.img = PhotoImage(data=icons.getAppIcon())
        self.master.wm_iconphoto(True, self.img)

        self.master.resizable(0, 0)

        self.menubar = Menu(master)
        filemenu = Menu(self.menubar, tearoff=0)

        self.menubar.add_cascade(label="Cerberus", menu=filemenu)
        self.addIcon = PhotoImage(data=icons.getAddIcon())
        filemenu.add_command(label="Εισαγωγή Υπηρεσίας",
                             image=self.addIcon,
                             compound='left',
                             command=self.getAddNewServiceForm)
        self.editIcon = PhotoImage(data=icons.getEditIcon())
        filemenu.add_command(label="Επεξεργασία Υπηρεσίας",
                             image=self.editIcon,
                             compound='left',
                             command=self.getEditServiceForm)
        self.deleteIcon = PhotoImage(data=icons.getDeleteIcon())
        filemenu.add_command(label="Διαγραφή Υπηρεσίας",
                             image=self.deleteIcon,
                             compound='left',
                             command=self.deleteService)
        filemenu.add_separator()
        self.excelIcon = PhotoImage(data=icons.getExcelIcon())
        filemenu.add_command(label="Εξαγωγή σε Excel",
                             image=self.excelIcon,
                             compound='left',
                             command=self.checkPasswordToExportToCSV)
        filemenu.add_separator()
        self.exitIcon = PhotoImage(data=icons.getExitIcon())
        filemenu.add_command(label="Έξοδος",
                             image=self.exitIcon,
                             compound='left',
                             command=self.exitApp)

        settingsMenu = Menu(self.menubar, tearoff=0)
        self.menubar.add_cascade(label="Ρυθμίσεις", menu=settingsMenu)
        self.settingsIcon = PhotoImage(data=icons.getSettingsIcon())
        settingsMenu.add_command(label="Επεξεργασία Στοιχείων",
                                 image=self.settingsIcon,
                                 compound='left')
        #command=self.getSettingsForm)

        aboutMenu = Menu(self.menubar, tearoff=0)
        self.menubar.add_cascade(label="Βοήθεια", menu=aboutMenu)
        self.infoIcon = PhotoImage(data=icons.getInfoIcon())
        aboutMenu.add_command(label="Περί",
                              image=self.infoIcon,
                              compound='left',
                              command=self.getAboutAppForm)

        self.master.config(menu=self.menubar)

        self.copyIcon = PhotoImage(data=icons.getCopyIcon())
        self.popup = Menu(root, tearoff=0)
        self.popup.add_command(label=" Αντιγραφή Email",
                               image=self.copyIcon,
                               compound='left',
                               command=self.copyEmail)
        self.popup.add_command(label=" Αντιγραφή Username",
                               image=self.copyIcon,
                               compound='left',
                               command=self.copyUsername)
        self.popup.add_command(label=" Αντιγραφή Κωδικού",
                               image=self.copyIcon,
                               compound='left',
                               command=self.copyPasswd)
        self.popup.add_command(label=" Αντιγραφή ID",
                               image=self.copyIcon,
                               compound='left',
                               command=self.copyID)
        self.popup.add_separator()
        self.popup.add_command(label=" Επεξεργασία Υπηρεσίας",
                               image=self.editIcon,
                               compound='left',
                               command=self.getEditServiceForm)
        self.popup.add_command(label=" Διαγραφή Υπηρεσίας",
                               image=self.deleteIcon,
                               compound='left',
                               command=self.deleteService)
        self.popup.add_separator()
        self.popup.add_command(label=" Έξοδος",
                               image=self.exitIcon,
                               compound='left',
                               command=self.exitApp)

        self.frame = Frame(self.master,
                           background="white",
                           borderwidth=1,
                           relief="sunken",
                           highlightthickness=1)
        self.frame.pack(side="top", fill="x", padx=4, pady=4)

        self.search = StringVar()

        self.searchEntry = Entry(self.frame,
                                 textvariable=self.search,
                                 borderwidth=0,
                                 highlightthickness=0,
                                 background="white")
        self.searchEntry.insert(0, 'Αναζήτηση Υπηρεσίας')
        self.searchEntry['fg'] = 'grey'
        self.search.trace(
            "w",
            lambda name, index, mode, sv=self.search: self.searchService())

        self.searchEntry.image = PhotoImage(data=icons.getSearchIcon())
        imageLabel = Label(self.frame, image=self.searchEntry.image)
        imageLabel.pack(side="left")
        imageLabel['bg'] = 'white'

        self.searchEntry.pack(side="left", fill="both", expand=True)

        # Fix BUG with Treeview colors in Python3.7
        def fixed_map(option):
            return [
                elm for elm in style.map('Treeview', query_opt=option)
                if elm[:2] != ('!disabled', '!selected')
            ]

        style = ttk.Style(root)
        style.map('Treeview',
                  foreground=fixed_map('foreground'),
                  background=fixed_map('background'))
        # Fix BUG with Treeview colors in Python3.7

        self.table = Treeview(self.master)
        self.table['show'] = 'headings'
        self.table['columns'] = ('Services', 'email', 'username', 'passwd',
                                 'id', 'category', 'url', 'ID')
        self.table["displaycolumns"] = ('Services', 'email', 'username',
                                        'passwd', 'id', 'category', 'url')

        for col in self.table['columns']:
            self.table.heading(
                col, command=lambda c=col: self.sortby(self.table, c, 0))

        self.table.heading('Services', text='Services')
        self.table.column('Services', anchor='center', width=200)

        self.table.heading('email', text='Email')
        self.table.column('email', anchor='center', width=200)

        self.table.heading('username', text='Username')
        self.table.column('username', anchor='center', width=100)

        self.table.heading('passwd', text='Password')
        self.table.column('passwd', anchor='center', width=100)

        self.table.heading('url', text='URL')
        self.table.column('url', anchor='center', width=120)

        self.table.heading('id', text='ID')
        self.table.column('id', anchor='center', width=100)

        self.table.heading('category', text='Category')
        self.table.column('category', anchor='center', width=100)

        self.table.heading('ID', text='ID')
        self.table.column('ID', anchor='center', width=200)

        self.table.tag_configure('oddrow', background='#e6eef2')
        self.table.tag_configure('evenrow', background='#b3cfdd')
        self.table.tag_configure('focus', background='#c6b6b4')
        self.last_focus = None
        self.last_focus_tag = None
        self.table.focus()
        self.table.pack(fill=BOTH, expand=1)
        self.table.bind("<<TreeviewSelect>>", self.onTableSelect)
        self.table.bind("<ButtonRelease-1>", self.openURLService)
        self.table.bind("<Motion>", self.changePointerOnHover)
        self.table.bind("<Button-3>", self.popupMenu)
        self.searchEntry.bind("<FocusIn>", self.foc_in)
        self.searchEntry.bind("<FocusOut>", self.foc_out)
        self.popup.bind("<FocusOut>", self.popupFocusOut)
        self.master.protocol("WM_DELETE_WINDOW", self.exitApp)

        self.loadTable(self)

        self.master.bind("<Escape>", self.exitApp)

    def popupFocusOut(self, event=None):
        self.popup.unpost()

    def foc_in(self, *args):
        if self.search.get() == 'Αναζήτηση Υπηρεσίας':
            self.searchEntry.delete('0', 'end')
            self.searchEntry['fg'] = 'black'

    def foc_out(self, *args):
        if not self.search.get():
            self.searchEntry.insert(0, 'Αναζήτηση Υπηρεσίας')
            self.searchEntry['fg'] = 'grey'
            self.loadTable(self)

    def changePointerOnHover(self, event):
        _iid = self.table.identify_row(event.y)

        if _iid != self.last_focus:
            if self.last_focus:
                self.table.item(self.last_focus, tags=[self.last_focus_tag])

            self.last_focus_tag = self.table.item(_iid, "tag")
            self.table.item(_iid, tags=['focus'])
            self.last_focus = _iid

        curItem = self.table.item(self.table.identify('item', event.x,
                                                      event.y))
        if curItem['values'] != '':
            col = self.table.identify_column(event.x)
            url = curItem['values'][int(col[-1]) - 1]

            if col[-1] == "7" and url != '---':
                self.master.config(cursor="hand2")
            else:
                self.master.config(cursor="")

    def openURLService(self, event):
        curItem = self.table.item(self.table.focus())
        col = self.table.identify_column(event.x)
        region = self.table.identify("region", event.x, event.y)

        if col[-1] == "7" and region != 'heading':
            url = curItem['values'][int(col[-1]) - 1]
            if url != '---':
                webbrowser.open_new_tab('http://' + str(url))

    def onTableSelect(self, event):
        for item in self.table.selection():
            item_text = self.table.item(item, "values")
            print(item_text[0])

    def getSelectedService(self, event):
        for item in self.table.selection():
            selectedRow = self.table.item(item, "value")
            return selectedRow

    def initApp(self):
        print("Initialize Cerberus App")
        try:
            conn = sqlite3.connect('cerberus.db')
        except sqlite3.Error as e:
            print(e)

        cur = conn.cursor()
        cur.execute(
            "SELECT version, masterToken, salt FROM cerberusParameters")
        row = cur.fetchone()
        cur.close()

        return row

    def copyEmail(self):
        for item in self.table.selection():
            item_text = self.table.item(item, "values")
            self.master.clipboard_clear()
            root.clipboard_append(item_text[1])

    def copyUsername(self):
        for item in self.table.selection():
            item_text = self.table.item(item, "values")
            self.master.clipboard_clear()
            root.clipboard_append(item_text[2])

    def copyPasswd(self):
        for item in self.table.selection():
            item_text = self.table.item(item, "values")
            self.master.clipboard_clear()
            root.clipboard_append(item_text[3])

    def copyID(self):
        for item in self.table.selection():
            item_text = self.table.item(item, "values")
            self.master.clipboard_clear()
            root.clipboard_append(item_text[4])

    def searchService(self):
        try:
            conn = sqlite3.connect('cerberus.db')
        except sqlite3.Error as e:
            print(e)

        cur = conn.cursor()

        if self.search.get() == 'Αναζήτηση Υπηρεσίας':
            pass
        elif self.search.get():
            cur.execute(
                "SELECT id, name, email, username, password, value, category, url FROM service WHERE name LIKE '%"
                + self.search.get() + "%' or name LIKE '%" +
                self.search.get().upper() +
                "%'")  # ('%'+self.search.get()+'%',),'Α')
        elif not self.search.get():
            cur.execute(
                "SELECT id, name, email, username, password, value, category, url FROM service "
            )

        rows = cur.fetchall()
        cur.close()

        for k in self.table.get_children():
            self.table.delete(k)

        i = 1
        for row in rows:
            if (i % 2) == 0:
                tag = "oddrow"
            else:
                tag = "evenrow"

            self.table.insert(
                '',
                'end',
                values=(
                    row[1],
                    self.cipher_suite.decrypt(row[2]).decode("utf-8").split(),
                    self.cipher_suite.decrypt(row[3]).decode("utf-8").split(),
                    self.cipher_suite.decrypt(row[4]).decode("utf-8").split(),
                    self.cipher_suite.decrypt(row[5]).decode("utf-8").split(),
                    self.cipher_suite.decrypt(row[6]).decode("utf-8").split(),
                    self.cipher_suite.decrypt(row[7]).decode("utf-8").split(),
                    row[0]),
                tags=tag)
            i = i + 1

    @staticmethod
    def exitApp(event=None):
        root.destroy()

    @staticmethod
    def getAboutAppForm():
        import aboutApp
        aboutApp.aboutApp()

    def getAddNewServiceForm(self):
        self.master.withdraw()
        import addNewServiceForm
        addNewServiceForm.addNewServiceForm(self)

    def getEditServiceForm(self):
        service = self.getSelectedService(self)

        if service is None:
            messagebox.showerror(
                "Μήνυμα Σφάλματος",
                "Παρακαλώ επιλέξτε την Υπηρεσία που θέλετε να Επεξεργαστείτε.")
        else:
            self.master.withdraw()
            import editServiceForm
            editServiceForm.editServiceForm(self, service)

    def getSettingsForm(self):
        import settingsForm
        settingsForm.settingsForm()

    def sortby(self, tree, col, descending):
        data = [(tree.set(child, col), child)
                for child in tree.get_children('')]
        data.sort(reverse=descending)
        for ix, item in enumerate(data):
            if (ix % 2) == 0:
                tag = "evenrow"
            else:
                tag = "oddrow"
            tree.move(item[1], '', ix)
            tree.item(item[1], tags=tag)
        # switch the heading so that it will sort in the opposite direction
        tree.heading(
            col,
            command=lambda x=col: self.sortby(tree, col, int(not descending)))

    @staticmethod
    def loadTable(self):
        try:
            conn = sqlite3.connect('cerberus.db')
        except sqlite3.Error as e:
            print(e)

        cur = conn.cursor()
        cur.execute(
            "SELECT id, name, email, username, password, value, category, url value FROM service"
        )

        rows = cur.fetchall()

        for row in self.table.get_children():
            self.table.delete(row)

        i = 1
        for row in rows:
            if (i % 2) == 0:
                tag = "oddrow"
            else:
                tag = "evenrow"

            self.table.insert(
                '',
                'end',
                values=(
                    row[1],
                    self.cipher_suite.decrypt(row[2]).decode("utf-8").split(),
                    self.cipher_suite.decrypt(row[3]).decode("utf-8").split(),
                    self.cipher_suite.decrypt(row[4]).decode("utf-8").split(),
                    self.cipher_suite.decrypt(row[5]).decode("utf-8").split(),
                    self.cipher_suite.decrypt(row[6]).decode("utf-8").split(),
                    self.cipher_suite.decrypt(row[7]).decode("utf-8").split(),
                    row[0]),
                tags=tag)

            i = i + 1

        conn.close()

        self.last_focus = None
        self.table.selection()

    def deleteService(self):
        service = self.getSelectedService(self)

        if service is None:
            messagebox.showerror(
                "Μήνυμα Σφάλματος",
                "Παρακαλώ επιλέξτε την Υπηρεσία που θέλετε να Διαγράξετε.")
        else:
            msgBox = messagebox.askquestion(
                'Διαγραφή: {}'.format(service[0]),
                'Είστε σίγουρος ότι θέλετε να διαγράψετε την Υπηρεσία: '
                '{}'
                ' ?'.format(service[0]),
                icon='warning')
            if msgBox == 'yes':
                try:
                    conn = sqlite3.connect('cerberus.db')
                except sqlite3.Error as e:
                    print(e)
                sql = 'DELETE FROM service WHERE id=?'
                cur = conn.cursor()
                cur.execute(sql, (service[-1], ))
                conn.commit()
                conn.close()
                self.loadTable(self)

    def popupMenu(self, event):
        serviceId = self.table.identify_row(event.y)
        if serviceId:
            self.table.selection_set(serviceId)
            try:
                self.popup.tk_popup(event.x_root, event.y_root)
            finally:
                self.popup.grab_release()

    def checkPasswordToExportToCSV(self):
        print("Check Password..")
        import logInForm
        self.master.withdraw()
        logInForm.logIn(self)

    @staticmethod
    def exportToCSV():
        print("Export Services to CSV...")
        try:
            conn = sqlite3.connect('cerberus.db')
        except sqlite3.Error as e:
            print(e)

        key = cerberusCryptography.getMasterKey()
        cipher_suite = Fernet(key)

        cur = conn.cursor()
        cur.execute(
            "SELECT category, name, email, username, password, value, url value FROM service"
        )

        rows = cur.fetchall()

        csvData = [[
            'Κατηγορία',
            'Υπηρεσία',
            'Email',
            'Όνομα Χρήστη',
            'Κωδικός',
            'ID',
            'URL',
        ]]

        for row in rows:
            csvData = csvData + [[
                cipher_suite.decrypt(row[0]).decode("utf-8").split(),
                cipher_suite.decrypt(row[1]).decode("utf-8").split(),
                cipher_suite.decrypt(row[2]).decode("utf-8").split(),
                cipher_suite.decrypt(row[3]).decode("utf-8").split(),
                cipher_suite.decrypt(row[4]).decode("utf-8").split(),
                cipher_suite.decrypt(row[5]).decode("utf-8").split(),
                cipher_suite.decrypt(row[6]).decode("utf-8").split(),
            ]]

        try:
            homeFolder = str(Path.home())
            filePath = filedialog.asksaveasfile(
                initialdir=homeFolder,
                initialfile='cerberus.csv',
                title="Επιλογή Αρχείου",
                filetypes=(("csv files", "*.csv"), ("all files", "*.*")))
            if filePath:
                try:
                    with open(filePath.name, 'w') as csvFile:
                        csvFile = csv.writer(csvFile, delimiter='\t')
                        csvFile.writerows(csvData)
                    messagebox.showinfo(
                        "Μήνυμα Επιτυχίας",
                        "Το αρχείο αποθηκέυτηκε με Επιτυχία στην τοποθεσία {}."
                        .format(filePath.name))
                except Exception as e:
                    messagebox.showerror(
                        "Μήνυμα Σφάλματος",
                        "Δεν ήταν δυνατή η Εξαγωγή του αρχείου.")
        except Exception as e:
            print(e)
            messagebox.showerror("Μήνυμα Σφάλματος",
                                 "Δεν ήταν δυνατή η Εξαγωγή του αρχείου.")
Exemple #18
0
class FormChildClassification:
    def __init__(self, frm_parent, connection):
        self.connection = connection
        self.directive = Message()
        self.decide = True
        self.id_selected = 0
        self.frm_child_list = LabelFrame(frm_parent)
        self.frm_child_crud = LabelFrame(frm_parent)
        self.frm_child_crud.config(fg=TEXT_COLOR, font=SUBTITLE_FONT)
        self.initialize_components()

    def initialize_components(self):
        """
        Method that initialize the visual components for each form associated with the local administration
        """
        # Resources for the Forms
        self.new_icon = PhotoImage(file=r"./Resources/create.png")
        self.view_icon = PhotoImage(file=r"./Resources/view.png")
        self.modify_icon = PhotoImage(file=r"./Resources/modify.png")
        self.remove_icon = PhotoImage(file=r"./Resources/delete.png")
        self.save_icon = PhotoImage(file=r"./Resources/save.png")
        self.back_icon = PhotoImage(file=r"./Resources/back.png")
        self.cancel_icon = PhotoImage(file=r"./Resources/cancel.png")

        # Components for List FRM
        lbl_sep1 = Label(self.frm_child_list)
        lbl_sep1.grid(row=0, column=0, padx=10, pady=25)
        self.trv_available = Treeview(self.frm_child_list,
                                      height=15,
                                      columns=('N', 'Name', '# categories'))
        self.trv_available.heading('#0', text='ID', anchor=CENTER)
        self.trv_available.heading('#1', text='N', anchor=CENTER)
        self.trv_available.heading('#2', text='Name', anchor=CENTER)
        self.trv_available.heading('#3', text='# categories', anchor=CENTER)
        self.trv_available.column('#0', width=0, minwidth=50, stretch=NO)
        self.trv_available.column('#1', width=20, minwidth=20, stretch=NO)
        self.trv_available.column('#2', width=200, minwidth=200, stretch=NO)
        self.trv_available.column('#3', width=150, minwidth=150, stretch=NO)
        self.trv_available.grid(row=0, column=1, sticky=W, pady=25)
        vsb_trv_av = Scrollbar(self.frm_child_list,
                               orient="vertical",
                               command=self.trv_available.yview)
        vsb_trv_av.grid(row=0, column=2, pady=25, sticky=NS)
        self.trv_available.configure(yscrollcommand=vsb_trv_av.set)
        frm_aux4 = Frame(self.frm_child_list)
        btn_new = Button(frm_aux4, image=self.new_icon, command=self.click_new)
        btn_new.grid(row=0, column=0, pady=5, padx=5, sticky=E)
        btn_new_ttp = CreateToolTip(btn_new, 'New classification')
        btn_view = Button(frm_aux4,
                          image=self.view_icon,
                          command=self.click_view)
        btn_view.grid(row=1, column=0, pady=5, padx=5, sticky=E)
        btn_view_ttp = CreateToolTip(btn_view, 'View classification')
        btn_edit = Button(frm_aux4,
                          image=self.modify_icon,
                          command=self.click_update)
        btn_edit.grid(row=2, column=0, pady=5, padx=5, sticky=E)
        btn_edit_ttp = CreateToolTip(btn_edit, 'Edit classification')
        btn_delete = Button(frm_aux4,
                            image=self.remove_icon,
                            command=self.click_delete)
        btn_delete.grid(row=3, column=0, pady=5, padx=5, sticky=E)
        btn_delete_ttp = CreateToolTip(btn_delete, 'Delete classification')
        frm_aux4.grid(row=0, column=4, pady=25, padx=25, sticky=NW)

        # Components CRUD
        self.frm_class = LabelFrame(self.frm_child_crud)
        self.frm_class.config(fg=TEXT_COLOR, font=SUBTITLE_FONT)
        lbl_class = Label(self.frm_child_crud, text='Name*')
        lbl_class.config(fg=TEXT_COLOR, font=LABEL_FONT)
        lbl_class.grid(row=0, column=0, pady=10, padx=20, sticky=W)
        lbl_desc_categories = Label(
            self.frm_child_crud,
            text='Enter categories separated by an Enter (\\n)')
        lbl_desc_categories.config(fg=TEXT_COLOR, font=LABEL_FONT)
        lbl_desc_categories.grid(row=1,
                                 column=0,
                                 pady=10,
                                 padx=20,
                                 columnspan=4,
                                 sticky=W)
        lbl_categories = Label(self.frm_child_crud, text='Categories*')
        lbl_categories.config(fg=TEXT_COLOR, font=LABEL_FONT)
        lbl_categories.grid(row=2, column=0, pady=10, padx=20, sticky=NW)
        lbl_sep2 = Label(self.frm_child_crud)
        lbl_sep2.grid(row=2, column=1, padx=20, pady=10)
        self.txt_name_class = Entry(self.frm_child_crud,
                                    width=50,
                                    font=TEXT_FONT)
        self.txt_name_class.grid(row=0,
                                 column=2,
                                 columnspan=2,
                                 pady=10,
                                 sticky=W)
        self.txt_categories = Text(self.frm_child_crud,
                                   height=10,
                                   width=50,
                                   font=TEXT_FONT)
        self.txt_categories.grid(row=2, column=2, pady=10, sticky=W)
        vsb_txt_cat = Scrollbar(self.frm_child_crud,
                                orient="vertical",
                                command=self.txt_categories.yview)
        vsb_txt_cat.grid(row=2, column=3, pady=10, sticky=NS)
        self.txt_categories.configure(yscrollcommand=vsb_txt_cat.set)
        sep_aux1 = Separator(self.frm_child_crud, orient=VERTICAL)
        sep_aux1.grid(row=0, column=4, sticky=NS, rowspan=3, padx=20)
        self.btn_save = Button(self.frm_child_crud,
                               image=self.save_icon,
                               command=self.click_save)
        btn_save_ttp = CreateToolTip(self.btn_save, 'Save classification')
        self.btn_back = Button(self.frm_child_crud,
                               image=self.back_icon,
                               command=self.click_back)
        btn_back_ttp = CreateToolTip(self.btn_back, 'Go back')
        self.btn_cancel = Button(self.frm_child_crud,
                                 image=self.cancel_icon,
                                 command=self.click_cancel)
        btn_cancel_ttp = CreateToolTip(self.btn_cancel, 'Cancel')

    def retrieve_list(self):
        # Remove existing elements in the list
        for item in self.trv_available.get_children():
            self.trv_available.delete(item)
        self.directive = Message(action=67)
        self.connection = self.directive.send_directive(self.connection)
        # Adding elements into the list
        for index, item in enumerate(self.connection.message.information):
            elements = item.split('¥')
            self.trv_available.insert('',
                                      'end',
                                      text=elements[0],
                                      values=(index + 1,
                                              summarize_text(elements[1], 200),
                                              summarize_text(elements[2],
                                                             150)))
        if len(self.trv_available.get_children()) != 0:
            self.trv_available.selection_set(
                self.trv_available.get_children()[0])

    def show_frm(self):
        self.retrieve_list()
        self.frm_child_list.grid(row=1,
                                 column=0,
                                 columnspan=9,
                                 rowspan=8,
                                 pady=10,
                                 padx=10)

    def hide_frm(self):
        self.clear_fields()
        self.frm_child_list.grid_forget()
        self.frm_child_crud.grid_forget()

    def click_new(self):
        self.classification = Classification()
        self.frm_child_crud['text'] = 'New Classification'
        self.txt_name_class.focus_set()
        self.btn_save.grid(row=0, column=5, padx=20)
        self.btn_cancel.grid(row=1, column=5, padx=20)
        self.frm_child_list.grid_forget()
        self.frm_child_crud.grid(row=1,
                                 column=0,
                                 columnspan=9,
                                 rowspan=8,
                                 pady=10,
                                 padx=10)

    def click_view(self):
        if len(self.trv_available.selection()) == 1:
            id_selected = int(
                self.trv_available.item(
                    self.trv_available.selection())['text'])
            # Retrieve selected classification from the database
            self.directive = Message(action=70, information=[id_selected])
            self.connection = self.directive.send_directive(self.connection)
            self.classification = Classification(
                id=id_selected,
                name=self.connection.message.information[0],
                categories=self.connection.message.information[1])
            # Insert information into visual components
            self.txt_name_class.insert(0, self.classification.name)
            # Section to insert categories in textbox
            length_string = 0
            for item in self.classification.categories:
                elements = item.split('¥')
                self.txt_categories.insert('end-1c', elements[1] + '\n')
                length_string += len(elements[1]) + 1
            self.txt_categories.delete('end-2c', 'end')
            self.frm_child_crud['text'] = 'View classification'
            self.txt_name_class['bg'] = DISABLED_COLOR
            self.txt_categories['bg'] = DISABLED_COLOR
            self.txt_name_class['state'] = DISABLED
            self.txt_categories['state'] = DISABLED
            self.btn_back.grid(row=0, column=5, padx=20)
            self.frm_child_list.grid_forget()
            self.frm_child_crud.grid(row=1,
                                     column=0,
                                     columnspan=9,
                                     rowspan=8,
                                     pady=10,
                                     padx=10)
        else:
            messagebox.showwarning(parent=self.frm_child_list,
                                   title='No selection',
                                   message='You must select one item')

    def click_update(self):
        if len(self.trv_available.selection()) == 1:
            id_selected = int(
                self.trv_available.item(
                    self.trv_available.selection())['text'])
            self.directive = Message(action=70,
                                     information=[id_selected, 'validate'])
            self.connection = self.directive.send_directive(self.connection)
            if self.connection.message.action == 5:  # An error ocurred while trying to update the item
                messagebox.showerror(
                    parent=self.frm_child_list,
                    title='Can not update the item',
                    message=self.connection.message.information[0])
            else:
                self.classification = Classification(
                    id=id_selected,
                    name=self.connection.message.information[0],
                    categories=self.connection.message.information[1])
                self.txt_name_class.insert(0, self.classification.name)
                # Section to insert categories in textbox
                length_string = 0
                for item in self.classification.categories:
                    elements = item.split('¥')
                    self.txt_categories.insert('end-1c', elements[1] + '\n')
                    length_string += len(elements[1]) + 1
                self.txt_categories.delete('end-2c', 'end')
                self.frm_child_crud['text'] = 'Update classification'
                self.txt_name_class.focus_set()
                self.btn_save.grid(row=0, column=5, padx=20)
                self.btn_cancel.grid(row=1, column=5, padx=20)
                self.frm_child_list.grid_forget()
                self.frm_child_crud.grid(row=1,
                                         column=0,
                                         columnspan=9,
                                         rowspan=8,
                                         pady=10,
                                         padx=10)
        else:
            messagebox.showwarning(parent=self.frm_child_list,
                                   title='No selection',
                                   message='You must select one item')

    def click_delete(self):
        if len(self.trv_available.selection()) == 1:
            decision = messagebox.askyesno(
                parent=self.frm_child_list,
                title='Confirmation',
                message='Are you sure you want to delete the item?')
            if decision:
                id_selected = int(
                    self.trv_available.item(
                        self.trv_available.selection())['text'])
                self.directive = Message(action=69, information=[id_selected])
                self.connection = self.directive.send_directive(
                    self.connection)
                if self.connection.message.action == 5:  # An error ocurred while deleting the item
                    messagebox.showerror(
                        parent=self.frm_child_list,
                        title='Can not delete the item',
                        message=self.connection.message.information[0])
                else:
                    self.retrieve_list()
        else:
            messagebox.showwarning(parent=self.frm_child_list,
                                   title='No selection',
                                   message='You must select one item')

    def click_save(self):
        if self.validate_fields():
            self.classification.name = self.txt_name_class.get()
            if self.classification.id == 0:  # Creating a new classification
                self.directive = Message(
                    action=66, information=[self.classification.name])
                self.connection = self.directive.send_directive(
                    self.connection)
                self.classification.id = self.connection.message.information[0]
                self.classification.categories = self.txt_categories.get(
                    '1.0', 'end-1c').split('\n')
                for item in self.classification.categories:
                    if item:  # None blank space will be saved
                        self.directive = Message(
                            action=71,
                            information=[item, self.classification.id])
                        self.connection = self.directive.send_directive(
                            self.connection)
            else:  # Updating a classification
                self.directive = Message(action=68,
                                         information=[
                                             self.classification.id,
                                             self.classification.name
                                         ])
                self.connection = self.directive.send_directive(
                    self.connection)
                # Delete categories associated with the current classification
                self.directive = Message(action=74,
                                         information=[self.classification.id])
                self.connection = self.directive.send_directive(
                    self.connection)
                # Create categories for the current classification
                self.classification.categories = self.txt_categories.get(
                    '1.0', 'end-1c').split('\n')
                for item in self.classification.categories:
                    if item:  # None blank space will be saved
                        self.directive = Message(
                            action=71,
                            information=[item, self.classification.id])
                        self.connection = self.directive.send_directive(
                            self.connection)
            self.click_back()

    def click_back(self):
        self.clear_fields()
        self.frm_child_crud.grid_forget()
        self.show_frm()

    def click_cancel(self):
        decision = True
        categories_aux = len(
            self.txt_categories.get('1.0', 'end-1c').split('\n'))
        categories_aux = categories_aux - 1 if self.classification.id == 0 else categories_aux
        if self.txt_name_class.get() != self.classification.name or \
                categories_aux != len(self.classification.categories):
            decision = messagebox.askyesno(
                parent=self.frm_child_crud,
                title='Cancel',
                message='Are you sure you want to cancel?')
        if decision:
            self.click_back()

    def validate_fields(self):
        if len(self.txt_name_class.get()) == 0:
            messagebox.showwarning(
                parent=self.frm_child_crud,
                title='Missing information',
                message='You must insert a name for the classification')
            return False
        if len(self.txt_categories.get('1.0', 'end-1c')) != 0:
            categories_aux = self.txt_categories.get('1.0',
                                                     'end-1c').split('\n')
            for item in categories_aux:
                for char in item:
                    if char.isspace(
                    ) or char == '\t' or not char or char == '\n':
                        messagebox.showwarning(
                            parent=self.frm_child_crud,
                            title='Missing information',
                            message='A category can not be empty')
                        return False
            return True
        else:
            messagebox.showwarning(
                parent=self.frm_child_crud,
                title='Missing information',
                message='You must insert at least one category')
            return False

    def clear_fields(self):
        self.btn_save.grid_forget()
        self.btn_cancel.grid_forget()
        self.btn_back.grid_forget()
        self.txt_name_class['state'] = NORMAL
        self.txt_categories['state'] = NORMAL
        self.txt_name_class['bg'] = ENABLED_COLOR
        self.txt_categories['bg'] = ENABLED_COLOR
        self.txt_name_class.delete(0, END)
        self.txt_categories.delete('1.0', 'end-1c')
Exemple #19
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')
Exemple #20
0
class ConfigurationControl(object):
    '''
    classdocs
    '''
    __root_id: str = ''

    __observers: List[ConfigurationControlObserver] = []

    __configuration: Treeview = None

    __inserted: List[str] = []

    def __init__(self, master: Frame):
        '''
        Constructor
        '''

        log_enter_func('ConfigurationControl', '__init__', {'master': master})

        self.__configuration = Treeview(master=master)
        self.__configuration.heading('#0',
                                     text='Configuration',
                                     anchor=tkinter.W)
        self.__configuration.pack(fill=BOTH, side=tkinter.LEFT, expand=True)
        self.__configuration.bind('<<TreeviewSelect>>',
                                  self.__notifiy_observer)

        log_leave_func('ConfigurationControl', '__init__')

    def select(self, _id: str):

        log_enter_func('ConfigurationControl', 'select', {'_id': _id})

        if not _id == None:
            self.__configuration.focus(_id)
            self.__configuration.selection_set(_id)

        log_leave_func('ConfigurationControl', 'select')

    def insert_conf(self,
                    conf_id: str,
                    conf_name: str,
                    parent: str = 'confs',
                    pos: str = 'end'):

        log_enter_func(
            'ConfigurationControl', 'insert_conf', {
                'conf_id': conf_id,
                'conf_name': conf_name,
                'parent': parent,
                'pos': pos
            })

        if parent == None:
            parent = self.__root_id

        log_set_var('ConfigurationControl', 'insert_conf', 'parent', parent)

        self.__inserted.append(conf_id)
        self.__configuration.insert(parent, pos, conf_id, text=conf_name)
        self.__configuration.item(conf_id, open=True)

        log_leave_func('ConfigurationControl', 'insert_conf')

    def __remove(self, conf_id):

        log_enter_func('ConfigurationControl', '__remove',
                       {'conf_id': conf_id})

        self.__configuration.delete(conf_id)

        log_leave_func('ConfigurationControl', '__remove')

    def remove_all(self):

        log_enter_func('ConfigurationControl', 'remove_all')

        for conf_id in self.__inserted:
            if self.__configuration.exists(conf_id):
                self.__remove(conf_id)

        self.__inserted.clear()

        log_leave_func('ConfigurationControl', 'remove_all')

    def add_obeserver(self, observer: ConfigurationControlObserver):

        log_enter_func('ConfigurationControl', 'add_obeserver',
                       {'observer': observer})

        self.__observers.append(observer)

        log_leave_func('ConfigurationControl', 'add_obeserver')

    def remove_observer(self, observer: ConfigurationControlObserver):

        log_enter_func('ConfigurationControl', 'remove_observer',
                       {'observer': observer})

        self.__observers.remove(observer)

        log_leave_func('ConfigurationControl', 'remove_observer')

    def __notifiy_observer(self, event):  # @UnusedVariable

        log_enter_func('ConfigurationControl', '__notifiy_observer',
                       {'event': event})

        conf_id = self.__configuration.focus()

        if conf_id == self.__root_id or conf_id == '':
            conf_id = None

        log_set_var('ConfigurationControl', '__notifiy_observer', 'conf_id',
                    conf_id)

        for observer in self.__observers:
            observer.on_conf_selected(conf_id)

        log_leave_func('ConfigurationControl', '__notifiy_observer')
Exemple #21
0
class StructEditor(tk.Frame, Subscriber, Observable):
    """Displays and allow editing of the coordinates and points of one superstructure

    Args:
        parent (tk.Frame): widget that is the parent of the editor
        structure (model.structure.Structure): the ship superstructure that will be edited
    """
    def __init__(self, parent, structure, command_stack):
        Subscriber.__init__(self, structure)
        Observable.__init__(self)
        tk.Frame.__init__(self, parent, borderwidth=4, relief="raised")
        self._structure = structure
        self._command_stack = command_stack

        self.bind("<Button-1>", self._on_click)
        self.bind("<FocusIn>", self._on_get_focus)
        self.bind("<FocusOut>", self._on_lost_focus)

        self._tree = Treeview(self,
                              columns=["#", "X", "Y"],
                              selectmode="browse")
        #kill the icon column
        self._tree.column("#0", minwidth=0, width=0)

        style = Style()
        style.configure("Treeview.Heading", font=(None, 16))

        self._tree.column("#", minwidth=20, width=40, anchor=tk.CENTER)
        self._tree.column("X", minwidth=20, width=40, anchor=tk.CENTER)
        self._tree.column("Y", minwidth=20, width=40, anchor=tk.CENTER)
        self._tree.heading("#", text="#")
        self._tree.heading("X", text="\u21d5")
        self._tree.heading("Y", text="\u21d4")
        self._tree.grid(row=0, column=POINTS_TABLE_COL, sticky=tk.N + tk.S)

        self._tree.bind("<<TreeviewSelect>>", self._on_point_selected)
        self._tree.bind("<FocusIn>", self._on_get_focus)
        self._tree.bind("<FocusOut>", self._on_lost_focus)

        scroll = Scrollbar(self, command=self._tree.yview)
        scroll.grid(row=0, column=SCROLL_COL, sticky=tk.N + tk.S)
        scroll.bind("<FocusIn>", self._on_get_focus)

        self._tree.configure(yscrollcommand=scroll.set)

        self._index_of_sel_point = -1
        self._fill_tree()

        self._edit_zone = EditZone(self, self._structure, command_stack,
                                   self._on_get_focus)
        self._edit_zone.grid(column=EDIT_ZONE_COL, row=0, sticky=tk.N)

    def _set_selection(self, new_sel_index):
        """Set the selected point to the new_sel_index

        Gives correct focus, update, etc to the editor's widgets
        if the index is outside of the self.points, does nothing
        """
        if new_sel_index >= 0 and new_sel_index <= len(self.points) - 1:
            iid = self._tree.get_children()[new_sel_index]
            self._tree.selection_set(iid)

    def _on_click(self, *_args):
        self._tree.focus_set()

    def _on_get_focus(self, *_args):
        if self._index_of_sel_point == -1:
            self._set_selection(0)
        self.configure(relief="sunken")
        self._notify("focus", {})

    def _on_lost_focus(self, event):
        if event.widget not in self.winfo_children():
            self.configure(relief="raised")

    def _on_point_selected(self, _event):
        """called back when a point is selected in the table/treeview

        Updates the editable fields
        """
        selected_iid = self._tree.selection()
        self._index_of_sel_point = self._tree.index(selected_iid)
        self._edit_zone.set_editable_point(
            self._tree.item(selected_iid)["values"][0])
        self._notify("focus", {})

    def _fill_tree(self):
        """fills the treeview with data from the structure
        """
        self._tree.delete(*self._tree.get_children())
        for point_index, point in enumerate(self._structure.points):
            self._tree.insert(
                '',
                'end',
                values=[point_index,
                        round(point[0]),
                        round(point[1])])
            if point_index == self._index_of_sel_point:
                self._set_selection(point_index)

    def _on_notification(self, observable, event_type, event_info):
        """Rebuild the treeview on structure update
        Depending on the structure state and the operation, change the selcted point
        """
        if event_type == "add_point":
            self._index_of_sel_point = event_info["index"]
            self._fill_tree()
        else:
            if self._index_of_sel_point >= len(self._structure.points):
                self._index_of_sel_point = len(self._structure.points)
                self._edit_zone.unset_point()
            self._fill_tree()
        self._notify("focus", {})

    def update_to_coord(self, point):
        """Move the selected point to the position of the given point

        Intended to be called from click on the top view
        Args:
            point (x, y): new position in funnel coordinates
        """
        if self._index_of_sel_point != -1 and self._index_of_sel_point <= len(
                self.points) - 1:
            self._command_stack.do(
                model.structure.UpdatePoint(self._structure,
                                            self._index_of_sel_point,
                                            round(point[0]), round(point[1])))
        elif self._index_of_sel_point == len(self.points) or not self.points:
            self._command_stack.do(
                model.structure.AddPoint(self._structure,
                                         self._index_of_sel_point + 1,
                                         round(point[0]), round(point[1])))
        if self._index_of_sel_point + 1 >= len(self.points):
            self.winfo_toplevel().update()
            self._index_of_sel_point = len(self.points)
        else:
            self._set_selection(self._index_of_sel_point + 1)
            self.winfo_toplevel().update()

    @property
    def points(self):
        """Pipe throught the struct's properties"""
        return self._structure.points

    @property
    def fill(self):
        """Pipe throught the struct's properties"""
        return self._structure.fill

    @property
    def selected_index(self):
        """the index in the struct's point list of the currently selected point

        Should be -1 if none selected
        """
        return self._index_of_sel_point
Exemple #22
0
class OverviewControl(object):
    '''
    classdocs
    '''
    __root_id: str = 'pages'

    __observers: List[OverviewControlObserver] = []

    __overview: Treeview = None

    __inserted: List[str] = []

    def __init__(self, master: tk.Frame):
        '''
        Constructor

        '''
        log_enter_func('OverviewControl', '__init__', {'master': master})

        self.__overview = Treeview(master=master, selectmode='browse')
        self.__overview.heading('#0', text='Overview', anchor=tk.W)
        self.__overview.pack(fill=tk.Y, side=tk.LEFT)
        self.__overview.bind('<<TreeviewSelect>>', self.__notifiy_observer)

        self.__overview.insert('', 0, self.__root_id, text='Pages')
        self.__overview.item(self.__root_id, open=True)

        self.__overview.insert('', 1, 'css_rules', text='CSS Rules')
        self.__overview.item('css_rules', open=True)

        self.__overview.insert('', 2, 'javascripts', text='JavaScripts')
        self.__overview.item('javascripts', open=True)

        self.__overview.insert('', 3, 'text', text='Text')
        self.__overview.item('text', open=True)

        self.__overview.insert('', 4, 'variables', text='Variables')
        self.__overview.item('variables', open=True)

        log_leave_func('OverviewControl', '__init__')

    def select(self, page_id: str):

        log_enter_func('OverviewControl', 'select', {'page_id': page_id})

        if not page_id == None:
            self.__overview.focus(page_id)
            self.__overview.selection_set(page_id)

        log_leave_func('OverviewControl', 'select')

    def insert(self, page_id: str, page_name: str):

        log_enter_func('OverviewControl', 'insert', {
            'page_id': page_id,
            'page_name': page_name
        })

        self.__inserted.append(page_id)

        parent_id = page_id.split(sep='.')[0]

        self.__overview.insert(parent_id, 'end', page_id, text=page_name)

        log_leave_func('OverviewControl', 'insert')

    def remove_page(self, page_id: str):

        log_enter_func('OverviewControl', 'remove_page', {'page_id': page_id})

        self.__remove(page_id)
        self.__inserted.remove(page_id)

        log_leave_func('OverviewControl', 'remove_page')

    def __remove(self, tag_id):

        log_enter_func('OverviewControl', '__remove', {'tag_id': tag_id})

        self.__overview.delete(tag_id)

        log_leave_func('OverviewControl', '__remove')

    def remove_all(self):

        log_enter_func('OverviewControl', 'remove_all')

        for page_id in self.__inserted:
            self.__remove(page_id)

        self.__inserted.clear()

        log_leave_func('OverviewControl', 'remove_all')

    def add_obeserver(self, observer: OverviewControlObserver):

        log_enter_func('OverviewControl', 'add_obeserver',
                       {'observer': observer})

        self.__observers.append(observer)

        log_leave_func('OverviewControl', 'add_obeserver')

    def remove_observer(self, observer: OverviewControlObserver):

        log_enter_func('OverviewControl', 'remove_observer',
                       {'observer': observer})

        self.__observers.remove(observer)

        log_leave_func('OverviewControl', 'remove_observer')

    def __notifiy_observer(self, event):  # @UnusedVariable

        log_enter_func('OverviewControl', '__notifiy_observer',
                       {'event': event})

        page_id = event.widget.selection()[0]

        if '.' not in page_id:
            page_id = None

        log_set_var('OverviewControl', '__notifiy_observer', 'page_id',
                    page_id)

        for observer in self.__observers:
            observer.on_page_selected(page_id)

        log_leave_func('OverviewControl', '__notifiy_observer')