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
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))
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)
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
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"])
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))
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])
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))
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
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)
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)
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']
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
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)
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))
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)
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()
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))
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
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')
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()
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()
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")
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() ]
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()
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()
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))
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
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
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
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()
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")
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")
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)
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()