class TableView(Frame):
    OPTIONS = {'font': ('bold', 11)}

    def __init__(self, window, **kwargs):
        super().__init__(window, **kwargs)
        self.tree = Treeview(self, selectmode='browse')
        self.tree.column("#0", width=100, minwidth=75, stretch=NO)
        self.tree.heading("#0", text='row', anchor='center')
        self.scroll = Scrollbar(self, command=self.tree.yview)
        self.tree.config(yscrollcommand=self.scroll.set)
        self.scroll.pack(side=RIGHT, fill=Y)
        self.tree.pack(expand=True, side=LEFT, fill=BOTH)

    def create_columns(self, *columns):
        for column in columns:
            self.tree.column(f"{column}", width=150, minwidth=150, stretch=YES)

    def create_heading(self, *columns):
        for index, column in enumerate(columns):
            self.tree.heading(f"#{index+1}", text=f"{column}", anchor='center')

    def fill_row(self, *args):
        if args:
            self.tree.delete(*self.tree.get_children())
            for index, contact in enumerate(args):
                self.tree.insert("",
                                 'end',
                                 contact.id,
                                 text=f"{index+1}",
                                 values=list(contact.informations.values()))
        else:
            return
Example #2
0
def sort_column(tree: ttk.Treeview, col: int, descending: bool):
    """
    Sort tree contents when a column header is clicked on.

    :param tree: the root tree to sort
    :param col: the column of the tree to sort
    :param descending: If True sort in descending order, otherwise sort in ascending order
    """
    data = [(tree.set(child, col), child) for child in tree.get_children('')]
    try_date = False
    try:
        data = [(float(d[0]), d[1]) for d in data]
    except ValueError:
        try_date = True

    if try_date:
        try:
            data = [(parser.parse(d[0]), d[1]) for d in data]
        except ValueError:
            pass

    data.sort(reverse=descending)
    for ix, item in enumerate(data):
        tree.move(item[1], '', ix)
    tree.heading(col,
                 command=lambda c=col: sort_column(tree, c, not descending))
Example #3
0
def set_table(table: Treeview, data_list: list):
    """
    :param table: A Tkinter table object will be fill in data.
    :param data_list: The list of data, each data should be a tuple.
    """
    table.delete(*table.get_children())
    for i in reversed(data_list):
        table.insert('', 0, text=i[0], values=i)
Example #4
0
 def clear_primitive_multi_list_box(
         self, primitive_multi_list_box: ttk.Treeview) -> None:
     """
     Clears all entries of a primitive-type (str, int, etc.) storing multi list box.
     :param primitive_multi_list_box: the multi list box to be cleared
     :return: void
     """
     primitive_multi_list_box.delete(
         *primitive_multi_list_box.get_children())
class PopupFrameView(Toplevel): # СОРТИРОВКА ФИЛЬМОВ;
    def __init__(self, x, y, *data):
        super().__init__()
        self.x = x
        self.y = y
        self.vr = data
        self.init_frame()
        self.db = db
        self.view = appl
        self.view_record()

    def init_frame(self):
        self.title('Просмотр записей')
        self.geometry('440x392+%d+%d' % (self.x + 180, self.y + 65))
        self.resizable(False, False)

        # НИЖНЯЯ ПАНЕЛЬ для КОЛИЧЕСТВА ЗАПИСЕЙ;
        TB_BOT = Frame(self, bg='#EDF0F5', bd=1)
        TB_BOT.pack(side=BOTTOM, fill=X)

        # ВЫВОД КОЛИЧЕСТВА ЗАПИСЕЙ на НИЖНЮЮ ПАНЕЛЬ;
        self.count = Label(TB_BOT, background='#EDF0F5', foreground='#425370')
        self.count.pack()

        _yscroll_ = Scrollbar(self)
        self.tree = Treeview(self, columns=('id', 'name'), height=18, show='headings', yscrollcommand=_yscroll_.set)
        _yscroll_.config(command=self.tree.yview)
        
        self.tree.column('id',   width=50, anchor=CENTER)
        self.tree.column('name', width=370)
        
        self.tree.heading('id',   text='№')
        self.tree.heading('name', text='Название')

        _yscroll_.pack(side=RIGHT, fill=Y)
        self.tree.pack()
        self.tree.bind('<Button-1>', lambda event: 'break' if self.tree.identify_region(event.x, event.y) == 'separator' else None)

        # УДЕРЖИВАЕМ НАШЕ ДИАЛОГОВОЕ ОКНО 'НА ВЕРХУ';
        self.grab_set()
        self.focus_set()

    def view_record(self): # ПРОСМОТР ОТСОРТИРОВАННЫХ ДАННЫХ;
        num = 0
        if (self.vr[0][0] != '' and self.vr[0][1] == '' and self.vr[0][2] == ''):
            self.db.c.execute(''' SELECT id, name FROM films WHERE mors=? ''', (self.vr[0][0], ))
        if (self.vr[0][0] == '' and self.vr[0][1] != '' and self.vr[0][2] == ''):
            self.db.c.execute(''' SELECT id, name FROM films WHERE janr=? ''', (self.vr[0][1], ))
        if (self.vr[0][0] == '' and self.vr[0][1] == '' and self.vr[0][2] != ''):
            self.db.c.execute(''' SELECT id, name FROM films WHERE flag=? ''', (self.vr[0][2], ))
        if (self.vr[0][0] != '' and self.vr[0][1] != '' and self.vr[0][2] != ''):
            self.db.c.execute(''' SELECT id, name FROM films WHERE mors=? AND janr=? AND flag=? ''', (self.vr[0][0], self.vr[0][1], self.vr[0][2]))
        [ self.tree.delete(i) for i in self.tree.get_children() ]
        for row in self.db.c.fetchall():
            [ self.tree.insert('', 'end', values=row) ]
            num += 1
        self.count['text'] = 'Количество записей: %d' % num
Example #6
0
class ResultForm(tk.Frame):
    def __init__(self, master, **kwargs):
        super().__init__(master, **kwargs)
        self.header_label = tk.Label(self, text="All your saved passwords:", font=("roboto slab", 12, "bold"))
        self.header_label.grid(row=0, column=0, pady=(0, 5), sticky=tk.W)

        self.scrollbar_y = tk.Scrollbar(self, orient=tk.VERTICAL)
        self.tree = Treeview(self, columns=("Place", "Password"), yscrollcommand=self.scrollbar_y.set)
        self.scrollbar_y.config(command=self.tree.yview)
        self.scrollbar_y.grid(row=1, column=1, sticky="nsew")

        self.tree.heading("Place", text="Place")
        self.tree.heading("Password", text="Password")

        self.tree.column("#0", minwidth=0, width=0)
        self.tree.column("#1", width=175)
        self.tree.column("#2", width=175)

        self.style = Style()
        # noinspection SpellCheckingInspection
        self.style.configure("Treeview", rowheight=15)
        # noinspection SpellCheckingInspection
        self.style.configure("Treeview.Heading", font=("roboto slab", 10, "bold"), foreground="#a6a6a6")

        self.tree.grid(row=1, column=0)

        self.instruction_label = tk.Label(self, text="* First select an item, then choose from below",
                                          font=("roboto slab", 8, "italic"))
        self.instruction_label.grid(row=2, column=0, sticky=tk.W)

    def insert_to_tree(self, values):
        for value in values:
            self.tree.insert("", tk.END, value=value)

    def empty_tree(self):
        if self.tree.get_children():
            self.tree.delete(*self.tree.get_children())

    def return_selected_item_values(self):
        """Returns the values of the highlighted item inside TreeView"""
        highlighted_item = self.tree.selection()
        if highlighted_item:
            return tuple(self.tree.item(highlighted_item)["values"])
Example #7
0
def treeview_sort_column(treeview: ttk.Treeview, col, reverse=False):
    l = [(treeview.set(k, col), k) for k in treeview.get_children('')]
    l.sort(reverse=reverse)

    # Rearranging items in sorted positions
    for index, (val, k) in enumerate(l):
        treeview.move(k, '', index)

    treeview.heading(
        col, command=lambda: treeview_sort_column(treeview, col, not reverse))
Example #8
0
class TreeFrame(LabelFrame):
    def __init__(self, root, model: ManageUIModel):
        super().__init__(root, text="Records", padding=10)
        self.model = model
        self.packed = False

        self._edit_img = load_image("edit", width=16, color=(38, 158, 54))

        cols = ["timestamp"]
        # cols=["timestamp", "source", "hash"]

        self.tree = Treeview(self, padding=5, columns=cols)
        self.tree.heading("timestamp", text="Timestamp")
        # self.tree.heading("source", text="Data Source")
        # self.tree.heading("hash", text="Hash")
        self.tree.pack(fill=BOTH, expand=True)

        self.frm_src_btns = Frame(self, padding=5)
        self.frm_src_btns.pack(fill=X)

        def show_rebuilder():
            RebuildDialog.create_dialog(self.model.var_metarecord.get())

        self.btn_src_add = Button(self.frm_src_btns, text="Import Previous Backups", command=show_rebuilder)
        self.btn_src_add.pack(side=LEFT)

        def show_clean():
            CleanDialog.create_dialog(self.model.var_metarecord.get())

        self.btn_src_add = Button(self.frm_src_btns, text="Remove Redundant Files", command=show_clean)
        self.btn_src_add.pack(side=LEFT)

        self.model.var_metarecord.trace_add(self._metarecord_changed)

    def do_pack(self):
        if not self.packed:
            self.pack(side=TOP, anchor=N, fill=BOTH, expand=True)
            self.packed = True

    def do_unpack(self):
        if self.packed:
            self.pack_forget()
            self.packed = False

    def _metarecord_changed(self, var, oldval, newval):
        self.tree.delete(*self.tree.get_children())
        if newval is not None:
            self._setup_tree(newval)
            self.do_pack()
        else:
            self.do_unpack()

    def _setup_tree(self, mr: MetaRecord):
        for r in mr.records:
            val = self.tree.insert("", END, text=r.name, values=[r.timestamp])
Example #9
0
def tv_column_sort(tv: ttk.Treeview, col: str, reverse: bool) -> None:
    l = [(tv.set(k, col), k) for k in tv.get_children('')]
    try:
        l.sort(key=lambda t: int(t[0]), reverse=reverse)
    except ValueError:
        l.sort(reverse=reverse)

    for index, (val, k) in enumerate(l):
        tv.move(k, '', index)

    tv.heading(col, command=lambda: tv_column_sort(tv, col, not reverse))
Example #10
0
def get_all_children_tree(tree: ttk.Treeview, item: str = "") -> List:
    """
    Get all of the children of item.

    :param tree: treeview to look through
    :param item: parent element to recursively look for children from
    :return: List of all of the children of item
    """
    children = tree.get_children(item)
    for child in children:
        children += get_all_children_tree(tree, child)
    return children
Example #11
0
    def update_data(self, table: ttk.Treeview):
        work_dict = jk.get_launch_data()
        sorted_date = sorted(work_dict, reverse=True)

        if len(sorted_date) > len(
                self.fold_iid):  # if we have new date in launches
            items = table.get_children()
            for item in items:
                table.delete(item)
            self.fold_iid = {}  # will be contain datetime:item-id as k:v
            self.get_data(table)
        else:
            for date in sorted_date:
                items = table.get_children(
                    self.fold_iid[date])  # number of items in folder
                values = work_dict[date]  # number of values in current date
                if len(values) > len(items):
                    for item in items:
                        table.delete(item)
                    for value in values:
                        table.insert(self.fold_iid[date], 'end', values=value)
        self.after(1000, self.update_data, table)
Example #12
0
class ContactsListView(Frame):
    def __init__(self, master, **kwargs):
        super().__init__(master, **kwargs)

        self.search_text = StringVar()
        self.console_text = StringVar()

        # Treeview setup
        col_ids = ("first_name", "surname", "phone", "email", "last_edited")
        self.contacts_list = Treeview(self, columns=col_ids, show='headings')
        for col_id, text in zip(
                col_ids,
            ["First name", "Surname", "Phone", "Email", "Last edited"]):
            self.contacts_list.column(col_id, stretch=True, width=160)
            self.contacts_list.heading(col_id, text=text)

        # Search panel
        search_panel = Frame(self)
        search_entry = Entry(search_panel, textvariable=self.search_text)
        self.search_btn = Button(search_panel, text="Search")
        search_entry.pack(side=LEFT)
        self.search_btn.pack(side=LEFT)

        # Controls
        self.add_btn = Button(self, text="Add")
        self.edit_btn = Button(self, text="Edit")
        self.delete_btn = Button(self, text="Delete")
        self.console = Label(self, textvariable=self.console_text)

        # Layout
        search_panel.pack()
        self.contacts_list.pack()
        self.console.pack()
        self.delete_btn.pack(side=RIGHT)
        self.edit_btn.pack(side=RIGHT)
        self.add_btn.pack(side=RIGHT)

    def set(self, contacts):
        self.contacts_list.delete(*self.contacts_list.get_children())
        for contact_fields in contacts:
            rowid, values = str(contact_fields[0]), contact_fields[1:]
            self.contacts_list.insert('', 'end', text=rowid, values=values)
        self.log()

    def get_selected_contacts_ids(self):
        focused_rows = self.contacts_list.selection()
        return map(lambda focus: self.contacts_list.item(focus)['text'],
                   focused_rows)

    def log(self, message=''):
        self.console_text.set(message)
Example #13
0
class askdataset( Dialog ):
    def __init__(self, parent, h5name, title=None):
        self.h5name = h5name
        Dialog.__init__(self, parent, title=title)

    def body(self, parent):
        Tk.Label(parent, text="HDF5 File: "+self.h5name).pack()
        self.tree = Treeview( parent, selectmode = "browse",
                              columns = ("path",'title',) )
        root = self.tree.insert("","end",text="/", values=('/',"",), open=True)
        self.additems( root )
        self.tree.pack(fill=Tk.BOTH, expand=1)
        self.tree.bind('<<TreeviewOpen>>', self.update_tree)
        self.tree.bind('<<TreeviewSelect>>', self.setresult)

    def additems( self, node ):
        gid = self.tree.set( node )['path']
        self.tree.delete( *self.tree.get_children(node) )
        with h5py.File( self.h5name, "r" ) as h5o:
            if gid == "/":
                grp = h5o
            else:
                grp = h5o[gid]
            if isinstance( grp, h5py.Dataset ):
                return
            for idx, item in enumerate(list(grp)):
                thing = grp[item]
                if isinstance(thing, h5py.Group):
                    if 'title' in grp[item]:
                        title = grp[item]['title'][()]
                    else:
                        title = ""
                elif isinstance(thing, h5py.Dataset):
                    title = str(thing.shape)
                else:
                    title = str(type(thing))
                if gid != "/":
                    fullpath = gid+"/"+item
                else:
                    fullpath = item
                oid = self.tree.insert(node, idx, text=item, values=(
                    fullpath, title,))
                if isinstance(thing, h5py.Group):
                    self.tree.insert( oid, "end" ) # dummy entry

    def update_tree( self, event ):
        self.tree = event.widget
        self.additems( self.tree.focus() )

    def setresult(self, event):
        self.result = self.tree.set( self.tree.focus() )['path']
Example #14
0
class SelectionFrame(Frame):
    def __init__(self, fruit_list, **kw):
        # init drop down
        super().__init__(**kw)
        self.fruit_list = fruit_list
        self.type_select = StringVar(self)
        header = Label(self)
        header.grid(row=0, column=0, columnspan=2, pady=10)
        type_options = OptionMenu(
            header, self.type_select, *models.TYPES, command=self.set_group)
        type_options.config(width=14)
        type_options.grid(row=0, column=0)
        # init start simulator button
        simulator_btn = Button(
            header, text="模拟器", command=lambda: sm.Simulator().mainloop())
        simulator_btn.grid(row=0, column=1)
        # init treeView
        self.tree = Treeview(self, columns=["名称", "效果"], show="headings")
        self.tree.column("名称", width=50, anchor='center')
        self.tree.column("效果", width=150, anchor='center')
        self.tree.heading("名称", text="名称")
        self.tree.heading("效果", text="效果")
        self.tree.grid(row=1, column=0)
        self.tree.bind("<Double-1>", self.select_item)
        self.select_item_callback = lambda x: x
        vbar = Scrollbar(self, orient=VERTICAL, command=self.tree.yview)
        self.tree.configure(yscrollcommand=vbar.set)
        vbar.grid(row=1, column=1, sticky=NS)
        # default value
        self.set_group(models.TYPES[0])

    def set_group(self, group):
        self.type_select.set(group)
        x = self.tree.get_children()
        for item in x:
            self.tree.delete(item)

        for x in self.fruit_list:
            if x.get_type() == group:
                self.tree.insert("", END, value=(x.name, x.description))

    def select_item(self, event):
        item = self.tree.selection()[0]
        name = self.tree.item(item, "value")[0]
        self.select_item_callback(
            next(filter(lambda x: x.name == name, self.fruit_list)))

    def set_select_callback(self, callback):
        self.select_item_callback = callback
Example #15
0
class DisplayLogs(tk.Frame):

    def __init__(self, parent, controller):

        self.parent = parent
        self.controller = controller

        tk.Frame.__init__(self, parent)

        self.status2 = tk.Label(self, text="Logs", font=LARGE_FONT)
        self.status2.grid(row = 0, column = 0)

        # Player list table
        columns = ['log']
        self.loggings = Treeview(self, columns=columns, show="headings")
        self.loggings.column("log", width=415)

        for col in columns[1:]:
            self.loggings.column(col, width=1)
            self.loggings.heading(col, text=col)

        #loggings.bind('<<TreeviewSelect>>', select_router)
        self.loggings.grid(row=1,column=0)

        # LOAD IN TABLE
        txt = open("./data/gui_logs.txt", "r", encoding="latin-1")

        #self.playerlist = []
        for aline in txt:
            values2 = aline.strip("\n").split(",")
            self.loggings.insert('', 'end', values=values2)

        self.update_logs()

    # Continuously updates log table
    def update_logs(self):
        for i in self.loggings.get_children():
            self.loggings.delete(i)

        txt = open("./data/gui_logs.txt", "r", encoding="latin-1")

        self.playerlist = []
        for aline in txt:
            values2 = aline.strip("\n").split(",")
            self.loggings.insert('', 'end', values=values2)
        txt.close()
        self.loggings.yview_moveto(1)
        self.after(100, self.update_logs)
Example #16
0
    def sort_by(self,
                col_name: str,
                is_descending: bool,
                tree: ttk.Treeview = None):
        if tree is None:
            tree = self.tree
        data_list = [(tree.set(child_id, col_name), child_id)
                     for child_id in tree.get_children('')]
        data_list.sort(reverse=is_descending)
        for new_idx, (item_name, item_id) in enumerate(data_list):
            tree.move(item_id, '', new_idx)

        # switch the heading so it will sort in the opposite direction
        tree.heading(col_name,
                     command=lambda col=col_name: self.sort_by(
                         col, (not is_descending), tree))
Example #17
0
    def set_primitive_multi_list_box_elements(
            multi_list_box: ttk.Treeview,
            list_of_elements: List[tuple]) -> None:
        """
        Sets a list of elements(tuples) to be displayed by a multi list box.
        This clears the multi list box beforehand!
        :param multi_list_box: the multi list box widget to be manipulated
        :param list_of_elements: List of elements, which are themselves tuples of primitive
                data types.
        :return: void
        """
        multi_list_box.delete(*multi_list_box.get_children())

        for element in list_of_elements:
            TkValueSetter.add_primitive_multi_list_box_element(
                multi_list_box, element)
Example #18
0
class GUI:
    def __init__(self):
        self.window = Tk()
        self.window.geometry('800x600')
        self.txt = Entry(self.window, width=20)
        self.txt.grid(column=1, row=0)
        self.tree = Treeview(self.window, selectmode='browse')
        self.db = Database()
        self.lbl_url = Label(self.window, text="Введите URL")
        self.lbl_tab = Label(self.window, text="Данные из базы")

    def load_into_db(self):
        res = f'{self.txt.get()}'
        self.db.write_db(Parser(res))
        messagebox.showinfo('TAGS', str(Parser(res).tags()))

    def load_from_db(self):
        self.tree.delete(*self.tree.get_children())
        for row in self.db.read_db():
            self.tree.insert("", 'end', text=row)

    def wind(self):
        self.lbl_url.place(x=375, y=15)
        self.lbl_tab.place(x=360, y=120)
        btn = Button(self.window,
                     text="Загрузить в базу",
                     command=self.load_into_db)
        btn.grid(column=2, row=0)
        btn1 = Button(self.window,
                      text="Показать из базы",
                      command=self.load_from_db)
        btn1.grid(column=2, row=0)
        self.tree.place(x=25, y=150, width=750, height=300)
        scroll = Scrollbar(self.window,
                           orient="vertical",
                           command=self.tree.yview)
        scroll.place(x=775, y=200, height=150)
        self.tree.configure(yscrollcommand=scroll.set)
        self.txt.place(x=150, y=50, width=500, height=35)
        btn.place(x=450, y=500)
        btn1.place(x=150, y=500)
        self.window.mainloop()
Example #19
0
    def print_messages(self, dataFrame):
        list_box = Treeview(self.messagesBox, columns=self.col_names, show='headings', height=19) #height is in number of rows:)
        for col in self.col_names:
            list_box.heading(col, text=col)
        list_box.grid(row=0, column=0, sticky='nsew', in_=self.messagesBox)
        list_box.tag_configure('odd', background='#f9f9f9')
        list_box.tag_configure('even', background='#DFDFDF')
        list_box.column("Timestamp", width=150)
        list_box.column("Data", width=400)
        scroll_bar = Scrollbar(self.messagesBox, orient="vertical", command=list_box.yview)
        scroll_bar.grid(row=0, column=1, sticky='ns')
        list_box.configure(yscrollcommand=scroll_bar.set)

        for index, row in dataFrame.iterrows():
            type = 'even'
            if (len(list_box.get_children()) % 2 == 1):
                type = 'odd'

            list_box.insert("", "end", values=(row.Timestamp, row.Data), tags=(type,))

        list_box.bind("<<TreeviewSelect>>", lambda event, t=list_box: self.message_click(event, t))
Example #20
0
def treeview_all_nodes(treeview: ttk.Treeview, parent=None):
    nodes = []
    for node in treeview.get_children(parent):
        nodes.append(node)
        nodes.extend(treeview_all_nodes(treeview, node))
    return nodes
Example #21
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')
Example #22
0
class DialogPluginManager(Toplevel):
    def __init__(self, mainWin, modulesWithNewerFileDates):
        super(DialogPluginManager, self).__init__(mainWin.parent)

        self.ENABLE = _("Enable")
        self.DISABLE = _("Disable")
        self.parent = mainWin.parent
        self.cntlr = mainWin

        # copy plugins for temporary display
        self.pluginConfig = PluginManager.pluginConfig
        self.pluginConfigChanged = False
        self.uiClassMethodsChanged = False
        self.modelClassesChanged = False
        self.customTransformsChanged = False
        self.disclosureSystemTypesChanged = False
        self.hostSystemFeaturesChanged = False
        self.modulesWithNewerFileDates = modulesWithNewerFileDates

        parentGeometry = re.match("(\d+)x(\d+)[+]?([-]?\d+)[+]?([-]?\d+)",
                                  self.parent.geometry())
        dialogX = int(parentGeometry.group(3))
        dialogY = int(parentGeometry.group(4))

        self.title(_("Plug-in Manager"))
        frame = Frame(self)

        # left button frame
        buttonFrame = Frame(frame, width=40)
        buttonFrame.columnconfigure(0, weight=1)
        addLabel = Label(buttonFrame,
                         text=_("Find plug-in modules:"),
                         wraplength=60,
                         justify="center")
        addSelectLocalButton = Button(buttonFrame,
                                      text=_("Select"),
                                      command=self.selectLocally)
        ToolTip(
            addSelectLocalButton,
            text=_(
                "Select python module files from the local plugin directory."),
            wraplength=240)
        addBrowseLocalButton = Button(buttonFrame,
                                      text=_("Browse"),
                                      command=self.browseLocally)
        ToolTip(
            addBrowseLocalButton,
            text=
            _("File chooser allows browsing and selecting python module files to add (or reload) plug-ins, from the local file system."
              ),
            wraplength=240)
        addWebButton = Button(buttonFrame,
                              text=_("On Web"),
                              command=self.findOnWeb)
        ToolTip(
            addWebButton,
            text=
            _("Dialog to enter URL full path to load (or reload) plug-ins, from the web or local file system."
              ),
            wraplength=240)
        addLabel.grid(row=0, column=0, pady=4)
        addSelectLocalButton.grid(row=1, column=0, pady=4)
        addBrowseLocalButton.grid(row=2, column=0, pady=4)
        addWebButton.grid(row=3, column=0, pady=4)
        buttonFrame.grid(row=0,
                         column=0,
                         rowspan=3,
                         sticky=(N, S, W),
                         padx=3,
                         pady=3)

        # right tree frame (plugins already known to arelle)
        modulesFrame = Frame(frame, width=720)
        vScrollbar = Scrollbar(modulesFrame, orient=VERTICAL)
        hScrollbar = Scrollbar(modulesFrame, orient=HORIZONTAL)
        self.modulesView = Treeview(modulesFrame,
                                    xscrollcommand=hScrollbar.set,
                                    yscrollcommand=vScrollbar.set,
                                    height=7)
        self.modulesView.grid(row=0, column=0, sticky=(N, S, E, W))
        self.modulesView.bind('<<TreeviewSelect>>', self.moduleSelect)
        hScrollbar["command"] = self.modulesView.xview
        hScrollbar.grid(row=1, column=0, sticky=(E, W))
        vScrollbar["command"] = self.modulesView.yview
        vScrollbar.grid(row=0, column=1, sticky=(N, S))
        modulesFrame.columnconfigure(0, weight=1)
        modulesFrame.rowconfigure(0, weight=1)
        modulesFrame.grid(row=0,
                          column=1,
                          columnspan=4,
                          sticky=(N, S, E, W),
                          padx=3,
                          pady=3)
        self.modulesView.focus_set()

        self.modulesView.column("#0", width=120, anchor="w")
        self.modulesView.heading("#0", text=_("Name"))
        self.modulesView["columns"] = ("author", "ver", "status", "date",
                                       "update", "descr", "license")
        self.modulesView.column("author", width=100, anchor="w", stretch=False)
        self.modulesView.heading("author", text=_("Author"))
        self.modulesView.column("ver", width=60, anchor="w", stretch=False)
        self.modulesView.heading("ver", text=_("Version"))
        self.modulesView.column("status", width=50, anchor="w", stretch=False)
        self.modulesView.heading("status", text=_("Status"))
        self.modulesView.column("date", width=70, anchor="w", stretch=False)
        self.modulesView.heading("date", text=_("File Date"))
        self.modulesView.column("update", width=50, anchor="w", stretch=False)
        self.modulesView.heading("update", text=_("Update"))
        self.modulesView.column("descr", width=200, anchor="w", stretch=False)
        self.modulesView.heading("descr", text=_("Description"))
        self.modulesView.column("license", width=70, anchor="w", stretch=False)
        self.modulesView.heading("license", text=_("License"))

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

        self.classesView.column("#0", width=200, anchor="w")
        self.classesView.heading("#0", text=_("Class"))
        self.classesView["columns"] = ("modules", )
        self.classesView.column("modules",
                                width=500,
                                anchor="w",
                                stretch=False)
        self.classesView.heading("modules", text=_("Modules"))

        # bottom frame module info details
        moduleInfoFrame = Frame(frame, width=700)
        moduleInfoFrame.columnconfigure(1, weight=1)

        self.moduleNameLabel = Label(moduleInfoFrame,
                                     wraplength=600,
                                     justify="left",
                                     font=font.Font(family='Helvetica',
                                                    size=12,
                                                    weight='bold'))
        self.moduleNameLabel.grid(row=0, column=0, columnspan=4, sticky=W)
        self.moduleAuthorHdr = Label(moduleInfoFrame,
                                     text=_("author:"),
                                     state=DISABLED)
        self.moduleAuthorHdr.grid(row=1, column=0, sticky=W)
        self.moduleAuthorLabel = Label(moduleInfoFrame,
                                       wraplength=600,
                                       justify="left")
        self.moduleAuthorLabel.grid(row=1, column=1, columnspan=3, sticky=W)
        self.moduleDescrHdr = Label(moduleInfoFrame,
                                    text=_("description:"),
                                    state=DISABLED)
        self.moduleDescrHdr.grid(row=2, column=0, sticky=W)
        self.moduleDescrLabel = Label(moduleInfoFrame,
                                      wraplength=600,
                                      justify="left")
        self.moduleDescrLabel.grid(row=2, column=1, columnspan=3, sticky=W)
        self.moduleClassesHdr = Label(moduleInfoFrame,
                                      text=_("classes:"),
                                      state=DISABLED)
        self.moduleClassesHdr.grid(row=3, column=0, sticky=W)
        self.moduleClassesLabel = Label(moduleInfoFrame,
                                        wraplength=600,
                                        justify="left")
        self.moduleClassesLabel.grid(row=3, column=1, columnspan=3, sticky=W)
        ToolTip(self.moduleClassesLabel,
                text=_("List of classes that this plug-in handles."),
                wraplength=240)
        self.moduleVersionHdr = Label(moduleInfoFrame,
                                      text=_("version:"),
                                      state=DISABLED)
        self.moduleVersionHdr.grid(row=4, column=0, sticky=W)
        self.moduleVersionLabel = Label(moduleInfoFrame,
                                        wraplength=600,
                                        justify="left")
        self.moduleVersionLabel.grid(row=4, column=1, columnspan=3, sticky=W)
        ToolTip(self.moduleVersionLabel,
                text=_("Version of plug-in module."),
                wraplength=240)
        self.moduleUrlHdr = Label(moduleInfoFrame,
                                  text=_("URL:"),
                                  state=DISABLED)
        self.moduleUrlHdr.grid(row=5, column=0, sticky=W)
        self.moduleUrlLabel = Label(moduleInfoFrame,
                                    wraplength=600,
                                    justify="left")
        self.moduleUrlLabel.grid(row=5, column=1, columnspan=3, sticky=W)
        ToolTip(
            self.moduleUrlLabel,
            text=_(
                "URL of plug-in module (local file path or web loaded file)."),
            wraplength=240)
        self.moduleDateHdr = Label(moduleInfoFrame,
                                   text=_("date:"),
                                   state=DISABLED)
        self.moduleDateHdr.grid(row=6, column=0, sticky=W)
        self.moduleDateLabel = Label(moduleInfoFrame,
                                     wraplength=600,
                                     justify="left")
        self.moduleDateLabel.grid(row=6, column=1, columnspan=3, sticky=W)
        ToolTip(
            self.moduleDateLabel,
            text=
            _("Date of currently loaded module file (with parenthetical node when an update is available)."
              ),
            wraplength=240)
        self.moduleLicenseHdr = Label(moduleInfoFrame,
                                      text=_("license:"),
                                      state=DISABLED)
        self.moduleLicenseHdr.grid(row=7, column=0, sticky=W)
        self.moduleLicenseLabel = Label(moduleInfoFrame,
                                        wraplength=600,
                                        justify="left")
        self.moduleLicenseLabel.grid(row=7, column=1, columnspan=3, sticky=W)
        self.moduleImportsHdr = Label(moduleInfoFrame,
                                      text=_("imports:"),
                                      state=DISABLED)
        self.moduleImportsHdr.grid(row=8, column=0, sticky=W)
        self.moduleImportsLabel = Label(moduleInfoFrame,
                                        wraplength=600,
                                        justify="left")
        self.moduleImportsLabel.grid(row=8, column=1, columnspan=3, sticky=W)
        self.moduleEnableButton = Button(moduleInfoFrame,
                                         text=self.ENABLE,
                                         state=DISABLED,
                                         command=self.moduleEnable)
        ToolTip(self.moduleEnableButton,
                text=_("Enable/disable plug in."),
                wraplength=240)
        self.moduleEnableButton.grid(row=9, column=1, sticky=E)
        self.moduleReloadButton = Button(moduleInfoFrame,
                                         text=_("Reload"),
                                         state=DISABLED,
                                         command=self.moduleReload)
        ToolTip(self.moduleReloadButton,
                text=_("Reload/update plug in."),
                wraplength=240)
        self.moduleReloadButton.grid(row=9, column=2, sticky=E)
        self.moduleRemoveButton = Button(moduleInfoFrame,
                                         text=_("Remove"),
                                         state=DISABLED,
                                         command=self.moduleRemove)
        ToolTip(
            self.moduleRemoveButton,
            text=
            _("Remove plug in from plug in table (does not erase the plug in's file)."
              ),
            wraplength=240)
        self.moduleRemoveButton.grid(row=9, column=3, sticky=E)
        moduleInfoFrame.grid(row=2,
                             column=0,
                             columnspan=5,
                             sticky=(N, S, E, W),
                             padx=3,
                             pady=3)
        moduleInfoFrame.config(borderwidth=4, relief="groove")

        okButton = Button(frame, text=_("Close"), command=self.ok)
        ToolTip(okButton,
                text=_("Accept and changes (if any) and close dialog."),
                wraplength=240)
        cancelButton = Button(frame, text=_("Cancel"), command=self.close)
        ToolTip(cancelButton,
                text=_("Cancel changes (if any) and close dialog."),
                wraplength=240)
        okButton.grid(row=3, column=3, sticky=(S, E), pady=3)
        cancelButton.grid(row=3, column=4, sticky=(S, E), pady=3, padx=3)

        enableDisableFrame = Frame(frame)
        enableDisableFrame.grid(row=3, column=1, sticky=(S, W), pady=3)
        enableAllButton = Button(enableDisableFrame,
                                 text=_("Enable All"),
                                 command=self.enableAll)
        ToolTip(enableAllButton,
                text=_("Enable all plug ins."),
                wraplength=240)
        disableAllButton = Button(enableDisableFrame,
                                  text=_("Disable All"),
                                  command=self.disableAll)
        ToolTip(disableAllButton,
                text=_("Disable all plug ins."),
                wraplength=240)
        enableAllButton.grid(row=1, column=1)
        disableAllButton.grid(row=1, column=2)

        self.loadTreeViews()

        self.geometry("+{0}+{1}".format(dialogX + 50, dialogY + 100))
        frame.grid(row=0, column=0, sticky=(N, S, E, W))
        frame.columnconfigure(0, weight=0)
        frame.columnconfigure(1, 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.protocol("WM_DELETE_WINDOW", self.close)
        self.grab_set()
        self.wait_window(self)

    def loadTreeViews(self):
        self.selectedModule = None

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

        def loadSubtree(parentNode, moduleItems):
            for moduleItem in sorted(moduleItems, key=lambda item: item[0]):
                moduleInfo = moduleItem[1]
                if parentNode or not moduleInfo.get("isImported"):
                    nodeName = moduleItem[0]
                    if parentNode:
                        nodeName = parentNode + GROUPSEP + nodeName
                    name = moduleInfo.get("name", nodeName)
                    node = self.modulesView.insert(parentNode,
                                                   "end",
                                                   nodeName,
                                                   text=name)
                    self.modulesView.set(node, "author",
                                         moduleInfo.get("author"))
                    self.modulesView.set(node, "ver",
                                         moduleInfo.get("version"))
                    self.modulesView.set(node, "status",
                                         moduleInfo.get("status"))
                    self.modulesView.set(node, "date",
                                         moduleInfo.get("fileDate"))
                    if name in self.modulesWithNewerFileDates:
                        self.modulesView.set(node, "update", _("available"))
                    self.modulesView.set(node, "descr",
                                         moduleInfo.get("description"))
                    self.modulesView.set(node, "license",
                                         moduleInfo.get("license"))
                    if moduleInfo.get("imports"):
                        loadSubtree(
                            node,
                            [(importModuleInfo["name"], importModuleInfo)
                             for importModuleInfo in moduleInfo["imports"]])

        loadSubtree("", self.pluginConfig.get("modules", {}).items())

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

        for i, classItem in enumerate(
                sorted(self.pluginConfig.get("classes", {}).items())):
            className, moduleList = classItem
            node = self.classesView.insert("",
                                           "end",
                                           className,
                                           text=className)
            self.classesView.set(node, "modules", ', '.join(moduleList))

        self.moduleSelect()  # clear out prior selection

    def ok(self, event=None):
        # check for orphaned classes (for which there is no longer a corresponding module)
        _moduleNames = self.pluginConfig.get("modules", {}).keys()
        _orphanedClassNames = set()
        for className, moduleList in self.pluginConfig.get("classes",
                                                           {}).items():
            for _moduleName in moduleList.copy():
                if _moduleName not in _moduleNames:  # it's orphaned
                    moduleList.remove(_moduleName)
                    self.pluginConfigChanged = True
            if not moduleList:  # now orphaned
                _orphanedClassNames.add(className)
                self.pluginConfigChanged = True
        for _orphanedClassName in _orphanedClassNames:
            del self.pluginConfig["classes"][_orphanedClassName]

        if self.pluginConfigChanged:
            PluginManager.pluginConfig = self.pluginConfig
            PluginManager.pluginConfigChanged = True
            PluginManager.reset()  # force reloading of modules
        if self.uiClassMethodsChanged or self.modelClassesChanged or self.customTransformsChanged or self.disclosureSystemTypesChanged or self.hostSystemFeaturesChanged:  # may require reloading UI
            affectedItems = ""
            if self.uiClassMethodsChanged:
                affectedItems += _("menus of the user interface")
            if self.modelClassesChanged:
                if affectedItems:
                    affectedItems += _(" and ")
                affectedItems += _("model objects of the processor")
            if self.customTransformsChanged:
                if affectedItems:
                    affectedItems += _(" and ")
                affectedItems += _("custom transforms")
            if self.disclosureSystemTypesChanged:
                if affectedItems:
                    affectedItems += _(" and ")
                affectedItems += _("disclosure system types")
            if self.hostSystemFeaturesChanged:
                if affectedItems:
                    affectedItems += _(" and ")
                affectedItems += _("host system features")
            if messagebox.askyesno(
                    _("User interface plug-in change"),
                    _("A change in plug-in class methods may have affected {0}.  "
                      "Please restart Arelle to due to these changes.  \n\n"
                      "Should Arelle restart itself now "
                      "(if there are any unsaved changes they would be lost!)?"
                      ).format(affectedItems),
                    parent=self):
                self.cntlr.uiThreadQueue.put((self.cntlr.quit, [None, True]))
        self.close()

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

    def moduleSelect(self, *args):
        node = (self.modulesView.selection() or (None, ))[0]
        if node:
            node = node.rpartition(GROUPSEP)[
                2]  # drop leading path names for module name
        moduleInfo = self.pluginConfig.get("modules", {}).get(node)
        if moduleInfo:
            self.selectedModule = node
            name = moduleInfo["name"]
            self.moduleNameLabel.config(text=name)
            self.moduleAuthorHdr.config(state=ACTIVE)
            self.moduleAuthorLabel.config(text=moduleInfo.get("author"))
            self.moduleDescrHdr.config(state=ACTIVE)
            self.moduleDescrLabel.config(text=moduleInfo.get("description"))
            self.moduleClassesHdr.config(state=ACTIVE)
            self.moduleClassesLabel.config(
                text=', '.join(moduleInfo["classMethods"]))
            self.moduleVersionHdr.config(state=ACTIVE)
            self.moduleVersionLabel.config(text=moduleInfo.get("version"))
            self.moduleUrlHdr.config(state=ACTIVE)
            self.moduleUrlLabel.config(text=moduleInfo["moduleURL"])
            self.moduleDateHdr.config(state=ACTIVE)
            self.moduleDateLabel.config(
                text=moduleInfo["fileDate"] + " " +
                (_("(an update is available)") if name in
                 self.modulesWithNewerFileDates else ""))
            self.moduleLicenseHdr.config(state=ACTIVE)
            self.moduleLicenseLabel.config(text=moduleInfo.get("license"))
            if moduleInfo.get("imports"):
                self.moduleImportsHdr.config(state=ACTIVE)
                _text = ", ".join(mi["name"]
                                  for mi in moduleInfo["imports"][:3])
                if len(moduleInfo["imports"]) >= 3:
                    _text += ", ..."
                self.moduleImportsLabel.config(text=_text)
            _buttonState = DISABLED if moduleInfo.get("isImported") else ACTIVE
            self.moduleEnableButton.config(state=_buttonState,
                                           text={
                                               "enabled": self.DISABLE,
                                               "disabled": self.ENABLE
                                           }[moduleInfo["status"]])
            self.moduleReloadButton.config(state=_buttonState)
            self.moduleRemoveButton.config(state=_buttonState)
        else:
            self.selectedModule = None
            self.moduleNameLabel.config(text="")
            self.moduleAuthorHdr.config(state=DISABLED)
            self.moduleAuthorLabel.config(text="")
            self.moduleDescrHdr.config(state=DISABLED)
            self.moduleDescrLabel.config(text="")
            self.moduleClassesHdr.config(state=DISABLED)
            self.moduleClassesLabel.config(text="")
            self.moduleVersionHdr.config(state=DISABLED)
            self.moduleVersionLabel.config(text="")
            self.moduleUrlHdr.config(state=DISABLED)
            self.moduleUrlLabel.config(text="")
            self.moduleDateHdr.config(state=DISABLED)
            self.moduleDateLabel.config(text="")
            self.moduleLicenseHdr.config(state=DISABLED)
            self.moduleLicenseLabel.config(text="")
            self.moduleImportsHdr.config(state=DISABLED)
            self.moduleImportsLabel.config(text="")
            self.moduleEnableButton.config(state=DISABLED, text=self.ENABLE)
            self.moduleReloadButton.config(state=DISABLED)
            self.moduleRemoveButton.config(state=DISABLED)

    def selectLocally(self):
        choices = []  # list of tuple of (file name, description)

        def sortOrder(key):
            return {
                "EdgarRenderer": "1",
                "validate": "2",
                "xbrlDB": "3"
            }.get(key, "4") + key.lower()

        def selectChoices(dir, indent=""):
            dirHasEntries = False
            for f in sorted(os.listdir(dir), key=sortOrder):
                if f not in (".", "..", "__pycache__", "__init__.py"):
                    fPath = os.path.join(dir, f)
                    fPkgInit = os.path.join(fPath, "__init__.py")
                    dirInsertPoint = len(choices)
                    moduleInfo = None
                    if ((os.path.isdir(fPath) and os.path.exists(fPkgInit)) or
                        ((os.path.isfile(fPath) and f.endswith(".py")))):
                        moduleInfo = PluginManager.moduleModuleInfo(fPath)
                        if moduleInfo:
                            choices.append((
                                indent + f,
                                "name: {}\ndescription: {}\nversion: {}\nlicense: {}"
                                .format(moduleInfo["name"],
                                        moduleInfo.get("description"),
                                        moduleInfo.get("version"),
                                        moduleInfo.get("license")), fPath,
                                moduleInfo["name"], moduleInfo.get("version"),
                                moduleInfo.get("description"),
                                moduleInfo.get("license")))
                            dirHasEntries = True
                    if os.path.isdir(fPath) and f not in ("DQC_US_Rules", ):
                        if selectChoices(fPath, indent=indent +
                                         "   ") and not moduleInfo:
                            choices.insert(dirInsertPoint,
                                           (indent + f, None, None, None, None,
                                            None, None))
            return dirHasEntries

        selectChoices(self.cntlr.pluginDir)
        selectedPath = DialogOpenArchive.selectPlugin(self, choices)
        if selectedPath:
            moduleInfo = PluginManager.moduleModuleInfo(
                selectedPath[len(self.cntlr.pluginDir) + 1:])
            self.loadFoundModuleInfo(moduleInfo, selectedPath)

    def browseLocally(self):
        initialdir = self.cntlr.pluginDir  # default plugin directory
        if not self.cntlr.isMac:  # can't navigate within app easily, always start in default directory
            initialdir = self.cntlr.config.setdefault("pluginOpenDir",
                                                      initialdir)
        filename = self.cntlr.uiFileDialog(
            "open",
            parent=self,
            title=_("Choose plug-in module file"),
            initialdir=initialdir,
            filetypes=[(_("Python files"), "*.py")],
            defaultextension=".py")
        if filename:
            # check if a package is selected (any file in a directory containing an __init__.py
            #if (os.path.basename(filename) == "__init__.py" and os.path.isdir(os.path.dirname(filename)) and
            #    os.path.isfile(filename)):
            #    filename = os.path.dirname(filename) # refer to the package instead
            self.cntlr.config["pluginOpenDir"] = os.path.dirname(filename)
            moduleInfo = PluginManager.moduleModuleInfo(filename)
            self.loadFoundModuleInfo(moduleInfo, filename)

    def findOnWeb(self):
        url = DialogURL.askURL(self)
        if url:  # url is the in-cache or local file
            moduleInfo = PluginManager.moduleModuleInfo(url)
            self.cntlr.showStatus("")  # clear web loading status
            self.loadFoundModuleInfo(moduleInfo, url)

    def loadFoundModuleInfo(self, moduleInfo, url):
        if moduleInfo and moduleInfo.get("name"):
            self.addPluginConfigModuleInfo(moduleInfo)
            self.loadTreeViews()
        else:
            messagebox.showwarning(
                _("Module is not itself a plug-in or in a directory with package __init__.py plug-in.  "
                  ),
                _("File does not itself contain a python program with an appropriate __pluginInfo__ declaration: \n\n{0}"
                  ).format(url),
                parent=self)

    def checkIfImported(self, moduleInfo):
        if moduleInfo.get("isImported"):
            messagebox.showwarning(
                _("Plug-in is imported by a parent plug-in.  "),
                _("Plug-in has a parent, please request operation on the parent: \n\n{0}"
                  ).format(moduleInfo.get("name")),
                parent=self)
            return True
        return False

    def checkClassMethodsChanged(self, moduleInfo):
        for classMethod in moduleInfo["classMethods"]:
            if classMethod.startswith("CntlrWinMain.Menu"):
                self.uiClassMethodsChanged = True  # may require reloading UI
            elif classMethod == "ModelObjectFactory.ElementSubstitutionClasses":
                self.modelClassesChanged = True  # model object factor classes changed
            elif classMethod == "ModelManager.LoadCustomTransforms":
                self.customTransformsChanged = True
            elif classMethod == "DisclosureSystem.Types":
                self.disclosureSystemTypesChanged = True  # disclosure system types changed
            elif classMethod.startswith("Proxy."):
                self.hostSystemFeaturesChanged = True  # system features (e.g., proxy) changed

    def removePluginConfigModuleInfo(self, name):
        moduleInfo = self.pluginConfig["modules"].get(name)
        if moduleInfo:
            if self.checkIfImported(moduleInfo):
                return

            def _removePluginConfigModuleInfo(moduleInfo):
                _name = moduleInfo.get("name")
                if _name:
                    self.checkClassMethodsChanged(moduleInfo)
                    for classMethod in moduleInfo["classMethods"]:
                        classMethods = self.pluginConfig["classes"].get(
                            classMethod)
                        if classMethods and _name in classMethods:
                            classMethods.remove(_name)
                            if not classMethods:  # list has become unused
                                del self.pluginConfig["classes"][
                                    classMethod]  # remove class
                    for importModuleInfo in moduleInfo.get(
                            "imports", EMPTYLIST):
                        _removePluginConfigModuleInfo(importModuleInfo)
                    self.pluginConfig["modules"].pop(_name, None)

            _removePluginConfigModuleInfo(moduleInfo)
            if not self.pluginConfig["modules"] and self.pluginConfig[
                    "classes"]:
                self.pluginConfig["classes"].clear()  # clean orphan classes
            self.pluginConfigChanged = True

    def addPluginConfigModuleInfo(self, moduleInfo):
        if self.checkIfImported(moduleInfo):
            return
        name = moduleInfo.get("name")
        self.removePluginConfigModuleInfo(
            name)  # remove any prior entry for this module

        def _addPlugin(moduleInfo):
            _name = moduleInfo.get("name")
            if _name:
                self.modulesWithNewerFileDates.discard(
                    _name)  # no longer has an update available
                self.pluginConfig["modules"][_name] = moduleInfo
                # add classes
                for classMethod in moduleInfo["classMethods"]:
                    classMethods = self.pluginConfig["classes"].setdefault(
                        classMethod, [])
                    if name not in classMethods:
                        classMethods.append(_name)
                self.checkClassMethodsChanged(moduleInfo)
            for importModuleInfo in moduleInfo.get("imports", EMPTYLIST):
                _addPlugin(importModuleInfo)

        _addPlugin(moduleInfo)
        self.pluginConfigChanged = True

    def moduleEnable(self):
        if self.selectedModule in self.pluginConfig["modules"]:
            moduleInfo = self.pluginConfig["modules"][self.selectedModule]
            if self.checkIfImported(moduleInfo):
                return

            def _moduleEnable(moduleInfo):
                if self.moduleEnableButton['text'] == self.ENABLE:
                    moduleInfo["status"] = "enabled"
                elif self.moduleEnableButton['text'] == self.DISABLE:
                    moduleInfo["status"] = "disabled"
                self.checkClassMethodsChanged(moduleInfo)
                for importModuleInfo in moduleInfo.get("imports", EMPTYLIST):
                    _moduleEnable(
                        importModuleInfo)  # set status on nested moduleInfo
                    if importModuleInfo['name'] in self.pluginConfig[
                            "modules"]:  # set status on top level moduleInfo
                        _moduleEnable(self.pluginConfig["modules"][
                            importModuleInfo['name']])

            _moduleEnable(moduleInfo)
            if self.moduleEnableButton['text'] == self.ENABLE:
                self.moduleEnableButton['text'] = self.DISABLE
            elif self.moduleEnableButton['text'] == self.DISABLE:
                self.moduleEnableButton['text'] = self.ENABLE
            self.pluginConfigChanged = True
            self.loadTreeViews()

    def moduleReload(self):
        if self.selectedModule in self.pluginConfig["modules"]:
            url = self.pluginConfig["modules"][self.selectedModule].get(
                "moduleURL")
            if url:
                moduleInfo = PluginManager.moduleModuleInfo(url, reload=True)
                if moduleInfo:
                    if self.checkIfImported(moduleInfo):
                        return
                    self.addPluginConfigModuleInfo(moduleInfo)
                    self.loadTreeViews()
                    self.cntlr.showStatus(_("{0} reloaded").format(
                        moduleInfo["name"]),
                                          clearAfter=5000)
                else:
                    messagebox.showwarning(
                        _("Module error"),
                        _("File or module cannot be reloaded: \n\n{0}").format(
                            url),
                        parent=self)

    def moduleRemove(self):
        if self.selectedModule in self.pluginConfig["modules"]:
            self.removePluginConfigModuleInfo(self.selectedModule)
            self.pluginConfigChanged = True
            self.loadTreeViews()

    def enableAll(self):
        self.enableDisableAll(True)

    def disableAll(self):
        self.enableDisableAll(False)

    def enableDisableAll(self, doEnable):
        for module in self.pluginConfig["modules"]:
            moduleInfo = self.pluginConfig["modules"][module]
            if not moduleInfo.get("isImported"):

                def _enableDisableAll(moduleInfo):
                    if doEnable:
                        moduleInfo["status"] = "enabled"
                    else:
                        moduleInfo["status"] = "disabled"
                    for importModuleInfo in moduleInfo.get(
                            "imports", EMPTYLIST):
                        _enableDisableAll(importModuleInfo)

                _enableDisableAll(moduleInfo)
                if doEnable:
                    self.moduleEnableButton['text'] = self.DISABLE
                else:
                    self.moduleEnableButton['text'] = self.ENABLE
        self.pluginConfigChanged = True
        self.loadTreeViews()
Example #23
0
File: tagview.py Project: gokai/tim
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
class DialogPluginManager(Toplevel):
    def __init__(self, mainWin, modulesWithNewerFileDates):
        super(DialogPluginManager, self).__init__(mainWin.parent)
        
        self.ENABLE = _("Enable")
        self.DISABLE = _("Disable")
        self.parent = mainWin.parent
        self.cntlr = mainWin
        
        # copy plugins for temporary display
        self.pluginConfig = PluginManager.pluginConfig
        self.pluginConfigChanged = False
        self.uiClassMethodsChanged = False
        self.modulesWithNewerFileDates = modulesWithNewerFileDates
        
        parentGeometry = re.match("(\d+)x(\d+)[+]?([-]?\d+)[+]?([-]?\d+)", self.parent.geometry())
        dialogX = int(parentGeometry.group(3))
        dialogY = int(parentGeometry.group(4))

        self.title(_("Plug-in Manager"))
        frame = Frame(self)
        
        # left button frame
        buttonFrame = Frame(frame, width=40)
        buttonFrame.columnconfigure(0, weight=1)
        addLabel = Label(buttonFrame, text=_("Find plug-in modules:"), wraplength=60, justify="center")
        addLocalButton = Button(buttonFrame, text=_("Locally"), command=self.findLocally)
        ToolTip(addLocalButton, text=_("File chooser allows selecting python module files to add (or reload) plug-ins, from the local file system."), wraplength=240)
        addWebButton = Button(buttonFrame, text=_("On Web"), command=self.findOnWeb)
        ToolTip(addWebButton, text=_("Dialog to enter URL full path to load (or reload) plug-ins, from the web or local file system."), wraplength=240)
        addLabel.grid(row=0, column=0, pady=4)
        addLocalButton.grid(row=1, column=0, pady=4)
        addWebButton.grid(row=2, column=0, pady=4)
        buttonFrame.grid(row=0, column=0, rowspan=2, sticky=(N, S, W), padx=3, pady=3)
        
        # right tree frame (plugins already known to arelle)
        modulesFrame = Frame(frame, width=700)
        vScrollbar = Scrollbar(modulesFrame, orient=VERTICAL)
        hScrollbar = Scrollbar(modulesFrame, orient=HORIZONTAL)
        self.modulesView = Treeview(modulesFrame, xscrollcommand=hScrollbar.set, yscrollcommand=vScrollbar.set, height=7)
        self.modulesView.grid(row=0, column=0, sticky=(N, S, E, W))
        self.modulesView.bind('<<TreeviewSelect>>', self.moduleSelect)
        hScrollbar["command"] = self.modulesView.xview
        hScrollbar.grid(row=1, column=0, sticky=(E,W))
        vScrollbar["command"] = self.modulesView.yview
        vScrollbar.grid(row=0, column=1, sticky=(N,S))
        modulesFrame.columnconfigure(0, weight=1)
        modulesFrame.rowconfigure(0, weight=1)
        modulesFrame.grid(row=0, column=1, columnspan=4, sticky=(N, S, E, W), padx=3, pady=3)
        self.modulesView.focus_set()

        self.modulesView.column("#0", width=120, anchor="w")
        self.modulesView.heading("#0", text=_("Name"))
        self.modulesView["columns"] = ("author", "ver", "status", "date", "update", "descr", "license")
        self.modulesView.column("author", width=100, anchor="w", stretch=False)
        self.modulesView.heading("author", text=_("Author"))
        self.modulesView.column("ver", width=50, anchor="w", stretch=False)
        self.modulesView.heading("ver", text=_("Version"))
        self.modulesView.column("status", width=50, anchor="w", stretch=False)
        self.modulesView.heading("status", text=_("Status"))
        self.modulesView.column("date", width=70, anchor="w", stretch=False)
        self.modulesView.heading("date", text=_("File Date"))
        self.modulesView.column("update", width=50, anchor="w", stretch=False)
        self.modulesView.heading("update", text=_("Update"))
        self.modulesView.column("descr", width=200, anchor="w", stretch=False)
        self.modulesView.heading("descr", text=_("Description"))
        self.modulesView.column("license", width=70, anchor="w", stretch=False)
        self.modulesView.heading("license", text=_("License"))

        classesFrame = Frame(frame)
        vScrollbar = Scrollbar(classesFrame, orient=VERTICAL)
        hScrollbar = Scrollbar(classesFrame, orient=HORIZONTAL)
        self.classesView = Treeview(classesFrame, xscrollcommand=hScrollbar.set, yscrollcommand=vScrollbar.set, height=5)
        self.classesView.grid(row=0, column=0, sticky=(N, S, E, W))
        hScrollbar["command"] = self.classesView.xview
        hScrollbar.grid(row=1, column=0, sticky=(E,W))
        vScrollbar["command"] = self.classesView.yview
        vScrollbar.grid(row=0, column=1, sticky=(N,S))
        classesFrame.columnconfigure(0, weight=1)
        classesFrame.rowconfigure(0, weight=1)
        classesFrame.grid(row=1, column=1, columnspan=4, sticky=(N, S, E, W), padx=3, pady=3)
        self.classesView.focus_set()
        
        self.classesView.column("#0", width=200, anchor="w")
        self.classesView.heading("#0", text=_("Class"))
        self.classesView["columns"] = ("modules")
        self.classesView.column("modules", width=500, anchor="w", stretch=False)
        self.classesView.heading("modules", text=_("Modules"))
        
        # bottom frame module info details
        moduleInfoFrame = Frame(frame, width=700)
        moduleInfoFrame.columnconfigure(1, weight=1)
        
        self.moduleNameLabel = Label(moduleInfoFrame, wraplength=600, justify="left", 
                                     font=font.Font(family='Helvetica', size=12, weight='bold'))
        self.moduleNameLabel.grid(row=0, column=0, columnspan=4, sticky=W)
        self.moduleAuthorHdr = Label(moduleInfoFrame, text=_("author:"), state=DISABLED)
        self.moduleAuthorHdr.grid(row=1, column=0, sticky=W)
        self.moduleAuthorLabel = Label(moduleInfoFrame, wraplength=600, justify="left")
        self.moduleAuthorLabel.grid(row=1, column=1, columnspan=3, sticky=W)
        self.moduleDescrHdr = Label(moduleInfoFrame, text=_("description:"), state=DISABLED)
        self.moduleDescrHdr.grid(row=2, column=0, sticky=W)
        self.moduleDescrLabel = Label(moduleInfoFrame, wraplength=600, justify="left")
        self.moduleDescrLabel.grid(row=2, column=1, columnspan=3, sticky=W)
        self.moduleClassesHdr = Label(moduleInfoFrame, text=_("classes:"), state=DISABLED)
        self.moduleClassesHdr.grid(row=3, column=0, sticky=W)
        self.moduleClassesLabel = Label(moduleInfoFrame, wraplength=600, justify="left")
        self.moduleClassesLabel.grid(row=3, column=1, columnspan=3, sticky=W)
        ToolTip(self.moduleClassesLabel, text=_("List of classes that this plug-in handles."), wraplength=240)
        self.moduleUrlHdr = Label(moduleInfoFrame, text=_("URL:"), state=DISABLED)
        self.moduleUrlHdr.grid(row=4, column=0, sticky=W)
        self.moduleUrlLabel = Label(moduleInfoFrame, wraplength=600, justify="left")
        self.moduleUrlLabel.grid(row=4, column=1, columnspan=3, sticky=W)
        ToolTip(self.moduleUrlLabel, text=_("URL of plug-in module (local file path or web loaded file)."), wraplength=240)
        self.moduleDateHdr = Label(moduleInfoFrame, text=_("date:"), state=DISABLED)
        self.moduleDateHdr.grid(row=5, column=0, sticky=W)
        self.moduleDateLabel = Label(moduleInfoFrame, wraplength=600, justify="left")
        self.moduleDateLabel.grid(row=5, column=1, columnspan=3, sticky=W)
        ToolTip(self.moduleDateLabel, text=_("Date of currently loaded module file (with parenthetical node when an update is available)."), wraplength=240)
        self.moduleLicenseHdr = Label(moduleInfoFrame, text=_("license:"), state=DISABLED)
        self.moduleLicenseHdr.grid(row=6, column=0, sticky=W)
        self.moduleLicenseLabel = Label(moduleInfoFrame, wraplength=600, justify="left")
        self.moduleLicenseLabel.grid(row=6, column=1, columnspan=3, sticky=W)
        self.moduleEnableButton = Button(moduleInfoFrame, text=self.ENABLE, state=DISABLED, command=self.moduleEnable)
        ToolTip(self.moduleEnableButton, text=_("Enable/disable plug in."), wraplength=240)
        self.moduleEnableButton.grid(row=7, column=1, sticky=E)
        self.moduleReloadButton = Button(moduleInfoFrame, text=_("Reload"), state=DISABLED, command=self.moduleReload)
        ToolTip(self.moduleReloadButton, text=_("Reload/update plug in."), wraplength=240)
        self.moduleReloadButton.grid(row=7, column=2, sticky=E)
        self.moduleRemoveButton = Button(moduleInfoFrame, text=_("Remove"), state=DISABLED, command=self.moduleRemove)
        ToolTip(self.moduleRemoveButton, text=_("Remove plug in from plug in table (does not erase the plug in's file)."), wraplength=240)
        self.moduleRemoveButton.grid(row=7, column=3, sticky=E)
        moduleInfoFrame.grid(row=2, column=0, columnspan=5, sticky=(N, S, E, W), padx=3, pady=3)
        moduleInfoFrame.config(borderwidth=4, relief="groove")
        
        okButton = Button(frame, text=_("Close"), command=self.ok)
        ToolTip(okButton, text=_("Accept and changes (if any) and close dialog."), wraplength=240)
        cancelButton = Button(frame, text=_("Cancel"), command=self.close)
        ToolTip(cancelButton, text=_("Cancel changes (if any) and close dialog."), wraplength=240)
        okButton.grid(row=3, column=3, sticky=(S,E), pady=3)
        cancelButton.grid(row=3, column=4, sticky=(S,E), pady=3, padx=3)
        
        self.loadTreeViews()

        frame.grid(row=0, column=0, sticky=(N,S,E,W))
        frame.columnconfigure(0, weight=1)
        frame.columnconfigure(1, 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.protocol("WM_DELETE_WINDOW", self.close)
        self.grab_set()
        self.wait_window(self)
        
    def loadTreeViews(self):
        self.selectedModule = None

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

        for i, moduleItem in enumerate(sorted(self.pluginConfig.get("modules", {}).items())):
            moduleInfo = moduleItem[1]
            name = moduleInfo.get("name", moduleItem[0])
            node = self.modulesView.insert("", "end", name, text=name)
            self.modulesView.set(node, "author", moduleInfo.get("author"))
            self.modulesView.set(node, "ver", moduleInfo.get("version"))
            self.modulesView.set(node, "status", moduleInfo.get("status"))
            self.modulesView.set(node, "date", moduleInfo.get("fileDate"))
            if name in self.modulesWithNewerFileDates:
                self.modulesView.set(node, "update", _("available"))
            self.modulesView.set(node, "descr", moduleInfo.get("description"))
            self.modulesView.set(node, "license", moduleInfo.get("license"))
        
        # clear previous treeview entries
        for previousNode in self.classesView.get_children(""): 
            self.classesView.delete(previousNode)

        for i, classItem in enumerate(sorted(self.pluginConfig.get("classes", {}).items())):
            className, moduleList = classItem
            node = self.classesView.insert("", "end", className, text=className)
            self.classesView.set(node, "modules", ', '.join(moduleList))
            
        self.moduleSelect()  # clear out prior selection

    def ok(self, event=None):
        if self.pluginConfigChanged:
            PluginManager.pluginConfig = self.pluginConfig
            PluginManager.pluginConfigChanged = True
            PluginManager.reset()  # force reloading of modules
        if self.uiClassMethodsChanged:  # may require reloading UI
            if messagebox.askyesno(_("User interface plug-in change"),
                                   _("A change in plug-in class methods may have affected the menus "
                                     "of the user interface.  It may be necessary to restart Arelle to "
                                     "access the menu entries or the changes to their plug-in methods.  \n\n"
                                     "Should Arelle restart with changed user interface language, "
                                     "(if there are any unsaved changes they would be lost!)?"),
                                   parent=self):
                self.cntlr.uiThreadQueue.put((self.cntlr.quit, [None, True]))
        self.close()
        
    def close(self, event=None):
        self.parent.focus_set()
        self.destroy()
                
    def moduleSelect(self, *args):
        node = (self.modulesView.selection() or (None,))[0]
        moduleInfo = self.pluginConfig.get("modules", {}).get(node)
        if moduleInfo:
            self.selectedModule = node
            name = moduleInfo["name"]
            self.moduleNameLabel.config(text=name)
            self.moduleAuthorHdr.config(state=ACTIVE)
            self.moduleAuthorLabel.config(text=moduleInfo["author"])
            self.moduleDescrHdr.config(state=ACTIVE)
            self.moduleDescrLabel.config(text=moduleInfo["description"])
            self.moduleClassesHdr.config(state=ACTIVE)
            self.moduleClassesLabel.config(text=', '.join(moduleInfo["classMethods"]))
            self.moduleUrlHdr.config(state=ACTIVE)
            self.moduleUrlLabel.config(text=moduleInfo["moduleURL"])
            self.moduleDateHdr.config(state=ACTIVE)
            self.moduleDateLabel.config(text=moduleInfo["fileDate"] + " " +
                    (_("(an update is available)") if name in self.modulesWithNewerFileDates else ""))
            self.moduleLicenseHdr.config(state=ACTIVE)
            self.moduleLicenseLabel.config(text=moduleInfo["license"])
            self.moduleEnableButton.config(state=ACTIVE,
                                           text={"enabled":self.DISABLE,
                                                 "disabled":self.ENABLE}[moduleInfo["status"]])
            self.moduleReloadButton.config(state=ACTIVE)
            self.moduleRemoveButton.config(state=ACTIVE)
        else:
            self.selectedModule = None
            self.moduleNameLabel.config(text="")
            self.moduleAuthorHdr.config(state=DISABLED)
            self.moduleAuthorLabel.config(text="")
            self.moduleDescrHdr.config(state=DISABLED)
            self.moduleDescrLabel.config(text="")
            self.moduleClassesHdr.config(state=DISABLED)
            self.moduleClassesLabel.config(text="")
            self.moduleUrlHdr.config(state=DISABLED)
            self.moduleUrlLabel.config(text="")
            self.moduleDateHdr.config(state=DISABLED)
            self.moduleDateLabel.config(text="")
            self.moduleLicenseHdr.config(state=DISABLED)
            self.moduleLicenseLabel.config(text="")
            self.moduleEnableButton.config(state=DISABLED, text=self.ENABLE)
            self.moduleReloadButton.config(state=DISABLED)
            self.moduleRemoveButton.config(state=DISABLED)
        
    def findLocally(self):
        filename = self.cntlr.uiFileDialog("open",
                                           owner=self,
                                           title=_("Choose plug-in module file"),
                                           initialdir=self.cntlr.config.setdefault("pluginOpenDir","."),
                                           filetypes=[(_("Python files"), "*.py")],
                                           defaultextension=".py")
        if filename:
            self.cntlr.config["pluginOpenDir"] = os.path.dirname(filename)
            moduleInfo = PluginManager.moduleModuleInfo(filename)
            self.loadFoundModuleInfo(moduleInfo, filename)
                

    def findOnWeb(self):
        url = DialogURL.askURL(self)
        if url:  # url is the in-cache or local file
            moduleInfo = PluginManager.moduleModuleInfo(url)
            self.cntlr.showStatus("") # clear web loading status
            self.loadFoundModuleInfo(moduleInfo, url)
                
    def loadFoundModuleInfo(self, moduleInfo, url):
        if moduleInfo and moduleInfo.get("name"):
            self.addPluginConfigModuleInfo(moduleInfo)
            self.loadTreeViews()
        else:
            messagebox.showwarning(_("Module is not a plug-in"),
                                   _("File does not contain a python program with an appropriate __pluginInfo__ declaration: \n\n{0}")
                                   .format(url),
                                   parent=self)
            
    def removePluginConfigModuleInfo(self, name):
        moduleInfo = self.pluginConfig["modules"].get(name)
        if moduleInfo:
            for classMethod in moduleInfo["classMethods"]:
                classMethods = self.pluginConfig["classes"].get(classMethod)
                if classMethods and name in classMethods:
                    classMethods.remove(name)
                    if not classMethods: # list has become unused
                        del self.pluginConfig["classes"][classMethod] # remove class
                    if classMethod.startswith("CntlrWinMain.Menu"):
                        self.uiClassMethodsChanged = True  # may require reloading UI
            del self.pluginConfig["modules"][name]
            self.pluginConfigChanged = True

    def addPluginConfigModuleInfo(self, moduleInfo):
        name = moduleInfo["name"]
        self.removePluginConfigModuleInfo(name)  # remove any prior entry for this module
        self.modulesWithNewerFileDates.discard(name) # no longer has an update available
        self.pluginConfig["modules"][name] = moduleInfo
        # add classes
        for classMethod in moduleInfo["classMethods"]:
            classMethods = self.pluginConfig["classes"].setdefault(classMethod, [])
            if name not in classMethods:
                classMethods.append(name)
            if classMethod.startswith("CntlrWinMain.Menu"):
                self.uiClassMethodsChanged = True  # may require reloading UI
        self.pluginConfigChanged = True

    def moduleEnable(self):
        if self.selectedModule in self.pluginConfig["modules"]:
            moduleInfo = self.pluginConfig["modules"][self.selectedModule]
            if self.moduleEnableButton['text'] == self.ENABLE:
                moduleInfo["status"] = "enabled"
                self.moduleEnableButton['text'] = self.DISABLE
            elif self.moduleEnableButton['text'] == self.DISABLE:
                moduleInfo["status"] = "disabled"
                self.moduleEnableButton['text'] = self.ENABLE
            self.pluginConfigChanged = True
            self.loadTreeViews()
            
    def moduleReload(self):
        if self.selectedModule in self.pluginConfig["modules"]:
            url = self.pluginConfig["modules"][self.selectedModule].get("moduleURL")
            if url:
                moduleInfo = PluginManager.moduleModuleInfo(url, reload=True)
                if moduleInfo:
                    self.addPluginConfigModuleInfo(moduleInfo)
                    self.loadTreeViews()
                    self.cntlr.showStatus(_("{0} reloaded").format(moduleInfo.get("name")), clearAfter=5000)
                else:
                    messagebox.showwarning(_("Module error"),
                                           _("File or module cannot be reloaded: \n\n{0}")
                                           .format(url),
                                           parent=self)

    def moduleRemove(self):
        if self.selectedModule in self.pluginConfig["modules"]:
            self.removePluginConfigModuleInfo(self.selectedModule)
            self.pluginConfigChanged = True
            self.loadTreeViews()
Example #25
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")
Example #26
0
class ElementListWidget(Frame):
	def __init__(self, parent, label, columns, showError):
		Frame.__init__(self, parent)
		
		self.showError = showError
		
		self.columnconfigure(0, weight = 1)
		self.rowconfigure(1, weight = 1)
		
		
		# Название таблицы
		self.titleLabel = Label(self, text = label)
		self.titleLabel.grid(column = 0, row = 0, sticky = W + E)
		
		
		# Таблица значений
		columns = ("Метка", "№") + columns
		self.tree = Treeview(self, columns = columns, displaycolumns = columns,
							 selectmode = "browse")
		self.tree.grid(column = 0, row = 1, sticky = W + N + E + S)
		
		# Настраиваем внешний вид таблицы (первые колонки)
		self.tree.column("#0", width = 0, stretch = 0)	# Прячем колонку с иконкой
		
		self.tree.column( columns[0], anchor = W, width = 150)
		self.tree.heading(columns[0], anchor = W, text = columns[0])
		
		self.tree.column( columns[1], anchor = E, width = 80)
		self.tree.heading(columns[1], anchor = E, text = columns[1])
		
		self.tree.bind("<<TreeviewSelect>>", self.onSelectionChanged)
		
		
		# Панель с кнопками
		self.buttonPanel = Frame(self)
		self.buttonPanel.grid(column = 0, row = 2, sticky = W + E)
		
		self.buttonPanel.columnconfigure(0, weight = 1)
		self.buttonPanel.columnconfigure(3, minsize = emptySpaceSize, weight = 0)
		self.buttonPanel.columnconfigure(6, minsize = emptySpaceSize, weight = 0)
		self.buttonPanel.columnconfigure(9, weight = 1)
		
		# Кнопки добавления/удаления элемента
		self.buttonAdd = Button(self.buttonPanel, text = "+", width = 3,
								command = self.onButtonAddClicked)
		self.buttonAdd.grid(column = 1, row = 0)
		
		self.buttonRemove = Button(self.buttonPanel, text = "-", width = 3, state = DISABLED,
								   command = self.onButtonRemoveClicked)
		self.buttonRemove.grid(column = 2, row = 0)
		
		# Кнопки перемещения элемента
		self.buttonUp = Button(self.buttonPanel, text = "↑", width = 3, state = DISABLED,
							   command = self.onButtonUpClicked)
		self.buttonUp.grid(column = 4, row = 0)
		
		self.buttonDown = Button(self.buttonPanel, text = "↓", width = 3, state = DISABLED,
								 command = self.onButtonDownClicked)
		self.buttonDown.grid(column = 5, row = 0)
		
		# Кнопки применить/отменить (для выбранного элемента)
		self.buttonCancel = Button(self.buttonPanel, text = "✗", width = 3,
								   command = self.updateSelectedFrame)
		self.buttonCancel.grid(column = 7, row = 0)
		
		self.buttonApply = Button(self.buttonPanel, text = "✓", width = 3,
								  command = self.onButtonApplyClicked)
		self.buttonApply.grid(column = 8, row = 0)
		
		
		# Редактирование выделенного элемента
		self.i     = StringVar()
		self.label = (StringVar(), StringVar())
		
		self.selectedFrame = Frame(self)
		self.selectedFrame.grid(column = 0, row = 3, sticky = W + E)
		
		# Номер
		Label(self.selectedFrame, text = "№:") \
			.grid(column = 0, row = 0)
		Label(self.selectedFrame, textvariable = self.i, width = 3, justify = RIGHT) \
			.grid(column = 1, row = 0)
		
		# Пустое пространство
		self.selectedFrame.columnconfigure(2, minsize = emptySpaceSize, weight = 0)
		
		# Метка
		Entry(self.selectedFrame, textvariable = self.label[0]) \
			.grid(column = 3, row = 0, sticky = W + E)
		
		Entry(self.selectedFrame, textvariable = self.label[1], bg = defaultValueBG) \
			.grid(column = 4, row = 0, sticky = W + E)
		
		# Виджет для элементов классов-потомков
		self.detailFrame = Frame(self.selectedFrame)
		self.detailFrame.grid(column = 3, row = 1, columnspan = 2, sticky = W + N + E + S)
		
		self.selectedFrame.columnconfigure(3, weight = 1)
		self.selectedFrame.columnconfigure(4, weight = 1)
		self.selectedFrame.rowconfigure(1, weight = 1)
	
	
	def onButtonUpClicked(self):
		item = self.selectedItem()
		if item is None: return
		
		prev = self.tree.prev(item)
		if prev != "":
			parent, index = self.tree.parent(item), self.tree.index(item)
			self.tree.move(item, parent, index - 1)
			
			# Корректируем номера элементов
			self.tree.set(item, "№", index - 1)
			self.tree.set(prev, "№", index)
			
			self.updateSelectedFrame(item)
	
	
	def onButtonDownClicked(self):
		item = self.selectedItem()
		if item is None: return
		
		next = self.tree.next(item)
		if next != "":
			parent, index = self.tree.parent(item), self.tree.index(item)
			self.tree.move(item, parent, index + 1)
			
			# Корректируем номера элементов
			self.tree.set(item, "№", index + 1)
			self.tree.set(next, "№", index)
			
			self.updateSelectedFrame(item)
	
	
	def onButtonAddClicked(self):
		pass
	
	
	def onButtonRemoveClicked(self):
		item = self.selectedItem()
		if item is None: return
		
		next = self.tree.next(item)
		self.tree.delete(item)
		
		while next != "":
			i = int(self.tree.set(next, "№"))
			self.tree.set(next, "№", i - 1)
			next = self.tree.next(next)
		
		self.onSelectionChanged()
	
	
	def onButtonApplyClicked(self, item = None):
		if item is None: item = self.selectedItem()
		if item is None: return None
		
		label = self.label[0].get()
		self.tree.set(item, "Метка", label)
		
		return item
	
	
	def onSelectionChanged(self, event = None):
		item = self.selectedItem()
		
		# Обновляем состояние кнопок
		state = DISABLED if item is None else NORMAL
		for x in (self.buttonRemove, self.buttonUp, self.buttonDown):
			x["state"] = state
		
		self.updateSelectedFrame(item)
	
	
	def selectedItem(self):
		selection = self.tree.selection()
		return None if type(selection) == type("") else selection[0]
	
	
	def clear(self):
		for item in self.tree.get_children():
			self.tree.delete(item)
	
	
	def updateSelectedFrame(self, item = None, values = None):
		if item is None: item = self.selectedItem()
		values = None
		
		if item is None:
			i     = ""
			label = ""
		else:
			if values is None: values = self.tree.set(item)
			
			i     = values["№"]
			label = values["Метка"]
		
		self.i.set(i)
		self.label[0].set(label)
		
		return (item, values)
	
	
	def addElement(self, values):
		self.tree.insert(parent = "", index = END, values = values)
	
	
	def setDefaultElement(self, label):
		self.label[1].set(label)
	
	
	def elementsCount(self):
		return len(self.tree.get_children())
	
	
	def elements(self, transform):
		return [ transform(item) for item in self.tree.get_children() ]
Example #27
0
class Ufd:
    """
        Universal File Dialog - "UFD"
        
        Unopinionated, minimalist, reusable, slightly configurable,
        general-purpose file-dialog.
    """
    def __init__(self,
                 title: str = "Universal File Dialog",
                 icon: str = "",
                 show_hidden: bool = False,
                 include_files: bool = True,
                 multiselect: bool = True,
                 select_dirs: bool = True,
                 select_files: bool = True,
                 unix_delimiter: bool = True,
                 stdout: bool = False):
        """
            Init kwargs as object attributes, save references to 
            Tk PhotoImages, & define the widgets + layout
        """

        if not isinstance(title, str):
            raise TypeError("Argument title must be type string.")

        self.title = title

        if icon:
            if not isinstance(icon, str):
                raise TypeError("Argument icon must be type string.")

            if not isfile(icon):
                raise FileNotFoundError(f"File not found: {icon}")

            self.icon = icon

        else:
            self.icon = ""

        if show_hidden:
            self.show_hidden = True
        else:
            self.show_hidden = False

        if include_files:
            self.include_files = True
        else:
            self.include_files = False

        if multiselect:
            self.multiselect = True
        else:
            self.multiselect = False

        if select_dirs:
            self.select_dirs = True
        else:
            self.select_dirs = False

        if select_files:
            self.select_files = True
        else:
            self.select_files = False

        if unix_delimiter:
            self.unix_delimiter = True
        else:
            self.unix_delimiter = False

        if stdout:
            self.stdout = True
        else:
            self.stdout = False

        # Tkinter:
        self.dialog = Tk()
        self.dialog.withdraw()
        self.dialog.title(self.title)
        self.dialog.minsize(width=300, height=200)
        self.dialog.geometry("500x300")
        self.dialog.update_idletasks()

        self.file_icon = PhotoImage(file=f"{dirname(__file__)}/file.gif",
                                    master=self.dialog).subsample(50)

        self.folder_icon = PhotoImage(file=f"{dirname(__file__)}/folder.gif",
                                      master=self.dialog).subsample(15)

        self.disk_icon = PhotoImage(file=f"{dirname(__file__)}/disk.gif",
                                    master=self.dialog).subsample(15)

        if self.icon:
            self.dialog.iconbitmap(self.icon)
        else:
            self.dialog.iconbitmap(f"{dirname(__file__)}/icon.ico")

        # Widgets:
        self.paneview = PanedWindow(
            self.dialog,
            sashwidth=7,
            bg="#cccccc",
            bd=0,
        )

        self.left_pane = PanedWindow(self.paneview)
        self.right_pane = PanedWindow(self.paneview)
        self.paneview.add(self.left_pane)
        self.paneview.add(self.right_pane)

        self.treeview_x_scrollbar = Scrollbar(self.left_pane,
                                              orient="horizontal")
        self.treeview_y_scrollbar = Scrollbar(self.left_pane,
                                              orient="vertical")
        self.list_box_x_scrollbar = Scrollbar(self.right_pane,
                                              orient="horizontal")
        self.list_box_y_scrollbar = Scrollbar(self.right_pane,
                                              orient="vertical")

        # tstyle = Style().configure(".", )

        self.treeview = Treeview(
            self.left_pane,
            xscrollcommand=self.treeview_x_scrollbar.set,
            yscrollcommand=self.treeview_y_scrollbar.set,
            show="tree",
            selectmode="browse",
            # style=tstyle
        )

        self.list_box = Listbox(self.right_pane,
                                xscrollcommand=self.list_box_x_scrollbar.set,
                                yscrollcommand=self.list_box_y_scrollbar.set,
                                width=34,
                                highlightthickness=0,
                                bd=2,
                                relief="ridge")

        if self.multiselect:
            self.list_box.config(selectmode="extended")
        else:
            self.list_box.config(selectmode="browse")

        self.cancel_button = Button(self.left_pane,
                                    text="Cancel",
                                    command=self.cancel)

        self.submit_button = Button(self.right_pane,
                                    text="Submit",
                                    command=self.submit)

        self.treeview_x_scrollbar.config(command=self.treeview.xview)
        self.treeview_y_scrollbar.config(command=self.treeview.yview)
        self.list_box_x_scrollbar.config(command=self.list_box.xview)
        self.list_box_y_scrollbar.config(command=self.list_box.yview)

        #Layout:
        self.dialog.rowconfigure(0, weight=1)
        self.dialog.columnconfigure(0, weight=1)

        self.left_pane.grid_rowconfigure(0, weight=1)
        self.left_pane.grid_columnconfigure(0, weight=1)
        self.right_pane.grid_rowconfigure(0, weight=1)
        self.right_pane.grid_columnconfigure(0, weight=1)

        self.paneview.paneconfigure(
            self.left_pane,
            minsize=100,
            #Start off w/ the sash centered in the GUI:
            width=(self.dialog.winfo_width() / 2) - ceil(
                (self.paneview.cget("sashwidth") * 1.5)),
        )
        self.paneview.paneconfigure(self.right_pane, minsize=100)

        self.paneview.grid(row=0, column=0, sticky="nsew")

        self.treeview.grid(row=0, column=0, sticky="nsew")
        self.treeview_y_scrollbar.grid(row=0, column=1, sticky="ns")
        self.treeview_x_scrollbar.grid(row=1,
                                       column=0,
                                       columnspan=2,
                                       sticky="ew")

        self.list_box.grid(row=0, column=0, sticky="nsew")
        self.list_box_y_scrollbar.grid(row=0, column=1, sticky="ns")
        self.list_box_x_scrollbar.grid(row=1,
                                       column=0,
                                       columnspan=2,
                                       sticky="ew")

        self.cancel_button.grid(row=2, column=0, sticky="w", padx=10, pady=10)
        self.submit_button.grid(row=2,
                                column=0,
                                columnspan=2,
                                sticky="e",
                                padx=10,
                                pady=10)

        #Bindings, Protocols, & Misc:
        self.dialog.bind("<Control-w>", self.cancel)
        self.treeview.bind("<<TreeviewSelect>>", self.treeview_select)
        self.treeview.bind("<Double-Button-1>", self.dialog_populate)
        self.treeview.bind("<Return>", self.dialog_populate)
        self.treeview.bind("<Right>", self.dialog_populate)
        self.list_box.bind("<<ListboxSelect>>", self.list_box_select)
        self.list_box.bind("<Return>", self.submit)
        self.dialog.protocol("WM_DELETE_WINDOW", self.cancel)

        self.dialog_selection = deque()
        self.selection_paths = deque()

        for disk in self.get_disks():
            self.treeview.insert(
                "",
                index="end",
                text=disk,
                image=self.disk_icon,
            )

        self.dialog.focus()

    def __call__(self):
        """
            Display dialog & return selection
        """

        (width_offset, height_offset) = self.get_offset(self.dialog)
        self.dialog.geometry(f"+{width_offset}+{height_offset}")
        self.dialog.update_idletasks()
        self.dialog.deiconify()

        self.dialog.wait_window()

        for i, path in enumerate(self.dialog_selection):
            if self.unix_delimiter:
                self.dialog_selection[i] = sub("\\\\", "/", path)
            else:
                self.dialog_selection[i] = sub("/", "\\\\", path)

        if self.stdout:
            [print(item) for item in self.dialog_selection]

        return list(self.dialog_selection)

    def __str__(self):
        """
            Return own address
        """

        return "Universal File Dialog"\
        f" @ {hex(id(self))}"

    def __repr__(self):
        """
            Return full string representation of constructor signature
        """

        return f"Ufd("\
        f"title=\"{self.title}\","\
        f" icon=\"{self.icon}\","\
        f" show_hidden={self.show_hidden},"\
        f" include_files={self.include_files},"\
        f" multiselect={self.multiselect},"\
        f" select_dirs={self.select_dirs},"\
        f" select_files={self.select_files},"\
        f" unix_delimiter={self.unix_delimiter})"\
        f" stdout={self.stdout})"\
        f" @ {hex(id(self))}"

    @staticmethod
    def get_offset(tk_window):
        """
            Returns an appropriate offset for a given tkinter toplevel,
            such that it always is created center screen on the primary display.
        """

        width_offset = int((tk_window.winfo_screenwidth() / 2) -
                           (tk_window.winfo_width() / 2))

        height_offset = int((tk_window.winfo_screenheight() / 2) -
                            (tk_window.winfo_height() / 2))

        return (width_offset, height_offset)

    @staticmethod
    def get_disks():
        """
            Returns all mounted disks (for Windows)

            >> ["A:", "B:", "C:"]
        """

        if system() != "Windows":
            raise OSError("For use with Windows platforms.")

        logicaldisks = run(["wmic", "logicaldisk", "get", "name"],
                           capture_output=True)

        return findall("[A-Z]:", str(logicaldisks.stdout))

    @staticmethod
    def list_dir(path, force=False):
        """
            Reads a directory with a shell call to dir.
            Truthiness of bool force determines whether 
            hidden items are returned or not. (For Windows)
        """

        path = sub("/", "\\\\", path)

        if force:
            dir_listing = run(["dir", path, "/b", "/a"],
                              shell=True,
                              capture_output=True)

        else:
            dir_listing = run(["dir", path, "/b"],
                              shell=True,
                              capture_output=True)

        output = dir_listing.stdout
        err = dir_listing.stderr

        if not output:
            return []

        if err:
            err = err.decode("utf-8")
            raise Exception(err)

        str_output = output.decode("utf-8")
        list_output = re_split("\r\n", str_output)

        return sorted([item for item in list_output if item])

    def climb(self, item):
        """
            Builds & returns a complete path to root directory,
            including the item name itself as the path tail.
            An extra delimiter is appeneded for the subsequent
            child node, which is normalized in dialog_populate()
        """

        item_text = self.treeview.item(item)["text"]
        parent = self.treeview.parent(item)
        path = ""
        parents = deque()

        while parent:
            parents.append(self.treeview.item(parent)["text"] + "/")
            parent = self.treeview.parent(parent)

        for parent in reversed(parents):
            path += parent

        path += item_text + "/"
        return path

    def dialog_populate(self, event=None):
        """
            Dynamically populates & updates the treeview, listbox,
            and keeps track of the full paths corresponding to each
            item in the listbox
        """
        if not self.treeview.focus():
            return

        self.treeview.column("#0", width=1000)

        existing_children = self.treeview.get_children(self.treeview.focus())
        [self.treeview.delete(child) for child in existing_children]

        self.list_box.delete(0, "end")
        self.selection_paths.clear()

        focus_item = self.treeview.focus()
        path = self.climb(focus_item)

        if self.show_hidden:
            children = self.list_dir(path, force=True)
        else:
            children = self.list_dir(path)

        for child in children:
            if isdir(path + child):

                self.treeview.insert(focus_item,
                                     index="end",
                                     text=child,
                                     image=self.folder_icon)

                if self.select_dirs:
                    self.list_box.insert("end", child)
                    self.selection_paths.append(path + child)

            elif isfile(path + child):

                if self.include_files:
                    self.treeview.insert(focus_item,
                                         index="end",
                                         text=child,
                                         image=self.file_icon)

                if self.select_files:
                    self.list_box.insert("end", child)
                    self.list_box.itemconfig("end", {"bg": "#EAEAEA"})
                    self.selection_paths.append(path + child)

        if isfile(normpath(path)):
            (head, tail) = path_split(normpath(path))
            head = sub("\\\\", "/", head)

            self.list_box.insert("end", tail)
            self.selection_paths.append(head + "/" + tail)
            self.list_box.itemconfig("end", {"bg": "#EAEAEA"})

    def list_box_select(self, event=None):
        """
            Dynamically refresh the dialog selection with
            what's selected in the listbox
            (Callback for <<ListboxSelect>>).
        """

        self.dialog_selection.clear()

        for i in self.list_box.curselection():
            self.dialog_selection.append(self.selection_paths[i])

    def treeview_select(self, event=None):
        """
            Dynamically refresh the dialog selection with
            what's selected in the treeview
            (Callback for <<TreeviewSelect>>).
        """

        for i in self.list_box.curselection():
            self.list_box.selection_clear(i)

        self.dialog_selection.clear()

        item = normpath(self.climb(self.treeview.focus()))
        self.dialog_selection.append(item)

    def submit(self, event=None):
        """
            Satisfies wait_window() in self.__call__() and validates selection

            (Callback for <Return>, <Button-1> on file_list, submit_button)
        """

        if self.select_dirs == False:
            for item in self.dialog_selection:
                if isdir(item):
                    messagebox.showwarning(
                        "Error - Invalid Selection",
                        "Unable to select directory. Please select a file(s).")
                    return

        if self.select_files == False:
            for item in self.dialog_selection:
                if isfile(item):
                    messagebox.showwarning(
                        "Error - Invalid Selection",
                        "Unable to select file. Please select a folder(s)")
                    return

        self.dialog.destroy()

    def cancel(self, event=None):
        """
            Satisfies wait_window() in self.__call__() 

            (Callback for <Button-1> on cancel_button)
            (Callback for protocol "WM_DELETE_WINDOW" on self.dialog)
        """

        self.dialog_selection.clear()
        self.dialog.destroy()
Example #28
0
class show:
    def __init__(self):
        self.window=Tk()
        self.window.title("Show | Sales Products")
        self.window.iconbitmap('shop.ico')
        self.window.geometry("1365x670+0+0")
        self.window.maxsize(1365,670)
        self.window.minsize(1365,670)
        # self.window.resizable(width=False,height=False)
    def add_background(self):
        self.image = Image.open("images/back.jpg")
        self.image = self.image.resize((1365, 700), Image.ANTIALIAS)
        self.img = ImageTk.PhotoImage(self.image)
        self.li = Label(image = self.img, text="TALHA")
        self.li.image = self.img
        self.li.pack()
        self.Menubar()

    def Menubar(self):
        self.menubar = Menu()
        self.menuitems_add = Menu(self.menubar, tearoff=0,bg="blue",fg="white")
        self.menuitems_add.add_command(label="Add Products", command=self.add_products)
        # self.menuitems.add_separator()

        self.menuitems_sales = Menu(self.menubar, tearoff=0,bg="blue",fg="white")
        self.menuitems_sales.add_command(label="Sale Products", command=self.sales_products)

        self.menuitems_customer = Menu(self.menubar, tearoff=0, bg="blue", fg="white")
        self.menuitems_customer.add_command(label="Add Customer", command=self.add_customer)
        self.menuitems_customer.add_command(label="Show Customer Records", command=self.customer_record)

        self.menuitems_reports = Menu(self.menubar, tearoff=0, bg="blue", fg="white")
        self.menuitems_reports.add_command(label="Check Products Quantity", command=self.check_products_quantity)
        self.menuitems_reports.add_command(label="Check Profit/Loss", command=self.check_profit_loss)
        self.menuitems_reports.add_command(label="Check Sales Products", command=self.check_products_sales)
        self.menuitems_reports.add_command(label="Check Sent Amount Details", command=self.check_amount)

        self.menubar.add_cascade(label="Add Products", menu=self.menuitems_add)
        self.menubar.add_cascade(label="Sales Products", menu=self.menuitems_sales)
        self.menubar.add_cascade(label="Customer", menu=self.menuitems_customer)
        self.menubar.add_cascade(label="Reports", menu=self.menuitems_reports)
        self.menubar.add_cascade(label="Exit", command=quit)
        self.window.config(menu=self.menubar)

    def add_table(self):
        self.l1=Label(self.window,text="**Sales Details**",font="Courier 25 bold",bg="black",fg="white",width=61)
        self.l1.place(x=82,y=20)

        self.l1 = Label(self.window, text="Developed by : Muhammad Talha | NTU", font="Courier 10 bold")
        self.l1.place(x=470, y=610)

        self.l1 = Label(self.window, text="Enter Product Name:", font="courier 16 bold",bg="blue",fg="white")
        self.l1.place(x=280, y=90)

        self.Entry_reg = Entry(self.window, font="courior 14 bold")
        self.Entry_reg.place(x=550, y=90, height=30)

        self.b1 = Button(self.window, text="Search", font="Times 13 bold",fg="white",bg="blue",width=10)
        self.b1.place(x=790, y=90)
        self.b1.bind('<Button-1>', self.search_product)


        # for Style Table
        style = Style()
        style.configure("mystyle.Treeview", highlightthickness=0, bd=0,
                        font=('courier 12 bold'),rowheight=43)  # Modify the font of the body
        style.configure("mystyle.Treeview.Heading", font=('Times 15 bold'),foreground="black")  # Modify the font of the headings
        # style.layout("mystyle.Treeview", [('mystyle.Treeview.treearea', {'sticky': 'nswe'})])  # Remove the borders
        # import TreeViewe
        self.tr=Treeview(self.window,columns=('A','B','C','D','E'),selectmode="extended",style='mystyle.Treeview')
        # heading key+text
        self.tr.heading("#0", text="Sr.No")
        self.tr.column("#0", minwidth=0, width=85, stretch=NO)

        self.tr.heading("#1",text="Product Name")
        self.tr.column("#1",minwidth=0,width=160,stretch=NO)

        self.tr.heading("#2", text="Product Catagory")
        self.tr.column("#2", minwidth=0, width=200, stretch=NO)

        self.tr.heading("#3", text="Quantity")
        self.tr.column("#3", minwidth=0, width=175, stretch=NO)

        self.tr.heading("#4", text="Price Per Product")
        self.tr.column("#4", minwidth=0, width=175, stretch=NO)

        self.tr.heading("#5", text="Sales Price")
        self.tr.column("#5", minwidth=0, width=160, stretch=NO)

        j=1
        for i in Database.show_sales_details():
            self.tr.insert('',index=j,text=j,values=(i[1],i[2],i[3],i[4],i[5]))
            j+=1

        self.Entry_reg.bind('<KeyRelease>', self.remove)

        self.sb = Scrollbar(self.tr)
        self.sb.place(x=935,y=2,height=452,width=22,bordermode=OUTSIDE)
        self.sb.config(command=self.tr.yview)
        self.tr.config(yscrollcommand=self.sb.set)
        self.tr.place(x=170,y=150)

        # to excel file
        self.b1 = Button(self.window, text="Save to Excel", font="Times 12 bold",bg="blue",fg="white",width=20,
        )
        self.b1.place(x=938, y=610)
        self.b1.bind('<Button-1>', self.excel)


        self.window.mainloop()


    def remove(self,event):
        for row in self.tr.get_children():
            self.tr.delete(row)
        j = 1
        for i in Database.show_sales_details():
            self.tr.insert('', index=j, text=j, values=(i[1], i[2], i[3], i[4], i[5]))
            j += 1

    def search_product(self,event):
        try:
            data = (self.Entry_reg.get(),)
            if self.Entry_reg.get() == "":
                msg.showwarning("Message ! ", "please insert product name !")
            else:
                j = 1
                D = Database.search_details_sales_table(data)
                if D:
                    for row in self.tr.get_children():
                        self.tr.delete(row)
                    self.Entry_reg.delete(0, END)
                    for i in D:
                        self.tr.insert('', index=j, text=j, values=(i[1],i[2],i[3],i[4],i[5]))
                        j += 1
                else:
                    msg.showwarning("Message!", "No result Found!")

        except:
            msg.showwarning("Error!", "Something went wrong! please contact with developer !!!")
    def excel(self,event):
        try:
            x=Database.show_sales_details()
            p_name=[]
            p_catagory=[]
            p_quantity=[]
            price=[]
            p_sales_price=[]
            for i in range(len(x)):
                z=x.__getitem__(i)
                p_name.append(z[1])
                p_catagory.append(z[2])
                p_quantity.append(z[3])
                price.append(z[4])
                p_sales_price.append(z[5])

            products = pd.DataFrame({
                "Product Name": p_name,
                "Product Catagory":p_catagory,
                "Quantity":p_quantity,
                "Price Per Product":price,
                "Sales Price":p_sales_price
            })

            with pd.ExcelWriter("Excel Files/Sales_Products_Records.xlsx") as writer:
                products.to_excel(writer, sheet_name="Sales_Products", index=False)
            msg.showinfo("Message ! ", "Your data has been inserted Susccessfully !")
        except:
            msg.showwarning("Message!","Something Went Wrong!please contact with developer!!!")

    def add_products(self):
        self.window.destroy()
        home = Home.data()
        home.add_background()
        home.add_frame()

    def sales_products(self):
        self.window.destroy()
        showP = Showing_Records.show()
        showP.add_background()
        showP.add_table()
    def check_products_quantity(self):
        self.window.destroy()
        showP = Check_Products_Quantity.show()
        showP.add_background()
        showP.add_table()
    def check_products_sales(self):
        pass

    def check_amount(self):
        self.window.destroy()
        showP = Check_Amount_Details.show()
        showP.add_background()
        showP.add_table()
    def check_profit_loss(self):
        self.window.destroy()
        showP = Check_Profit_Loss_GUI.show()
        showP.add_background()
        showP.add_table()
    def add_customer(self):
        self.window.destroy()
        home = Add_Customer.data()
        home.add_background()
        home.add_frame()
    def customer_record(self):
        self.window.destroy()
        showP = Customer_Records.show()
        showP.add_background()
        showP.add_table()
Example #29
0
class ViewStudentList(Frame):
    def __init__(self, parent, controller):
        Frame.__init__(self, parent)
        self.controller = controller

        self.student_tree = Treeview(self,
                                     columns=('#1', '#2', '#3', '#4', '#5',
                                              '#6'))

        self.student_tree.heading('#1', text='Student Name')
        self.student_tree.heading('#2', text='Subject Name')
        self.student_tree.heading('#3', text='IA1 Marks')
        self.student_tree.heading('#4', text='IA2 Marks')
        self.student_tree.heading('#5', text='IA3 Marks')
        self.student_tree.heading('#6', text='External Marks')

        self.student_tree.column('#1', stretch=YES, width=95)
        self.student_tree.column('#2', stretch=YES, width=205)
        self.student_tree.column('#3', stretch=YES, width=70)
        self.student_tree.column('#4', stretch=YES, width=70)
        self.student_tree.column('#5', stretch=YES, width=70)
        self.student_tree.column('#6', stretch=YES, width=90)

        self.student_tree.grid(row=2,
                               column=0,
                               columnspan=10,
                               padx=10,
                               pady=10,
                               sticky='nsew')
        self.student_tree['show'] = 'headings'

        student_usn = get_student_usns()

        option = []

        for name in student_usn:
            option.append(name)

        options = list(set(option))  #to obtain only unique events
        self.variable = StringVar(self)
        self.variable.set(options[0])
        self.variable.trace("w", self.update_table)

        self.select = OptionMenu(self,
                                 self.variable,
                                 *options,
                                 options[0],
                                 command=self.get_value).grid(row=1,
                                                              column=2,
                                                              padx=10,
                                                              pady=10)

        Label(self, text=" Student USN", font=10).grid(row=1,
                                                       column=0,
                                                       padx=10,
                                                       pady=10,
                                                       sticky="W")

        back_btn = Button(self,
                          text="Back",
                          command=lambda: controller.show_frame(Main),
                          width=30,
                          fg="#000fc0")
        back_btn.grid(row=3, column=0, padx=20, pady=20, sticky="W")

        heading_label = Label(self, text="View Student Details", font=MED_FONT)
        heading_label.grid(row=0, column=0, padx=5, pady=5, sticky="W")

    def update_table(self, *args):
        self.student_tree.delete(*self.student_tree.get_children())
        student_usn = self.variable.get()

        student_list = view_students([
            student_usn,
        ])

        for students in student_list:
            self.student_tree.insert("",
                                     'end',
                                     values=[
                                         students.student_name,
                                         students.subject_name,
                                         students.internal_assessment_1_marks,
                                         students.internal_assessment_2_marks,
                                         students.internal_assessment_3_marks,
                                         students.external_marks
                                     ])

    def get_value(self, value):
        self.id = value
    def setUI(self):
        self.photo = ImageTk.PhotoImage(Image.open('淘宝&天猫.jpg').resize((320,320),Image.ANTIALIAS))
        self.photoLabel = Label( self, image=self.photo, width=320, height=320 )
        # start_button = Button( self, text=u"开始", width=4, height=6, command=self.start_crawl )
        # stop_button = Button( self, text=u"停止", width=4, height=6, command=self.stop_crawl )
        # setting_button = Button( self, text=u"设置", width=4, height=6, command=self.setup_config )
        # directory_button = Button( self, text=u"目录", width=4, height=6, command=self.open_finder )

        start_button = Button(self, text=u"开始", width=7, height=2, command=self.start_crawl,state=ACTIVE)
        stop_button = Button(self, text=u"暂停", width=7, height=2, command=self.stop_crawl,state=ACTIVE)
        setting_button = Button(self, text=u"设置", width=7, height=2, command=self.setup_config,state=ACTIVE)
        directory_button = Button(self, text=u"查询", width=7, height=2,command=self.robo3t,state=ACTIVE)

        returnImg = Image.open('返回.jpg').resize((30, 30), Image.ANTIALIAS)
        self.returnPhoto = ImageTk.PhotoImage(returnImg)

        return_button = Button(self,width=30,height=30,command=self.returnPrimary,image=self.returnPhoto,text = u'返回',state=ACTIVE)
        return_button.place(x=2,y=2)



        # 无需设置修改
        if self.startFlag == 1:
            time.sleep( 1 )
            print( "******" )
            self.downloadSpeed = random.randint( SpeedLimited - 100, SpeedLimited )

        self.downloadingSpeed = Label( self, text=u"下载速度:" + str( self.downloadSpeed ) + 'kb / s')

        # 需要用设置修改
        self.threadingNum = Label( self, text=u"线程数:" + str( self.ThreadNum ) )
        self.delayTime = Label( self, text=u"下载延时:" + str( self.DelayTime ) )
        self.crawlTimeLabel = Label( self, text=u"爬取时间:" + str( self.CrawlTime ) )

        # self.photoLabel.grid( row=0, rowspan=4, pady=20, padx=20 )
        # start_button.grid( row=0, column=1, padx=60 )
        # setting_button.grid( row=2, column=1, padx=60 )
        # stop_button.grid( row=1, column=1, padx=60 )
        # directory_button.grid( row=3, column=1, padx=60 )
        #
        # self.downloadingSpeed.grid( row=0, column=2, padx=60, sticky=W )
        # self.threadingNum.grid( row=1, column=2, padx=60, sticky=W )
        # self.delayTime.grid( row=2, column=2, padx=60, sticky=W )
        # self.crawlTimeLabel.grid( row=3, column=2, padx=60, sticky=W )

        self.photoLabel.place(x=20, y=20)
        start_button.place(x=420, y=65)
        setting_button.place(x=540, y=65)
        stop_button.place(x=420, y=180)
        directory_button.place(x=540, y=180)

        self.downloadingSpeed.place(x=680, y=65)
        self.threadingNum.place(x=680, y=120)
        self.delayTime.place(x=680, y=175)
        self.crawlTimeLabel.place(x=680, y=230)



        bottomFrame = Frame( self, width=80 )
        # bottomFrame.grid( row=5, column=0, columnspan=3, padx=24 )

        bottomFrame.place(x=35, y=390)


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

        # 进度条位置
        self.ProgressBar = Progressbar( self, orient="horizontal", length=320, mode='determinate' )

        # self.ProgressBar.grid( row=4, column=2, columnspan=2, sticky=W + E)
        self.ProgressBar.place(x=400, y=300)

        tree = Treeview( bottomFrame, column=('c1', 'c2', 'c3', 'c4', 'c5', 'c6'), show="headings",
                         yscrollcommand=scrollBar.set )
        tree.column( 'c1', width=220, anchor='center' )
        tree.column( 'c2', width=80, anchor='center' )
        tree.column( 'c3', width=80, anchor='center' )
        tree.column( 'c4', width=80, anchor='center' )
        tree.column( 'c5', width=120, anchor='center' )
        tree.column( 'c6', width=120, anchor='center' )
        # 设置每列表头标题文本
        tree.heading( 'c1', text='标题' )
        tree.heading( 'c2', text='日期' )
        tree.heading( 'c3', text='作者' )
        tree.heading( 'c4', text='浏览量' )
        tree.heading( 'c5', text='关键词' )
        tree.heading( 'c6', text='链接' )

        tree.pack( side=LEFT, fill="both" )

        scrollBar.config( command=tree.yview )

        items = tree.get_children()
        for item in items:
            tree.delete( item )
class Window:
    def fillTree(self,path, parent, list):
        for file in os.listdir(path):
            abspath = os.path.join(path,file)
            color = ""
            treelist = None
            for mini in list:
                if abspath in mini:
                    color = 'red'
                    treelist = mini
                else:
                    for lk in mini:
                        if abspath in lk:
                            color = 'purple'
            child = None
            if color == 'red':
                child = self.tree.insert(parent,'end',text=file,open=False,tags=(abspath,'red',str(treelist)),)
            elif color == 'purple':
                child = self.tree.insert(parent,'end',text=file,open=False,tags=(abspath,'purple'))
            else:
                child = self.tree.insert(parent,'end',text=file,open=False,tags=(abspath,'white'))
            if(os.path.isdir(abspath)):
                self.tree.insert(child,'end',text='',open=False)
    def __init__(self,list,dirlist):
        self.root = Tk()
        self.root.wm_title("Duplicate_Files")
        self.min = None
        self.list = list
        self.root.geometry('600x600+0+0')
        self.tree = Treeview(self.root ,height=15)
        self.tree.pack(expand='yes',fill='both')
        self.tree.heading('#0',text="files")
        self.tree.tag_configure('red',foreground='red')
        self.tree.tag_configure('purple',foreground='#cc00ff')
        self.tree.bind("<Double-1>",self.onDoubleClick)
        self.tree.bind("<<TreeviewOpen>>",self.onOpen)
        self.tree.bind("<<TreeviewClose>>",self.onClose)
        for path in dirlist:
            branch = self.tree.insert('','end',text=path,open=True,tags=(path,'white'))
            self.fillTree(path,branch,list)
        self.root.mainloop()


    def onDoubleClick(self,event):
        item = self.tree.selection()[0]
        print ("clicked" + str(self.tree.item(item,'tags')[0]))
        if str(self.tree.item(item,'tags')[1]) == "red":
            list_of_files = ast.literal_eval(str(self.tree.item(item,'tags')[2]))
            if self.min != None:
                if self.min.mini.winfo_exists():
                    self.min.mini.destroy()
            self.min = MiniWindow(self.root,list_of_files)

    def onOpen(self,event):
        item = self.tree.selection()[0]
        if self.tree.parent(item) != '':
            if len(self.tree.get_children(item))>0:
                self.tree.delete(self.tree.get_children(item))
            abspath = str(self.tree.item(item,'tags')[0])
            if(os.path.isdir(abspath)):
                self.fillTree(abspath, item,self.list)
    def onClose(self,event):
        item = self.tree.selection()[0]
        if self.tree.parent(item) != '':
            if len(self.tree.get_children(item))>0:
                self.tree.delete(self.tree.get_children(item))
Example #32
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
Example #33
0
class Gr():
    def __init__(self,root,data,SCRY=None):
        self.data=data
        self.columns=[x for x in range(1,8)]+['day']
        root.rowconfigure(1,weight=1)
        root.columnconfigure(0,weight=1)
        root.columnconfigure(1,weight=1)
        root.columnconfigure(2,weight=1)
        f=Frame(root)
        f.columnconfigure(0,weight=1)
        f.rowconfigure(1,weight=1)
        self.v=Combobox(root)
        self.v.grid(row=0,column=0)
        self.v.bind('<<ComboboxSelected>>',self.select_ver)
        f.grid(row=1,column=0,columnspan=3,sticky=N+S)
        self.tree=Treeview(f,
                columns=self.columns,
                displaycolumns=['day']+self.columns[:-1],
                show='headings')
        #self.tree.tag_configure('odd',background='white')
        #self.tree.tag_configure('even',background='gray')
        self.tree.tag_configure('dif',foreground='brown')
        self.tree.tag_configure('work',background='white')
        self.tree.tag_configure('short',background='#F5EFE0')
        self.tree.tag_configure('rest',background='#E0B0B0')
        self.tree.tag_configure('holyday',background='#E7B7A4')
        for c in self.columns:
            self.tree.heading(c,text=c)
            self.tree.column(c,width=65,anchor='center')
        self.tree.column('day',width=30)
        scrX=Scrollbar(f,orient='horizontal',command=self.tree.xview)
        self.tree['xscrollcommand']=scrX.set
        if not SCRY:
            self.scrY=Scrollbar(f,orient='vertical',command=self.yview)
            self.tree['yscrollcommand']=self.scrY.set
        else:
            self.tree['yscrollcommand']=SCRY.set
        self.tree.grid(row=1,column=0,sticky=N+S)
        if not SCRY:
            self.scrY.grid(row=1,column=1,sticky=N+S)
        scrX.grid(row=2,column=0,sticky=E+W)
    def set(self,y,m):
        self.y=y
        self.m=m
        self.show()
    def yview(self,*args):
        self.tree.yview(*args)
        self.yview2(*args)
    def yview2(self,*args):
        pass
    def show(self):
        d=self.data[self.y][self.m]
        V=list(d['degur'].keys())
        self.v['values']=V
        self.v.set(V[0])
        self.select_ver()
    def select_ver(self,*e):
        self.tree.delete(*self.tree.get_children())
        d=self.data[self.y][self.m]
        offset=d['offset']
        v=self.v.get()
        col=[]
        for i,deg in enumerate(d['degurs']):
            self.tree.heading(i+1,text=deg)
            col.append(i+1)
        self.tree.configure(displaycolumns=['day']+col)
        items=dict()

        if 'табель' in d['degur']:
            a=[''.join(x) for x in zip(*[[x for x in d['degur']['план'][j]] \
                    for j in d['degurs']])]
            b=[''.join(x) for x in zip(*[[x for x in d['degur']['табель'][j]] \
                    for j in d['degurs']])]
            c=[x!=y for x,y  in zip(a,b)]
        else:
            c=[False]*32

        for i in range(1,d['days']+1):
            tag = (i+offset) % 7 in [0,6] and 'rest' or 'work'
            if i in d['holydays'] : tag='holyday'
            elif i in d['restdays'] : tag='rest'
            elif i in d['shortdays'] : tag='short'
            elif i in d['workdays'] : tag='work'
            if c[i]: tag=[tag,'dif']
            ii=self.tree.insert('','end',values=['-','-','-','-','-'],tag=tag)
            self.tree.set(ii,column='day',value=i)
            items[i]=ii

        
        for j,s in d['degur'][v].items(): # j-degur
            if not s: continue
            for i,val in enumerate(s[1:-1]):
                if val=='J':
                    val='до'
                elif val=='j':
                    val='од'
                elif val=='a':
                    val='10'
                self.tree.set(items[i+1],column=d['degurs'].index(j)+1,value=val)
            if s[0]=='Н':
                if s[1]=='-':
                    self.tree.set(items[1],column=d['degurs'].index(j)+1,value='Н(8)')
                else:
                    self.tree.set(items[1],column=d['degurs'].index(j)+1,value='!')
            if s[-2]=='Н':
                if s[-1]=='-':
                    self.tree.set(items[len(s)-2],column=d['degurs'].index(j)+1,value='Н(4)')
                else:
                    self.tree.set(items[len(s)-2],column=d['degurs'].index(j)+1,value='!')
        self.calc(self.y,self.m)
    def calc(self,y,m):
        d=self.data[y][m]
        offset=d['offset']
        WH=0
        for i in range(1,d['days']+1):
            if i in d['holydays']: wh=0
            elif i in d['restdays'] : wh=0
            elif i in d['shortdays'] : wh=7
            elif i in d['workdays'] : wh=8
            elif (i+offset) % 7 in [0,6]: wh=0
            else: wh=8
            WH+=wh
Example #34
0
class Program(Frame):
    """Class to represent a main window"""
    def __init__(self, parent):
        # Some top-level settings.
        Frame.__init__(self, parent)
        self.content = Frame(self, padding=(5, 5, 5, 20))
        self.parent = parent
        self.parent.title('Restaurant manager')
        self.parent.minsize(DEFAULT_MINSIZE_WIDTH, DEFAULT_MINSIZE_HEIGHT)
        self.parent.protocol('WM_DELETE_WINDOW', self.onQuit)

        # Some variables.
        self.track_weight = IntVar()
        self.custom_entries = []
        self.jsonReader = JsonReader(json_path)
        self.frames = []
        self.entries = {}
        self.dishes = self.jsonReader.getDishesDict()
        self.dishes_names = self.jsonReader.getDishesNames()
        self.parent.iconphoto(
            True, PhotoImage(file=os.path.join(sys.path[0], logo_path)))

        # Initialize all widgets.
        self.initWidgets()

    def initWidgets(self):
        """
        Initialize all widgets in window here.
        Entries are saved in [self.entries] list.
        ----------------------------------------------------------
        5 frames are initialized and saved into [self.frames] list here:
        1) [About frame] - Frame to contain all non-food entries.
        2) [Separator frame] - Frame to contain a simple separator.
        3) [New order frame] - Frame to contain entries for adding orders to the main table.
        4) [Orders table frame] - Frame to contain table with list of added orders.
        5) [Save report frame] - Frame to contain buttons, which save a report.
        ----------------------------------------------------------
        """
        # Create 5 frames here.
        for i in range(5):
            self.frames.append(Frame(self.content))
        # Set the weights of cols and rows in the grid.
        self.configureGrid()
        # Center a window.
        self.centerWindow()

        # Place 4 frames to the window.
        self.frames[0].grid(column=0, row=0, sticky=N + E + W, padx=5, pady=3)
        self.frames[1].grid(column=0,
                            row=1,
                            sticky=N + S + E + W,
                            padx=5,
                            pady=3)
        self.frames[2].grid(column=0,
                            row=2,
                            sticky=N + S + E + W,
                            padx=5,
                            pady=3)
        self.frames[3].grid(column=0,
                            row=3,
                            sticky=N + S + E + W,
                            padx=5,
                            pady=10)
        self.frames[4].grid(column=0, row=4, sticky=S + E + W, padx=5, pady=3)

        # About frame widgets.
        Label(self.frames[0], text='Заказчик').grid(row=0,
                                                    column=0,
                                                    sticky=E,
                                                    pady=5)
        Label(self.frames[0], text='Менеджер').grid(row=0,
                                                    column=2,
                                                    sticky=E,
                                                    pady=5)
        Label(self.frames[0],
              text='Вид мероприятия',
              justify=RIGHT,
              wraplength=90).grid(row=0, column=4, sticky=E, pady=5)
        Label(self.frames[0], text='Дата').grid(row=1,
                                                column=0,
                                                sticky=E,
                                                pady=5)
        Label(self.frames[0], text='Время').grid(row=1,
                                                 column=2,
                                                 sticky=E,
                                                 pady=5)
        Label(self.frames[0],
              text='Место проведения',
              justify=RIGHT,
              wraplength=90).grid(row=1, column=4, sticky=E, pady=5)
        Label(self.frames[0],
              text='Количество персон',
              justify=RIGHT,
              wraplength=90).grid(row=1, column=6, sticky=E, pady=5)

        self.entries['client'] = Entry(self.frames[0])
        self.entries['manager'] = Entry(self.frames[0])
        self.entries['type'] = Entry(self.frames[0], width=10)
        self.entries['date'] = Entry(self.frames[0])
        self.entries['time'] = Entry(self.frames[0])
        self.entries['location'] = Entry(self.frames[0], width=10)
        self.entries['persons'] = Entry(self.frames[0], width=10)

        self.entries['client'].focus_set()
        today = date.today().isoformat().split('-')[::-1]
        self.entries['date'].insert(0, '.'.join(today))

        self.entries['client'].grid(row=0,
                                    column=1,
                                    sticky=E + W,
                                    padx=(3, 13),
                                    pady=5)
        self.entries['manager'].grid(row=0,
                                     column=3,
                                     sticky=E + W,
                                     padx=(3, 13),
                                     pady=5)
        self.entries['type'].grid(row=0,
                                  column=5,
                                  columnspan=3,
                                  sticky=E + W,
                                  padx=(3, 13),
                                  pady=5)
        self.entries['date'].grid(row=1,
                                  column=1,
                                  sticky=E + W,
                                  padx=(3, 13),
                                  pady=5)
        self.entries['time'].grid(row=1,
                                  column=3,
                                  sticky=E + W,
                                  padx=(3, 13),
                                  pady=5)
        self.entries['location'].grid(row=1,
                                      column=5,
                                      sticky=E + W,
                                      padx=(3, 13),
                                      pady=5)
        self.entries['persons'].grid(row=1,
                                     column=7,
                                     sticky=E + W,
                                     padx=(3, 13),
                                     pady=5)

        # Add a separator between [about] and [new order] frames
        sep1 = Frame(self.frames[1], height=2, borderwidth=1, relief='sunken')
        sep1.pack(fill=X, padx=1, pady=10)

        # New Order frame widgets.
        Label(self.frames[2], text='Название', anchor=E).grid(row=0,
                                                              column=0,
                                                              sticky=E)
        Label(self.frames[2], text='Комментарий', anchor=E).grid(row=1,
                                                                 column=0,
                                                                 sticky=E)
        Label(self.frames[2], text='Количество', anchor=E).grid(row=2,
                                                                column=0,
                                                                sticky=E)
        self.sum_lbl = Label(self.frames[2],
                             text='Текущая сумма заказа:\n 0 грн',
                             anchor=E,
                             justify=RIGHT)

        self.entries['name'] = AutocompleteEntry(self.frames[2])
        self.entries['comment'] = Entry(self.frames[2])
        self.entries['amount'] = Entry(self.frames[2])
        addOrder_btn = Button(self.frames[2], text='Добавить блюдо в отчет')

        self.entries['name'].set_completion_list(self.dishes_names)
        self.entries['amount'].insert(0, '1')
        addOrder_btn['command'] = lambda: self.addDish()

        self.entries['name'].grid(row=0,
                                  column=1,
                                  columnspan=5,
                                  sticky=W + E,
                                  pady=3,
                                  padx=(3, 15))
        self.entries['comment'].grid(row=1,
                                     column=1,
                                     columnspan=5,
                                     sticky=W + E,
                                     pady=3,
                                     padx=(3, 15))
        self.entries['amount'].grid(row=2,
                                    column=1,
                                    columnspan=2,
                                    sticky=W + E,
                                    pady=3,
                                    padx=(3, 15))
        addOrder_btn.grid(row=3, column=1, sticky=W, pady=3, padx=3)
        self.sum_lbl.grid(row=4, column=5, pady=0, padx=(190, 0))

        customEntry_btn = Button(self.frames[2],
                                 text='Добавить собственную строку в отчет')
        customEntry_btn['command'] = lambda: self.addCustomEntry()
        customEntry_btn.grid(row=4, column=1, sticky=W, pady=3, padx=3)

        # Orders Table frame widgets.
        self.orders_view = Treeview(self.frames[3])
        self.orders_view['columns'] = ('Weight', 'Amount', 'Comment', 'Price',
                                       'Sum')
        self.orders_view.bind('<Delete>',
                              lambda e: self.deleteEntry(e, self.orders_view))

        self.orders_view.heading('#0', text='Название')
        self.orders_view.column('#0', anchor='w', minwidth=307, width=307)
        self.orders_view.heading('Weight', text='Выход')
        self.orders_view.column('Weight',
                                anchor=CENTER,
                                minwidth=100,
                                width=100,
                                stretch=NO)
        self.orders_view.heading('Amount', text='Количество')
        self.orders_view.column('Amount',
                                anchor=CENTER,
                                minwidth=100,
                                width=100,
                                stretch=NO)
        self.orders_view.heading('Comment', text='Комментарий')
        self.orders_view.column('Comment', anchor='w', minwidth=130, width=130)
        self.orders_view.heading('Price', text='Цена, грн')
        self.orders_view.column('Price',
                                anchor=CENTER,
                                minwidth=110,
                                width=110,
                                stretch=NO)
        self.orders_view.heading('Sum', text='Сумма, грн')
        self.orders_view.column('Sum',
                                anchor=CENTER,
                                minwidth=108,
                                width=108,
                                stretch=NO)

        self.orders_view.grid(row=0,
                              column=0,
                              sticky=N + S + E + W,
                              padx=3,
                              pady=3)

        # NOTE: next [for] block is for testing purposes only.
        """
        for dish in self.dishes:
            self.orders_view.insert('', 'end',
                text=dish,
                values=[
                    self.dishes[dish]['weight'],
                    5,  # [amount] column
                    'wats up?',  # [comment] column
                    self.dishes[dish]['price'],
                    5 * float(self.dishes[dish]['price'])
                ]
            )
        """

        orders_scrlbar = Scrollbar(self.frames[3],
                                   orient=VERTICAL,
                                   command=self.orders_view.yview)
        self.orders_view['yscrollcommand'] = orders_scrlbar.set
        orders_scrlbar.grid(row=0, column=1, sticky=N + S)

        # Save Report frame widgets.
        saveWeb_btn = Button(self.frames[4], text='Сохранить отчет', width=20)
        trackweight_chkbox = Checkbutton(self.frames[4],
                                         text='Учитывать средний вес',
                                         variable=self.track_weight)

        saveWeb_btn['command'] = lambda: self.saveWeb()

        saveWeb_btn.pack(side='left', anchor=CENTER, padx=5, pady=3)
        trackweight_chkbox.pack(side='left', anchor=CENTER, pady=3, padx=3)

    def configureGrid(self):
        """Configure weights of grids columns and rows"""
        # Top-level configuration.
        self.grid(sticky=N + S + E + W)
        top = self.winfo_toplevel()
        top.rowconfigure(0, weight=1)
        top.columnconfigure(0, weight=1)
        self.rowconfigure(0, weight=1)
        self.columnconfigure(0, weight=1)

        # Configuration of main content frame.
        self.configureFrame(self.content, [1], [0, 0, 0, 3, 0])
        # Configuration of about frame.
        self.configureFrame(self.frames[0], [0, 1, 0, 1, 0, 0, 0, 0],
                            [0, 0, 0])
        # Configuration of new order frame.
        self.configureFrame(self.frames[2], [0, 1, 1, 1, 1, 1], [0, 0, 0, 0])
        # Configuration of orders table frame.
        self.configureFrame(self.frames[3], [1, 0], [1])

    def centerWindow(self):
        """Place the main window in the center of screen"""
        window_w = DEFAULT_MINSIZE_WIDTH
        window_h = DEFAULT_MINSIZE_HEIGHT
        screen_w = self.winfo_screenwidth()
        screen_h = self.winfo_screenheight()
        x = (screen_w - window_w) / 2
        y = (screen_h - window_h) / 2
        self.parent.geometry('%dx%d+%d+%d' % (window_w, window_h, x, y))

    def onQuit(self):
        """Before the program exits, ask user, if he really wants it"""
        if messagebox.askyesno('Выход из программы',
                               'Вы действительно хотите выйти из программы?'):
            self.quit()

    @staticmethod
    def deleteEntry(e, tree):
        """Event to handle deletion in TreeView woth [Delete] button"""
        selected_item = tree.selection()[0]  # get selected item
        tree.delete(selected_item)

    @staticmethod
    def configureFrame(frame, columns_wght, rows_wght):
        """Function to organize frame configuration routine"""
        frame.grid(column=0, row=0, sticky=N + S + E + W)
        for i, weight in enumerate(columns_wght):
            frame.columnconfigure(i, weight=weight)
        for i, weight in enumerate(rows_wght):
            frame.rowconfigure(i, weight=weight)

    def saveWeb(self):
        """Save data from the TreeView to the html report"""
        # Validation: if there is no items in tree_view
        if not self.orders_view.get_children():
            messagebox.showerror('Ошибка', 'Ошибка создания отчета: нет блюд.')
            return

        # Open file dialog to choose saving path.
        file = filedialog.asksaveasfile(mode='w',
                                        defaultextension='.html',
                                        filetypes=[('Веб страница', '.html'),
                                                   ('Все файлы', '.*')])
        # If user pressed [Cancel] or closed a file dialog.
        if file is None:
            return

        # Pack files to send to HTML saver module.
        # Dictionary looks like: { name:{weight,amount,comment,price,total}, name:... }
        packed_dishes = {
            'global': {
                'trackw': self.track_weight.get(),  # flag - is weight tracked
                'totalsum':
                self.sum_lbl['text'][21:-4],  # str - total sum of dishes
                'centries': self.custom_entries  # [5*str] - custom entries
            },
            'about': {
                'client': self.entries['client'].get(),
                'manager': self.entries['manager'].get(),
                'type': self.entries['type'].get(),
                'date': self.entries['date'].get(),
                'time': self.entries['time'].get(),
                'location': self.entries['location'].get(),
                'persons': self.entries['persons'].get()
            },
            'dishes': {}
        }
        for child in self.orders_view.get_children():
            child_content = self.orders_view.item(child)
            child_values = child_content['values']
            child_name = child_content['text']
            child_type = self.dishes[child_name]['type']
            # Pack data about a certain dish.
            packed_child = {
                child_name: {
                    'weight': child_values[0],
                    'amount': child_values[1],
                    'comment': child_values[2],
                    'price': child_values[3],
                    'total': child_values[4],
                    'type': child_type
                }
            }
            packed_dishes['dishes'].update(packed_child)
        # Save the HTML report
        html_writer = DishesHTMLReport(file, data=packed_dishes)
        html_writer.create_html()
        messagebox.showinfo('Успешное сохранение',
                            'Данные были успешно сохранены.')

    def addCustomEntry(self):
        """This functions opens a window, which gives access for creating custom entries to report"""
        newentry_dialog = NewEntryDialog(
            self, title='Добавить собственную строку в отчет')
        if newentry_dialog.result:
            self.custom_entries.append(newentry_dialog.result)

    def addDish(self):
        """This function adds an entry to the order_view TreeView widget."""
        # Get packed_info as a dictionary.
        packed_info = self.packInfo()
        # If info packed successfully, add a new entry to orders TreeView.
        if not packed_info:
            return
        # Add a dish to the TreeView.
        self.orders_view.insert('',
                                'end',
                                text=packed_info['dish'],
                                values=packed_info['values'])
        # Clear all entries.
        entries_toclear = ['name', 'comment', 'amount']
        for entry_key in entries_toclear:
            self.entries[entry_key].delete(0, END)
            self.entries[entry_key].insert(0, '')
        self.entries['amount'].insert(0, '1')
        # Change [sum] label.
        total_sum = float(self.sum_lbl['text'][21:-4]) + float(
            packed_info['values'][4])
        self.sum_lbl['text'] = 'Текущая сумма заказа:\n %.2f грн' % total_sum
        # Set focus to ['name'] entry.
        self.entries['name'].focus_set()

    def packInfo(self):
        """Pack values, that were inserted into Entries and Text, into dictionary"""
        msg = self.validateForm()
        if msg == 'OK':
            name = self.entries['name'].get()
            amount = self.entries['amount'].get()
            total_price = float(amount) * float(self.dishes[name]['price'])
            pack = {
                'dish':
                name,
                'values': [
                    self.dishes[name]['weight'],
                    str(amount), self.entries['comment'].get(),
                    self.dishes[name]['price'],
                    '%.2f' % total_price
                ]
            }
            return pack
        elif msg == 'NEWDISH_CANCELED':
            return False
        else:
            messagebox.showerror('Ошибка ввода',
                                 'При вводе случились ошибки: \n%s' % msg)
            return False

    def validateForm(self):
        """Validate all Entry and Text Widgets"""
        msg = ''
        index = 1
        translations = {
            'client': 'Клиент',
            'manager': 'Менеджер',
            'type': 'Вид мероприятия',
            'date': 'Дата',
            'time': 'Время',
            'location': 'Место проведения',
            'persons': 'Количество персон',
            'name': 'Название',
            'comment': 'Комментарий',
            'amount': 'Количество',
        }

        for k in self.entries:
            # Check if some entry is empty.
            if self.entries[k].get() == '':
                if k == 'comment':
                    continue
                msg += '%i) Поле [%s] пустое.\n' % (index, translations[k])
                index += 1

        # Persons entry should contain only digits.
        if not all(letter.isdigit()
                   for letter in self.entries['persons'].get()):
            msg += '%i) Поле [Количество персон] должно содержать только цифры.\n' % index
            index += 1
        # Check date entry.
        match = re.search(r'(\d{2})[.](\d{2})[.](\d{4})$',
                          self.entries['date'].get())
        if not match:
            msg += '%i) Неправильный формат даты.\nПравильный формат: дд.мм.гггг\n' % index
            index += 1
        # Check amount entry.
        check = all(letter.isdigit() or letter == '.'
                    for letter in self.entries['amount'].get())
        if not check:
            msg += '%i) Поле [Количество] должно содержать только цифры, или точки.\n' % index
            index += 1

        # Ask about [name] entry only if there are no more errors left.
        if msg != '':
            return msg

        # Check name entry.
        if (self.entries['name'].get() not in self.dishes_names
                and self.entries['name'].get() != ''):
            if messagebox.askyesno(
                    'Добавление нового блюда',
                    'Блюда %s нет в списке. Добавить?' %
                    self.entries['name'].get()):
                # Create a new window, which will contain a [result] dictionary {name,type,weight,price}
                newdish_dialog = NewDishDialog(self,
                                               self.entries['name'].get(),
                                               'Добавить новое блюдо')
                if newdish_dialog.result:
                    dishToJSON = {
                        newdish_dialog.result['name']: {
                            'weight': newdish_dialog.result['weight'],
                            'price': newdish_dialog.result['price'],
                            'type': newdish_dialog.result['type']
                        }
                    }
                    self.jsonReader.writeDish(dishToJSON)
                    self.jsonReader.prepInformation()
                    self.dishes = self.jsonReader.getDishesDict()
                    self.dishes_names = self.jsonReader.getDishesNames()
                    self.entries['name'].set_completion_list(self.dishes_names)
                else:
                    # if [exit] was pressed
                    return 'NEWDISH_CANCELED'
            else:
                return 'NEWDISH_CANCELED'
        # Check if the same dish is in the orderslist.
        for child in self.orders_view.get_children():
            child_content = self.orders_view.item(child)
            if self.entries['name'].get() == child_content['text']:
                msg += '%i) Ошибка в поле [Название] - такое блюдо уже есть в списке.' % index
                index += 1

        # If all tests passed correctly, msg is 'OK'
        if msg == '':
            msg = 'OK'
        return msg
Example #35
0
class BioInfo(Tk):
    def __init__(self):
        Tk.__init__(self)
        self.wm_title("BioInfo : comparaison des listes")
        self.resizable(width=FALSE, height=FALSE)
        self.SortDir = False

        # Lists Types
        self.typeList1 = None
        self.typeList2 = None

        # Frame content
        self.frameContent = Frame(self)
        self.frameContent.pack(side=TOP, fill=X)

        # ScrollBar
        scrollbar = Scrollbar(self.frameContent, orient=VERTICAL)
        scrollbar.pack(side=RIGHT, fill=Y)

        # Result Content
        self.dataCols = ('microArn_A', 'microArn_B', 'FoldC', 'p-Value', 'Note')
        self.tree = Treeview(self.frameContent, columns=self.dataCols, show = 'headings', yscrollcommand=scrollbar.set)

        # configure column headings
        for c in self.dataCols:
            self.tree.heading(c, text=c, command=lambda c=c: self.columnSort(c, self.SortDir))
            self.tree.column(c, width=10)

        self.tree.pack(side=LEFT, fill=X, expand="yes")

        scrollbar.config(command=self.tree.yview)

        # Frame Lists
        self.frameLists = Frame(self)
        self.frameLists.pack(side=LEFT)

        # Frame Forms
        self.frameForms = Frame(self)
        self.frameForms.pack(side=LEFT, padx=20)

        #Liste n°1 selection
        self.frameList1 = Frame(self.frameLists)
        self.frameList1.pack()

        self.typeListStr1 = StringVar(self.frameList1)
        self.typeListStr1.set(str(ListBioType.TypeA))

        self.buttonTypeList1 = OptionMenu(self.frameList1, self.typeListStr1, str(ListBioType.TypeA), str(ListBioType.TypeB)).pack(side=LEFT)

        self.entrylist1 = Entry(self.frameList1, width=30)
        self.entrylist1.pack(side=LEFT)

        self.buttonBrowseList1 = Button(self.frameList1, text="Parcourir", command=self.load_fileList1, width=10)
        self.buttonBrowseList1.pack(side=LEFT, padx=5)

        # List n°2 selection
        self.frameList2 = Frame(self.frameLists)
        self.frameList2.pack(side=BOTTOM)

        self.typeListStr2 = StringVar(self.frameList2)
        self.typeListStr2.set(str(ListBioType.TypeB))

        self.buttonTypeList2 = OptionMenu(self.frameList2, self.typeListStr2, str(ListBioType.TypeA), str(ListBioType.TypeB)).pack(side=LEFT)

        self.entrylist2 = Entry(self.frameList2, width=30)
        self.entrylist2.pack(side=LEFT)

        self.buttonBrowseList2 = Button(self.frameList2, text="Parcourir", command=self.load_fileList2, width=10)
        self.buttonBrowseList2.pack(side=LEFT, padx=5)

        # Form pValue
        self.framePVal = Frame(self.frameForms)
        self.framePVal.pack()

        Label(self.framePVal, text="pValue").pack(side=LEFT)
        self.entryPVal = Entry(self.framePVal, width=6)
        self.entryPVal.pack(side=LEFT)

        # Form foldC
        self.frameFoldC = Frame(self.frameForms)
        self.frameFoldC.pack()

        Label(self.frameFoldC, text="foldCh").pack(side=LEFT)
        self.entryFoldC = Entry(self.frameFoldC, width=6)
        self.entryFoldC.pack(side=LEFT)

        # Form note
        self.frameNote = Frame(self.frameForms)
        self.frameNote.pack()

        Label(self.frameNote, text="note    ").pack(side=LEFT)
        self.entryNote = Entry(self.frameNote, width=6)
        self.entryNote.pack(side=LEFT)

        # Bouton comparer
        self.buttonComparer = Button(self, text="Comparer", command=self.compare, width=10, state=DISABLED)
        self.buttonComparer.pack(fill= X, expand="yes", padx=20, pady=(10,0))

        #Bouton exporter
        self.buttonExport = Button(self, text="Exporter", command=self.export, width=10, state=DISABLED)
        self.buttonExport.pack(fill= X, expand="yes", padx=20)

        # Réinitialiser
        self.buttonReset = Button(self, text="Réinitialiser", command=self.reset, width=10)
        self.buttonReset.pack(fill= X, expand="yes", padx=20, pady=(0,10))

        # file members
        self.list1 = None
        self.list2 = None

    def load_fileList1(self):
        fname = askopenfilename(filetypes=(("CSV files", "*.csv"),
                                           ("All files", "*.*") ))
        if fname:
            self.entrylist1.delete(0, END)
            self.list1 = fname
            self.entrylist1.insert(0,fname)

            self.buttonComparer.config(state=NORMAL)


    def load_fileList2(self):
        fname = askopenfilename(filetypes=(("CSV files", "*.csv"),
                                           ("All files", "*.*") ))
        if fname:
            self.entrylist2.delete(0, END)
            self.list2 = fname
            self.entrylist2.insert(0,fname)

            self.buttonComparer.config(state=NORMAL)

        else:
            showerror("Erreur : fichier B", "La liste B est introuvable")


    def resetTree (self):
        for i in self.tree.get_children():
            self.tree.delete(i)

    def reset(self):
        self.list1 = None
        self.entrylist1.delete(0, END)

        self.list2 = None
        self.entrylist2.delete(0, END)

        self.entryPVal.delete(0,END)
        self.entryFoldC.delete(0, END)
        self.entryNote.delete(0, END)

        self.typeList1 = None
        self.typeList2 = None

        self.buttonExport.config(state=DISABLED)
        self.buttonComparer.config(state=DISABLED)

        self.resetTree()

    def isValidfoldC(self, s):
        try:
            float(s)
            return True
        except ValueError:
            return False

    def isValidPValue(self, s):
        try:
            f = float(s)
            if f >= 0 and f <= 1:
                return True
            else:
                return False
        except:
            return False

    def isValidNote (self, s):
        try:
            f = int(s)
            return True
        except:
            return False

    def compare(self):
        self.buttonExport.config(state=NORMAL)

        # Détermination type Listes

        # List 1

        if self.typeListStr1.get() == str(ListBioType.TypeA):
            self.typeList1 = ListBioType.TypeA
        elif self.typeListStr1.get() == str(ListBioType.TypeB):
            self.typeList1 = ListBioType.TypeB
        else:
            self.typeList1 = None

        # List 2
        if self.typeListStr2.get() == str(ListBioType.TypeA):
            self.typeList2 = ListBioType.TypeA
        elif self.typeListStr2.get() == str(ListBioType.TypeB):
            self.typeList2 = ListBioType.TypeB
        else:
            self.typeList2 = None


        if not self.isValidfoldC(self.entryFoldC.get()) and not self.entryFoldC.get() == "":
            showerror("Erreur : foldC","La valeur fold Change n'est pas un nombre")

        elif not self.isValidPValue(self.entryPVal.get()) and not self.entryPVal.get() == "":
            showerror("Erreur : pValue","La valeur pValue n'est pas un nombre compris entre 0 et 1")

        elif not self.isValidNote(self.entryNote.get()) and not self.entryNote.get() == "":
            showerror("Erreur : note", "La valeur note n'est pas un nombre entier")

        # (List A and No List) or (No List and List A)
        elif ((self.list1 is not None and self.typeList1 == ListBioType.TypeA) and (self.list2 is None)) or\
             ((self.list2 is not None and self.typeList2 == ListBioType.TypeA) and (self.list1 is None)):

            self.resetTree()

            try:
                listComp = ListComparator(self.list1, self.list2, self.entryPVal.get(), self.entryFoldC.get(), self.entryNote.get())
                for e in listComp.getFilterListA():
                    self.tree.insert('', 'end', values=e)

            except IndexError:
                showerror("Erreur : liste A invalide", "Le fichier liste A n'est pas un fichier valide")


        # (List B and No List) or (No List and List B)
        elif ((self.list1 is not None and self.typeList1 == ListBioType.TypeB) and (self.list2 is None)) or\
             ((self.list2 is not None and self.typeList2 == ListBioType.TypeB) and (self.list1 is None)):

            self.resetTree()

            try:
                listComp = ListComparator(self.list1, self.list2, self.entryPVal.get(), self.entryFoldC.get())
                for e in listComp.getFilterListB():
                    self.tree.insert('', 'end', values=e)
            
            except IndexError:
                showerror("Erreur : liste A invalide", "Le fichier liste A n'est pas un fichier valide")

        # (List A and List B) or (List B and List A)
        elif ((self.list1 is not None and self.typeList1 == ListBioType.TypeA) and \
             (self.list2 is not None and self.typeList2 == ListBioType.TypeB)) or \
             ((self.list1 is not None and self.typeList1 == ListBioType.TypeB) and \
             (self.list2 is not None and self.typeList2 == ListBioType.TypeA)):

            self.resetTree()

            listA = ""
            listB = ""
            if self.typeList1 == ListBioType.TypeA:
                listA = self.list1
            else:
                listA = self.list2

            if self.typeList1 == ListBioType.TypeB:
                listB = self.list1
            else:
                listB = self.list2
            try:
                listComp = ListComparator(listA, listB, self.entryPVal.get(), self.entryFoldC.get(), self.entryNote.get())
                for e in listComp.getDiffAandB():
                    self.tree.insert('', 'end', values=e)

            except IndexError:
                showerror("Erreur : liste A ou B invalide", "Le fichier liste A ou B n'est pas un fichier valide")

        # (List A and List A)
        elif ((self.list1 is not None and self.typeList1 == ListBioType.TypeA) and \
             (self.list2 is not None and self.typeList2 == ListBioType.TypeA)):

            self.resetTree()

            try:
                listComp = ListComparator(self.list1, self.list2, self.entryPVal.get(), self.entryFoldC.get(), self.entryNote.get())
                for e in listComp.getDiffAandA():
                    self.tree.insert('', 'end', values=e)

            except IndexError:
                showerror("Erreur : liste A ou B invalide", "Le fichier liste A ou B n'est pas un fichier valide")

        # (List B and List B)
        elif ((self.list1 is not None and self.typeList1 == ListBioType.TypeB) and \
             (self.list2 is not None and self.typeList2 == ListBioType.TypeB)):

            self.resetTree()

            try:
                listComp = ListComparator(self.list1, self.list2, self.entryPVal.get(), self.entryFoldC.get())
                for e in listComp.getDiffBandB():
                    self.tree.insert('', 'end', values=e)

            except IndexError:
                showerror("Erreur : liste A ou B invalide", "Le fichier liste A ou B n'est pas un fichier valide")
        else:
            showerror("Erreur : Combinaisons de listes invalides", "Votre choix de types de listes ne correspond à aucune combinaison possible, contacter le developpeur")


    def export(self):
        if len(self.tree.get_children()) == 0:
            showinfo("Export", "Il n'y a rien à exporter")
            return

        fname = asksaveasfilename(filetypes=(("CSV files", "*.csv"),
                                             ("All files", "*.*") ))

        if fname:
            resExp = []
            for it in self.tree.get_children():
                resExp.append(self.tree.item(it)["values"])

            expTabToCSV = TreeExportator(resExp, fname)
            expTabToCSV.export()

            showinfo("Export", "Exportation au format CSV réussi")

    def columnSort (self, col, descending=False):
        data = [(self.tree.set(child, col), child) for child in self.tree.get_children('')]

        data.sort(reverse=descending)
        for indx, item in enumerate(data):
            self.tree.move(item[1], '', indx)

        # reverse sort direction for next sort operation
        self.SortDir = not descending
class DialogPackageManager(Toplevel):
    def __init__(self, mainWin, packageNamesWithNewerFileDates):
        super(DialogPackageManager, self).__init__(mainWin.parent)
        
        self.ENABLE = _("Enable")
        self.DISABLE = _("Disable")
        self.parent = mainWin.parent
        self.cntlr = mainWin
        
        # copy plugins for temporary display
        self.packagesConfig = PackageManager.packagesConfig
        self.packagesConfigChanged = False
        self.packageNamesWithNewerFileDates = packageNamesWithNewerFileDates
        
        parentGeometry = re.match("(\d+)x(\d+)[+]?([-]?\d+)[+]?([-]?\d+)", self.parent.geometry())
        dialogX = int(parentGeometry.group(3))
        dialogY = int(parentGeometry.group(4))

        self.title(_("Taxonomy Packages Manager"))
        frame = Frame(self)
        
        # left button frame
        buttonFrame = Frame(frame, width=40)
        buttonFrame.columnconfigure(0, weight=1)
        addLabel = Label(buttonFrame, text=_("Find taxonomy packages:"), wraplength=64, justify="center")
        addLocalButton = Button(buttonFrame, text=_("Locally"), command=self.findLocally)
        ToolTip(addLocalButton, text=_("File chooser allows selecting taxonomy packages to add (or reload), from the local file system.  "
                                       "Select either a taxonomy package zip file, or a taxonomy manifest (.taxonomyPackage.xml) within an unzipped taxonomy package.  "), wraplength=240)
        addWebButton = Button(buttonFrame, text=_("On Web"), command=self.findOnWeb)
        ToolTip(addWebButton, text=_("Dialog to enter URL full path to load (or reload) package, from the web or local file system.  "
                                     "URL may be either a taxonomy package zip file, or a taxonomy manifest (.taxonomyPackage.xml) within an unzipped taxonomy package.  "), wraplength=240)
        manifestNameButton = Button(buttonFrame, text=_("Manifest"), command=self.manifestName)
        ToolTip(manifestNameButton, text=_("Provide non-standard archive manifest file name pattern (e.g., *taxonomyPackage.xml).  "
                                           "Uses unix file name pattern matching.  "
                                           "Multiple manifest files are supported in archive (such as oasis catalogs).  "
                                           "(Replaces search for either .taxonomyPackage.xml or catalog.xml).  "), wraplength=240)
        self.manifestNamePattern = ""
        addLabel.grid(row=0, column=0, pady=4)
        addLocalButton.grid(row=1, column=0, pady=4)
        addWebButton.grid(row=2, column=0, pady=4)
        manifestNameButton.grid(row=3, column=0, pady=4)
        buttonFrame.grid(row=0, column=0, rowspan=3, sticky=(N, S, W), padx=3, pady=3)
        
        # right tree frame (packages already known to arelle)
        packagesFrame = Frame(frame, width=700)
        vScrollbar = Scrollbar(packagesFrame, orient=VERTICAL)
        hScrollbar = Scrollbar(packagesFrame, orient=HORIZONTAL)
        self.packagesView = Treeview(packagesFrame, xscrollcommand=hScrollbar.set, yscrollcommand=vScrollbar.set, height=7)
        self.packagesView.grid(row=0, column=0, sticky=(N, S, E, W))
        self.packagesView.bind('<<TreeviewSelect>>', self.packageSelect)
        hScrollbar["command"] = self.packagesView.xview
        hScrollbar.grid(row=1, column=0, sticky=(E,W))
        vScrollbar["command"] = self.packagesView.yview
        vScrollbar.grid(row=0, column=1, sticky=(N,S))
        packagesFrame.columnconfigure(0, weight=1)
        packagesFrame.rowconfigure(0, weight=1)
        packagesFrame.grid(row=0, column=1, columnspan=4, sticky=(N, S, E, W), padx=3, pady=3)
        self.packagesView.focus_set()

        self.packagesView.column("#0", width=120, anchor="w")
        self.packagesView.heading("#0", text=_("Name"))
        self.packagesView["columns"] = ("ver", "status", "date", "update", "descr")
        self.packagesView.column("ver", width=150, anchor="w", stretch=False)
        self.packagesView.heading("ver", text=_("Version"))
        self.packagesView.column("status", width=50, anchor="w", stretch=False)
        self.packagesView.heading("status", text=_("Status"))
        self.packagesView.column("date", width=170, anchor="w", stretch=False)
        self.packagesView.heading("date", text=_("File Date"))
        self.packagesView.column("update", width=50, anchor="w", stretch=False)
        self.packagesView.heading("update", text=_("Update"))
        self.packagesView.column("descr", width=200, anchor="w", stretch=False)
        self.packagesView.heading("descr", text=_("Description"))

        remappingsFrame = Frame(frame)
        vScrollbar = Scrollbar(remappingsFrame, orient=VERTICAL)
        hScrollbar = Scrollbar(remappingsFrame, orient=HORIZONTAL)
        self.remappingsView = Treeview(remappingsFrame, xscrollcommand=hScrollbar.set, yscrollcommand=vScrollbar.set, height=5)
        self.remappingsView.grid(row=0, column=0, sticky=(N, S, E, W))
        hScrollbar["command"] = self.remappingsView.xview
        hScrollbar.grid(row=1, column=0, sticky=(E,W))
        vScrollbar["command"] = self.remappingsView.yview
        vScrollbar.grid(row=0, column=1, sticky=(N,S))
        remappingsFrame.columnconfigure(0, weight=1)
        remappingsFrame.rowconfigure(0, weight=1)
        remappingsFrame.grid(row=1, column=1, columnspan=4, sticky=(N, S, E, W), padx=3, pady=3)
        self.remappingsView.focus_set()
        
        self.remappingsView.column("#0", width=200, anchor="w")
        self.remappingsView.heading("#0", text=_("Prefix"))
        self.remappingsView["columns"] = ("remapping")
        self.remappingsView.column("remapping", width=500, anchor="w", stretch=False)
        self.remappingsView.heading("remapping", text=_("Remapping"))
        
        # bottom frame package info details
        packageInfoFrame = Frame(frame, width=700)
        packageInfoFrame.columnconfigure(1, weight=1)
        
        self.packageNameLabel = Label(packageInfoFrame, wraplength=600, justify="left", 
                                      font=font.Font(family='Helvetica', size=12, weight='bold'))
        self.packageNameLabel.grid(row=0, column=0, columnspan=6, sticky=W)
        self.packageVersionHdr = Label(packageInfoFrame, text=_("version:"), state=DISABLED)
        self.packageVersionHdr.grid(row=1, column=0, sticky=W)
        self.packageVersionLabel = Label(packageInfoFrame, wraplength=600, justify="left")
        self.packageVersionLabel.grid(row=1, column=1, columnspan=5, sticky=W)
        self.packageDescrHdr = Label(packageInfoFrame, text=_("description:"), state=DISABLED)
        self.packageDescrHdr.grid(row=2, column=0, sticky=W)
        self.packageDescrLabel = Label(packageInfoFrame, wraplength=600, justify="left")
        self.packageDescrLabel.grid(row=2, column=1, columnspan=5, sticky=W)
        self.packagePrefixesHdr = Label(packageInfoFrame, text=_("prefixes:"), state=DISABLED)
        self.packagePrefixesHdr.grid(row=3, column=0, sticky=W)
        self.packagePrefixesLabel = Label(packageInfoFrame, wraplength=600, justify="left")
        self.packagePrefixesLabel.grid(row=3, column=1, columnspan=5, sticky=W)
        ToolTip(self.packagePrefixesLabel, text=_("List of prefixes that this package remaps."), wraplength=240)
        self.packageUrlHdr = Label(packageInfoFrame, text=_("URL:"), state=DISABLED)
        self.packageUrlHdr.grid(row=4, column=0, sticky=W)
        self.packageUrlLabel = Label(packageInfoFrame, wraplength=600, justify="left")
        self.packageUrlLabel.grid(row=4, column=1, columnspan=5, sticky=W)
        ToolTip(self.packageUrlLabel, text=_("URL of taxonomy package (local file path or web loaded file)."), wraplength=240)
        self.packageDateHdr = Label(packageInfoFrame, text=_("date:"), state=DISABLED)
        self.packageDateHdr.grid(row=5, column=0, sticky=W)
        self.packageDateLabel = Label(packageInfoFrame, wraplength=600, justify="left")
        self.packageDateLabel.grid(row=5, column=1, columnspan=5, sticky=W)
        ToolTip(self.packageDateLabel, text=_("Date of currently loaded package file (with parenthetical node when an update is available)."), wraplength=240)
        self.packageEnableButton = Button(packageInfoFrame, text=self.ENABLE, state=DISABLED, command=self.packageEnable)
        ToolTip(self.packageEnableButton, text=_("Enable/disable package."), wraplength=240)
        self.packageEnableButton.grid(row=6, column=1, sticky=E)
        self.packageMoveUpButton = Button(packageInfoFrame, text=_("Move Up"), state=DISABLED, command=self.packageMoveUp)
        ToolTip(self.packageMoveUpButton, text=_("Move package up (above other remappings)."), wraplength=240)
        self.packageMoveUpButton.grid(row=6, column=2, sticky=E)
        self.packageMoveDownButton = Button(packageInfoFrame, text=_("Move Down"), state=DISABLED, command=self.packageMoveDown)
        ToolTip(self.packageMoveDownButton, text=_("Move package down (below other remappings)."), wraplength=240)
        self.packageMoveDownButton.grid(row=6, column=3, sticky=E)
        self.packageReloadButton = Button(packageInfoFrame, text=_("Reload"), state=DISABLED, command=self.packageReload)
        ToolTip(self.packageReloadButton, text=_("Reload/update package."), wraplength=240)
        self.packageReloadButton.grid(row=6, column=4, sticky=E)
        self.packageRemoveButton = Button(packageInfoFrame, text=_("Remove"), state=DISABLED, command=self.packageRemove)
        ToolTip(self.packageRemoveButton, text=_("Remove package from packages table (does not erase the package file)."), wraplength=240)
        self.packageRemoveButton.grid(row=6, column=5, sticky=E)
        packageInfoFrame.grid(row=2, column=0, columnspan=5, sticky=(N, S, E, W), padx=3, pady=3)
        packageInfoFrame.config(borderwidth=4, relief="groove")
        
        okButton = Button(frame, text=_("Close"), command=self.ok)
        ToolTip(okButton, text=_("Accept and changes (if any) and close dialog."), wraplength=240)
        cancelButton = Button(frame, text=_("Cancel"), command=self.close)
        ToolTip(cancelButton, text=_("Cancel changes (if any) and close dialog."), wraplength=240)
        okButton.grid(row=3, column=3, sticky=(S,E), pady=3)
        cancelButton.grid(row=3, column=4, sticky=(S,E), pady=3, padx=3)
        
        self.loadTreeViews()

        self.geometry("+{0}+{1}".format(dialogX+50,dialogY+100))
        frame.grid(row=0, column=0, sticky=(N,S,E,W))
        frame.columnconfigure(0, weight=0)
        frame.columnconfigure(1, 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.protocol("WM_DELETE_WINDOW", self.close)
        self.grab_set()
        self.wait_window(self)
        
    def loadTreeViews(self):
        self.selectedModule = None

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

        for i, packageInfo in enumerate(self.packagesConfig.get("packages", [])):
            name = packageInfo.get("name", "package{}".format(i))
            node = self.packagesView.insert("", "end", "_{}".format(i), text=name)
            self.packagesView.set(node, "ver", packageInfo.get("version"))
            self.packagesView.set(node, "status", packageInfo.get("status"))
            self.packagesView.set(node, "date", packageInfo.get("fileDate"))
            if name in self.packageNamesWithNewerFileDates:
                self.packagesView.set(node, "update", _("available"))
            self.packagesView.set(node, "descr", packageInfo.get("description"))
        
        # clear previous treeview entries
        for previousNode in self.remappingsView.get_children(""): 
            self.remappingsView.delete(previousNode)

        for i, remappingItem in enumerate(sorted(self.packagesConfig.get("remappings", {}).items())):
            prefix, remapping = remappingItem
            node = self.remappingsView.insert("", "end", prefix, text=prefix)
            self.remappingsView.set(node, "remapping", remapping)
            
        self.packageSelect()  # clear out prior selection

    def ok(self, event=None):
        if self.packagesConfigChanged:
            PackageManager.packagesConfig = self.packagesConfig
            PackageManager.packagesConfigChanged = True
            self.cntlr.onPackageEnablementChanged()
        self.close()
        
    def close(self, event=None):
        self.parent.focus_set()
        self.destroy()
                
    def packageSelect(self, *args):
        node = (self.packagesView.selection() or (None,))[0]
        try:
            nodeIndex = int(node[1:])
        except (ValueError, TypeError):
            nodeIndex = -1
        if 0 <= nodeIndex < len(self.packagesConfig["packages"]):
            packageInfo = self.packagesConfig["packages"][nodeIndex]
            self.selectedPackageIndex = nodeIndex
            name = packageInfo["name"]
            self.packageNameLabel.config(text=name)
            self.packageVersionHdr.config(state=ACTIVE)
            self.packageVersionLabel.config(text=packageInfo["version"])
            self.packageDescrHdr.config(state=ACTIVE)
            self.packageDescrLabel.config(text=packageInfo["description"])
            self.packagePrefixesHdr.config(state=ACTIVE)
            self.packagePrefixesLabel.config(text=', '.join(packageInfo["remappings"].keys()))
            self.packageUrlHdr.config(state=ACTIVE)
            self.packageUrlLabel.config(text=packageInfo["URL"])
            self.packageDateHdr.config(state=ACTIVE)
            self.packageDateLabel.config(text=packageInfo["fileDate"] + " " +
                    (_("(an update is available)") if name in self.packageNamesWithNewerFileDates else ""))
            self.packageEnableButton.config(state=ACTIVE,
                                           text={"enabled":self.DISABLE,
                                                 "disabled":self.ENABLE}[packageInfo["status"]])
            self.packageMoveUpButton.config(state=ACTIVE if 0 < nodeIndex else DISABLED)
            self.packageMoveDownButton.config(state=ACTIVE if nodeIndex < (len(self.packagesConfig["packages"]) - 1) else DISABLED)
            self.packageReloadButton.config(state=ACTIVE)
            self.packageRemoveButton.config(state=ACTIVE)
        else:
            self.selectedPackageIndex = -1
            self.packageNameLabel.config(text="")
            self.packageVersionHdr.config(state=DISABLED)
            self.packageVersionLabel.config(text="")
            self.packageDescrHdr.config(state=DISABLED)
            self.packageDescrLabel.config(text="")
            self.packagePrefixesHdr.config(state=DISABLED)
            self.packagePrefixesLabel.config(text="")
            self.packageUrlHdr.config(state=DISABLED)
            self.packageUrlLabel.config(text="")
            self.packageDateHdr.config(state=DISABLED)
            self.packageDateLabel.config(text="")

            self.packageEnableButton.config(state=DISABLED, text=self.ENABLE)
            self.packageMoveUpButton.config(state=DISABLED)
            self.packageMoveDownButton.config(state=DISABLED)
            self.packageReloadButton.config(state=DISABLED)
            self.packageRemoveButton.config(state=DISABLED)
        
    def findLocally(self):
        initialdir = self.cntlr.pluginDir # default plugin directory
        if not self.cntlr.isMac: # can't navigate within app easily, always start in default directory
            initialdir = self.cntlr.config.setdefault("packageOpenDir", initialdir)
        filename = self.cntlr.uiFileDialog("open",
                                           parent=self,
                                           title=_("Choose taxonomy package file"),
                                           initialdir=initialdir,
                                           filetypes=[(_("Taxonomy package files (*.zip)"), "*.zip"),
                                                      (_("Manifest (*.taxonomyPackage.xml)"), "*.taxonomyPackage.xml"),
                                                      (_("Oasis Catalog (*catalog.xml)"), "*catalog.xml")],
                                           defaultextension=".zip")
        if filename:
            # check if a package is selected (any file in a directory containing an __init__.py
            self.cntlr.config["packageOpenDir"] = os.path.dirname(filename)
            packageInfo = PackageManager.packageInfo(filename, packageManifestName=self.manifestNamePattern)
            self.loadFoundPackageInfo(packageInfo, filename)
                

    def findOnWeb(self):
        url = DialogURL.askURL(self)
        if url:  # url is the in-cache or local file
            packageInfo = PackageManager.packageInfo(url, packageManifestName=self.manifestNamePattern)
            self.cntlr.showStatus("") # clear web loading status
            self.loadFoundPackageInfo(packageInfo, url)
                
    def manifestName(self):
        self.manifestNamePattern = simpledialog.askstring(_("Archive manifest file name pattern"),
                                                          _("Provide non-standard archive manifest file name pattern (e.g., *taxonomyPackage.xml).  \n"
                                                            "Uses unix file name pattern matching.  \n"
                                                            "Multiple manifest files are supported in archive (such as oasis catalogs).  \n"
                                                            "(If blank, search for either .taxonomyPackage.xml or catalog.xml).  "),
                                                          initialvalue=self.manifestNamePattern,
                                                          parent=self)
                
    def loadFoundPackageInfo(self, packageInfo, url):
        if packageInfo and packageInfo.get("name"):
            self.addPackageInfo(packageInfo)
            self.loadTreeViews()
        else:
            messagebox.showwarning(_("Package is not itself a taxonomy package.  "),
                                   _("File does not itself contain a manifest file: \n\n{0}\n\n  "
                                     "If opening an archive file, the manifest file search pattern currently is \"\", please press \"Manifest\" to change manifest file name pattern, e.g.,, \"*.taxonomyPackage.xml\", if needed.  ")
                                   .format(url),
                                   parent=self)
            
    def removePackageInfo(self, name, version):
        # find package entry
        packagesList = self.packagesConfig["packages"]
        j = -1
        for i, packageInfo in enumerate(packagesList):
            if packageInfo['name'] == name and packageInfo['version'] == version:
                j = i
                break
        if 0 <= j < len(packagesList):
            del self.packagesConfig["packages"][i]
            self.packagesConfigChanged = True

    def addPackageInfo(self, packageInfo):
        name = packageInfo["name"]
        version = packageInfo["version"]
        self.removePackageInfo(name, version)  # remove any prior entry for this package
        self.packageNamesWithNewerFileDates.discard(name) # no longer has an update available
        self.packagesConfig["packages"].append(packageInfo)
        PackageManager.rebuildRemappings()
        self.packagesConfigChanged = True

    def packageEnable(self):
        if 0 <= self.selectedPackageIndex < len(self.packagesConfig["packages"]):
            packageInfo = self.packagesConfig["packages"][self.selectedPackageIndex]
            if self.packageEnableButton['text'] == self.ENABLE:
                packageInfo["status"] = "enabled"
                self.packageEnableButton['text'] = self.DISABLE
            elif self.packageEnableButton['text'] == self.DISABLE:
                packageInfo["status"] = "disabled"
                self.packageEnableButton['text'] = self.ENABLE
            self.packagesConfigChanged = True
            PackageManager.rebuildRemappings()
            self.loadTreeViews()
            
    def packageMoveUp(self):
        if 1 <= self.selectedPackageIndex < len(self.packagesConfig["packages"]):
            packages = self.packagesConfig["packages"]
            packageInfo = packages[self.selectedPackageIndex]
            del packages[self.selectedPackageIndex]
            packages.insert(self.selectedPackageIndex -1, packageInfo)
            self.packagesConfigChanged = True
            PackageManager.rebuildRemappings()
            self.loadTreeViews()
            
    def packageMoveDown(self):
        if 0 <= self.selectedPackageIndex < len(self.packagesConfig["packages"]) - 1:
            packages = self.packagesConfig["packages"]
            packageInfo = packages[self.selectedPackageIndex]
            del packages[self.selectedPackageIndex]
            packages.insert(self.selectedPackageIndex + 1, packageInfo)
            self.packagesConfigChanged = True
            PackageManager.rebuildRemappings()
            self.loadTreeViews()
            
    def packageReload(self):
        if 0 <= self.selectedPackageIndex < len(self.packagesConfig["packages"]):
            packageInfo = self.packagesConfig["packages"][self.selectedPackageIndex]
            url = packageInfo.get("URL")
            if url:
                packageInfo = PackageManager.packageInfo(url, reload=True, packageManifestName=packageInfo.get("manifestName"))
                if packageInfo:
                    self.addPackageInfo(packageInfo)
                    PackageManager.rebuildRemappings()
                    self.loadTreeViews()
                    self.cntlr.showStatus(_("{0} reloaded").format(packageInfo.get("name")), clearAfter=5000)
                else:
                    messagebox.showwarning(_("Package error"),
                                           _("File or package cannot be reloaded: \n\n{0}")
                                           .format(url),
                                           parent=self)

    def packageRemove(self):
        if 0 <= self.selectedPackageIndex < len(self.packagesConfig["packages"]):
            packageInfo = self.packagesConfig["packages"][self.selectedPackageIndex]
            self.removePackageInfo(packageInfo["name"], packageInfo["version"])
            self.packagesConfigChanged = True
            PackageManager.rebuildRemappings()
            self.loadTreeViews()
Example #37
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")
Example #38
0
class DialogPluginManager(Toplevel):
    def __init__(self, mainWin, modulesWithNewerFileDates):
        super(DialogPluginManager, self).__init__(mainWin.parent)
        
        self.ENABLE = _("Enable")
        self.DISABLE = _("Disable")
        self.parent = mainWin.parent
        self.cntlr = mainWin
        
        # copy plugins for temporary display
        self.pluginConfig = PluginManager.pluginConfig
        self.pluginConfigChanged = False
        self.uiClassMethodsChanged = False
        self.modelClassesChanged = False
        self.disclosureSystemTypesChanged = False
        self.modulesWithNewerFileDates = modulesWithNewerFileDates
        
        parentGeometry = re.match("(\d+)x(\d+)[+]?([-]?\d+)[+]?([-]?\d+)", self.parent.geometry())
        dialogX = int(parentGeometry.group(3))
        dialogY = int(parentGeometry.group(4))

        self.title(_("Plug-in Manager"))
        frame = Frame(self)
        
        # left button frame
        buttonFrame = Frame(frame, width=40)
        buttonFrame.columnconfigure(0, weight=1)
        addLabel = Label(buttonFrame, text=_("Find plug-in modules:"), wraplength=60, justify="center")
        addLocalButton = Button(buttonFrame, text=_("Locally"), command=self.findLocally)
        ToolTip(addLocalButton, text=_("File chooser allows selecting python module files to add (or reload) plug-ins, from the local file system."), wraplength=240)
        addWebButton = Button(buttonFrame, text=_("On Web"), command=self.findOnWeb)
        ToolTip(addWebButton, text=_("Dialog to enter URL full path to load (or reload) plug-ins, from the web or local file system."), wraplength=240)
        addLabel.grid(row=0, column=0, pady=4)
        addLocalButton.grid(row=1, column=0, pady=4)
        addWebButton.grid(row=2, column=0, pady=4)
        buttonFrame.grid(row=0, column=0, rowspan=2, sticky=(N, S, W), padx=3, pady=3)
        
        # right tree frame (plugins already known to arelle)
        modulesFrame = Frame(frame, width=700)
        vScrollbar = Scrollbar(modulesFrame, orient=VERTICAL)
        hScrollbar = Scrollbar(modulesFrame, orient=HORIZONTAL)
        self.modulesView = Treeview(modulesFrame, xscrollcommand=hScrollbar.set, yscrollcommand=vScrollbar.set, height=7)
        self.modulesView.grid(row=0, column=0, sticky=(N, S, E, W))
        self.modulesView.bind('<<TreeviewSelect>>', self.moduleSelect)
        hScrollbar["command"] = self.modulesView.xview
        hScrollbar.grid(row=1, column=0, sticky=(E,W))
        vScrollbar["command"] = self.modulesView.yview
        vScrollbar.grid(row=0, column=1, sticky=(N,S))
        modulesFrame.columnconfigure(0, weight=1)
        modulesFrame.rowconfigure(0, weight=1)
        modulesFrame.grid(row=0, column=1, columnspan=4, sticky=(N, S, E, W), padx=3, pady=3)
        self.modulesView.focus_set()

        self.modulesView.column("#0", width=120, anchor="w")
        self.modulesView.heading("#0", text=_("Name"))
        self.modulesView["columns"] = ("author", "ver", "status", "date", "update", "descr", "license")
        self.modulesView.column("author", width=100, anchor="w", stretch=False)
        self.modulesView.heading("author", text=_("Author"))
        self.modulesView.column("ver", width=50, anchor="w", stretch=False)
        self.modulesView.heading("ver", text=_("Version"))
        self.modulesView.column("status", width=50, anchor="w", stretch=False)
        self.modulesView.heading("status", text=_("Status"))
        self.modulesView.column("date", width=70, anchor="w", stretch=False)
        self.modulesView.heading("date", text=_("File Date"))
        self.modulesView.column("update", width=50, anchor="w", stretch=False)
        self.modulesView.heading("update", text=_("Update"))
        self.modulesView.column("descr", width=200, anchor="w", stretch=False)
        self.modulesView.heading("descr", text=_("Description"))
        self.modulesView.column("license", width=70, anchor="w", stretch=False)
        self.modulesView.heading("license", text=_("License"))

        classesFrame = Frame(frame)
        vScrollbar = Scrollbar(classesFrame, orient=VERTICAL)
        hScrollbar = Scrollbar(classesFrame, orient=HORIZONTAL)
        self.classesView = Treeview(classesFrame, xscrollcommand=hScrollbar.set, yscrollcommand=vScrollbar.set, height=5)
        self.classesView.grid(row=0, column=0, sticky=(N, S, E, W))
        hScrollbar["command"] = self.classesView.xview
        hScrollbar.grid(row=1, column=0, sticky=(E,W))
        vScrollbar["command"] = self.classesView.yview
        vScrollbar.grid(row=0, column=1, sticky=(N,S))
        classesFrame.columnconfigure(0, weight=1)
        classesFrame.rowconfigure(0, weight=1)
        classesFrame.grid(row=1, column=1, columnspan=4, sticky=(N, S, E, W), padx=3, pady=3)
        self.classesView.focus_set()
        
        self.classesView.column("#0", width=200, anchor="w")
        self.classesView.heading("#0", text=_("Class"))
        self.classesView["columns"] = ("modules",)
        self.classesView.column("modules", width=500, anchor="w", stretch=False)
        self.classesView.heading("modules", text=_("Modules"))
        
        # bottom frame module info details
        moduleInfoFrame = Frame(frame, width=700)
        moduleInfoFrame.columnconfigure(1, weight=1)
        
        self.moduleNameLabel = Label(moduleInfoFrame, wraplength=600, justify="left", 
                                     font=font.Font(family='Helvetica', size=12, weight='bold'))
        self.moduleNameLabel.grid(row=0, column=0, columnspan=4, sticky=W)
        self.moduleAuthorHdr = Label(moduleInfoFrame, text=_("author:"), state=DISABLED)
        self.moduleAuthorHdr.grid(row=1, column=0, sticky=W)
        self.moduleAuthorLabel = Label(moduleInfoFrame, wraplength=600, justify="left")
        self.moduleAuthorLabel.grid(row=1, column=1, columnspan=3, sticky=W)
        self.moduleDescrHdr = Label(moduleInfoFrame, text=_("description:"), state=DISABLED)
        self.moduleDescrHdr.grid(row=2, column=0, sticky=W)
        self.moduleDescrLabel = Label(moduleInfoFrame, wraplength=600, justify="left")
        self.moduleDescrLabel.grid(row=2, column=1, columnspan=3, sticky=W)
        self.moduleClassesHdr = Label(moduleInfoFrame, text=_("classes:"), state=DISABLED)
        self.moduleClassesHdr.grid(row=3, column=0, sticky=W)
        self.moduleClassesLabel = Label(moduleInfoFrame, wraplength=600, justify="left")
        self.moduleClassesLabel.grid(row=3, column=1, columnspan=3, sticky=W)
        ToolTip(self.moduleClassesLabel, text=_("List of classes that this plug-in handles."), wraplength=240)
        self.moduleUrlHdr = Label(moduleInfoFrame, text=_("URL:"), state=DISABLED)
        self.moduleUrlHdr.grid(row=4, column=0, sticky=W)
        self.moduleUrlLabel = Label(moduleInfoFrame, wraplength=600, justify="left")
        self.moduleUrlLabel.grid(row=4, column=1, columnspan=3, sticky=W)
        ToolTip(self.moduleUrlLabel, text=_("URL of plug-in module (local file path or web loaded file)."), wraplength=240)
        self.moduleDateHdr = Label(moduleInfoFrame, text=_("date:"), state=DISABLED)
        self.moduleDateHdr.grid(row=5, column=0, sticky=W)
        self.moduleDateLabel = Label(moduleInfoFrame, wraplength=600, justify="left")
        self.moduleDateLabel.grid(row=5, column=1, columnspan=3, sticky=W)
        ToolTip(self.moduleDateLabel, text=_("Date of currently loaded module file (with parenthetical node when an update is available)."), wraplength=240)
        self.moduleLicenseHdr = Label(moduleInfoFrame, text=_("license:"), state=DISABLED)
        self.moduleLicenseHdr.grid(row=6, column=0, sticky=W)
        self.moduleLicenseLabel = Label(moduleInfoFrame, wraplength=600, justify="left")
        self.moduleLicenseLabel.grid(row=6, column=1, columnspan=3, sticky=W)
        self.moduleEnableButton = Button(moduleInfoFrame, text=self.ENABLE, state=DISABLED, command=self.moduleEnable)
        ToolTip(self.moduleEnableButton, text=_("Enable/disable plug in."), wraplength=240)
        self.moduleEnableButton.grid(row=7, column=1, sticky=E)
        self.moduleReloadButton = Button(moduleInfoFrame, text=_("Reload"), state=DISABLED, command=self.moduleReload)
        ToolTip(self.moduleReloadButton, text=_("Reload/update plug in."), wraplength=240)
        self.moduleReloadButton.grid(row=7, column=2, sticky=E)
        self.moduleRemoveButton = Button(moduleInfoFrame, text=_("Remove"), state=DISABLED, command=self.moduleRemove)
        ToolTip(self.moduleRemoveButton, text=_("Remove plug in from plug in table (does not erase the plug in's file)."), wraplength=240)
        self.moduleRemoveButton.grid(row=7, column=3, sticky=E)
        moduleInfoFrame.grid(row=2, column=0, columnspan=5, sticky=(N, S, E, W), padx=3, pady=3)
        moduleInfoFrame.config(borderwidth=4, relief="groove")
        
        okButton = Button(frame, text=_("Close"), command=self.ok)
        ToolTip(okButton, text=_("Accept and changes (if any) and close dialog."), wraplength=240)
        cancelButton = Button(frame, text=_("Cancel"), command=self.close)
        ToolTip(cancelButton, text=_("Cancel changes (if any) and close dialog."), wraplength=240)
        okButton.grid(row=3, column=3, sticky=(S,E), pady=3)
        cancelButton.grid(row=3, column=4, sticky=(S,E), pady=3, padx=3)
        
        enableDisableFrame = Frame(frame)
        enableDisableFrame.grid(row=3, column=1, sticky=(S,W), pady=3)
        enableAllButton = Button(enableDisableFrame, text=_("Enable All"), command=self.enableAll)
        ToolTip(enableAllButton, text=_("Enable all plug ins."), wraplength=240)
        disableAllButton = Button(enableDisableFrame, text=_("Disable All"), command=self.disableAll)
        ToolTip(disableAllButton, text=_("Disable all plug ins."), wraplength=240)
        enableAllButton.grid(row=1, column=1)
        disableAllButton.grid(row=1, column=2)
        
        self.loadTreeViews()

        self.geometry("+{0}+{1}".format(dialogX+50,dialogY+100))
        frame.grid(row=0, column=0, sticky=(N,S,E,W))
        frame.columnconfigure(0, weight=0)
        frame.columnconfigure(1, 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.protocol("WM_DELETE_WINDOW", self.close)
        self.grab_set()
        self.wait_window(self)
        
    def loadTreeViews(self):
        self.selectedModule = None

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

        for i, moduleItem in enumerate(sorted(self.pluginConfig.get("modules", {}).items())):
            moduleInfo = moduleItem[1]
            name = moduleInfo.get("name", moduleItem[0])
            node = self.modulesView.insert("", "end", name, text=name)
            self.modulesView.set(node, "author", moduleInfo.get("author"))
            self.modulesView.set(node, "ver", moduleInfo.get("version"))
            self.modulesView.set(node, "status", moduleInfo.get("status"))
            self.modulesView.set(node, "date", moduleInfo.get("fileDate"))
            if name in self.modulesWithNewerFileDates:
                self.modulesView.set(node, "update", _("available"))
            self.modulesView.set(node, "descr", moduleInfo.get("description"))
            self.modulesView.set(node, "license", moduleInfo.get("license"))
        
        # clear previous treeview entries
        for previousNode in self.classesView.get_children(""): 
            self.classesView.delete(previousNode)

        for i, classItem in enumerate(sorted(self.pluginConfig.get("classes", {}).items())):
            className, moduleList = classItem
            node = self.classesView.insert("", "end", className, text=className)
            self.classesView.set(node, "modules", ', '.join(moduleList))
            
        self.moduleSelect()  # clear out prior selection

    def ok(self, event=None):
        if self.pluginConfigChanged:
            PluginManager.pluginConfig = self.pluginConfig
            PluginManager.pluginConfigChanged = True
            PluginManager.reset()  # force reloading of modules
        if self.uiClassMethodsChanged or self.modelClassesChanged or self.disclosureSystemTypesChanged:  # may require reloading UI
            affectedItems = ""
            if self.uiClassMethodsChanged:
                affectedItems += _("menus of the user interface")
            if self.modelClassesChanged:
                if self.uiClassMethodsChanged:
                    affectedItems += _(" and ")
                affectedItems += _("model objects of the processor")
            if (self.uiClassMethodsChanged or self.modelClassesChanged):
                affectedItems += _(" and ")
            if self.disclosureSystemTypesChanged:
                if (self.uiClassMethodsChanged or self.modelClassesChanged):
                    affectedItems += _(" and ")
                affectedItems += _("disclosure system types")
            if messagebox.askyesno(_("User interface plug-in change"),
                                   _("A change in plug-in class methods may have affected {0}.  " 
                                     "Please restart Arelle to due to these changes.  \n\n"
                                     "Should Arelle restart itself now "
                                     "(if there are any unsaved changes they would be lost!)?"
                                     ).format(affectedItems),
                                   parent=self):
                self.cntlr.uiThreadQueue.put((self.cntlr.quit, [None, True]))
        self.close()
        
    def close(self, event=None):
        self.parent.focus_set()
        self.destroy()
                
    def moduleSelect(self, *args):
        node = (self.modulesView.selection() or (None,))[0]
        moduleInfo = self.pluginConfig.get("modules", {}).get(node)
        if moduleInfo:
            self.selectedModule = node
            name = moduleInfo["name"]
            self.moduleNameLabel.config(text=name)
            self.moduleAuthorHdr.config(state=ACTIVE)
            self.moduleAuthorLabel.config(text=moduleInfo["author"])
            self.moduleDescrHdr.config(state=ACTIVE)
            self.moduleDescrLabel.config(text=moduleInfo["description"])
            self.moduleClassesHdr.config(state=ACTIVE)
            self.moduleClassesLabel.config(text=', '.join(moduleInfo["classMethods"]))
            self.moduleUrlHdr.config(state=ACTIVE)
            self.moduleUrlLabel.config(text=moduleInfo["moduleURL"])
            self.moduleDateHdr.config(state=ACTIVE)
            self.moduleDateLabel.config(text=moduleInfo["fileDate"] + " " +
                    (_("(an update is available)") if name in self.modulesWithNewerFileDates else ""))
            self.moduleLicenseHdr.config(state=ACTIVE)
            self.moduleLicenseLabel.config(text=moduleInfo["license"])
            self.moduleEnableButton.config(state=ACTIVE,
                                           text={"enabled":self.DISABLE,
                                                 "disabled":self.ENABLE}[moduleInfo["status"]])
            self.moduleReloadButton.config(state=ACTIVE)
            self.moduleRemoveButton.config(state=ACTIVE)
        else:
            self.selectedModule = None
            self.moduleNameLabel.config(text="")
            self.moduleAuthorHdr.config(state=DISABLED)
            self.moduleAuthorLabel.config(text="")
            self.moduleDescrHdr.config(state=DISABLED)
            self.moduleDescrLabel.config(text="")
            self.moduleClassesHdr.config(state=DISABLED)
            self.moduleClassesLabel.config(text="")
            self.moduleUrlHdr.config(state=DISABLED)
            self.moduleUrlLabel.config(text="")
            self.moduleDateHdr.config(state=DISABLED)
            self.moduleDateLabel.config(text="")
            self.moduleLicenseHdr.config(state=DISABLED)
            self.moduleLicenseLabel.config(text="")
            self.moduleEnableButton.config(state=DISABLED, text=self.ENABLE)
            self.moduleReloadButton.config(state=DISABLED)
            self.moduleRemoveButton.config(state=DISABLED)
        
    def findLocally(self):
        initialdir = self.cntlr.pluginDir # default plugin directory
        if not self.cntlr.isMac: # can't navigate within app easily, always start in default directory
            initialdir = self.cntlr.config.setdefault("pluginOpenDir", initialdir)
        filename = self.cntlr.uiFileDialog("open",
                                           parent=self,
                                           title=_("Choose plug-in module file"),
                                           initialdir=initialdir,
                                           filetypes=[(_("Python files"), "*.py")],
                                           defaultextension=".py")
        if filename:
            # check if a package is selected (any file in a directory containing an __init__.py
            if (os.path.isdir(os.path.dirname(filename)) and
                os.path.isfile(os.path.join(os.path.dirname(filename), "__init__.py"))):
                filename = os.path.dirname(filename) # refer to the package instead
            self.cntlr.config["pluginOpenDir"] = os.path.dirname(filename)
            moduleInfo = PluginManager.moduleModuleInfo(filename)
            self.loadFoundModuleInfo(moduleInfo, filename)
                

    def findOnWeb(self):
        url = DialogURL.askURL(self)
        if url:  # url is the in-cache or local file
            moduleInfo = PluginManager.moduleModuleInfo(url)
            self.cntlr.showStatus("") # clear web loading status
            self.loadFoundModuleInfo(moduleInfo, url)
                
    def loadFoundModuleInfo(self, moduleInfo, url):
        if moduleInfo and moduleInfo.get("name"):
            self.addPluginConfigModuleInfo(moduleInfo)
            self.loadTreeViews()
        else:
            messagebox.showwarning(_("Module is not itself a plug-in or in a directory with package __init__.py plug-in.  "),
                                   _("File does not itself contain a python program with an appropriate __pluginInfo__ declaration: \n\n{0}")
                                   .format(url),
                                   parent=self)
            
    def removePluginConfigModuleInfo(self, name):
        moduleInfo = self.pluginConfig["modules"].get(name)
        if moduleInfo:
            for classMethod in moduleInfo["classMethods"]:
                classMethods = self.pluginConfig["classes"].get(classMethod)
                if classMethods and name in classMethods:
                    classMethods.remove(name)
                    if not classMethods: # list has become unused
                        del self.pluginConfig["classes"][classMethod] # remove class
                    if classMethod.startswith("CntlrWinMain.Menu"):
                        self.uiClassMethodsChanged = True  # may require reloading UI
                    elif classMethod == "ModelObjectFactory.ElementSubstitutionClasses":
                        self.modelClassesChanged = True # model object factor classes changed
                    elif classMethod == "DisclosureSystem.Types":
                        self.disclosureSystemTypesChanged = True # disclosure system types changed
            del self.pluginConfig["modules"][name]
            self.pluginConfigChanged = True

    def addPluginConfigModuleInfo(self, moduleInfo):
        name = moduleInfo["name"]
        self.removePluginConfigModuleInfo(name)  # remove any prior entry for this module
        self.modulesWithNewerFileDates.discard(name) # no longer has an update available
        self.pluginConfig["modules"][name] = moduleInfo
        # add classes
        for classMethod in moduleInfo["classMethods"]:
            classMethods = self.pluginConfig["classes"].setdefault(classMethod, [])
            if name not in classMethods:
                classMethods.append(name)
            if classMethod.startswith("CntlrWinMain.Menu"):
                self.uiClassMethodsChanged = True  # may require reloading UI
            elif classMethod == "ModelObjectFactory.ElementSubstitutionClasses":
                self.modelClassesChanged = True # model object factor classes changed
            elif classMethod == "DisclosureSystem.Types":
                self.disclosureSystemTypesChanged = True # disclosure system types changed
        self.pluginConfigChanged = True

    def moduleEnable(self):
        if self.selectedModule in self.pluginConfig["modules"]:
            moduleInfo = self.pluginConfig["modules"][self.selectedModule]
            if self.moduleEnableButton['text'] == self.ENABLE:
                moduleInfo["status"] = "enabled"
                self.moduleEnableButton['text'] = self.DISABLE
            elif self.moduleEnableButton['text'] == self.DISABLE:
                moduleInfo["status"] = "disabled"
                self.moduleEnableButton['text'] = self.ENABLE
            self.pluginConfigChanged = True
            self.loadTreeViews()
            
    def moduleReload(self):
        if self.selectedModule in self.pluginConfig["modules"]:
            url = self.pluginConfig["modules"][self.selectedModule].get("moduleURL")
            if url:
                moduleInfo = PluginManager.moduleModuleInfo(url, reload=True)
                if moduleInfo:
                    self.addPluginConfigModuleInfo(moduleInfo)
                    self.loadTreeViews()
                    self.cntlr.showStatus(_("{0} reloaded").format(moduleInfo.get("name")), clearAfter=5000)
                else:
                    messagebox.showwarning(_("Module error"),
                                           _("File or module cannot be reloaded: \n\n{0}")
                                           .format(url),
                                           parent=self)

    def moduleRemove(self):
        if self.selectedModule in self.pluginConfig["modules"]:
            self.removePluginConfigModuleInfo(self.selectedModule)
            self.pluginConfigChanged = True
            self.loadTreeViews()
                    
    def enableAll(self):
        self.enableDisableAll(True)
                    
    def disableAll(self):
        self.enableDisableAll(False)
                    
    def enableDisableAll(self, doEnable):
        for module in self.pluginConfig["modules"]:
            moduleInfo = self.pluginConfig["modules"][module]
            if doEnable:
                moduleInfo["status"] = "enabled"
                self.moduleEnableButton['text'] = self.DISABLE
            else:
                moduleInfo["status"] = "disabled"
                self.moduleEnableButton['text'] = self.ENABLE
        self.pluginConfigChanged = True
        self.loadTreeViews()
            
class ComponentsDumpWindow(Toplevel):
	def __init__(self, application, barNumber = None, **kwargs):
		Toplevel.__init__(self)
		
		self.application = application
		self.barNumber = barNumber
		
		
		self.fromLabel = Label(self)
		self.fromLabel.grid(column = 0, row = 0)
		
		self.fromVar = StringVar()
		self.fromEntry = Entry(self, textvariable = self.fromVar, justify = RIGHT)
		self.fromEntry.grid(column = 1, row = 0, sticky = W + E)
		self.columnconfigure(1, weight = 1)
		
		
		self.toLabel = Label(self)
		self.toLabel.grid(column = 2, row = 0)
		
		self.toVar = StringVar()
		self.toEntry = Entry(self, textvariable = self.toVar, justify = RIGHT)
		self.toEntry.grid(column = 3, row = 0, sticky = W + E)
		self.columnconfigure(3, weight = 1)
		
		
		self.stepLabel = Label(self, text = "dx:")
		self.stepLabel.grid(column = 4, row = 0)
		
		self.stepVar = StringVar()
		self.stepEntry = Entry(self, textvariable = self.stepVar, justify = RIGHT)
		self.stepEntry.grid(column = 5, row = 0, sticky = W + E)
		self.columnconfigure(5, weight = 1)
		
		
		self.calculateButton = Button(self, text = "Рассчитать",
									  command = self.onCalculateButtonClicked)
		self.calculateButton.grid(column = 6, row = 0, columnspan = 2)
		
		
		# Таблица рассчитанных значений
		columns = ("№ стержня", "x", "Nx", "U", "σ")
		
		self.tree = Treeview(self, columns = columns, displaycolumns = columns)
		self.tree.grid(column = 0, row = 1, columnspan = 7, sticky = W + N + E + S)
		self.tree.column("#0", width = 0, stretch = 0)
		
		# Настройки отображения таблицы
		self.tree.column(columns[0], anchor = CENTER)
		self.tree.heading(columns[0], text = columns[0], anchor = CENTER)
		
		for x in columns[1:]:
			self.tree.column(x, anchor = E)
			self.tree.heading(x, text = x, anchor = E)
		
		self.rowconfigure(1, weight = 1)
		
		
		self.bind("<Destroy>", self.onWindowDestroy)
		
		
		self.updateTitles()
		self.onConstructionChanged(False)
	
	
	def updateTitles(self):
		if self.barNumber is None:
			titleDescStr = ""
			xDescStr     = "global"
		else:
			titleDescStr = "%sСтержень (%d)" % (self.application.nameDelim, self.barNumber)
			xDescStr     = "local"
		
		self.title("%s%sКомпоненты%s" \
				   % (self.application.name, self.application.nameDelim, titleDescStr))
		
		self.fromLabel["text"] = "От x(" + xDescStr + "):"
		self.toLabel["text"] = "До x(" + xDescStr + "):"
	
	
	def onWindowDestroy(self, event):
		self.application.onWindowDestroy(self)
	
	
	def onCalculateButtonClicked(self):
		try:
			self.calculate()
		except Exception as e:
			self.showError(e)
	
	
	def onConstructionChanged(self, constructed = True):
		if not constructed:
			for var in self.fromVar, self.toVar, self.stepVar:
				var.set("0")
			return
		
		try:
			self.calculate()
		except Exception:
			self.clear()
	
	
	def onPointCalculated(self, barNumber, x, N, U, Sigma):
		if self.barNumber is not None and barNumber != self.barNumber:
			return
		
		self.tree.insert(parent = "", index = "end",
						 values = ("—" if barNumber is None else str(barNumber),
								   "%.14f" % x, "%.14f" % N,
								   "%.14f" % U, "%.14f" % Sigma))
	
	
	def clear(self):
		self.tree.delete(*self.tree.get_children())
	
	
	def calculate(self):
		self.clear()
		
		if self.barNumber not in range(0, self.application.logic.barsCount()):
			self.barNumber = None
			self.updateTitles()
		
		for var in self.fromVar, self.toVar, self.stepVar:
			try:
				float(var.get())
			except ValueError:
				var.set("0")
		
		xFrom = float(self.fromVar.get())
		xTo = float(self.toVar.get())
		xStep = float(self.stepVar.get())
		
		self.application.logic.calculateComponents(xFrom, xTo, xStep, barNumber = self.barNumber,
												   onPointCalculated = self.onPointCalculated)
	
	
	def showError(self, message):
		tkinter.messagebox.showerror("Ошибка", message)
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")
Example #41
0
class Tree(LibraryListener):
    def __init__(self, config, master):
        self.views = config['views']
        self.library = None
        self.__make_widgets(master)

    def __make_widgets(self, root):
        self.tree = Treeview(root)

    def on_library_change(self, library):
        self.library = library
        self.__make_root_nodes()

    def __make_root_nodes(self):
        for i in self.tree.get_children():
            self.tree.delete(i)
        root_nodes = self.query_library({}, self.views[0])
        for node in root_nodes:
            child = self.tree.insert("", "end", self.__make_node_id(None, self.views[0], node), text=node,
                                     tags=self.views[0])
            self.fetch_children(child)

    @staticmethod
    def __make_node_id(parent_node, node_type, node_value):
        node_id = {}
        if parent_node:
            node_id = copy(parent_node)
        node_id[node_type] = node_value
        return node_id

    def can_be_expanded(self, node_type):
        return node_type != self.views[len(self.views) - 1]

    def get_child_node_type(self, node_type):
        return self.views[self.views.index(node_type) + 1]

    def query_library(self, node_id, view):
        return self.library.aggregate(node_id, view)

    def __has_children(self, node):
        return len(self.children(node)) > 0

    def fetch_children(self, node):
        if not self.__has_children(node):
            node_type = self.tree.item(node, "tags")[0]
            if self.can_be_expanded(node_type):
                children_node_type = self.get_child_node_type(node_type)
                node_id = literal_eval(node)
                nodes = self.query_library(node_id, children_node_type)
                for child_node in nodes:
                    self.tree.insert(node, "end", self.__make_node_id(node_id, children_node_type, child_node),
                                     text=child_node, tags=children_node_type)

    def delete_children(self, parent_node):
        children = self.children(parent_node)
        for node in children:
            self.tree.delete(node)

    @property
    def selected_node(self):
        if len(self.tree.selection()):
            return self.tree.selection()[0]

    def children(self, node):
        return self.tree.get_children(node)
Example #42
0
class MainFrame(Frame):
    """ Główna ramka aplikacji
    """

    def __init__(self, ui: SharedrawUI):
        Frame.__init__(self, ui.root)
        self.parent = ui.root
        self.ui = ui

        self.parent.title("Sharedraw [%s:%s, id: %s]" % (self.ui.peer_pool.ip, self.ui.peer_pool.port, own_id))
        self.drawer = Drawer(self.parent, WIDTH, HEIGHT, self.save)
        self.clients_table = Treeview(self.parent, columns=('R', 'G', 'from'))
        self.clients_table.heading('#0', text='Id')
        self.clients_table.heading('R', text='R')
        self.clients_table.heading('G', text='G')
        self.clients_table.heading('from', text='Otrzymano od:')
        self.clients_table.pack()
        self.token_owner_label = MutableLabel(self.parent, 'Posiadacz tokena: %s', lambda s: s if s else 'Nikt', None)
        self.token_owner_label.pack()
        self.locked_label = MutableLabel(self.parent, 'Tablica %s', lambda b: 'zablokowana' if b else 'odblokowana',
                                         False)
        self.locked_label.pack()
        # Przycisk czyszczenia
        self.clean_btn = self._create_button(text="Czyść", func=self.clean)
        # Przycisk podłączenia
        self.connect_btn = self._create_button(text="Podłącz", func=self.connect)
        # Przycisk żądania przejęcia na własność
        self.req_btn = self._create_button(text="Chcę przejąć tablicę", func=self._make_request)
        # Rezygnacja z posiadania blokady
        self.resign_btn = self._create_button(text='Zrezygnuj z blokady', func=self._resign)

    def _create_button(self, text: str, func):
        """ Tworzy nowy przycisk w bieżącej ramce
        :param text: tekst przycisku
        :param func: funkcja wywoływana po naciśnięciu
        :return: przycisk
        """
        btn = Button(self.parent, text=text, command=func)
        btn.pack()
        return btn

    def save(self):
        """ Wysyła komunikat o zmianie obrazka do innych klientów
        :return:
        """
        if self.drawer.changed_pxs:
            msg = PaintMessage(self.drawer.changed_pxs, self.drawer.color)
            self.ui.peer_pool.send(msg)
            # Reset listy punktów
            self.drawer.changed_pxs = []

    def clean(self):
        """ Czyści obrazek oraz wysyła komunikat o wyczyszczeniu
        :return:
        """
        self.drawer.clean_img()
        msg = CleanMessage(own_id)
        self.ui.peer_pool.send(msg)

    def _make_request(self):
        """ Żąda przejęcia tablicy na własność
        :return:
        """
        clients_info = self.ui.om.claim_ownership()
        self.update_clients_info(clients_info)

    def _resign(self):
        clients_info = self.ui.om.resign()
        self.update_clients_info(clients_info)

    def connect(self):
        """ Uruchamia okno dialogowe do podłączenia się z innym klientem
        :return:
        """
        d = ConnectDialog(self.ui)
        self.parent.wait_window(d.top)

    def update_clients_info(self, clients: ClientsTable):
        # Aktualizacja listy klientów
        for ch in self.clients_table.get_children():
            self.clients_table.delete(ch)
        for client in clients.clients:
            self.clients_table.insert('', 'end', text=client.id,
                                      values=(client.requested, client.granted, client.received_from_id))
        # Aktualizacja info o właścicielu tokena i blokadzie
        self.locked_label.update_text(clients.locked)
        self.token_owner_label.update_text(clients.token_owner)
        # Aktualizacja blokady
        has_token = clients.token_owner == own_id
        self.__set_lock_state(clients.locked, has_token)
        # Aktualizacja przycisków
        # jeśli zablokowaliśmy, to nie możemy tego zrobić drugi raz
        is_locker = (has_token and clients.locked)
        # tak samo jeśli już zażądaliśmy
        has_requested = clients.find_self().has_requested()
        self.__set_button_enabled(self.req_btn, not (is_locker or has_requested))
        # jeśli nie zablokowaliśmy, to nie możemy rezygnować
        self.__set_button_enabled(self.resign_btn, is_locker)
        # Możemy się podłączyć, tylko, jeśli nie jesteśmy do nikogo podłączeni
        self.__set_button_enabled(self.connect_btn, len(clients.clients) <= 1)
        # Przycisk czyść aktywny jeśli możemy rysować
        self.__set_button_enabled(self.clean_btn, has_token or not clients.locked)

    @staticmethod
    def __set_button_enabled(btn: Button, enabled: bool):
        btn.configure(state=(NORMAL if enabled else DISABLED))

    def __set_lock_state(self, locked: bool, has_token: bool):
        self.drawer.locked = locked and not has_token
        # Dodatkowo ustawiamy kolor tła, żeby było ładnie widać
        if locked:
            if has_token:
                color = '#66FF66'
            else:
                color = '#FF9999'
        else:
            color = '#FFFFFF'
        self.parent.configure(bg=color)
class DialogPluginManager(Toplevel):
    def __init__(self, mainWin, modulesWithNewerFileDates):
        super(DialogPluginManager, self).__init__(mainWin.parent)
        
        self.ENABLE = _("Enable")
        self.DISABLE = _("Disable")
        self.parent = mainWin.parent
        self.cntlr = mainWin
        
        # copy plugins for temporary display
        self.pluginConfig = PluginManager.pluginConfig
        self.pluginConfigChanged = False
        self.uiClassMethodsChanged = False
        self.modelClassesChanged = False
        self.disclosureSystemTypesChanged = False
        self.hostSystemFeaturesChanged = False
        self.modulesWithNewerFileDates = modulesWithNewerFileDates
        
        parentGeometry = re.match("(\d+)x(\d+)[+]?([-]?\d+)[+]?([-]?\d+)", self.parent.geometry())
        dialogX = int(parentGeometry.group(3))
        dialogY = int(parentGeometry.group(4))

        self.title(_("Plug-in Manager"))
        frame = Frame(self)
        
        # left button frame
        buttonFrame = Frame(frame, width=40)
        buttonFrame.columnconfigure(0, weight=1)
        addLabel = Label(buttonFrame, text=_("Find plug-in modules:"), wraplength=60, justify="center")
        addLocalButton = Button(buttonFrame, text=_("Locally"), command=self.findLocally)
        ToolTip(addLocalButton, text=_("File chooser allows selecting python module files to add (or reload) plug-ins, from the local file system."), wraplength=240)
        addWebButton = Button(buttonFrame, text=_("On Web"), command=self.findOnWeb)
        ToolTip(addWebButton, text=_("Dialog to enter URL full path to load (or reload) plug-ins, from the web or local file system."), wraplength=240)
        addLabel.grid(row=0, column=0, pady=4)
        addLocalButton.grid(row=1, column=0, pady=4)
        addWebButton.grid(row=2, column=0, pady=4)
        buttonFrame.grid(row=0, column=0, rowspan=2, sticky=(N, S, W), padx=3, pady=3)
        
        # right tree frame (plugins already known to arelle)
        modulesFrame = Frame(frame, width=700)
        vScrollbar = Scrollbar(modulesFrame, orient=VERTICAL)
        hScrollbar = Scrollbar(modulesFrame, orient=HORIZONTAL)
        self.modulesView = Treeview(modulesFrame, xscrollcommand=hScrollbar.set, yscrollcommand=vScrollbar.set, height=7)
        self.modulesView.grid(row=0, column=0, sticky=(N, S, E, W))
        self.modulesView.bind('<<TreeviewSelect>>', self.moduleSelect)
        hScrollbar["command"] = self.modulesView.xview
        hScrollbar.grid(row=1, column=0, sticky=(E,W))
        vScrollbar["command"] = self.modulesView.yview
        vScrollbar.grid(row=0, column=1, sticky=(N,S))
        modulesFrame.columnconfigure(0, weight=1)
        modulesFrame.rowconfigure(0, weight=1)
        modulesFrame.grid(row=0, column=1, columnspan=4, sticky=(N, S, E, W), padx=3, pady=3)
        self.modulesView.focus_set()

        self.modulesView.column("#0", width=120, anchor="w")
        self.modulesView.heading("#0", text=_("Name"))
        self.modulesView["columns"] = ("author", "ver", "status", "date", "update", "descr", "license")
        self.modulesView.column("author", width=100, anchor="w", stretch=False)
        self.modulesView.heading("author", text=_("Author"))
        self.modulesView.column("ver", width=50, anchor="w", stretch=False)
        self.modulesView.heading("ver", text=_("Version"))
        self.modulesView.column("status", width=50, anchor="w", stretch=False)
        self.modulesView.heading("status", text=_("Status"))
        self.modulesView.column("date", width=70, anchor="w", stretch=False)
        self.modulesView.heading("date", text=_("File Date"))
        self.modulesView.column("update", width=50, anchor="w", stretch=False)
        self.modulesView.heading("update", text=_("Update"))
        self.modulesView.column("descr", width=200, anchor="w", stretch=False)
        self.modulesView.heading("descr", text=_("Description"))
        self.modulesView.column("license", width=70, anchor="w", stretch=False)
        self.modulesView.heading("license", text=_("License"))

        classesFrame = Frame(frame)
        vScrollbar = Scrollbar(classesFrame, orient=VERTICAL)
        hScrollbar = Scrollbar(classesFrame, orient=HORIZONTAL)
        self.classesView = Treeview(classesFrame, xscrollcommand=hScrollbar.set, yscrollcommand=vScrollbar.set, height=5)
        self.classesView.grid(row=0, column=0, sticky=(N, S, E, W))
        hScrollbar["command"] = self.classesView.xview
        hScrollbar.grid(row=1, column=0, sticky=(E,W))
        vScrollbar["command"] = self.classesView.yview
        vScrollbar.grid(row=0, column=1, sticky=(N,S))
        classesFrame.columnconfigure(0, weight=1)
        classesFrame.rowconfigure(0, weight=1)
        classesFrame.grid(row=1, column=1, columnspan=4, sticky=(N, S, E, W), padx=3, pady=3)
        self.classesView.focus_set()
        
        self.classesView.column("#0", width=200, anchor="w")
        self.classesView.heading("#0", text=_("Class"))
        self.classesView["columns"] = ("modules",)
        self.classesView.column("modules", width=500, anchor="w", stretch=False)
        self.classesView.heading("modules", text=_("Modules"))
        
        # bottom frame module info details
        moduleInfoFrame = Frame(frame, width=700)
        moduleInfoFrame.columnconfigure(1, weight=1)
        
        self.moduleNameLabel = Label(moduleInfoFrame, wraplength=600, justify="left", 
                                     font=font.Font(family='Helvetica', size=12, weight='bold'))
        self.moduleNameLabel.grid(row=0, column=0, columnspan=4, sticky=W)
        self.moduleAuthorHdr = Label(moduleInfoFrame, text=_("author:"), state=DISABLED)
        self.moduleAuthorHdr.grid(row=1, column=0, sticky=W)
        self.moduleAuthorLabel = Label(moduleInfoFrame, wraplength=600, justify="left")
        self.moduleAuthorLabel.grid(row=1, column=1, columnspan=3, sticky=W)
        self.moduleDescrHdr = Label(moduleInfoFrame, text=_("description:"), state=DISABLED)
        self.moduleDescrHdr.grid(row=2, column=0, sticky=W)
        self.moduleDescrLabel = Label(moduleInfoFrame, wraplength=600, justify="left")
        self.moduleDescrLabel.grid(row=2, column=1, columnspan=3, sticky=W)
        self.moduleClassesHdr = Label(moduleInfoFrame, text=_("classes:"), state=DISABLED)
        self.moduleClassesHdr.grid(row=3, column=0, sticky=W)
        self.moduleClassesLabel = Label(moduleInfoFrame, wraplength=600, justify="left")
        self.moduleClassesLabel.grid(row=3, column=1, columnspan=3, sticky=W)
        ToolTip(self.moduleClassesLabel, text=_("List of classes that this plug-in handles."), wraplength=240)
        self.moduleUrlHdr = Label(moduleInfoFrame, text=_("URL:"), state=DISABLED)
        self.moduleUrlHdr.grid(row=4, column=0, sticky=W)
        self.moduleUrlLabel = Label(moduleInfoFrame, wraplength=600, justify="left")
        self.moduleUrlLabel.grid(row=4, column=1, columnspan=3, sticky=W)
        ToolTip(self.moduleUrlLabel, text=_("URL of plug-in module (local file path or web loaded file)."), wraplength=240)
        self.moduleDateHdr = Label(moduleInfoFrame, text=_("date:"), state=DISABLED)
        self.moduleDateHdr.grid(row=5, column=0, sticky=W)
        self.moduleDateLabel = Label(moduleInfoFrame, wraplength=600, justify="left")
        self.moduleDateLabel.grid(row=5, column=1, columnspan=3, sticky=W)
        ToolTip(self.moduleDateLabel, text=_("Date of currently loaded module file (with parenthetical node when an update is available)."), wraplength=240)
        self.moduleLicenseHdr = Label(moduleInfoFrame, text=_("license:"), state=DISABLED)
        self.moduleLicenseHdr.grid(row=6, column=0, sticky=W)
        self.moduleLicenseLabel = Label(moduleInfoFrame, wraplength=600, justify="left")
        self.moduleLicenseLabel.grid(row=6, column=1, columnspan=3, sticky=W)
        self.moduleImportsHdr = Label(moduleInfoFrame, text=_("imports:"), state=DISABLED)
        self.moduleImportsHdr.grid(row=7, column=0, sticky=W)
        self.moduleImportsLabel = Label(moduleInfoFrame, wraplength=600, justify="left")
        self.moduleImportsLabel.grid(row=7, column=1, columnspan=3, sticky=W)
        self.moduleEnableButton = Button(moduleInfoFrame, text=self.ENABLE, state=DISABLED, command=self.moduleEnable)
        ToolTip(self.moduleEnableButton, text=_("Enable/disable plug in."), wraplength=240)
        self.moduleEnableButton.grid(row=8, column=1, sticky=E)
        self.moduleReloadButton = Button(moduleInfoFrame, text=_("Reload"), state=DISABLED, command=self.moduleReload)
        ToolTip(self.moduleReloadButton, text=_("Reload/update plug in."), wraplength=240)
        self.moduleReloadButton.grid(row=8, column=2, sticky=E)
        self.moduleRemoveButton = Button(moduleInfoFrame, text=_("Remove"), state=DISABLED, command=self.moduleRemove)
        ToolTip(self.moduleRemoveButton, text=_("Remove plug in from plug in table (does not erase the plug in's file)."), wraplength=240)
        self.moduleRemoveButton.grid(row=8, column=3, sticky=E)
        moduleInfoFrame.grid(row=2, column=0, columnspan=5, sticky=(N, S, E, W), padx=3, pady=3)
        moduleInfoFrame.config(borderwidth=4, relief="groove")
        
        okButton = Button(frame, text=_("Close"), command=self.ok)
        ToolTip(okButton, text=_("Accept and changes (if any) and close dialog."), wraplength=240)
        cancelButton = Button(frame, text=_("Cancel"), command=self.close)
        ToolTip(cancelButton, text=_("Cancel changes (if any) and close dialog."), wraplength=240)
        okButton.grid(row=3, column=3, sticky=(S,E), pady=3)
        cancelButton.grid(row=3, column=4, sticky=(S,E), pady=3, padx=3)
        
        enableDisableFrame = Frame(frame)
        enableDisableFrame.grid(row=3, column=1, sticky=(S,W), pady=3)
        enableAllButton = Button(enableDisableFrame, text=_("Enable All"), command=self.enableAll)
        ToolTip(enableAllButton, text=_("Enable all plug ins."), wraplength=240)
        disableAllButton = Button(enableDisableFrame, text=_("Disable All"), command=self.disableAll)
        ToolTip(disableAllButton, text=_("Disable all plug ins."), wraplength=240)
        enableAllButton.grid(row=1, column=1)
        disableAllButton.grid(row=1, column=2)
        
        self.loadTreeViews()

        self.geometry("+{0}+{1}".format(dialogX+50,dialogY+100))
        frame.grid(row=0, column=0, sticky=(N,S,E,W))
        frame.columnconfigure(0, weight=0)
        frame.columnconfigure(1, 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.protocol("WM_DELETE_WINDOW", self.close)
        self.grab_set()
        self.wait_window(self)
        
    def loadTreeViews(self):
        self.selectedModule = None

        # clear previous treeview entries
        for previousNode in self.modulesView.get_children(""): 
            self.modulesView.delete(previousNode)
            
        def loadSubtree(parentNode, moduleItems):
            for moduleItem in sorted(moduleItems, key=lambda item: item[0]):
                moduleInfo = moduleItem[1]
                if parentNode or not moduleInfo.get("isImported"):
                    nodeName = moduleItem[0]
                    if parentNode:
                        nodeName = parentNode + GROUPSEP + nodeName
                    name = moduleInfo.get("name", nodeName)
                    node = self.modulesView.insert(parentNode, "end", nodeName, text=name)
                    self.modulesView.set(node, "author", moduleInfo.get("author"))
                    self.modulesView.set(node, "ver", moduleInfo.get("version"))
                    self.modulesView.set(node, "status", moduleInfo.get("status"))
                    self.modulesView.set(node, "date", moduleInfo.get("fileDate"))
                    if name in self.modulesWithNewerFileDates:
                        self.modulesView.set(node, "update", _("available"))
                    self.modulesView.set(node, "descr", moduleInfo.get("description"))
                    self.modulesView.set(node, "license", moduleInfo.get("license"))
                    if moduleInfo.get("imports"):
                        loadSubtree(node, [(importModuleInfo["name"],importModuleInfo)
                                           for importModuleInfo in moduleInfo["imports"]])
            
        loadSubtree("", self.pluginConfig.get("modules", {}).items())
        
        # clear previous treeview entries
        for previousNode in self.classesView.get_children(""): 
            self.classesView.delete(previousNode)

        for i, classItem in enumerate(sorted(self.pluginConfig.get("classes", {}).items())):
            className, moduleList = classItem
            node = self.classesView.insert("", "end", className, text=className)
            self.classesView.set(node, "modules", ', '.join(moduleList))
            
        self.moduleSelect()  # clear out prior selection

    def ok(self, event=None):
        if self.pluginConfigChanged:
            PluginManager.pluginConfig = self.pluginConfig
            PluginManager.pluginConfigChanged = True
            PluginManager.reset()  # force reloading of modules
        if self.uiClassMethodsChanged or self.modelClassesChanged or self.disclosureSystemTypesChanged or self.hostSystemFeaturesChanged:  # may require reloading UI
            affectedItems = ""
            if self.uiClassMethodsChanged:
                affectedItems += _("menus of the user interface")
            if self.modelClassesChanged:
                if affectedItems:
                    affectedItems += _(" and ")
                affectedItems += _("model objects of the processor")
            if self.disclosureSystemTypesChanged:
                if affectedItems:
                    affectedItems += _(" and ")
                affectedItems += _("disclosure system types")
            if self.hostSystemFeaturesChanged:
                if affectedItems:
                    affectedItems += _(" and ")
                affectedItems += _("host system features")
            if messagebox.askyesno(_("User interface plug-in change"),
                                   _("A change in plug-in class methods may have affected {0}.  " 
                                     "Please restart Arelle to due to these changes.  \n\n"
                                     "Should Arelle restart itself now "
                                     "(if there are any unsaved changes they would be lost!)?"
                                     ).format(affectedItems),
                                   parent=self):
                self.cntlr.uiThreadQueue.put((self.cntlr.quit, [None, True]))
        self.close()
        
    def close(self, event=None):
        self.parent.focus_set()
        self.destroy()
                
    def moduleSelect(self, *args):
        node = (self.modulesView.selection() or (None,))[0]
        if node:
            node = node.rpartition(GROUPSEP)[2] # drop leading path names for module name
        moduleInfo = self.pluginConfig.get("modules", {}).get(node)
        if moduleInfo:
            self.selectedModule = node
            name = moduleInfo["name"]
            self.moduleNameLabel.config(text=name)
            self.moduleAuthorHdr.config(state=ACTIVE)
            self.moduleAuthorLabel.config(text=moduleInfo["author"])
            self.moduleDescrHdr.config(state=ACTIVE)
            self.moduleDescrLabel.config(text=moduleInfo["description"])
            self.moduleClassesHdr.config(state=ACTIVE)
            self.moduleClassesLabel.config(text=', '.join(moduleInfo["classMethods"]))
            self.moduleUrlHdr.config(state=ACTIVE)
            self.moduleUrlLabel.config(text=moduleInfo["moduleURL"])
            self.moduleDateHdr.config(state=ACTIVE)
            self.moduleDateLabel.config(text=moduleInfo["fileDate"] + " " +
                    (_("(an update is available)") if name in self.modulesWithNewerFileDates else ""))
            self.moduleLicenseHdr.config(state=ACTIVE)
            self.moduleLicenseLabel.config(text=moduleInfo["license"])
            if moduleInfo.get("imports"):
                self.moduleImportsHdr.config(state=ACTIVE)
                _text = ", ".join(mi["name"] for mi in moduleInfo["imports"][:3])
                if len(moduleInfo["imports"]) >= 3:
                    _text += ", ..."
                self.moduleImportsLabel.config(text=_text)
            _buttonState = DISABLED if moduleInfo.get("isImported") else ACTIVE
            self.moduleEnableButton.config(state=_buttonState,
                                           text={"enabled":self.DISABLE,
                                                 "disabled":self.ENABLE}[moduleInfo["status"]])
            self.moduleReloadButton.config(state=_buttonState)
            self.moduleRemoveButton.config(state=_buttonState)
        else:
            self.selectedModule = None
            self.moduleNameLabel.config(text="")
            self.moduleAuthorHdr.config(state=DISABLED)
            self.moduleAuthorLabel.config(text="")
            self.moduleDescrHdr.config(state=DISABLED)
            self.moduleDescrLabel.config(text="")
            self.moduleClassesHdr.config(state=DISABLED)
            self.moduleClassesLabel.config(text="")
            self.moduleUrlHdr.config(state=DISABLED)
            self.moduleUrlLabel.config(text="")
            self.moduleDateHdr.config(state=DISABLED)
            self.moduleDateLabel.config(text="")
            self.moduleLicenseHdr.config(state=DISABLED)
            self.moduleLicenseLabel.config(text="")
            self.moduleImportsHdr.config(state=DISABLED)
            self.moduleImportsLabel.config(text="")
            self.moduleEnableButton.config(state=DISABLED, text=self.ENABLE)
            self.moduleReloadButton.config(state=DISABLED)
            self.moduleRemoveButton.config(state=DISABLED)
        
    def findLocally(self):
        initialdir = self.cntlr.pluginDir # default plugin directory
        if not self.cntlr.isMac: # can't navigate within app easily, always start in default directory
            initialdir = self.cntlr.config.setdefault("pluginOpenDir", initialdir)
        filename = self.cntlr.uiFileDialog("open",
                                           parent=self,
                                           title=_("Choose plug-in module file"),
                                           initialdir=initialdir,
                                           filetypes=[(_("Python files"), "*.py")],
                                           defaultextension=".py")
        if filename:
            # check if a package is selected (any file in a directory containing an __init__.py
            #if (os.path.basename(filename) == "__init__.py" and os.path.isdir(os.path.dirname(filename)) and
            #    os.path.isfile(filename)):
            #    filename = os.path.dirname(filename) # refer to the package instead
            self.cntlr.config["pluginOpenDir"] = os.path.dirname(filename)
            moduleInfo = PluginManager.moduleModuleInfo(filename)
            self.loadFoundModuleInfo(moduleInfo, filename)
                

    def findOnWeb(self):
        url = DialogURL.askURL(self)
        if url:  # url is the in-cache or local file
            moduleInfo = PluginManager.moduleModuleInfo(url)
            self.cntlr.showStatus("") # clear web loading status
            self.loadFoundModuleInfo(moduleInfo, url)
                
    def loadFoundModuleInfo(self, moduleInfo, url):
        if moduleInfo and moduleInfo.get("name"):
            self.addPluginConfigModuleInfo(moduleInfo)
            self.loadTreeViews()
        else:
            messagebox.showwarning(_("Module is not itself a plug-in or in a directory with package __init__.py plug-in.  "),
                                   _("File does not itself contain a python program with an appropriate __pluginInfo__ declaration: \n\n{0}")
                                   .format(url),
                                   parent=self)
        
    def checkIfImported(self, moduleInfo):
        if moduleInfo.get("isImported"):
            messagebox.showwarning(_("Plug-in is imported by a parent plug-in.  "),
                                   _("Plug-in has a parent, please request operation on the parent: \n\n{0}")
                                   .format(moduleInfo.get("name")),
                                   parent=self)
            return True
        return False
    
    def removePluginConfigModuleInfo(self, name):
        moduleInfo = self.pluginConfig["modules"].get(name)
        if moduleInfo:
            if self.checkIfImported(moduleInfo):
                return;
            def _removePluginConfigModuleInfo(moduleInfo):
                _name = moduleInfo.get("name")
                if _name:
                    for classMethod in moduleInfo["classMethods"]:
                        classMethods = self.pluginConfig["classes"].get(classMethod)
                        if classMethods and _name in classMethods:
                            classMethods.remove(_name)
                            if not classMethods: # list has become unused
                                del self.pluginConfig["classes"][classMethod] # remove class
                            if classMethod.startswith("CntlrWinMain.Menu"):
                                self.uiClassMethodsChanged = True  # may require reloading UI
                            elif classMethod == "ModelObjectFactory.ElementSubstitutionClasses":
                                self.modelClassesChanged = True # model object factor classes changed
                            elif classMethod == "DisclosureSystem.Types":
                                self.disclosureSystemTypesChanged = True # disclosure system types changed
                            elif classMethod.startswith("Proxy."):
                                self.hostSystemFeaturesChanged = True # system features (e.g., proxy) changed
                    for importModuleInfo in moduleInfo.get("imports", EMPTYLIST):
                        _removePluginConfigModuleInfo(importModuleInfo)
                    self.pluginConfig["modules"].pop(_name, None)
            _removePluginConfigModuleInfo(moduleInfo)
            self.pluginConfigChanged = True

    def addPluginConfigModuleInfo(self, moduleInfo):
        if self.checkIfImported(moduleInfo):
            return;
        name = moduleInfo.get("name")
        self.removePluginConfigModuleInfo(name)  # remove any prior entry for this module
        def _addPlugin(moduleInfo):
            _name = moduleInfo.get("name")
            if _name:
                self.modulesWithNewerFileDates.discard(_name) # no longer has an update available
                self.pluginConfig["modules"][_name] = moduleInfo
                # add classes
                for classMethod in moduleInfo["classMethods"]:
                    classMethods = self.pluginConfig["classes"].setdefault(classMethod, [])
                    if name not in classMethods:
                        classMethods.append(_name)
                    if classMethod.startswith("CntlrWinMain.Menu"):
                        self.uiClassMethodsChanged = True  # may require reloading UI
                    elif classMethod == "ModelObjectFactory.ElementSubstitutionClasses":
                        self.modelClassesChanged = True # model object factor classes changed
                    elif classMethod == "DisclosureSystem.Types":
                        self.disclosureSystemTypesChanged = True # disclosure system types changed
                    elif classMethod.startswith("Proxy."):
                        self.hostSystemFeaturesChanged = True # system features (e.g., proxy) changed
            for importModuleInfo in moduleInfo.get("imports", EMPTYLIST):
                _addPlugin(importModuleInfo)
        _addPlugin(moduleInfo)
        self.pluginConfigChanged = True

    def moduleEnable(self):
        if self.selectedModule in self.pluginConfig["modules"]:
            moduleInfo = self.pluginConfig["modules"][self.selectedModule]
            if self.checkIfImported(moduleInfo):
                return;
            def _moduleEnable(moduleInfo):
                if self.moduleEnableButton['text'] == self.ENABLE:
                    moduleInfo["status"] = "enabled"
                elif self.moduleEnableButton['text'] == self.DISABLE:
                    moduleInfo["status"] = "disabled"
                for importModuleInfo in moduleInfo.get("imports", EMPTYLIST):
                    _moduleEnable(importModuleInfo)
            _moduleEnable(moduleInfo)
            if self.moduleEnableButton['text'] == self.ENABLE:
                self.moduleEnableButton['text'] = self.DISABLE
            elif self.moduleEnableButton['text'] == self.DISABLE:
                self.moduleEnableButton['text'] = self.ENABLE
            self.pluginConfigChanged = True
            self.loadTreeViews()
            
    def moduleReload(self):
        if self.selectedModule in self.pluginConfig["modules"]:
            url = self.pluginConfig["modules"][self.selectedModule].get("moduleURL")
            if url:
                moduleInfo = PluginManager.moduleModuleInfo(url, reload=True)
                if moduleInfo:
                    if self.checkIfImported(moduleInfo):
                        return;
                    self.addPluginConfigModuleInfo(moduleInfo)
                    self.loadTreeViews()
                    self.cntlr.showStatus(_("{0} reloaded").format(moduleInfo["name"]), clearAfter=5000)
                else:
                    messagebox.showwarning(_("Module error"),
                                           _("File or module cannot be reloaded: \n\n{0}")
                                           .format(url),
                                           parent=self)

    def moduleRemove(self):
        if self.selectedModule in self.pluginConfig["modules"]:
            self.removePluginConfigModuleInfo(self.selectedModule)
            self.pluginConfigChanged = True
            self.loadTreeViews()
                    
    def enableAll(self):
        self.enableDisableAll(True)
                    
    def disableAll(self):
        self.enableDisableAll(False)
                    
    def enableDisableAll(self, doEnable):
        for module in self.pluginConfig["modules"]:
            if not module.get("isImported"):
                moduleInfo = self.pluginConfig["modules"][module]
                def _enableDisableAll(moduleInfo):
                    if doEnable:
                        moduleInfo["status"] = "enabled"
                    else:
                        moduleInfo["status"] = "disabled"
                    for importModuleInfo in moduleInfo.get("imports", EMPTYLIST):
                        _enableDisableAll(importModuleInfo)
                _enableDisableAll(moduleInfo)
                if doEnable:
                    self.moduleEnableButton['text'] = self.DISABLE
                else:
                    self.moduleEnableButton['text'] = self.ENABLE
        self.pluginConfigChanged = True
        self.loadTreeViews()