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']
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 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))
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 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))
class Manager(Toplevel): def __init__(self, master): Toplevel.__init__(self, master, class_=APP_NAME) self.title(_("Manage Feeds")) self.grab_set() self.columnconfigure(0, weight=1) self.rowconfigure(0, weight=1) self.im_moins = PhotoImage(master=self, file=IM_MOINS) self.im_moins_sel = PhotoImage(master=self, file=IM_MOINS_SEL) self.im_moins_clicked = PhotoImage(master=self, file=IM_MOINS_CLICKED) self.im_plus = PhotoImage(master=self, file=IM_PLUS) self._no_edit_entry = self.register(lambda: False) self.change_made = False self.categories = set(LATESTS.sections()) self.categories.remove('All') self.categories.add('') # --- treeview self.tree = Treeview(self, columns=('Title', 'URL', 'Category', 'Remove'), style='manager.Treeview', selectmode='none') self.tree.heading('Title', text=_('Title'), command=lambda: self._sort_column('Title', False)) self.tree.heading('URL', text=_('URL'), command=lambda: self._sort_column('URL', False)) self.tree.heading('Category', text=_('Category'), command=lambda: self._sort_column('Category', False)) self.tree.column('#0', width=6) self.tree.column('Title', width=250) self.tree.column('URL', width=350) self.tree.column('Category', width=150) self.tree.column('Remove', width=20, minwidth=20, stretch=False) y_scroll = AutoScrollbar(self, orient='vertical', command=self.tree.yview) x_scroll = AutoScrollbar(self, orient='horizontal', command=self.tree.xview) self.tree.configure(xscrollcommand=x_scroll.set, yscrollcommand=y_scroll.set) self.tree.bind('<Motion>', self._highlight_active) self.tree.bind('<Leave>', self._leave) self._last_active_item = None # --- populate treeview for title in sorted(FEEDS.sections(), key=lambda x: x.lower()): item = self.tree.insert('', 'end', values=(title, FEEDS.get(title, 'url'), FEEDS.get(title, 'category', fallback=''), '')) if FEEDS.getboolean(title, 'active', fallback=True): self.tree.selection_add(item) self.tree.item(item, tags=item) self.tree.tag_configure(item, image=self.im_moins) self.tree.tag_bind( item, '<ButtonRelease-1>', lambda event, i=item: self._click_release(event, i)) self.tree.tag_bind(item, '<ButtonPress-1>', lambda event, i=item: self._press(event, i)) self.tree.tag_bind(item, '<Double-1>', lambda event, i=item: self._edit(event, i)) self.tree.grid(row=0, column=0, sticky='ewsn') x_scroll.grid(row=1, column=0, sticky='ew') y_scroll.grid(row=0, column=1, sticky='ns') Button(self, image=self.im_plus, command=self.feed_add, style='manager.TButton').grid(row=2, column=0, columnspan=2, sticky='e', padx=4, pady=4) self._check_add_id = '' def destroy(self): try: self.after_cancel(self._check_add_id) except ValueError: pass Toplevel.destroy(self) def _edit(self, event, item): """Edit feed title.""" column = self.tree.identify_column(event.x) if column in ['#1', '#2']: bbox = self.tree.bbox(item, column) entry = Entry(self.tree) entry.place(x=bbox[0], y=bbox[1], width=bbox[2], height=bbox[3], anchor='nw') entry.bind('<Escape>', lambda e: entry.destroy()) entry.bind('<FocusOut>', lambda e: entry.destroy()) if column == '#1': entry.insert(0, self.tree.item(item, 'values')[0]) entry.configure(style='manager.TEntry') def ok(event): name = entry.get() if name: name = self.master.feed_rename( self.tree.set(item, 'Title'), name) self.tree.set(item, 'Title', name) entry.destroy() entry.bind('<Return>', ok) else: entry.insert(0, self.tree.item(item, 'values')[1]) entry.configure(style='no_edit.TEntry', validate='key', validatecommand=self._no_edit_entry) entry.selection_range(0, 'end') entry.focus_set() elif column == '#3': def focus_out(event): x, y = self.tree.winfo_pointerxy() x0 = combo.winfo_rootx() x1 = x0 + combo.winfo_width() y0 = combo.winfo_rooty() y1 = y0 + combo.winfo_height() if not (x0 <= x <= x1 and y0 <= y <= y1): combo.destroy() def ok(event): category = combo.get().strip() self.categories.add(category) self.master.feed_change_cat(self.tree.set(item, 'Title'), self.tree.set(item, 'Category'), category) self.tree.set(item, 'Category', category) combo.destroy() bbox = self.tree.bbox(item, column) cat = list(self.categories) combo = AutoCompleteCombobox(self.tree, values=cat, allow_other_values=True) combo.place(x=bbox[0], y=bbox[1], width=bbox[2], height=bbox[3], anchor='nw') combo.bind('<Escape>', lambda e: combo.destroy()) combo.bind('<FocusOut>', focus_out) combo.bind('<Return>', ok) combo.bind('<<ComboboxSelected>>', ok) combo.current(cat.index(self.tree.set(item, '#3'))) def _press(self, event, item): if self.tree.identify_column(event.x) == '#4': self.tree.tag_configure(item, image=self.im_moins_clicked) def _click_release(self, event, item): """Handle click on items.""" if self.tree.identify_row(event.y) == item: if self.tree.identify_column(event.x) == '#4': title = self.tree.item(item, 'values')[0] rep = True if CONFIG.getboolean('General', 'confirm_remove', fallback=True): rep = askokcancel( _('Confirmation'), _('Do you want to remove the feed {feed}?').format( feed=title)) if rep: self.master.feed_remove(title) self.tree.delete(item) self.change_made = True elif self.tree.identify_element( event.x, event.y) == 'Checkbutton.indicator': sel = self.tree.selection() if item in sel: self.tree.selection_remove(item) self.master.feed_set_active(self.tree.set(item, '#1'), False) else: self.tree.selection_add(item) self.master.feed_set_active(self.tree.set(item, '#1'), True) self.change_made = True else: self.tree.tag_configure(item, image=self.im_moins) def _leave(self, event): """Remove highlight when mouse leave the treeview.""" if self._last_active_item is not None: self.tree.tag_configure(self._last_active_item, image=self.im_moins) def _highlight_active(self, event): """Highlight minus icon under the mouse.""" if self._last_active_item is not None: self.tree.tag_configure(self._last_active_item, image=self.im_moins) if self.tree.identify_column(event.x) == '#4': item = self.tree.identify_row(event.y) if item: self.tree.tag_configure(item, image=self.im_moins_sel) self._last_active_item = item else: self._last_active_item = None else: self._last_active_item = None def _sort_column(self, column, reverse): """Sort column by (reversed) alphabetical order.""" l = [(self.tree.set(c, column), c) for c in self.tree.get_children('')] l.sort(reverse=reverse, key=lambda x: x[0].lower()) for index, (val, c) in enumerate(l): self.tree.move(c, "", index) self.tree.heading( column, command=lambda: self._sort_column(column, not reverse)) def feed_add(self): dialog = Add(self) self.wait_window(dialog) url = dialog.url if url: self.configure(cursor='watch') queue = self.master.feed_add(url, manager=True) self._check_add_id = self.after(1000, self._check_add_finished, url, queue) def _check_add_finished(self, url, queue): if queue.empty(): self._check_add_id = self.after(1000, self._check_add_finished, url, queue) else: title = queue.get(False) if title: item = self.tree.insert('', 'end', values=(title, url, '')) self.tree.item(item, tags=item) self.tree.tag_configure(item, image=self.im_moins) self.tree.tag_bind( item, '<ButtonRelease-1>', lambda event: self._click_release(event, item)) self.tree.tag_bind(item, '<ButtonPress-1>', lambda event: self._press(event, item)) self.tree.tag_bind(item, '<Double-1>', lambda event: self._edit(event, item)) self.tree.selection_add(item) self.change_made = True self.configure(cursor='arrow') self.focus_set() self.grab_set()
class Overview: def __init__(self, master): self._NAME = "Coinz.py" self._SOUNDS = "sounds" self.master = master self.master.title(self._NAME) self.sound_name = "320181__dland__hint.wav" self.sound_array = self.get_sounds() root.protocol("WM_DELETE_WINDOW", self.on_closing) self.market_prices = { 'btc': get_last_price['Bittrex']("usdt", "btc"), 'eth': get_last_price['Bittrex']("usdt", "eth"), 'bnb': get_last_price['Binance']("usdt", "bnb") } self.rt = RepeatedTimer(15, self.update_coins) self.coin_list = [] #create menubar self.menubar = Menu(master) self.settingsmenu = Menu(self.menubar, tearoff=0) self.settingsmenu.add_command(label="Settings", command=self.edit_settings) self.settingsmenu.add_command(label="Sounds", command=self.edit_sounds) self.menubar.add_cascade(label="File", menu=self.settingsmenu) root.config(menu=self.menubar) #create treeview ct_header = [ "exchange", "market", "shitcoin", "market price", "usd price", "alerts" ] ct_width = [80, 80, 128, 100, 100, 40] self.cointree = Treeview(master, selectmode="browse", columns=ct_header, show="headings") for i in range(len(ct_header)): self.cointree.heading(ct_header[i], text=ct_header[i]) self.cointree.column(ct_header[i], width=ct_width[i], stretch=False) self.cointree.grid(column=0, row=0, columnspan=6, rowspan=6) #create buttons button_frame = Frame(master) self.addcoin_button = Button(button_frame, text="Add Coin", command=self.add_coin) self.addcoin_button.grid(column=0, row=0, sticky="NEWS") self.removecoin_button = Button(button_frame, text="Remove Coin", command=self.remove_coin) self.removecoin_button.grid(column=0, row=1, sticky="NEWS") self.button_separator = Separator(button_frame, orient="horizontal") self.button_separator.grid(column=0, row=2, sticky="NEWS", pady=5) self.addalert_button = Button(button_frame, text="Add Alert", command=self.add_alert) self.addalert_button.grid(column=0, row=3, sticky="NEWS") self.viewalerts_button = Button(button_frame, text="View Alerts", command=self.view_alerts) self.viewalerts_button.grid(column=0, row=4, sticky="NEWS") button_frame.grid(column=6, row=0) #status label status_frame = Frame(master) self.status_info_text = StringVar() self.status_info_text.set("example info") self.status_price_text = StringVar() self.status_price_text.set("example price") self.status_info = Label(status_frame, relief="sunken", textvariable=self.status_info_text) self.status_price = Label(status_frame, relief="sunken", textvariable=self.status_price_text) self.status_info.grid(column=0, row=0) self.status_price.grid(column=1, row=0) status_frame.grid(column=0, row=6, columnspan=6) ##load shit if os.path.exists(os.path.join(os.getcwd(), "coinlist.pickle")): with open('coinlist.pickle', 'rb') as f: self.coin_list = pickle.load(f) temp_sound_name = "" if os.path.exists(os.path.join(os.getcwd(), "soundname.pickle")): with open('soundname.pickle', 'rb') as f: temp_sound_name = pickle.load(f) ind = next( iter([ i for i in range(len(self.sound_array)) if self.sound_array[i].find(temp_sound_name) != -1 ]), None) if ind != None: self.sound_name = temp_sound_name else: ErrorPopup( self.master, "saved sound " + temp_sound_name + " not found! \\n it will be replaced with the default on close" ) del (temp_sound_name) #update treeview self.display_coinlist(True) def edit_sounds(self): self.edit_sounds_popup = EditSoundsPopup(self, self.master) self.master.wait_window(self.edit_sounds_popup.top) def edit_settings(self): self.edit_settings_popup = EditSettingsPopup(self, self.master) self.master.wait_window(self.edit_settings_popup.top) def get_sounds(self): sound_array = [] for sound in [ f for f in listdir(self._SOUNDS) if isfile(join(self._SOUNDS, f)) ]: sound_array.append(join(self._SOUNDS, sound)) return sound_array def get_selected_sound(self): return self.sound_array[self.sound_index] def update_coins(self): #update btc price and btc display self.market_prices = { 'btc': get_last_price['Bittrex']("usdt", "btc"), 'eth': get_last_price['Bittrex']("usdt", "eth"), 'bnb': get_last_price['Binance']("usdt", "bnb") } self.status_price_text.set( ("BTC: " + self.format_dollar(self.market_prices['btc']))) #update coin objects for c in self.coin_list: if c.market is not Market.USDT.name: c.market_price = self.format_satoshi( get_last_price[c.exchange](c.market, c.name)) c.usd_price = self.format_dollar( get_last_price[c.exchange](c.market, c.name) * self.market_prices[c.market.lower()]) else: c.market_price = self.format_dollar(get_last_price[c.exchange]( c.market, c.name)) c.usd_price = "-" #this will return alert events if any are tripped alert_events = c.check_alerts() if alert_events is not None and len(alert_events) > 0: #dont spam alert sounds, one will suffice self.master.deiconify() self.master.focus_force() self.play_notif_sound() #display each one for a in alert_events: AlertPopup(self.master, c, a.info) #coin object is updated, time to update display item = self.find_item_from_coin(c) if item is None: ErrorPopup(self.master, "item not found!") return self.cointree.set(item, column="market price", value=c.market_price) self.cointree.set(item, column="usd price", value=c.usd_price) self.cointree.set(item, column="alerts", value=len(c.alerts)) def add_coin(self): self.add_coin_popup = AddCoinPopup(self.master, self) self.addcoin_button["state"] = "disabled" self.master.wait_window(self.add_coin_popup.top) self.addcoin_button["state"] = "normal" ##check if user did not fill out form if not hasattr(self.add_coin_popup, "value"): print("no value 'value' in add_coin_popup") return #get ticker from input c_form = self.add_coin_popup.value print(c_form) for c in self.coin_list: if c_form[2].lower() == c.name.lower() and c_form[0].lower( ) == c.exchange.lower() and c_form[1].lower() == c.market.lower(): InfoPopup(self.master, "You already added this coin!") return _market_price = 0 _usd_price = 0 if c_form[1] is not Market.USDT.name: _market_price = self.format_satoshi(get_last_price[c_form[0]]( c_form[1], c_form[2])) _usd_price = self.format_dollar( get_last_price[c_form[0]](c_form[1], c_form[2]) * self.market_prices[c_form[1].lower()]) else: _market_price = self.format_dollar(get_last_price[c_form[0]]( c_form[1], c_form[2])) _usd_price = "-" _coin = Coin(c_form[2].upper(), c_form[0], c_form[1], _market_price, _usd_price) self.coin_list.append(_coin) self.display_coinlist(False) def remove_coin(self): selected_coin = self.cointree.selection()[0] if len( self.cointree.selection()) > 0 else None #if shit in None show a popup if selected_coin is None: InfoPopup(self.master, "You must select a coin to remove it!") return #do you wanna do dis? AreYouSure = AreYouSurePopup( self.master, "remove " + self.cointree.item(selected_coin)["values"][2]) self.removecoin_button["state"] = "disabled" self.master.wait_window(AreYouSure.top) self.removecoin_button["state"] = "normal" remove_successful = False if AreYouSure.value: coin = self.find_coin_from_item(selected_coin) if coin is None: ErrorPopup(self.master, "could not find coin") #found it #remove from treeview self.cointree.delete(selected_coin) #remove from coinlist self.coin_list = [x for x in self.coin_list if x != coin] #ool flip remove_successful = True if not remove_successful: ErrorPopup( self.master, "Something went wrong during removal! The coin was not deleted." ) def display_coinlist(self, first_time): #this adds a coin to the treeview only if the "displayed" internal value of it is false if first_time: for c in self.coin_list: if c.market == Market.USDT.name: c.market_price = self.format_dollar(float(c.market_price)) self.cointree.insert('', 'end', values=(c.exchange, c.market, c.name, c.market_price, c.usd_price, len(c.alerts))) else: for c in [x for x in self.coin_list if x.displayed != True]: c.displayed = True self.cointree.insert('', 'end', values=(c.exchange, c.market, c.name, c.market_price, c.usd_price, len(c.alerts))) def view_alerts(self): selected_coin = self.cointree.selection()[0] if len( self.cointree.selection()) > 0 else None #if shit in None show a popup if selected_coin is None: InfoPopup(self.master, "You must select a coin to view alerts!") return #find coin obj from selected treeview item coin_obj = self.find_coin_from_item(selected_coin) #make sure shit is there if coin_obj is None: ErrorPopup(self.master, "Selected coin not found in the internal coin list!") return self.view_alerts_popup = ViewAlertsPopup(self.master, coin_obj) self.viewalerts_button["state"] = "disabled" self.master.wait_window(self.view_alerts_popup.top) self.viewalerts_button["state"] = "normal" ##user might have deleted alerts, so update the alert column self.cointree.set(selected_coin, column="alerts", value=len(coin_obj.alerts)) def find_coin_from_item(self, selection): c = next( iter([ x for x in self.coin_list if x.name.lower() == self.cointree.item(selection)["values"] [2].lower() and x.exchange.lower() == self.cointree.item( selection)["values"][0].lower() and x.market.lower() == self.cointree.item(selection)["values"][1].lower() ]), None) if c is None: print("not found!") return return c def find_item_from_coin(self, coin): i = next( iter([ x for x in self.cointree.get_children() if self.cointree.item(x)["values"][2].lower() == coin.name.lower() and self.cointree.item(x)["values"] [0].lower() == coin.exchange.lower() and self.cointree.item( x)["values"][1].lower() == coin.market.lower() ]), None) if i is None: print("not found!") return return i def add_alert(self): selected_coin = self.cointree.selection()[0] if len( self.cointree.selection()) > 0 else None #if shit in None show a popup if selected_coin is None: InfoPopup(self.master, "You must select a coin to add an alert!") return #find coin obj from selected treeview item coin_obj = self.find_coin_from_item(selected_coin) self.add_alert_popup = AddAlertPopup(self.master, coin_obj) self.addalert_button["state"] = "disabled" self.master.wait_window(self.add_alert_popup.top) self.addalert_button["state"] = "normal" ##check if user did not fill out form if not hasattr(self.add_alert_popup, "value"): print("no value 'value' in add_alert_popup") return alert_data = self.add_alert_popup.value #todo:check data for validity print(alert_data) ##add alert to the coin object if shit is there coin_obj.add_alert( Alert(alert_data[2], alert_data[1], True if alert_data[0] == 'above' else False)) ##update alert count in the treeview self.cointree.set(selected_coin, column="alerts", value=len(coin_obj.alerts)) def format_satoshi(self, val): return "{:10.8f}".format(val) def format_dollar(self, val): return "{:10.2f}".format(val) def play_notif_sound(self): ind = next( iter([ i for i in range(len(self.sound_array)) if self.sound_array[i].find(self.sound_name) != -1 ]), None) if ind is None: ErrorPopup(self.master, "sound " + self.sound_name + " not found!") return ns = AudioSegment.from_file(self.sound_array[ind]) ns = ns[:3 * 1000] play(ns) def on_closing(self): with open("soundname.pickle", 'wb') as f: pickle.dump(self.sound_name, f) with open("coinlist.pickle", 'wb') as f: pickle.dump(self.coin_list, f) self.rt.stop() root.destroy()
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 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 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 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 Decryptor(object): def __init__(self): self.fileBrowserShadowInformation = dict() self.startUI() def startUI(self): self.root = Tk() self.root.title('Decryptor') self.root.columnconfigure(0, weight=1) self.root.rowconfigure(0, weight=1) mainFrame = Frame(self.root) mainFrame.grid(row=0, column=0, padx=5, pady=5, sticky='nswe') mainFrame.rowconfigure(1, weight=1) mainFrame.columnconfigure(0, weight=1) optionsFrame = Frame(mainFrame) optionsFrame.grid(row=0, column=0, pady=(5,0), sticky='nswe') optionsFrame.columnconfigure(0, pad=5) optionsFrame.columnconfigure(1, weight=1) optionsFrame.columnconfigure(2, pad=5) browserFrame = Frame(mainFrame) browserFrame.grid(row=1, column=0, pady=(5,0), sticky='nswe') browserFrame.rowconfigure(0, weight=1) browserFrame.columnconfigure(0, weight=1) actionFrame = Frame(mainFrame) actionFrame.grid(row=2, column=0, pady=(5,0), sticky='e') actionFrame.columnconfigure(0, weight=1) Label(optionsFrame, text="Secret").grid(row=0, column=0, sticky='w') self.secret = Entry(optionsFrame) self.secret.grid(row=0, column=1, sticky='nswe') self.secret.focus_set() self.secretToggleButton = Button(optionsFrame, text="Set", command=self.toggleSecret, width=8) self.secretToggleButton.grid(row=0, column=2, sticky='e') self.secretToggleButton.setvar('set', False) Label(optionsFrame, text="Encrypted Folder").grid(row=1, column=0, sticky='w') self.folderPath = Entry(optionsFrame, state='readonly') self.browseButton = Button(optionsFrame, text="Browse", command=self.chooseFolder, width=8) self.browseButton.grid(row=1, column=2, sticky='e') self.browseButton.configure(state='disabled') self.folderPath.grid(row=1, column=1, sticky='nswe') fileBrowserColumns = ('size', 'modified', 'encrypted name') self.fileBrowser = Treeview(browserFrame, columns=fileBrowserColumns) self.fileBrowser.heading('#0', text='File') for i in range(0, len(fileBrowserColumns)): self.fileBrowser.heading('#{0}'.format(i+1), text=fileBrowserColumns[i].title()) self.fileBrowser.grid(row=0, column=0, sticky='nswe') scrollBarY = Scrollbar(orient=VERTICAL, command=self.fileBrowser.yview) scrollBarX = Scrollbar(orient=HORIZONTAL, command=self.fileBrowser.xview) self.fileBrowser['yscroll'] = scrollBarY.set self.fileBrowser['xscroll'] = scrollBarX.set scrollBarY.grid(in_=browserFrame, row=0, column=1, sticky='ns') scrollBarX.grid(in_=browserFrame, row=1, column=0, sticky='ew') Button(actionFrame, text="Decrypt Selected", command=self.decryptSelected).grid(row=0, column=0) Button(actionFrame, text="Decrypt All", command=self.decryptAll).grid(row=0, column=1) self.root.mainloop() def chooseFolder(self): dirPath = askdirectory(parent=self.root, mustexist=True) if os.name == 'nt': dirPath = '\\\\?\\' + os.path.normpath(dirPath) self.folderPath.configure(state='normal') self.folderPath.delete(0, len(self.folderPath.get())) self.folderPath.insert(0, dirPath) self.folderPath.configure(state='readonly') self.initializeFileBrowser() def toggleSecret(self): if self.secretToggleButton.getvar('set'): self.secret.configure(state='normal') self.secretToggleButton.setvar('set', False) self.secretToggleButton.configure(text='Set') self.browseButton.configure(state='disabled') self.__clearFileBrowser() else: if self.secret.get() == '': showerror('Invalid Secret', 'The provided secret must not be empty.') return self.secret.configure(state='readonly') self.secretToggleButton.setvar('set', True) self.secretToggleButton.configure(text='Change') self.browseButton.configure(state='normal') self.decryptor = Encryption(self.secret.get(), None, None) def initializeFileBrowser(self): self.__clearFileBrowser() self.__initializeFileBrowser(self.decryptor, self.folderPath.get()) self.fileBrowser.focus_set() def decryptSelected(self): selectedItems = self.fileBrowser.selection() if len(selectedItems) < 1: return destinationFolder = self.__askForDestinationFolder() if destinationFolder is None: return transitiveSelectedItems = set() for selectedItem in selectedItems: transitiveSelectedItems.update(self.__getAllFileBrowserChildren(selectedItem)) filteredSelectedItems = filter(lambda x: not x in transitiveSelectedItems, selectedItems) for selectedItem in filteredSelectedItems: self.__decryptFileBrowserItemToDestination(selectedItem, destinationFolder) def decryptAll(self): destinationFolder = self.__askForDestinationFolder() if destinationFolder is None: return children = self.fileBrowser.get_children('') for child in children: self.__decryptFileBrowserItemToDestination(child, destinationFolder) def __getAllFileBrowserChildren(self, identifier): children = set() queue = list() queue.append(identifier) while len(queue) > 0: for item in self.fileBrowser.get_children(queue.pop()): children.add(item) queue.append(item) return children def __askForDestinationFolder(self): return askdirectory(parent=self.root, mustexist=True, title='Destination Folder') def __decryptFileBrowserItemToDestination(self, selectedItem, destinationFolder): decryptedFileName = self.fileBrowser.item(selectedItem, option='text') destinationFullPath = os.path.join(destinationFolder, decryptedFileName) encryptedFullPath = self.fileBrowserShadowInformation.get(selectedItem) if os.path.isdir(encryptedFullPath): self.__decryptDirToDestination(encryptedFullPath, destinationFullPath) else: self.__decryptFileToDestination(encryptedFullPath, destinationFullPath) def __decryptFileToDestination(self, encryptedFullPath, destinationPath): chunkSize = 4096 encryptedVirtualFile = None filename, segmentNumber = FilenameUtils.splitSegmentedFileName(encryptedFullPath) if segmentNumber is None: encryptedVirtualFile = VirtualFile(encryptedFullPath) else: encryptedVirtualFile = SegmentedVirtualFile(filename) try: decryptedFileSize = self.decryptor.decryptedFileSize(encryptedVirtualFile) with open(destinationPath, 'wb') as destinationFile: for offset in range(0, decryptedFileSize, chunkSize): destinationFile.write(self.decryptor.decryptedContent(encryptedVirtualFile, offset, chunkSize)) finally: encryptedVirtualFile.closeFileHandle() def __decryptDirToDestination(self, encryptedFullPath, destinationFullPath): if not os.path.isdir(destinationFullPath): os.mkdir(destinationFullPath) for entry in os.listdir(encryptedFullPath): entryEncryptedFullPath = os.path.join(encryptedFullPath, entry) decryptedEntryName = self.decryptor.decryptFileName(entry) entryDestinationFullPath = os.path.join(destinationFullPath, decryptedEntryName) if os.path.isdir(entryEncryptedFullPath): self.__decryptDirToDestination(entryEncryptedFullPath, entryDestinationFullPath) else: self.__decryptFileToDestination(entryEncryptedFullPath, entryDestinationFullPath) def __clearFileBrowser(self): for child in self.fileBrowser.get_children(): self.fileBrowser.delete(child) self.fileBrowserShadowInformation.clear() def __initializeFileBrowser(self, decryption, encryptedDir, parent=''): for entry in os.listdir(encryptedDir): # Check if file is segmented filename, segment = FilenameUtils.splitSegmentedFileName(entry) if segment is not None and segment is not 0: continue encryptedFullPathForEntry = os.path.join(encryptedDir, entry) decryptedName = decryption.decryptFileName(filename) identifier = None printableModificationDate = datetime.fromtimestamp(os.path.getmtime(encryptedFullPathForEntry)).strftime('%Y-%m-%d %H:%M:%S') fileSize = None if os.path.isfile(encryptedFullPathForEntry): encryptedVirtualFile = None if segment is not None: encryptedVirtualFile = SegmentedVirtualFile(os.path.join(encryptedDir, filename)) else: encryptedVirtualFile = VirtualFile(encryptedFullPathForEntry) fileSize = decryption.decryptedFileSize(encryptedVirtualFile) encryptedVirtualFile.closeFileHandle() identifier = self.fileBrowser.insert(parent, 'end', text=decryptedName) else: identifier = self.fileBrowser.insert(parent, 'end', text=decryptedName) self.__initializeFileBrowser(decryption, encryptedFullPathForEntry, identifier) fileSize = os.path.getsize(encryptedFullPathForEntry) self.fileBrowser.set(identifier, 'size', hurry.filesize.size(fileSize)) self.fileBrowser.set(identifier, 'modified', printableModificationDate) self.fileBrowser.set(identifier, 'encrypted name', entry) self.fileBrowserShadowInformation[identifier] = encryptedFullPathForEntry
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 DialogOpenArchive(Toplevel): def __init__(self, mainWin, openType, filesource, filenames, title, colHeader, showAltViewButton=False): parent = mainWin.parent super(DialogOpenArchive, self).__init__(parent) self.parent = parent self.showAltViewButton = showAltViewButton parentGeometry = re.match("(\d+)x(\d+)[+]?([-]?\d+)[+]?([-]?\d+)", parent.geometry()) dialogX = int(parentGeometry.group(3)) dialogY = int(parentGeometry.group(4)) self.accepted = False self.transient(self.parent) frame = Frame(self) treeFrame = Frame(frame, width=500) vScrollbar = Scrollbar(treeFrame, orient=VERTICAL) hScrollbar = Scrollbar(treeFrame, orient=HORIZONTAL) self.treeView = Treeview(treeFrame, xscrollcommand=hScrollbar.set, yscrollcommand=vScrollbar.set) self.treeView.grid(row=0, column=0, sticky=(N, S, E, W)) hScrollbar["command"] = self.treeView.xview hScrollbar.grid(row=1, column=0, sticky=(E,W)) vScrollbar["command"] = self.treeView.yview vScrollbar.grid(row=0, column=1, sticky=(N,S)) treeFrame.columnconfigure(0, weight=1) treeFrame.rowconfigure(0, weight=1) treeFrame.grid(row=0, column=0, columnspan=4, sticky=(N, S, E, W), padx=3, pady=3) self.treeView.focus_set() mainWin.showStatus(_("loading archive {0}").format(filesource.url)) self.filesource = filesource self.filenames = filenames self.selection = filesource.selection self.hasToolTip = False selectedNode = None if openType == ENTRY_POINTS: try: metadataFiles = filesource.taxonomyPackageMetadataFiles ''' take first for now if len(metadataFiles) != 1: raise IOError(_("Taxonomy package contained more than one metadata file: {0}.") .format(', '.join(metadataFiles))) ''' metadataFile = metadataFiles[0] metadata = filesource.url + os.sep + metadataFile self.metadataFilePrefix = os.sep.join(os.path.split(metadataFile)[:-1]) if self.metadataFilePrefix: self.metadataFilePrefix += "/" # zip contents have /, never \ file seps self.taxonomyPkgMetaInf = '{}/META-INF/'.format( os.path.splitext(os.path.basename(filesource.url))[0]) self.taxonomyPackage = parsePackage(mainWin, filesource, metadata, os.sep.join(os.path.split(metadata)[:-1]) + os.sep) # may be a catalog file with no entry oint names if not self.taxonomyPackage["nameToUrls"]: openType = ARCHIVE # no entry points to show, just archive self.showAltViewButton = False except Exception as e: self.close() err = _("Failed to parse metadata; the underlying error was: {0}").format(e) messagebox.showerror(_("Malformed taxonomy package"), err) mainWin.addToLog(err) return mainWin.showStatus(None) if openType == DISCLOSURE_SYSTEM: y = 3 else: y = 1 okButton = Button(frame, text=_("OK"), command=self.ok) cancelButton = Button(frame, text=_("Cancel"), command=self.close) okButton.grid(row=y, column=2, sticky=(S,E,W), pady=3) cancelButton.grid(row=y, column=3, sticky=(S,E,W), pady=3, padx=3) if self.showAltViewButton: self.altViewButton = Button(frame, command=self.showAltView) self.altViewButton.grid(row=y, column=0, sticky=(S,W), pady=3, padx=3) self.loadTreeView(openType, colHeader, title) self.geometry("+{0}+{1}".format(dialogX+50,dialogY+100)) frame.grid(row=0, column=0, sticky=(N,S,E,W)) frame.columnconfigure(0, weight=1) frame.rowconfigure(0, weight=1) window = self.winfo_toplevel() window.columnconfigure(0, weight=1) window.rowconfigure(0, weight=1) self.bind("<Return>", self.ok) self.bind("<Escape>", self.close) self.toolTipText = StringVar() if self.hasToolTip: self.treeView.bind("<Motion>", self.motion, '+') self.treeView.bind("<Leave>", self.leave, '+') self.toolTipText = StringVar() self.toolTip = ToolTip(self.treeView, textvariable=self.toolTipText, wraplength=640, follow_mouse=True, state="disabled") self.toolTipRowId = None self.protocol("WM_DELETE_WINDOW", self.close) self.grab_set() self.wait_window(self) def loadTreeView(self, openType, title, colHeader): self.title(title) self.openType = openType selectedNode = None # clear previous treeview entries for previousNode in self.treeView.get_children(""): self.treeView.delete(previousNode) # set up treeView widget and tabbed pane if openType in (ARCHIVE, DISCLOSURE_SYSTEM): self.treeView.column("#0", width=500, anchor="w") self.treeView.heading("#0", text=colHeader) try: self.isRss = self.filesource.isRss if self.isRss: self.treeView.column("#0", width=350, anchor="w") self.treeView["columns"] = ("descr", "date", "instDoc") self.treeView.column("descr", width=50, anchor="center", stretch=False) self.treeView.heading("descr", text="Form") self.treeView.column("date", width=170, anchor="w", stretch=False) self.treeView.heading("date", text="Pub Date") self.treeView.column("instDoc", width=200, anchor="w", stretch=False) self.treeView.heading("instDoc", text="Instance Document") except AttributeError: self.isRss = False self.treeView["columns"] = tuple() loadedPaths = [] for i, filename in enumerate(self.filenames): if isinstance(filename,tuple): if self.isRss: form, date, instDoc = filename[2:5] filename = filename[0] # ignore tooltip self.hasToolTip = True if filename.endswith("/"): filename = filename[:-1] path = filename.split("/") if not self.isRss and len(path) > 1 and path[:-1] in loadedPaths: parent = "file{0}".format(loadedPaths.index(path[:-1])) else: parent = "" node = self.treeView.insert(parent, "end", "file{0}".format(i), text=path[-1]) if self.isRss: self.treeView.set(node, "descr", form) self.treeView.set(node, "date", date) self.treeView.set(node, "instDoc", os.path.basename(instDoc)) if self.selection == filename: selectedNode = node loadedPaths.append(path) elif openType == ENTRY_POINTS: self.treeView.column("#0", width=150, anchor="w") self.treeView.heading("#0", text="Name") self.treeView["columns"] = ("url",) self.treeView.column("url", width=350, anchor="w") self.treeView.heading("url", text="URL") for name, urls in self.taxonomyPackage["nameToUrls"].items(): displayUrl = urls[1] # display the canonical URL self.treeView.insert("", "end", name, values=[displayUrl], text=name) self.hasToolTip = True else: # unknown openType return None if selectedNode: self.treeView.see(selectedNode) self.treeView.selection_set(selectedNode) if self.showAltViewButton: self.altViewButton.config(text=_("Show Files") if openType == ENTRY_POINTS else _("Show Entries")) def ok(self, event=None): selection = self.treeView.selection() if len(selection) > 0: if hasattr(self, "taxonomyPackage"): # load file source remappings self.filesource.mappedPaths = self.taxonomyPackage["remappings"] filename = None if self.openType in (ARCHIVE, DISCLOSURE_SYSTEM): filename = self.filenames[int(selection[0][4:])] if isinstance(filename,tuple): if self.isRss: filename = filename[4] else: filename = filename[0] elif self.openType == ENTRY_POINTS: epName = selection[0] #index 0 is the remapped Url, as opposed to the canonical one used for display filename = self.taxonomyPackage["nameToUrls"][epName][0] if not filename.endswith("/"): # check if it's an absolute URL rather than a path into the archive if not isHttpUrl(filename) and self.metadataFilePrefix != self.taxonomyPkgMetaInf: # assume it's a path inside the archive: filename = self.metadataFilePrefix + filename if filename is not None and not filename.endswith("/"): if hasattr(self, "taxonomyPackage"): # attempt to unmap the filename to original file # will be mapped again in loading, but this allows schemaLocation to be unmapped for prefix, remapping in self.taxonomyPackage["remappings"].items(): if isHttpUrl(remapping): remapStart = remapping else: remapStart = self.metadataFilePrefix + remapping if filename.startswith(remapStart): # set unmmapped file filename = prefix + filename[len(remapStart):] break self.filesource.select(filename) self.accepted = True self.close() def close(self, event=None): self.parent.focus_set() self.destroy() def showAltView(self, event=None): if self.openType == ENTRY_POINTS: self.loadTreeView(ARCHIVE, _("Select Entry Point"), _("File")) else: self.loadTreeView(ENTRY_POINTS, _("Select Archive File"), _("File")) def leave(self, *args): self.toolTipRowId = None def motion(self, *args): tvRowId = self.treeView.identify_row(args[0].y) if tvRowId != self.toolTipRowId: text = None if self.openType in (ARCHIVE, DISCLOSURE_SYSTEM): self.toolTipRowId = tvRowId if tvRowId and len(tvRowId) > 4: try: text = self.filenames[ int(tvRowId[4:]) ] if isinstance(text, tuple): text = text[1].replace("\\n","\n") except (KeyError, ValueError): pass elif self.openType == ENTRY_POINTS: try: epUrl = self.taxonomyPackage["nameToUrls"][tvRowId][1] text = "{0}\n{1}".format(tvRowId, epUrl) except KeyError: pass self.setToolTip(text) def setToolTip(self, text): self.toolTip._hide() if text: self.toolTipText.set(text) self.toolTip.configure(state="normal") self.toolTip._schedule() else: self.toolTipText.set("") self.toolTip.configure(state="disabled")
class ListOfData(Labelframe): """Contains list of student in database""" def __init__(self, window, base, **kwargs): Labelframe.__init__(self, window, **kwargs) self.base = base self.notebook = Notebook(self) self.notebook.grid(row=0, column=0, padx=5, pady=5) self.student = Frame(self.notebook) self.student.grid(row=0, column=0) self.student_list = Treeview(self.student, height=24, columns=("name", "pesel"), displaycolumns="name") self.student_list.heading('name', text="Nazwisko i imię", anchor='w') self.student_list.column('name', width=240) self.student_list['show'] = 'headings' self.student_list.grid(row=0, column=0) self.button = Button(self.student, text="Usuń", command=self.delete_from_student_list) self.button.grid(row=1, column=0) self.staff_meeting = Frame(self.notebook) self.staff_meeting.grid(row=0, column=0) self.table = Treeview(self.staff_meeting, height=24, columns=("name", "id", "student_id"), displaycolumns="name") self.table.heading('name', text='Zespoły', anchor='w') self.table.column('name', width=235) self.table.column('#0', stretch=0, width=10) self.table.grid(row=0, column=0) self.delete_staffmeeting = Button( self.staff_meeting, text="Usuń wpis", command=self.delete_from_staffmeeting) self.delete_staffmeeting.grid(row=1, column=0) self.notebook.add(self.student, text="Dzieci") self.notebook.add(self.staff_meeting, text="Zespoły") def get_students(self): return self.base.get_dict_of_students() def get_pesel_from_name(self, name): students = self.get_students() for pesel in students: if students[pesel] == name: return pesel def fill_student_list(self): self.student_list.delete(*self.student_list.get_children()) if not self.get_students(): return for student, pesel in zip(self.get_students().values(), self.get_students().keys()): self.student_list.insert("", 'end', values=(student, pesel)) def delete_from_student_list(self): selected_item = self.student_list.selection() pesel = str(self.student_list.item(selected_item)['values'][1]) if len(pesel) < 11: pesel = "0" + pesel self.base.delete_student(pesel) self.fill_student_list() self.fill_staffmeeting_list() def fill_staffmeeting_list(self): self.table.delete(*self.table.get_children()) staff_meeting = StaffMeeting.select() meeting_in_table = {} # {value: iid} for i in staff_meeting: if i.date not in meeting_in_table.keys(): meeting_in_table[i.date] = self.table.insert("", 'end', values=(i.date, )) actual_iid = self.table.insert( meeting_in_table[i.date], 'end', values=(self.base.get_student_name(i.student), )) self.table.set(actual_iid, column="id", value=i.id) self.table.set(actual_iid, column="student_id", value=i.student) else: actual_iid = self.table.insert( meeting_in_table[i.date], 'end', values=(self.base.get_student_name(i.student), )) self.table.set(actual_iid, column="id", value=i[0]) self.table.set(actual_iid, column="student_id", value=i[20]) for idd in meeting_in_table.values(): if not self.table.parent(idd): self.table.item(idd, open=True) def delete_from_staffmeeting(self): selected_item = self.table.selection() staffmeeting_id = self.table.item(selected_item)['values'][1] StaffMeeting.get(StaffMeeting.id == staffmeeting_id).delete_instance() self.fill_staffmeeting_list()
class Demo(Tk): icon_res = [] file_name = None def __init__(self): super().__init__() self._set_windows_() self._create_menu_bar_() self._create_shortcut_bar_() self._create_body_() self.DATAS = pd.DataFrame(columns=INFOS) def _set_windows_(self): self.title('学生成绩管理系统') scn_width, scn_height = self.maxsize() wm_val = '1200x450+%d+%d' % ((scn_width - 800) / 2, (scn_height - 450) / 2) self.geometry(wm_val) # self.iconbitmap("img/editor.ico") def _create_menu_bar_(self): menu_bar = Menu(self) file_menu = Menu(menu_bar, tearoff=0) file_menu.add_command(label='add', accelerator='Ctrl+N', command=self.add) file_menu.add_command(label='save_to_file', command=self.save_to_file) file_menu.add_command(label='Exit', command=self.edit) file_menu.add_command(label='Search', command=self.search) file_menu.add_command(label='Total', command=self.total) file_menu.add_command(label='save_to_DATAS', command=self.save_to_DATAS) file_menu.add_command(label='Average', command=self.average) file_menu.add_command(label='open_file', command=self.open_file) file_menu.add_command(label='add', command=self.add) file_menu.add_command(label='delete_selected_item', command=self.delete_item) file_menu.add_command(label='clear_all', command=self.clear_all) file_menu.add_command(label='delete_item', command=self.delete_item) file_menu.add_command(label='init_demo', command=self.init_demo) file_menu.add_command(label='sort_as_total', command=self.sort_as_total) file_menu.add_command(label='average_stu', command=self.average_stu) menu_bar.add_cascade(label='程序中所有函数(测试)', menu=file_menu) help_menu = Menu(menu_bar, tearoff=0) help_menu.add_command(label='帮助', accelerator='Ctrl+H', command=self.about) help_menu.add_command(label='关于', accelerator='Ctrl+H', command=self.about) menu_bar.add_cascade(label='---> 请先阅读帮助中的说明!!!<---', menu=help_menu) self["menu"] = menu_bar def _create_shortcut_bar_(self): shortcut_bar = Frame(self, height=25, background='#00CED1') shortcut_bar.pack(fill=X) right_bar = Frame(self, width=25, background='#FF8C00') right_bar.pack(side=RIGHT, fill=Y) for i, icon in enumerate(ICONS): icon_img = PhotoImage(file='img/%s.gif' % icon) btn = Button(shortcut_bar, image=icon_img, command=lambda x=icon: self._shortcut_action_(x)) btn.pack(side=LEFT) self.icon_res.append(icon_img) def _create_body_(self): scrollBar = Scrollbar(self) scrollBar.pack(side=RIGHT, fill=Y) self.tree = Treeview(self, show='headings', yscrollcommand=scrollBar.set) # 表格 index = tuple([str(i) for i in range(len(INFOS))]) self.tree["columns"] = index for i, info in zip(index, INFOS): self.tree.column(i, width=len(i) * 10, anchor='center') self.tree.heading(i, text=info) #显示表头 self.tree.pack(fill=BOTH, ipady=500) # 将滚动条绑定至Treeview scrollBar.config(command=self.tree.yview) # 快捷键相关设置 # 函数参数需要有event=None self.tree.bind('<Double-Button-1>', self.edit) # 响应快捷菜单 def _shortcut_action_(self, type): if type == "open_file": self.open_file() elif type == "add": self.add() elif type == "edit": self.edit() elif type == "save_file": self.save_to_file() elif type == "save_DATAS": self.save_to_DATAS() elif type == "delete": self.delete_item() elif type == "clear_all": self.clear_all() elif type == "search": self.search() elif type == "total": try: self.total() except: messagebox.showinfo(title='警告', message='请检查输入信息') elif type == "average": try: self.average() except: messagebox.showinfo(title='警告', message='请检查输入信息') elif type == "sort": try: self.sort_as_total() except: messagebox.showinfo(title='警告', message='请检查输入信息') elif type == "sort_no": try: self.sort_as_no() except: messagebox.showinfo(title='警告', message='请检查输入信息') elif type == "exit": self.exit() elif type == "average_stu": try: self.average_stu() except: messagebox.showinfo(title='警告', message='请检查输入信息') elif type == "about": self.about() def showline(self, event=None): print(self.tree.selection()) for item in self.tree.selection(): item_text = self.tree.item(item, 'values') print(item_text) def clear_all(self): print('-------------follow item will be cleared:BEG-------------') print(self.tree.get_children()) print('\n') for item in self.tree.get_children(): self.tree.delete(item) print(item + ' : ', end=' ') print('deleted', end=' ') print('\n-------------items had been cleared:END-------------') def init_demo(self): self.clear_all() self.DATAS = pd.DataFrame(columns=INFOS) print('---------------demo inited----------------') def delete_item(self, event=None): print('-------------Your selection:BEG-------------') print(self.tree.selection()) for item in self.tree.selection(): self.tree.delete(item) print('-------------Your selection cleared:END-------------') def add(self, event=None): add_windows = Toplevel(self) scn_width, scn_height = self.maxsize() wm_val = '320x400+%d+%d' % ((scn_width - 320) / 2, (scn_height - 400) / 2) add_windows.geometry(wm_val) add_windows.resizable(0, 0) add_windows.title('添加新的学生') frame = Frame(add_windows) frame.pack(fill=Y) self.entryList = locals() for i, info in enumerate(INFOS[:-2]): # -2 不添加总分 和 平均分 Label(frame, text=info + ' : ').grid(row=i, column=0, pady=5) self.entryList[info] = Entry(frame) self.entryList[info].grid(row=i, column=1, pady=5) frame_btn = Frame(add_windows) frame_btn.pack(fill=Y) Button(frame_btn, text='添加', command=lambda: update()).grid(row=0, column=0, pady=5) Button(frame_btn, text='清空', command=lambda: clear()).grid(row=0, column=1, pady=5) Button(frame_btn, text='取消', command=lambda: exit()).grid(row=0, column=2, pady=5) def update(): DATAS = [] for info in INFOS: data = self.entryList[info].get() DATAS.append(data) for i in DATAS[:-1]: if i == '': messagebox.showwarning(title='警告', message='输入空白') clear() return self.tree.insert("", END, values=DATAS) def clear(): for info in INFOS: self.entryList[info].delete(0, END) def exit(): add_windows.destroy() def open_file(self, options=None): if options: input_file = options else: input_file = filedialog.askopenfilename( filetypes=[("所有文件", "*.*"), ("Excel文档", "*.xlsx")]) if input_file: print('-----------------成功导入文件:', input_file, type(input_file), '---------------') def read_excel(file): df = pd.read_excel(file) print('---------------reading excel!-----------------') print(df) print('---------------read excel done!-----------------') return df df = read_excel(input_file) # input data to Treeview print('---------------inputing to TreeView!-----------------') for i in df.iloc: data = i.tolist() # 若导入的文件中缺少数据,则缺少的数据用空补上 for i in range(len(INFOS) - len(data)): data.append('') # 打印测试 print(data) # 测试用 把 平均分 和 总分 置空 data[-1] = '' data[-2] = '' self.tree.insert("", END, values=data) print('---------------input to TreeView done!-----------------') def save_to_DATAS(self): print('---------------saving to DATAS!-----------------') self.DATAS = pd.DataFrame(columns=INFOS) indexs = self.tree.get_children() for i in indexs: value = self.tree.item(i, 'values') print(value) self.DATAS.loc[len(self.DATAS)] = list(value) print('---------------save to DATAS done!-----------------') def save_to_file(self): outputfile = filedialog.asksaveasfilename( filetypes=[("所有文件", "*.*"), ("Excel文档", "*.xlsx")]) self.save_to_DATAS() # outputfile = 'new.xlsx' self.DATAS.to_excel(outputfile, index=FALSE) print( '---------------------------save to file done!---------------------------------' ) def edit(self, event=None): item = self.tree.selection() # Layout edit_windows = Toplevel(self) scn_width, scn_height = self.maxsize() wm_val = '320x450+%d+%d' % ((scn_width - 320) / 2, (scn_height - 450) / 2) edit_windows.geometry(wm_val) edit_windows.resizable(0, 0) edit_windows.title('编辑学生信息') # 创建标签和输入框 frame = Frame(edit_windows) frame.pack(fill=Y) self.entryList = locals() for i, info in enumerate(INFOS): Label(frame, text=info + ' : ').grid(row=i, column=0, pady=5) self.entryList[info] = Entry(frame) self.entryList[info].grid(row=i, column=1, pady=5) # 创建按钮布局 frame_btn = Frame(edit_windows) frame_btn.pack(fill=Y) Button(frame_btn, text='确定', command=lambda: update()).grid(row=0, column=0, pady=5) Button(frame_btn, text='清空', command=lambda: clear()).grid(row=0, column=1, pady=5) Button(frame_btn, text='取消', command=lambda: exit()).grid(row=0, column=2, pady=5) # 将待修改的数据导入输入框(方便修改,不用全部重新输入) values = self.tree.item(item, 'values') for value, info in zip(values, INFOS): self.entryList[info].insert(END, value) print(values) print('--------------input to entry done!------------') def update(): temp = [] for i, info in enumerate(INFOS): data = self.entryList[info].get() temp.append(data) self.tree.set(item, i, value=data) print('----------------editing data!-----------------') print(temp) print('---------------edit data done!-----------------') exit() def clear(): for info in INFOS: self.entryList[info].delete(0, END) def exit(): edit_windows.destroy() # item = self.tree.selection() # self.tree.set(item, 0, value='helloworld') def search(self): search_windows = Toplevel(self) search_windows.title('搜索全部') search_windows.transient(self) search_windows.resizable(0, 0) scn_width, scn_height = self.maxsize() wm_val = '380x70+%d+%d' % ((scn_width - 380) / 2, (scn_height - 70) / 2) search_windows.geometry(wm_val) Label(search_windows, text='查找全部:').grid(row=0, column=0, sticky=E) search_entry = Entry(search_windows, width=25) search_entry.grid(row=0, column=1, padx=2, pady=20, sticky='we') search_entry.focus_set() Button(search_windows, text='查找', command=lambda: search_result()).grid(row=0, column=2, sticky=E + W, padx=2, pady=2) def search_result(): result = [] indexs = self.tree.get_children() search_data = search_entry.get() print(search_data, type(search_data)) for i, index in enumerate(indexs): values = self.tree.item(index, 'values') print(values, end=' ') for value in list(values)[:2]: if search_data in value: result.append(search_data) print(result) self.tree.selection_set(index) self.tree.yview_moveto(i / len(indexs)) break else: print('No Found') if result: return else: messagebox.showinfo(title='提示', message='未找到匹配项') # 选择 # self.tree.selection_set('I001') # self.tree.yview_moveto(1) # 取消选择 # self.tree.selection_remove('I001') def update_to_tree(self): # 删除Treeview中所有元素 for index in self.tree.get_children(): self.tree.delete(index) # 插入DATAS中的元素到TreeView中 for i in self.DATAS.iloc: data = i.tolist() self.tree.insert("", END, values=data) def total(self): self.save_to_DATAS() temp = self.DATAS[['成绩A', '成绩B', '成绩C']].astype('int') self.DATAS['总分'] = temp.sum(axis=1) print( '---------------------------Total calculate done!-----------------------------' ) print(self.DATAS) print( '---------------------------Total calculate done!-----------------------------' ) # # 删除Treeview中所有元素 # for index in self.tree.get_children(): # self.tree.delete(index) # # 插入DATAS中的元素到TreeView中 # for i in self.DATAS.iloc: # data = i.tolist() # self.tree.insert("", END, values=data) self.update_to_tree() def average_stu(self): self.save_to_DATAS() temp = self.DATAS[['成绩A', '成绩B', '成绩C']].astype('int') # round 计算并保留两位小数 round( *, 2 ) self.DATAS['平均分'] = round(temp.mean(axis=1), 2) print( '---------------------------per average calculate done!-----------------------------' ) print(self.DATAS) print( '---------------------------per average done!-----------------------------' ) self.update_to_tree() def average(self): self.total() temp = self.DATAS[['总分']].astype('float64') av = temp.mean(axis=0) print('---------------------------AV-----------------------------') print('平均分:', float(av)) print('---------------------------AV-----------------------------') class_grade = '\t总平均分: %.2f' % float(av) messagebox.showinfo(title='平均分', message=class_grade) def sort_as_total(self): self.total() self.DATAS.sort_values(by='总分', ascending=False, inplace=True) print('--------------------sorting values!----------------') print(self.DATAS) print('--------------------sort values done!--------------') # drop 清洗列表 去掉NaN的行 self.DATAS.reset_index(drop=True, inplace=True) print('--------------------cleaning datas!----------------') print(self.DATAS) print('--------------------clean datas done!--------------') self.update_to_tree() print('--------------------update to tree done!--------------') # Text self.DATAS.to_excel('text.xlsx', index=False) def sort_as_no(self): self.save_to_DATAS() self.DATAS['学号'] = self.DATAS[['学号']].astype('int') self.DATAS.sort_values(by='学号', inplace=True) print(self.DATAS) print('-------------------sort as no done!-----------------') self.DATAS.reset_index(drop=True, inplace=True) print(self.DATAS) print('--------------------clean data done!-----------------') self.update_to_tree() print('--------------------update to tree done!--------------') # Text self.DATAS.to_excel('text_sort_no.xlsx', index=False) def about(self): # info = 'Github Page: %s \nWeb: %s\n' % (ADRS[0], ADRS[1]) # messagebox.showinfo(title='About', message=info) help = [ '打开Excel文件 其中所有学生信息将导入', '添加一个学生信息 单个添加', '编辑学生信息 选中一个数据后点击 也可直接双击数据编辑', '保存学生信息至Excel文件 文件可用于导入', '刷新数据列表 将窗口内所有学生信息更新至DATAS 用于计算', '删除学生 选中一个或多个(Shift)数据后单击 即可删除该数据 ', '清空所有信息', '查找学生信息 输入姓名 或 学号即可自动遍历查找', '求各个学生的总分', '求所有学生的平均分', '将所有学生按总分排序 会先自动计算总分', '求各个学生的平均分 会先自动线计算总分', '将所有学生按学号排序', '关于帮助', '退出程序' ] about_windows = Toplevel(self) about_windows.title('程序使用说明') # 容器框 (LabelFrame) group = LabelFrame(about_windows, text="Help", padx=5, pady=5) group.grid(padx=10, pady=10) # w = Label(group, text='本学习项目由 http://pegasu.cn 出品 \n\nGithub: https://github.com/pegasuswiki') # w.pack() for i, icon in enumerate(ICONS): icon_img = PhotoImage(file='img/%s.gif' % icon) Label(group, image=icon_img).grid(row=i, column=0, stick=NW, padx=5, pady=5) Label(group, text=help[i]).grid(row=i, column=1, stick=W, padx=5, pady=5) self.icon_res.append(icon_img) # 必须有 作用: 保存图片 w = Label( about_windows, text= '本学习项目由 http://pegasu.cn 出品 \n\nGithub: https://github.com/pegasuswiki' ) w.grid(pady=10) def exit(self): if messagebox.askokcancel("退出?", "确定退出吗?"): self.quit()
class HomeScreen(Frame): def __init__(self, parent, username): Frame.__init__(self, parent) self.parent = parent self.data = Database() self.userCreate = username self.drawScreen() self.linkAccelerator() date = datetime.datetime.now().date().strftime("%d-%m-%Y") self.listBill = self.data.getListBillAtDate(date) for bill in self.listBill: self.addBillIntoTree(bill) def linkAccelerator(self): if self.userCreate == "root": self.bind_all("<Control-U>", self.onViewListUser) self.bind_all("<Control-l>", self.onViewActivityLog) self.bind_all("<Control-n>", self.onNewItem) self.bind_all("<Control-N>", self.onNewType) self.bind_all("<Control-q>", self.onQuit) self.bind_all("<Control-L>", self.onLogOut) self.bind_all("<Delete>", self.onDeleteBill) self.bind_all("<Control-A>", self.onAddBill) self.bind_all("<Control-v>", self.onViewItemTable) self.bind_all("<Control-V>", self.onViewBillTable) def drawScreen(self): self.pack(fill=BOTH, anchor="w", expand=True, padx=10, pady=10) ########## Listing all Bills which has been created ########### layoutList = Frame(self) self.drawTreeOfBill(layoutList) layoutList.pack(side=LEFT, fill=BOTH, expand=True) ########## Create Menu for HomePage ########## self.menu = Menu(self.parent) self.parent.config(menu=self.menu) self.drawMenu(self.menu) def drawMenu(self, parent): # File Menu fileMenu = Menu(parent) newPopupMenu = Menu(fileMenu) newPopupMenu.add_command(label="Thêm mặt hàng ...", command=self.onNewItem, accelerator="Ctrl+N") newPopupMenu.add_command(label="Thêm loại hàng ...", command=self.onNewType, accelerator="Ctrl+Shift+N") fileMenu.add_cascade(label="Thêm", menu=newPopupMenu) fileMenu.add_separator() exportPopupMenu = Menu(fileMenu) exportPopupMenu.add_command(label="Xuất File Mặt Hàng", command=self.onExportExcelFile) exportPopupMenu.add_command(label="Xuất File Đơn Hàng", command=self.onExportExcelBillFile) fileMenu.add_cascade(label="Xuất File Excel", menu=exportPopupMenu) statPopupMenu = Menu(fileMenu) statPopupMenu.add_command(label="Thống Kê Mặt Hàng", command=self.onViewItemTable, accelerator="Ctrl+V") statPopupMenu.add_command(label="Thống Kê Hóa Đơn", command=self.onViewBillTable, accelerator="Ctrl+Shift+V") fileMenu.add_cascade(label="Thống kê", menu=statPopupMenu) if self.userCreate == "root": fileMenu.add_command(label="Xem nhật kí hoạt động", command=self.onViewActivityLog) fileMenu.add_separator() fileMenu.add_command(label="Thoát", command=self.onQuit, accelerator="Ctrl+Q") parent.add_cascade(label="Tệp", menu=fileMenu, underline=0) # User Menu userMenu = Menu(parent) userMenu.add_command(label="Xem thông tin User", command=self.onViewInfo) if self.userCreate == "root": userMenu.add_command(label="Danh sách các User", command=self.onViewListUser) userMenu.add_separator() userMenu.add_command(label="Đăng xuất", command=self.onLogOut, accelerator="Ctrl+Shift+L") parent.add_cascade(label="User", menu=userMenu, underline=0) # About Menu windowMenu = Menu(parent) windowMenu.add_command(label="About", command=self.onAbout) parent.add_cascade(label="Windows", menu=windowMenu, underline=0) def drawTreeOfBill(self, parent): listAttribute = [ "Mặt hàng", "Loại hàng", "ID", "Số lượng xuất", "Thành giá", "Người tạo" ] yScrollTree = Scrollbar(parent, orient=VERTICAL) xScrollTree = Scrollbar(parent, orient=HORIZONTAL) # Create Delete, Add and Edit Button layoutButton = Frame(parent) btnAddBill = Button(layoutButton, text="+", fg="green", command=self.onAddBill) btnDelBill = Button(layoutButton, text="-", fg="red", command=self.onDeleteBill) # btnEditBill = Button(layoutButton, text = "Edit", fg = "blue") btnAddBill.pack(side=LEFT) btnDelBill.pack(side=LEFT) # btnEditBill.pack(side = LEFT) layoutButton.pack(side=TOP, anchor="w") # Create Tree View self.treeBill = Treeview(parent, column=listAttribute, yscrollcommand=yScrollTree.set, xscrollcommand=xScrollTree.set) self.treeBill.bind(sequence="<Double-Button-1>", func=self.onEditBill) self.treeBill.column(column="#0", width=100, minwidth=100) for nameAttr in listAttribute: self.treeBill.heading(column=nameAttr, text=nameAttr) self.treeBill.column(column=nameAttr, width=100, minwidth=100) #Create Scrollbar for tree view yScrollTree.pack(side=RIGHT, fill=Y) xScrollTree.pack(side=BOTTOM, fill=X) self.treeBill.pack(side=TOP, anchor="w", fill=BOTH, expand=True) yScrollTree.config(command=self.treeBill.yview) xScrollTree.config(command=self.treeBill.xview) def addBillIntoTree(self, billInput): bill = billInput if not self.treeBill.exists(bill.CreatedDate): self.treeBill.insert("", "end", str(bill.CreatedDate), text=bill.CreatedDate, tags="parents") self.treeBill.item(bill.CreatedDate, open=True) self.treeBill.insert(bill.CreatedDate, END, bill.CreatedTime, text=bill.CreatedTime, tags="childs") self.treeBill.set(bill.CreatedTime, "Mặt hàng", bill.name) self.treeBill.set(bill.CreatedTime, "Loại hàng", bill.type) self.treeBill.set(bill.CreatedTime, "ID", bill.id) self.treeBill.set(bill.CreatedTime, "Số lượng xuất", bill.amount) self.treeBill.set(bill.CreatedTime, "Thành giá", int(bill.price)) self.treeBill.set(bill.CreatedTime, "Người tạo", bill.CreatedUser) def onAddBill(self, event=None): AddBillDialog(self) def onEditBill(self, event): pass def onDeleteBill(self, event=None): # if len(self.treeBill.selection()) == 0: # messagebox.showwarning(title = "Empty !!!", message = "Xin hãy chọn Hóa đơn bạn muốn xóa") # return # for choose in self.treeBill.selection(): # treeItem = self.treeBill.item(choose) # if treeItem["tags"][0] == "childs": # self.data.deleteBillAtTime(treeItem["text"]) # elif treeItem["tags"][0] == "parents": # self.data.deleteAllBillAtDate(treeItem["text"]) # self.treeBill.delete(treeItem["text"]) messagebox.showerror("Opps !!!!", "Bạn không thể xóa !!!!", parent=self) ######### Menu Function ##################### def onExportExcelFile(self): self.filePath = filedialog.asksaveasfile( title="Choose where you want to save File", filetypes=(("Microsoft Excel", "*.xlsx"), ("All Files", "*.*"))) if self.filePath == None: return wb = Workbook() ws = wb.active data = Database() listItem = data.getItemList() for item in listItem: ws = createSheetAsItem(wb, item) createTitleTypeItem(ws, item) adjustColumn(ws) addTypeIntoSheet(ws, item.type) wb.remove_sheet(wb.active) wb.save(self.filePath.name) def onExportExcelBillFile(self): self.filePath = filedialog.asksaveasfile( title="Choose where you want to save File", filetypes=(("Microsoft Excel", "*.xlsx"), ("All Files", "*.*"))) if self.filePath == None: return wb = Workbook() ws = wb.active data = Database() listBill = data.getBillList() listDate = [listBill[0].CreatedDate] for bill in listBill: if bill.CreatedDate not in listDate: listDate.append(bill.CreatedDate) for date in listDate: listBillAtDate = data.getListBillAtDate(date) ws = createSheetAsDate(wb, date) createTitleBillInDate(ws, date) adjustColumn(ws) addBillIntoSheet(ws, listBillAtDate) wb.remove_sheet(wb.active) wb.save(self.filePath.name) def onViewItemTable(self, event=None): ShowDataWindow(self) def onViewBillTable(self, event=None): ShowBillDialog(self) def onQuit(self, event=None): exit() def onViewInfo(self): UserInfoDialog(self, self.userCreate) def onLogOut(self, event=None): self.destroy() self.menu.destroy() self.parent.geometry("400x400") self.parent.backToPreviousFrame() def onNewType(self, event=None): NewTypeDialog(self) def onNewItem(self, event=None): NewItemDialog(self) def onViewListUser(self, event=None): ShowListUserDialog(self) def onViewActivityLog(self, event=None): pass def onAbout(self): about = """ Developer: Trần Dũng Tên Ứng dụng: Managing Selling App Version: 1.0 Release: 06-01-2019 """ popup = Toplevel(self) popup.title("About") layoutAbout = Frame(popup) lblAbout = Label(layoutAbout, text=about) lblAbout.pack(side=TOP, fill=BOTH, expand=True) layoutAbout.pack(side=TOP, fill=BOTH, expand=True, padx=10, pady=10)
class SearchApplication(GenericFrame): FAVICON = "../assets/favicon.ico" def __init__(self, parent=None, app_window=None): self.lastValue = None self.category_option = StringVar("") self.column_id = [ 'ProductDescription', 'ManufacturerName', 'ManufacturerPartNumber', 'DigiKeyPartNumber', 'Category' ] Frame.__init__(self, parent) self.pack() self.parent = parent self.app_window = app_window self.selectedField = None self.parent.title("Partlocater - Advanced Database Search") self.parent.iconbitmap(self.FAVICON) self.menubar = Frame(self, background='white') self.menubar.pack(side=TOP, fill=X, expand=YES) self.win_frame = Frame(self) self.win_frame.pack(side=TOP, fill=BOTH, expand=YES) self.editbutton = Menubutton(self.menubar, text='Edit', background='grey98') self.editbutton.pack(side=LEFT, fill=X) self.editmenu = Menu(self.editbutton, tearoff=0) self.editbutton.config(menu=self.editmenu) self.copySourcesMenu = Menu(self.editbutton, tearoff=0) self.editmenu.add_cascade(label='Copy', menu=self.copySourcesMenu) self.copySourcesMenu.add_command(label='Part Number', state=DISABLED, command=self.on_copy_partnumber) self.partnumber_index = 0 self.copySourcesMenu.add_command(label='Selected Parameter', state=DISABLED, command=self.on_copy_parameters) self.selectedParameter_index = 1 self.copySourcesMenu.add_command(label='Selected Part All Parameters', state=DISABLED, command=self.on_copy_all_parameters) self.allParameters_index = 2 self.editmenu.add_command(label='Delete Part', state=DISABLED, command=self.on_delete) self.searchLF = LabelFrame(self.win_frame, text="Search") self.searchLF.pack(side=LEFT, fill=X, expand=YES, pady=4, padx=6) self.searchLeftF = Frame(self.searchLF) self.searchLeftF.pack(side=LEFT, anchor=W) self.searchRightF = Frame(self.searchLF) self.searchRightF.pack(side=LEFT, anchor=N) self.searchLabelWidth = 20 self.catF = Frame(self.searchLeftF) self.catF.pack(side=TOP, anchor=W) self.catL = Label(self.catF, text='Category', width=self.searchLabelWidth, anchor=W, justify=LEFT) self.catL.pack(side=LEFT, fill=X, expand=YES) self.cat = StringVar() self.catE = Entry(self.catF, textvariable=self.cat, width=50, state=DISABLED) self.catE.config(disabledbackground=self.catE.cget("bg")) self.catE.config(disabledforeground=self.catE.cget("fg")) self.catE.pack(side=LEFT, fill=X, expand=YES, pady=4) self.category_option = StringVar() self.cat.set("All") option_list = ['All', 'All'] + Config().tables self.catM = OptionMenu(self.searchRightF, self.category_option, *option_list, command=self.on_category) self.catM.pack(side=TOP, anchor=N, fill=X, expand=YES) self.manF = Frame(self.searchLeftF) self.manF.pack(side=TOP, anchor=W) self.manL = Label(self.manF, text='ManufacturerName', width=self.searchLabelWidth, anchor=W, justify=LEFT) self.manL.pack(side=LEFT, fill=X, expand=YES, pady=4) self.man = StringVar() self.manE = Entry(self.manF, width=50, textvariable=self.man) self.manE.pack(side=LEFT, fill=X, expand=YES, pady=4) self.mpnF = Frame(self.searchLeftF) self.mpnF.pack(side=TOP, anchor=W) self.mpnL = Label(self.mpnF, text='ManufacturerPartNumber', width=self.searchLabelWidth, anchor=W, justify=LEFT) self.mpnL.pack(side=LEFT, fill=X, expand=YES, pady=4) self.mpn = StringVar() self.mpnE = Entry(self.mpnF, width=50, textvariable=self.mpn) self.mpnE.pack(side=LEFT, fill=X, expand=YES, pady=4) self.spnF = Frame(self.searchLeftF) self.spnF.pack(side=TOP, anchor=W) self.spnL = Label(self.spnF, text='DigiKeyPartNumber', width=self.searchLabelWidth, anchor=W, justify=LEFT) self.spnL.pack(side=LEFT, fill=X, expand=YES, pady=4) self.spn = StringVar() self.spnE = Entry(self.spnF, width=50, textvariable=self.spn) self.spnE.pack(side=LEFT, fill=X, expand=YES, pady=4) self.descF = Frame(self.searchLeftF) self.descF.pack(side=TOP, anchor=W) self.descL = Label(self.descF, text='ProductDescription', width=self.searchLabelWidth, anchor=W, justify=LEFT) self.descL.pack(side=LEFT, fill=X, expand=YES, pady=4) self.desc = StringVar() self.descE = Entry(self.descF, width=50, textvariable=self.desc) self.descE.pack(side=LEFT, fill=X, expand=YES, pady=4) self.descE.focus_force() self.findF = Frame(self.searchLeftF) self.findF.pack(side=TOP, anchor=E) self.findB = ttk.Button(self.findF, text="Find", width=12, command=lambda event=None: self.do_find(event)) self.findB.pack(side=LEFT, pady=4) self.clearB = ttk.Button(self.findF, text="Clear", width=6, command=self.on_clear_search) self.clearB.pack(side=LEFT, pady=4) self.partsLF = LabelFrame(self, text="Found Components") self.partsLF.pack(side=TOP, fill=X, expand=YES, pady=4, padx=4) self.partsF = Frame(self.partsLF) self.partsF.pack(side=TOP, pady=4, padx=4) # change treeview for search here self.partsTV = Treeview(self.partsF, selectmode=BROWSE, show='tree headings', columns=self.column_id) self.partsTV.bind('<Double-Button-1>', self.on_edit_item) self.partsTV.bind('<<TreeviewSelect>>', self.fieldChanged) self.partsTV.bind('<Escape>', self.clearSelection) self.partsTV.bind('<MouseWheel>', self.mousewheel) self.partsTV.bind('<Button-4>', self.mousewheel) self.partsTV.bind('<Button-5>', self.mousewheel) vcmd = (self.register(self.validateEntry), '%P') self.editfield = ttk.Entry(self.partsTV, validate='key', validatecommand=vcmd) self.editfield.bind('<Return>', self.updateField) self.editfield.bind('<Escape>', self.clearSelection) self.partsTV.bind('<Control-c>', self.on_copy_element) self.partsTV.column("#0", minwidth=0, width=18, stretch=NO) for t in self.column_id: self.partsTV.heading(t, text=Config().parameter[t]) self.partsTV.column('Category', width=60) self.scrollbar = Scrollbar(self.partsF, orient='vertical', command=self.partsTV.yview) self.scrollbar.pack(side=RIGHT, fill=Y, expand=YES, anchor=E) self.partsTV.configure(yscroll=self.scrollbar.set) self.scrollbar.config(command=self.yview) self.partsTV.pack(side=TOP, anchor=W, fill=X, expand=YES) self.partsTV.delete(*self.partsTV.get_children()) # end change of treeview # change the following to menu item #self.part_buttonF = Frame(self.partsLF) #self.delete_partB = ttk.Button(self.partsLF, text="Delete Part from Database", command=self.on_delete, #state=DISABLED) #self.delete_partB.pack(side=RIGHT, anchor=W, expand=NO, pady=4, padx=6) #self.partsB = ttk.Button(self.partsLF, text="Copy Selected To Part Find", command=self.on_copy, state=DISABLED) #self.partsB.pack(side=RIGHT, anchor=W, expand=NO, pady=4, padx=6) #self.part_buttonF.pack(side=BOTTOM) # start remove vvv #self.element_labelframe = LabelFrame(self, text="Modify Name/Value") #self.element_labelframe.pack(side=TOP, fill=X, expand=YES, pady=4, padx=6) #self.element_frame = Frame(self.element_labelframe) #self.element_frame.pack(side=TOP) #self.element_name = StringVar() #self.element_label = Label(self.element_frame, textvariable=self.element_name, width=30, anchor=W, justify=LEFT) #self.element_label.pack(side=LEFT, anchor=W, fill=X, expand=YES, pady=4) #self.element_value = StringVar() #self.element_entry = Entry(self.element_frame, width=50, textvariable=self.element_value) #self.element_entry.pack(side=LEFT, fill=X, expand=YES, pady=4) #self.default_color = self.element_entry.cget('background') #self.element_update = ttk.Button(self.element_frame, text="Update", command=self.on_update_element, #state=DISABLED) #self.element_update.pack(side=LEFT, fill=X, expand=YES, pady=4) #self.element_cancel = ttk.Button(self.element_frame, text="Cancel", command=self.on_clear_element, #state=DISABLED) #self.element_cancel.pack(side=LEFT, fill=X, expand=YES, pady=4) # end remove ^^^ self.statusLF = LabelFrame(self, text="Status") self.statusLF.pack(side=BOTTOM, fill=X, expand=YES, pady=4, padx=6) self.statusF = Frame(self.statusLF) self.statusF.pack(side=TOP, fill=X, expand=YES, padx=6) self.status = self.StatusBar(self.statusF, self) def validateEntry(self, P): if (len(P) <= 120): return True else: self.bell() return False # scroll bar event def yview(self, *args): if self.selectedField is not None: self.editfield.place_forget() self.selectedField = None self.partsTV.yview(*args) # mousewheel and button4/5 event def mousewheel(self, event): if self.selectedField is not None: self.editfield.place_forget() self.selectedField = None # escape event in treeview or editfield def clearSelection(self, event): self.editfield.place_forget() self.selectedField = None self.partsTV.selection_remove(self.partsTV.selection()) self.status.set("") # double button event def on_edit_item(self, event): if self.partsTV.parent(self.partsTV.selection() ) == '': # testing should not edit a parent self.selectedField = None return if (self.partsTV.identify_region(event.x, event.y) == 'cell'): self.selectedField = self.partsTV.identify_row(event.y) x, y, width, height = self.partsTV.bbox(self.selectedField, '#2') v = self.partsTV.set(self.selectedField, 1) self.editfield.pack() self.editfield.delete(0, len(self.editfield.get())) self.editfield.insert(0, v) self.editfield.selection_range(0, 'end') self.editfield.focus_force() self.editfield.place(x=x, y=y, width=width, height=height) # find button event def on_find(self): category = self.cat.get() search_list = [] col_list = [] search_str = self.man.get() if not (validate(search_str)): raise Exception("Invalid Manufacture Name") search_list.append(search_str) col_list.append(Config().parameter['ManufacturerName']) search_str = self.mpn.get() if not (validate(search_str)): raise Exception("Invalid Manufacture Part Number") search_list.append(search_str) col_list.append(Config().parameter['ManufacturerPartNumber']) search_str = self.spn.get() if not (validate(search_str)): raise Exception("Invalid Supplier Part Number") search_list.append(search_str) col_list.append(Config().parameter['DigiKeyPartNumber']) search_str = self.desc.get().split() if not (validate(search_str)): raise Exception("Invalid Description") search_list += search_str col_list.append(Config().parameter['ProductDescription']) select = "SELECT * FROM `" + Config().loaded_db.name + "`." where = "WHERE" like = "" i = 0 for item in search_list: if len(item) > 0: item = item.replace('%', '\\%') item = item.replace('"', '') item = item.replace("'", "") if i < 3: like += where + " `" + col_list[ i] + "` LIKE '" + item + "%'" else: like += where + " (`" + col_list[i] + "` LIKE '" + item + "%' OR `" + \ col_list[i] + "` LIKE '% " + item + "%')" where = " AND" i = i + 1 if (i < 3) else i self.partsTV.delete(*self.partsTV.get_children()) count = 0 if category == "All": for table in Config().tables: qry = select + "`" + table + "` " + like result = Config().loaded_db.query(qry) for record in result: v = [] spn = record[Config().parameter['DigiKeyPartNumber']] count += 1 for id in self.column_id: if id == 'Category': v.append(table) else: v.append(record[Config().parameter[id]]) id = self.partsTV.insert('', 'end', iid=spn, text=spn, values=v) for params in record: if record[params] is not None: self.partsTV.insert(id, 'end', text=spn, values=(params, record[params])) else: qry = select + "`" + category + "` " + like result = Config().loaded_db.query(qry) for record in result: v = [] count += 1 spn = record[Config().parameter['DigiKeyPartNumber']] for id in self.column_id: if id == 'Category': v.append(category) else: v.append(record[Config().parameter[id]]) id = self.partsTV.insert('', 'end', iid=spn, text=spn, values=v) for params in record: if record[params] is not None: self.partsTV.insert(id, 'end', text=spn, values=(params, record[params])) self.status.set(("No" if count == 0 else str(count)) + " items found") # return event def updateField(self, event): value = self.editfield.get() self.editfield.place_forget() name = self.partsTV.item(self.selectedField, "text") if not validate(value): self.status.seterror("Invalid value, must not have quotes") return self.partsTV.set(self.selectedField, "#2", value) key = self.partsTV.set(self.selectedField, "#1") self.editfield.place_forget() element_parent = self.partsTV.parent(self.selectedField) table_name = self.partsTV.item( element_parent, "values")[self.column_id.index('Category')] part_number = self.partsTV.item( element_parent, "values")[self.column_id.index('DigiKeyPartNumber')] set_param = "SET `" + key + "` = '" + value + "' " where = "WHERE `" + Config( ).parameter['DigiKeyPartNumber'] + "` = '" + part_number + "'" qry = "UPDATE `" + Config( ).loaded_db.name + "`.`" + table_name + "` " + set_param + where print(qry) try: Config().loaded_db.query(qry) except Exception as e: self.status.seterror("Database query failed: %s", e) return self.status.set("Changed " + key + " to " + value + " for part " + part_number + ".") self.partsTV.see(self.selectedField) # clear button in search frame def on_clear_search(self): self.man.set("") self.mpn.set("") self.spn.set("") self.desc.set("") self.cat.set("All") self.category_option.set("All") self.partsTV.delete(*self.partsTV.get_children()) def do_flash(self): current_color = self.element_entry.cget("background") if current_color == self.default_color: self.element_entry.config(background="red") else: self.element_entry.config(background=self.default_color) return self.after(250, self.do_flash) # category option menu def on_category(self, value): self.catE.config(state=NORMAL) self.cat.set(value) self.catE.config(state=DISABLED) #def on_copy(self): #selected = self.partsTV.selection()[0] #key = self.partsTV.item(selected, "values")[self.column_id.index('DigiKeyPartNumber')] #self.app_window.part_num_string.set(key) #self.status.set("Part Number '" + key + "' copied to Part Find") # Edit -> Delete menu def on_delete(self): selected = self.partsTV.selection()[0] key = self.partsTV.item( selected, "values")[self.column_id.index('DigiKeyPartNumber')] table = self.partsTV.item(selected, "values")[self.column_id.index('Category')] if messagebox.askokcancel( "Delete", "Click OK if you really want to delete '" + key + "' from database?"): Config().loaded_db.query("DELETE FROM `" + table + "` WHERE `" + Config().parameter['DigiKeyPartNumber'] + "` = '" + key + "'") self.status.set("Part Number '" + key + "' deleted from database") # treeview select event def fieldChanged(self, event): selected = self.partsTV.selection() if len(selected) > 0: self.copySourcesMenu.entryconfig(self.partnumber_index, state=NORMAL) self.copySourcesMenu.entryconfig(self.allParameters_index, state=NORMAL) else: self.copySourcesMenu.entryconfig(self.partnumber_index, state=DISABLED) self.copySourcesMenu.entryconfig(self.allParameters_index, state=DISABLED) return if self.partsTV.parent(selected) == '': self.copySourcesMenu.entryconfig(self.selectedParameter_index, state=DISABLED) else: self.copySourcesMenu.entryconfig(self.selectedParameter_index, state=NORMAL) if selected != self.selectedField: self.editfield.place_forget() self.selectedField = None def on_copy_parameters(self): selected = self.partsTV.selection() if len(selected) == 0 or self.partsTV.parent(selected) == '': return try: property = self.partsTV.item(selected, "values") self.parent.clipboard_clear() self.parent.clipboard_append(property[0] + '\t' + property[1]) self.parent.update() self.status.set(property[0] + ' ' + property[1] + " copied to clipboard") except Exception as e: pass def on_copy_partnumber(self): selected = self.partsTV.selection() if len(selected) == 0 or self.partsTV.parent(selected) == '': return try: if self.partsTV.parent(selected) != '': selected = self.partsTV.parent(selected) partnumber = self.partsTV.item( selected, "values")[self.column_id.index('DigiKeyPartNumber')] self.parent.clipboard_clear() self.parent.clipboard_append(partnumber) self.parent.update() self.status.set(" '" + partnumber + "' copied to clipboard") except Exception as e: pass def on_copy_all_parameters(self): selected = self.partsTV.selection() if len(selected) == 0: return try: if self.partsTV.parent(selected) != '': selected = self.partsTV.parent(selected) partnumber = self.partsTV.item( selected, "values")[self.column_id.index('DigiKeyPartNumber')] elements = self.partsTV.get_children(selected) self.parent.clipboard_clear() self.parent.clipboard_clear() for i in elements: element = self.partsTV.item(i, "values") self.parent.clipboard_append(element[0] + "\t" + element[1] + "\n") self.parent.update() self.status.set("All properties of " + partnumber + " copied to clipboard") except Exception as e: pass # deprecate def on_copy_element(self, event): try: selected = self.partsTV.selection()[0] if self.partsTV.parent(selected) == '': partnumber = self.partsTV.item elements = self.partsTV.get_children(selected) self.parent.clipboard_clear() for i in elements: element = self.partsTV.item(i, "values") self.parent.clipboard_append(element[0] + "\t" + element[1] + "\n") self.parent.update() self.status.set("All properties of " + self.partsTV.item(selected, "values")[3] + " copied to clipboard") else: key = self.partsTV.item(selected, "values")[0] val = self.partsTV.item(selected, "values")[1] self.parent.clipboard_clear() self.parent.clipboard_append(val) self.parent.update() self.status.set(key + " '" + val + "' copied to clipboard") except Exception as e: pass def do_find(self, event): try: self.on_find() except Exception as e: self.status.seterror(e)
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 ShowBillDialog: def __init__(self, parent): self.parent = parent self.data = Database() self.listBill = self.data.getBillList() self.dialog = Toplevel(parent) self.dialog.title("Bảng thống kê Hóa đơn") self.dialog.geometry("1000x400") self.drawDialog(self.dialog) def drawDialog(self, parent): layoutTree = Frame(parent) self.drawTreeBill(layoutTree) for bill in self.listBill: self.addBillIntoTree(bill) layoutTree.pack(side=TOP, fill=BOTH, expand=True) def drawTreeBill(self, parent): listAttribute = [ "Mặt hàng", "Loại hàng", "ID", "Số lượng xuất", "Thành giá", "Người tạo" ] yScrollTree = Scrollbar(parent, orient=VERTICAL) xScrollTree = Scrollbar(parent, orient=HORIZONTAL) # Create Delete, Add and Edit Button # layoutButton = Frame(parent) # btnAddBill = Button(layoutButton, text = "+", fg = "green", command = self.onAddBill) # btnDelBill = Button(layoutButton, text = "-", fg = "red", command = self.onDeleteBill) # btnEditBill = Button(layoutButton, text = "Edit", fg = "blue") # btnAddBill.pack(side = LEFT) # btnDelBill.pack(side = LEFT) # btnEditBill.pack(side = LEFT) # layoutButton.pack(side = TOP, anchor = "w") # Create Tree View self.treeBill = Treeview(parent, column=listAttribute, yscrollcommand=yScrollTree.set, xscrollcommand=xScrollTree.set) # self.treeBill.bind(sequence="<Double-Button-1>",func=self.onEditBill) self.treeBill.column(column="#0", width=100, minwidth=100) for nameAttr in listAttribute: self.treeBill.heading(column=nameAttr, text=nameAttr) self.treeBill.column(column=nameAttr, width=100, minwidth=100) #Create Scrollbar for tree view yScrollTree.pack(side=RIGHT, fill=Y) xScrollTree.pack(side=BOTTOM, fill=X) self.treeBill.pack(side=TOP, anchor="w", fill=BOTH, expand=True) yScrollTree.config(command=self.treeBill.yview) xScrollTree.config(command=self.treeBill.xview) def addBillIntoTree(self, billInput): bill = billInput if not self.treeBill.exists(bill.CreatedDate): self.treeBill.insert("", "end", str(bill.CreatedDate), text=bill.CreatedDate, tags="parents") self.treeBill.item(bill.CreatedDate, open=True) self.treeBill.insert(bill.CreatedDate, END, bill.CreatedTime, text=bill.CreatedTime, tags="childs") self.treeBill.set(bill.CreatedTime, "Mặt hàng", bill.name) self.treeBill.set(bill.CreatedTime, "Loại hàng", bill.type) self.treeBill.set(bill.CreatedTime, "ID", bill.id) self.treeBill.set(bill.CreatedTime, "Số lượng xuất", bill.amount) self.treeBill.set(bill.CreatedTime, "Thành giá", int(bill.price)) self.treeBill.set(bill.CreatedTime, "Người tạo", bill.CreatedUser)
class TreeType(Frame): def __init__(self, parent, item, dialog): Frame.__init__(self, parent) self.parent = parent self.item = item self.numTypeAdded = 0 self.dialogParent = dialog self.listType = item.type self.data = Database() self.drawScreen() self.linkAccelerator() for typeItem in self.listType: self.addTypeIntoTree(typeItem) def linkAccelerator(self): self.bind_all("<Shift-Delete>", self.onDeleteItem) self.bind_all("<Control-N>", self.onAddType) self.bind_all("<Delete>", self.onDeleteType) def drawScreen(self): # self.pack(fill = BOTH, anchor = "w", expand = True) ########## Listing all Bills which has been created ########### layoutList = Frame(self) self.drawTreeOfType(layoutList) layoutList.pack(side=LEFT, fill=BOTH, expand=True) self.menu = Menu(self) self.dialogParent.config(menu=self.menu) self.drawMenu(self.menu) def drawTreeOfType(self, parent): self.pack(fill=BOTH, expand=True) listAttribute = ["Loại hàng", "Số lượng tồn", "Đơn giá"] yScrollTree = Scrollbar(parent, orient=VERTICAL) xScrollTree = Scrollbar(parent, orient=HORIZONTAL) # Create Delete, Add and Edit Button layoutButton = Frame(self) btnAddType = Button(layoutButton, text="+", fg="green", command=self.onAddType) btnDelType = Button(layoutButton, text="-", fg="red", command=self.onDeleteType) btnRefresh = Button(layoutButton, text="Refresh", fg="blue", command=self.dialogParent.onRefresh) btnAddType.pack(side=LEFT) btnDelType.pack(side=LEFT) btnRefresh.pack(side=RIGHT) layoutButton.pack(side=TOP, fill=X, anchor="w") # Create Tree View self.treeType = Treeview(parent, column=listAttribute, yscrollcommand=yScrollTree.set, xscrollcommand=xScrollTree.set) self.treeType.bind(sequence="<Double-Button-1>", func=self.onEditType) self.treeType.heading(column="#0", text="ID") self.treeType.column(column="#0", width=100, minwidth=100) for nameAttr in listAttribute: self.treeType.heading(column=nameAttr, text=nameAttr) self.treeType.column(column=nameAttr, width=100, minwidth=100) #Create Scrollbar for tree view yScrollTree.pack(side=RIGHT, fill=Y) xScrollTree.pack(side=BOTTOM, fill=X) self.treeType.pack(side=TOP, anchor="w", fill=BOTH, expand=True) yScrollTree.config(command=self.treeType.yview) xScrollTree.config(command=self.treeType.xview) def drawMenu(self, parent): # Item Menu itemMenu = Menu(parent) itemMenu.add_command(label="Thêm mặt hàng", command=self.dialogParent.onAddItem, accelerator="Ctrl+N") itemMenu.add_command(label="Xóa mặt hàng", command=self.onDeleteItem, accelerator="Shift+Delete") parent.add_cascade(label="Mặt hàng", menu=itemMenu) # Type Menu typeMenu = Menu(parent) typeMenu.add_command(label="Thêm loại hàng", command=self.onAddType, accelerator="Ctrl+Shift+N") typeMenu.add_command(label="Xóa loại hàng", command=self.onDeleteType, accelerator="Delete") parent.add_cascade(label="Loại hàng", menu=typeMenu) def addTypeIntoTree(self, typeInput): temp = typeInput # if not self.treeType.exists(temp.idParent) : # self.treeType.insert("","end", str(temp.idParent), text = temp.idParent) # self.treeType.item(temp.idParent, open = True) self.treeType.insert("", "end", str(temp.idType), text=temp.idType) self.treeType.set(temp.idType, "Loại hàng", temp.name) self.treeType.set(temp.idType, "Số lượng tồn", temp.amount) self.treeType.set(temp.idType, "Đơn giá", int(temp.unitPrice)) def onEditType(self, event=None): listTemp = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] curItem = self.treeType.item(self.treeType.focus()) col = self.treeType.identify_column(event.x) print(curItem) print(col) if curItem["text"] in listTemp and col != "#0": if col != "#0": messagebox.showinfo( "Thêm loại", "Sửa ID loại tạm này để lưu lại vào Database", parent=self) return cellValue = None if col == "#0": temp = simpledialog.askstring("Đổi ID", "Nhập ID mới", parent=self) if temp != None: if len(temp) > 0: cellValue = curItem["text"] try: if cellValue in listTemp: idTab = self.parent.select() frameChosen = self.parent._nametowidget(idTab) typeItem = TypeItem(curItem["values"][0], 0, 0, temp, frameChosen.item.id) self.data.insertTypeItem(typeItem) frameChosen.treeType.update() print("Sau khi sua:", self.treeType.item(self.treeType.focus())) else: self.data.updateIdOfType(cellValue, temp) except (sqlite3.IntegrityError, TclError): messagebox.showwarning( "Opps !!!!", message="ID bạn nhập đã tồn tại !!!!", parent=self) return if cellValue in listTemp: frameChosen.numTypeAdded -= 1 self.treeType.update() else: messagebox.showwarning("Empty !!!", "ID không thể để trống", parent=self) self.treeType.insert( "", str(self.treeType.index(self.treeType.focus())), temp, text=temp, values=curItem["values"]) self.treeType.delete(self.treeType.focus()) return if col == "#1": temp = simpledialog.askstring("Đổi tên loại hàng", "Nhập tên mới", parent=self) if temp != None: if len(temp) > 0: cellValue = curItem["values"][0] self.data.updateNameOfType(curItem["text"], temp) curItem["values"][0] = temp self.treeType.item(curItem["text"], values=curItem["values"]) self.treeType.update() else: messagebox.showwarning("Empty !!!", "Tên loại hàng không thể để trống", parent=self) return if col == "#2": temp = simpledialog.askinteger("Đổi số lượng tồn", "Nhập số lượng tồn mới: ", parent=self) if temp != None: if temp >= 0: cellValue = curItem["values"][1] self.data.updateAmountOfType(curItem["text"], temp) curItem["values"][1] = temp print(curItem["text"]) self.treeType.item(curItem["text"], values=curItem["values"]) self.treeType.update() else: messagebox.showwarning("Empty !!!", "Số lượng không thể là số âm", parent=self) return if col == "#3": temp = simpledialog.askfloat("Đổi đơn giá", "Nhập đơn giá mới: ", parent=self) if temp != None: if temp >= 0: cellValue = curItem["values"][2] self.data.updateUnitOfType(curItem["text"], temp) curItem["values"][2] = temp print(curItem["text"]) self.treeType.item(curItem["text"], values=curItem["values"]) self.treeType.update() else: messagebox.showwarning("Empty !!!", "Đơn giá không thể là số âm", parent=self) return print("Cell Values = ", cellValue) def onAddType(self, event=None): idTab = self.parent.select() frameChosen = self.parent._nametowidget(idTab) if frameChosen.numTypeAdded > 9: messagebox.showwarning( "Warning", "Bạn chỉ có thể thêm tạm 10 loại \n Hãy Sửa ID để lưu lại những loại tạm trên", parent=self) return typeItem = TypeItem(name=frameChosen.numTypeAdded, amount=0, unitPrice=0, idType="#", idParent=frameChosen.item.id) frameChosen.addTypeIntoTree(typeItem) frameChosen.numTypeAdded += 1 def onDeleteType(self, event=None): idTab = self.parent.select() frameChosen = self.parent._nametowidget(idTab) curItem = frameChosen.treeType.selection() accept = messagebox.askokcancel( "Xóa loại hàng này", "Bạn thật sự muốn xóa!!! Bạn không thể hoàn tác hành động này", parent=self) if accept == True: if len(curItem) == 0: messagebox.showwarning( title="Empty !!!", message="Xin hãy chọn loại hàng bạn muốn xóa", parent=self) return for choose in curItem: treeItem = frameChosen.treeType.item(choose) self.data.deleteType(treeItem["values"][0]) frameChosen.treeType.delete(treeItem["text"]) def onDeleteItem(self, event=None): idTab = self.parent.select() frameChosen = self.parent._nametowidget(idTab) accept = messagebox.askokcancel( "Xóa loại hàng này", "Bạn thật sự muốn xóa!!! Bạn không thể hoàn tác hành động này", parent=self) if accept == True: idTab = self.parent.select() frameChosen = self.parent._nametowidget(idTab) self.data.deleteItem(frameChosen.item.id) self.parent.forget(idTab) self.parent.update() def onDestroy(self, event): # ask = """ # Bạn thực sự muốn đóng ứng dụng # """ # messagebox.askokcancel("Closing!!!",) self.dialogParent.destroy()
class Configurator(tk.Tk): """ The main Tk window representing the main app. Attributes ---------- treeview : :py:class:`~tkinter.Treeview` The treeview widget. treeview_popup_target_id : `int` The pop target id relating to the id of the selected element. treeview_popup : :py:class:`~tkinter.Widget` The treeview popup widget. cfg_file_name : `str` The file name of the current configuration. element_dict : `dict` The dictionary of elements. Keys are the element ids. root_element : :py:class:`~enrich2.base.storemanager.StoreManager` An instance inheriting from storemanager that acts as a root object. force_recalculate :py:class:`tkinter.BooleanVar` The tkinter boolean variable for this option. component_outliers :py:class:`tkinter.BooleanVar` The tkinter boolean variable for this option. tsv_requested : :py:class:`tkinter.BooleanVar` The tkinter boolean variable for this option. treeview_buttons : `list` The ``new``, ``edit`` and ``delete`` buttons. go_button : :py:class`~tkinter.ttk.Button` The button that begins the analysis scorer_widget : :py:class:`~enrich2.gui.options_frame.ScorerScriptsDropDown` The ScorerScriptsDropDown instance associated with this app. scorer : :py:class:`~enrich2.plugins.scoring.BaseScorerPlugin` The scorer class loaded from a plugin scorer_attrs : `dict` The scoring attributes for the plugin. scorer_path : `str` The path to the currently selected scoring plugin. analysis_thread : :py:class:`~threading.Thread` The thread object that runs the computation method to prevent GUI blocking. Methods ------- create_main_frame create_menubar create_treeview_context_menu create_new_element menu_open menu_save menu_saveas menu_selectall refresh_treeview treeview_context_menu set_treeview_properties populate_tree go_button_press new_button_press edit_button_press delete_button_press delete_element apply_seqlib_fastq get_element get_focused_element get_selected_elements get_selected_scorer_class get_selected_scorer_attrs get_selected_scorer_path run_analysis set_gui_state configure_analysis refresh_plugins show_plugin_source_window See Also -------- :py:class:`~tkinter.Tk` """ def __init__(self): tk.Tk.__init__(self) self.title("Enrich 2") # Main app variables self.cfg_file_name = tk.StringVar() self.element_dict = dict() self.root_element = None self.analysis_thread = None self.plugin_source_window = None self.queue = queue.Queue() # Treeview variables self.treeview = None self.treeview_popup_target_id = None self.treeview_popup = None # analysis options self.force_recalculate = tk.BooleanVar() self.component_outliers = tk.BooleanVar() self.tsv_requested = tk.BooleanVar() # allow resizing self.rowconfigure(0, weight=1) self.columnconfigure(0, weight=1) # create UI elements self.treeview_buttons = [] self.go_button = None self.scorer_widget = None self.scorer = None self.scorer_attrs = None self.scorer_path = None self.create_main_frame() self.create_menubar() self.create_treeview_context_menu() self.after(10, self.poll_logging_queue) self.plugin_source_window = SourceWindow(master=self) self.plugin_source_window.hide() self.refresh_plugins() # ---------------------------------------------------------------------- # # Creation Methods # ---------------------------------------------------------------------- # def create_treeview_context_menu(self): """ This creates the tree-like view rendering the experiment heirachy. """ self.treeview_popup = tk.Menu(self, tearoff=0) self.treeview_popup.add_command(label="Apply FASTQ...", command=self.apply_seqlib_fastq) def create_main_frame(self): """ Large function creating all the basic elements of the main app frame. Creates the treeview and associated buttons, the scoring plugin frame and the go button. """ # Frame for the Treeview and New/Edit/Delete buttons main = Frame(self, padding=(3, 3, 12, 12)) main.rowconfigure(0, weight=1) main.columnconfigure(0, weight=1) main.columnconfigure(1, weight=0) main.grid(row=0, column=0, sticky="nsew") # ------------------------------------------------------- # # Frame for the Treeview and its scrollbars tree_frame = Frame(main, padding=(3, 3, 12, 12)) tree_frame.rowconfigure(0, weight=1) tree_frame.rowconfigure(1, weight=0) tree_frame.columnconfigure(0, weight=1) tree_frame.columnconfigure(1, weight=0) tree_frame.grid(row=0, column=0, sticky="nsew") # ------------------------------------------------------- # # Treeview with column headings self.treeview = Treeview(tree_frame) self.treeview["columns"] = ("class", "barcodes", "variants") self.treeview.column("class", width=120) self.treeview.heading("class", text="Type") self.treeview.column("barcodes", width=25, stretch=tk.NO, anchor=tk.CENTER) self.treeview.heading("barcodes", text="BC") self.treeview.column("variants", width=25, stretch=tk.NO, anchor=tk.CENTER) self.treeview.heading("variants", text="V") self.treeview.grid(row=0, column=0, sticky="nsew") # Treeview context menu bindings self.treeview.bind("<Button-2>", self.treeview_context_menu) # Treeview scrollbars tree_ysb = tk.Scrollbar(tree_frame, orient="vertical", command=self.treeview.yview) tree_xsb = tk.Scrollbar(tree_frame, orient="horizontal", command=self.treeview.xview) tree_ysb.grid(row=0, column=1, sticky="nsw") tree_xsb.grid(row=1, column=0, sticky="ewn") self.treeview.config(yscroll=tree_ysb.set, xscroll=tree_xsb.set) # ------------------------------------------------------- # # Frame for New/Edit/Delete buttons button_frame = Frame(main, padding=(3, 3, 12, 12)) button_frame.grid(row=1, column=0) new_button = Button(button_frame, text="New...", command=self.new_button_press) new_button.grid(row=0, column=0) edit_button = Button(button_frame, text="Edit...", command=self.edit_button_press) edit_button.grid(row=0, column=1) delete_button = Button(button_frame, text="Delete", command=self.delete_button_press) delete_button.grid(row=0, column=2) self.treeview_buttons = [new_button, delete_button, edit_button] # ------------------------------------------------------- # # Frame for Plugin and Analysis Options right_frame = Frame(main, padding=(3, 3, 12, 12)) right_frame.rowconfigure(0, weight=1) right_frame.rowconfigure(1, weight=0) right_frame.columnconfigure(0, weight=1) right_frame.columnconfigure(1, weight=0) right_frame.grid(row=0, column=1, sticky="new") # ------------------------------------------------------- # # LabelFrame for plugin and options scoring_plugin = ScorerScriptsDropDown(right_frame, text="Scoring Options", padding=(3, 3, 12, 12)) scoring_plugin.grid(row=0, column=0, sticky="new") self.scorer_widget = scoring_plugin # ------------------------------------------------------- # # LabelFrame for Analysis Options row = 0 options_frame = LabelFrame(right_frame, text="Analysis Options", padding=(3, 3, 12, 12)) options_frame.grid(row=1, column=0, sticky="new", pady=4) # force recalculate force_recalculate = Checkbutton(options_frame, text="Force Recalculation", variable=self.force_recalculate) force_recalculate.grid(column=0, row=row, sticky="w") row += 1 # component outliers component_outliers = Checkbutton( options_frame, text="Component Outlier Statistics", variable=self.component_outliers, ) component_outliers.grid(column=0, row=row, sticky="w") row += 1 # write tsv tsv_requested = Checkbutton(options_frame, text="Write TSV Files", variable=self.tsv_requested) tsv_requested.grid(column=0, row=row, sticky="w") tsv_requested.invoke() row += 1 # ------------------------------------------------------- # # Run Analysis button frame go_button_frame = Frame(main, padding=(3, 3, 12, 12)) go_button_frame.grid(row=1, column=1) go_button = Button(go_button_frame, text="Run Analysis", command=self.go_button_press) go_button.grid(column=0, row=0) self.go_button = go_button def create_new_element(self): """ Create and return a new element based on the current selection. This element is not added to the treeview. """ element = None parent_element = self.get_focused_element() if isinstance(parent_element, Experiment): element = Condition() element.parent = parent_element elif isinstance(parent_element, Condition): element = Selection() element.parent = parent_element elif isinstance(parent_element, Selection): element = CreateSeqLibDialog(self).element_type() element.parent = parent_element elif isinstance(parent_element, SeqLib): # special case: creates a copy of the selected SeqLib as a sibling element = type(parent_element)() element.configure(parent_element.serialize()) element.parent = parent_element.parent # clear out the seqlib-specific values element.name = None element.timepoint = None element.counts_file = None element.reads = None else: raise ValueError("Unrecognized parent object " "type '{}'".format(type(parent_element))) return element def create_menubar(self): """ Creates the menubar for the main app, with associated drop down menus. """ # make platform-specific keybinds if platform.system() == "Darwin": accel_string = "Command+" accel_bind = "Command-" else: accel_string = "Ctrl+" accel_bind = "Control-" # create the menubar menubar = tk.Menu(self) # file menu filemenu = tk.Menu(menubar, tearoff=0) filemenu.add_command( label="Open...", accelerator="{}O".format(accel_string), command=self.menu_open, ) filemenu.add_command(label="Save", accelerator="{}S".format(accel_string), command=self.menu_save) filemenu.add_command( label="Save As...", accelerator="{}Shift+S".format(accel_string), command=self.menu_saveas, ) menubar.add_cascade(label="File", menu=filemenu) # edit menu filemenu = tk.Menu(menubar, tearoff=0) filemenu.add_command( label="Select All", accelerator="{}A".format(accel_string), command=self.menu_selectall, ) menubar.add_cascade(label="Edit", menu=filemenu) # tools menu filemenu = tk.Menu(menubar, tearoff=0) filemenu.add_command( label="Show Log", accelerator="{}L".format(accel_string), command=show_log_window, ) filemenu.add_command( label="Plugin Sources", accelerator="{}P".format(accel_string), command=self.show_plugin_source_window, ) filemenu.add_command( label="Refresh Plugins", accelerator="{}R".format(accel_string), command=self.refresh_plugins, ) menubar.add_cascade(label="Tools", menu=filemenu) # add the menubar self.config(menu=menubar) # add file menu keybinds self.bind("<{}o>".format(accel_bind), lambda event: self.menu_open()) self.bind("<{}s>".format(accel_bind), lambda event: self.menu_save()) self.bind("<{}Shift-s>".format(accel_bind), lambda event: self.menu_saveas()) # add edit menu keybinds self.bind("<{}a>".format(accel_bind), lambda event: self.menu_selectall()) # add show log menu keybinds # add edit menu keybinds self.bind("<{}l>".format(accel_bind), lambda event: show_log_window()) self.bind("<{}p>".format(accel_bind), lambda event: self.show_plugin_source_window()) self.bind("<{}r>".format(accel_bind), lambda event: self.refresh_plugins()) # ---------------------------------------------------------------------- # # Treeview Methods # ---------------------------------------------------------------------- # def treeview_context_menu(self, click): """ Sets the currently selected treeview object id in the variable ``treeview_popup_target_id``. Parameters ---------- click : tkinter click event """ target = self.treeview.identify_row(click.y) if target != "": self.treeview_popup_target_id = target self.treeview_popup.post(click.x_root, click.y_root) self.treeview_popup_target_id = None def apply_seqlib_fastq(self): """ Applies settings to the seqlib object by running the configuration method. """ SeqLibApplyDialog(self, self, self.treeview_popup_target_id) def new_button_press(self): """ Spawns a dialog box depending on the currently selected treeview item to create a new element. """ if self.treeview.focus() == "" and self.root_element is not None: tkinter.messagebox.showwarning(None, "No parent element selected.") else: if self.treeview.focus() == "" and self.root_element is None: element = CreateRootDialog(self).element if isinstance(element, SeqLib): EditDialog(self, self, element) self.root_element = element else: element = self.create_new_element() EditDialog(self, self, element) # refresh the treeview and re-assign treeview id's self.refresh_treeview() # select the newly added element if it was successfully added if element.treeview_id in list(self.element_dict.keys()): self.treeview.focus(element.treeview_id) self.treeview.selection_set(element.treeview_id) else: if element.parent is not None: self.treeview.focus(element.parent.treeview_id) self.treeview.selection_set(element.parent.treeview_id) del element def edit_button_press(self): """ Spawns a dialog box depending on the currently selected treeview item to edit the selected element. """ if self.treeview.focus() == "": tkinter.messagebox.showwarning(None, "No element selected.") else: EditDialog(self, self, self.get_focused_element()) def delete_button_press(self): """ Deletes the selected treeview element and it's children. """ if self.treeview.focus() == "": tkinter.messagebox.showwarning(None, "No element selected.") else: DeleteDialog(self, self) def delete_element(self, tree_id): """ Delete element with Treeview id *tree_id* from the tree, from the element dictionary, and from the associated data structure. Recursively deletes all children of *tree_id*. The tree should be refreshed using :py:meth:`refresh_tree` after each deletion. This is the responsibility of the caller. Parameters ---------- tree_id : `int` The id of the currently selected treeview element. """ if tree_id in self.element_dict: # recursively delete children if self.element_dict[tree_id].children is not None: for child in self.element_dict[tree_id].children: self.delete_element(child.treeview_id) # check if deleting the root element if self.root_element.treeview_id == tree_id: # clear the root element print("None {}".format(tree_id)) self.root_element = None else: try: # remove the element from its parent's list of children self.element_dict[tree_id].parent.remove_child_id(tree_id) except AttributeError: raise AttributeError( "Non-root element lacks proper parent") # delete the element from the dictionary del self.element_dict[tree_id] def refresh_treeview(self): """ Clears the Treeview and repopulates it with the current contents of the tree. """ # clear the entries in the Treeview for x in self.treeview.get_children(): self.treeview.delete(x) # clear the id-element dictionary # elements may be given new id's after repopulation self.element_dict.clear() # repopulate if self.root_element is not None: self.populate_tree(self.root_element) def set_treeview_properties(self, element): """ Set the information text for the Treeview *element*. Parameters ---------- element : :py:class:`~enrich2.base.storemanager.StoreManager` The storemanager object to configure. """ # set class property self.treeview.set(element.treeview_id, "class", element.treeview_class_name) # add the check marks for barcodes/variants if "variants" in element.labels: self.treeview.set(element.treeview_id, "variants", u"\u2713") else: self.treeview.set(element.treeview_id, "variants", "") if "barcodes" in element.labels: self.treeview.set(element.treeview_id, "barcodes", u"\u2713") else: self.treeview.set(element.treeview_id, "barcodes", "") self.treeview.set(element.treeview_id, "class", element.treeview_class_name) def populate_tree(self, element, parent_id=""): """ Recursively populate the Treeview. Also populates the *id_cfgstrings*. Parameters ---------- element : :py:class:`~enrich2.base.storemanager.StoreManager` The storemanager object to configure. parent_id : `int` ``treeview_id`` of element's parent. """ # insert into the Treeview element.treeview_id = self.treeview.insert(parent_id, "end", text=element.name, open=True) # add id-element pair to dictionary self.element_dict[element.treeview_id] = element # set information fields self.set_treeview_properties(element) # populate for children if element.children is not None: for child in element.children: self.populate_tree(child, parent_id=element.treeview_id) # ---------------------------------------------------------------------- # # Getter Methods # ---------------------------------------------------------------------- # def get_selected_scorer_class(self): """ Returns the currently selected scoring class object. """ return self.scorer def get_selected_scorer_attrs(self): """ Returns the currently selected scoring class attribute `dict`. """ return self.scorer_attrs def get_selected_scorer_path(self): """ Returns the currently selected scoring path. """ return self.scorer_path def get_element(self, treeview_id): """ Returns the element with *treeview_id*. Parameters ---------- treeview_id : `int` ``treeview_id`` attribute of element to get. Returns ------- :py:class:`~enrich2.base.storemanager.StoreManager` The instance with matching ``treeview_id`` """ return self.element_dict[treeview_id] def get_focused_element(self): """ Gets the focused element in the treeview. Returns ------- :py:class:`~enrich2.base.storemanager.StoreManager` Returns the element that is currently being focused in the Treeview. ``None`` if nothing is focused. """ if self.treeview.focus() != "": return self.get_element(self.treeview.focus()) else: return None def get_selected_elements(self): """ Returns a list of currently selected elements in the treeview. Returns ------- `list` Returns a list of elements that are currently selected in the Treeview. If no elements are selected, it returns an empty list. """ return [self.get_element(x) for x in self.treeview.selection()] # ---------------------------------------------------------------------- # # Menubar Methods # ---------------------------------------------------------------------- # def menu_open(self): """ Spawns an `askopenfilename` dialog to open a configuration file. """ message_title = "Open Configuration" fname = tkinter.filedialog.askopenfilename() if len(fname) > 0: # file was selected try: with open(fname, "rU") as handle: cfg = json.load(handle) except ValueError: tkinter.messagebox.showerror(message_title, "Failed to parse config file.") except IOError: tkinter.messagebox.showerror(message_title, "Could not read config file.") else: if is_experiment(cfg): obj = Experiment() elif is_selection(cfg): obj = Selection() elif is_seqlib(cfg): sltype = seqlib_type(cfg) obj = globals()[sltype]() else: tkinter.messagebox.showerror( message_title, "Unrecognized config format.") return obj.output_dir_override = False try: if isinstance(obj, Experiment) or isinstance( obj, Selection): obj.configure(cfg, init_from_gui=True) else: obj.configure(cfg) # Try load the scorer into the GUI scorer_path = cfg.get(SCORER, {}).get(SCORER_PATH, "") scorer_attrs = cfg.get(SCORER, {}).get(SCORER_OPTIONS, {}) if scorer_path: self.scorer_widget.load_from_cfg_file( scorer_path, scorer_attrs) else: log_message( logging_callback=logging.warning, msg="No plugin could be loaded from configuration.", extra={"oname": self.__class__.__name__}, ) except Exception as e: tkinter.messagebox.showerror( message_title, "Failed to load config file:\n\n{}".format(e)) else: self.root_element = obj self.cfg_file_name.set(fname) self.refresh_treeview() def menu_save(self): """ Asks the user where to save the current configuration. """ if len(self.cfg_file_name.get()) == 0: self.menu_saveas() elif self.root_element is None: tkinter.messagebox.showwarning("Save Configuration", "Cannot save empty configuration.") else: save = askyesno("Save Configuration", "Overwrite existing configuration?") if not save: return try: with open(self.cfg_file_name.get(), "w") as handle: cfg = self.root_element.serialize() # Get the currently selected scorer if not isinstance(self.root_element, SeqLib) and not isinstance( self.root_element, Condition): ( _, attrs, scorer_path, ) = self.scorer_widget.get_scorer_class_attrs_path() cfg[SCORER] = { SCORER_PATH: scorer_path, SCORER_OPTIONS: attrs } write_json(cfg, handle) except IOError: tkinter.messagebox.showerror("Save Configuration", "Failed to save config file.") else: tkinter.messagebox.showinfo( "Save Configuration", "Saved file at location:\n\n{}".format( self.cfg_file_name.get()), ) def menu_saveas(self): """ Asks the user where to save the current configuration. """ if self.root_element is None: tkinter.messagebox.showwarning("Save Configuration", "Cannot save empty configuration.") else: fname = tkinter.filedialog.asksaveasfilename() if len(fname) > 0: # file was selected try: with open(fname, "w") as handle: cfg = self.root_element.serialize() # Get the currently selected scorer if not isinstance(self.root_element, SeqLib) and not isinstance( self.root_element, Condition): ( _, attrs, scorer_path, ) = self.scorer_widget.get_scorer_class_attrs_path( ) cfg[SCORER] = { SCORER_PATH: scorer_path, SCORER_OPTIONS: attrs, } write_json(cfg, handle) except IOError: tkinter.messagebox.showerror( "Save Configuration", "Failed to save config file.") else: self.cfg_file_name.set(fname) tkinter.messagebox.showinfo( "Save Configuration", "Saved file at location:\n\n{}".format( self.cfg_file_name.get()), ) def menu_selectall(self): """ Add all elements in the Treeview to the selection. """ for k in self.element_dict.keys(): self.treeview.selection_add(k) def show_plugin_source_window(self): """ Show the pop-up window to modify plugin sources """ if not self.plugin_source_window: self.plugin_source_window = SourceWindow(master=self) else: self.plugin_source_window.toggle_show() # ---------------------------------------------------------------------- # # Run Analysis Methods # ---------------------------------------------------------------------- # def go_button_press(self): """ Starts the analysis if all elements have been properly configured. This will run the analysis in a new thread and block out GUI editing to prevent the analysis breaking. """ ( self.scorer, self.scorer_attrs, self.scorer_path, ) = self.scorer_widget.get_scorer_class_attrs_path() if self.scorer is None or self.scorer_attrs is None: tkinter.messagebox.showwarning("Incomplete Configuration", "No scoring plugin selected.") elif self.root_element is None: tkinter.messagebox.showwarning( "Incomplete Configuration", "No experimental design specified.") else: plugin, *_ = self.scorer_widget.get_selected_plugin() if plugin.md5_has_changed(): proceed = askokcancel( "Selected plugin has been modified.", "The selected plugin has been modified on disk. Do you " "want to proceed with the current version? To see changes " "click 'Cancel' and refresh plugins before proceeding.", ) if not proceed: return if askyesno( "Save Configuration?", "Would you like to save the confiugration " "file before proceeding?", ): self.menu_save() run = askyesno( "Begin Analysis?", "Click Yes when you are ready to start.\n\nThis could " "take some time so grab a cup of tea, or a beer if that's " "your thing, and enjoy the show.", ) if run: self.configure_analysis() self.set_gui_state(tk.DISABLED) thread = threading.Thread(target=self.run_analysis) thread.setDaemon(True) self.analysis_thread = thread self.analysis_thread.start() self.after(100, self.poll_analysis_thread) def poll_logging_queue(self): """ Polls the logging queue for messages to log. """ try: log = get_logging_queue(init=True).get(0) log[CALLBACK](log[MESSAGE], **log[KWARGS]) self.after(10, self.poll_logging_queue) except queue.Empty: self.after(10, self.poll_logging_queue) def poll_analysis_thread(self): """ Polls the thread to check it's state. When it is finished, all stores are closed. """ try: analysis_result = self.queue.get(0) self.handle_analysis_result(analysis_result) except queue.Empty: self.after(100, self.poll_analysis_thread) def handle_analysis_result(self, success): """ Shows the appropriate messagebox and logs exceptions upon analysis completing. Parameters ---------- success : `bool` Exception object if an error occured during analysis, otherwise None to indicate successful computation. """ log_message( logging_callback=logging.info, msg="Closing stores...", extra={"oname": self.root_element.name}, ) self.root_element.store_close(children=True) log_message( logging_callback=logging.info, msg="Stores closed.", extra={"oname": self.root_element.name}, ) if success: showinfo("Analysis completed.", "Analysis has completed successfully!") log_message( logging_callback=logging.info, msg="Completed successfully!", extra={"oname": self.root_element.name}, ) else: showwarning( "Error during analysis.", "An error occurred during the analysis. See log for details", ) log_message( logging_callback=logging.info, msg="Completed, but with errors!", extra={"oname": self.root_element.name}, ) self.set_gui_state(tk.NORMAL) def run_analysis(self): """ Runs the storemanager compute method. """ try: self.root_element.validate() self.root_element.store_open(children=True) self.root_element.calculate() if self.root_element.tsv_requested: self.root_element.write_tsv() self.queue.put(True, block=False) except Exception as exception: log_message( logging_callback=logging.exception, msg=exception, extra={"oname": self.root_element.name}, ) self.queue.put(False, block=False) finally: return def configure_analysis(self): """ Configures the attributes of the root_element by querying the GUI options. """ try: self.root_element.force_recalculate = self.force_recalculate.get() self.root_element.component_outliers = self.component_outliers.get( ) self.root_element.tsv_requested = self.tsv_requested.get() scorer_class = self.get_selected_scorer_class() scorer_class_attrs = self.get_selected_scorer_attrs() scorer_path = self.get_selected_scorer_path() self.root_element.scorer_class = scorer_class self.root_element.scorer_class_attrs = scorer_class_attrs self.root_element.scorer_path = scorer_path except Exception as e: log_message( logging_callback=logging.info, msg="An error occurred when trying to configure the " "root element.", extra={"oname": self.root_element.name}, ) log_message( logging_callback=logging.exception, msg=e, extra={"oname": self.root_element.name}, ) # ---------------------------------------------------------------------- # # GUI Modifications # ---------------------------------------------------------------------- # def set_gui_state(self, state): """ Sets the state of the `go_button`, `treeview` and `treeview_buttons`. Parameters ---------- state : `str` State to set, usually ``'normal'`` or ``'disabled'`` """ for btn in self.treeview_buttons: btn.config(state=state) self.go_button.config(state=state) if state == "normal": self.treeview.bind("<Button-2>", self.treeview_context_menu) else: self.treeview.bind("<Button-2>", lambda event: event) def refresh_plugins(self): """ Refresh the plugins by re-checking the sources file. """ if self.plugin_source_window: sources = self.plugin_source_window.sources self.scorer_widget.refresh_sources(sources)
class ShowListUserDialog(Toplevel): def __init__(self,parent,**kw): Toplevel.__init__(self,parent,**kw) self.parent = parent self.numberTempUserAdd = 0 self.data = Database() self.listUser = self.data.getUserList() self.menu = Menu(self) self.config(menu = self.menu) self.drawMenu(self.menu) self.drawDialog() for user in self.listUser: self.addUserIntoTree(user) self.linkAccelerator() def linkAccelerator(self): self.bind_all("<Control-N>",self.onAddUser) self.bind_all("<Delete>", self.onDeleteUser) self.treeUser.bind(sequence="<Double-Button-1>",func=self.onEditUser) def drawMenu(self, parent): # Type Menu typeMenu = Menu(parent) typeMenu.add_command(label = "Thêm User", command = self.onAddUser, accelerator = "Ctrl+Shift+N") typeMenu.add_command(label = "Xóa User", command = self.onDeleteUser, accelerator = "Delete") parent.add_cascade(label = "Chức năng", menu = typeMenu) def drawDialog(self): layoutTreeUser = Frame(self) listAttribute = ["Password", "Họ tên"] yScrollTree = Scrollbar(layoutTreeUser, orient = VERTICAL) xScrollTree = Scrollbar(layoutTreeUser, orient = HORIZONTAL) # Create Delete, Add and Edit Button # Create Tree View self.treeUser = Treeview(layoutTreeUser, column = listAttribute, yscrollcommand = yScrollTree.set, xscrollcommand = xScrollTree.set ) self.treeUser.heading(column = "#0", text = "Username") self.treeUser.column(column = "#0", width = 100, minwidth = 100) for nameAttr in listAttribute: self.treeUser.heading(column = nameAttr, text = nameAttr) self.treeUser.column(column = nameAttr, width = 100, minwidth = 100) #Create Scrollbar for tree view yScrollTree.pack(side = RIGHT, fill = Y) xScrollTree.pack(side = BOTTOM, fill = X) self.treeUser.pack(side = TOP, anchor = "w", fill = BOTH, expand = True) yScrollTree.config(command = self.treeUser.yview) xScrollTree.config(command = self.treeUser.xview) layoutTreeUser.pack(side = TOP, fill = BOTH, expand = True) def addUserIntoTree(self,userInput): temp = userInput # if not self.treeUser.exists(temp.idParent) : # self.treeUser.insert("","end", str(temp.idParent), text = temp.idParent) # self.treeUser.item(temp.idParent, open = True) self.treeUser.insert("","end", str(temp.username), text = temp.username) self.treeUser.set(temp.username, "Password", temp.passWord) self.treeUser.set(temp.username, "Họ tên", temp.name) def onAddUser(self, event = None): if self.numberTempUserAdd > 9: messagebox.showwarning("Warning","Bạn chỉ có thể thêm tạm 10 username \n Hãy Sửa ID để lưu lại những loại tạm trên", parent = self) return userTemp = User(name = "#",username=str(self.numberTempUserAdd),passWord="******") self.addUserIntoTree(userTemp) self.numberTempUserAdd += 1 def onEditUser(self, event = None): listTemp = ["0","1","2","3","4","5","6","7","8","9"] curUser = self.treeUser.item(self.treeUser.focus()) col = self.treeUser.identify_column(event.x) print(curUser) print(col) if curUser["text"] in listTemp and col != "#0": if col != "#0": messagebox.showinfo("Thêm User","Sửa username tạm này để lưu lại vào Database",parent = self) return cellValue = None if col == "#0": temp = simpledialog.askstring("Đổi Username", "Nhập mới", parent = self) if temp != None: if len(temp)>0: cellValue = curUser["text"] try: if cellValue in listTemp: newUser = User(curUser["values"][1],temp,curUser["values"][0]) self.data.insertUser(newUser) self.treeUser.update() print("Sau khi sua:",self.treeUser.item(self.treeUser.focus())) else: self.data.updateUsernameOfUser(cellValue,temp) except (sqlite3.IntegrityError, TclError): messagebox.showwarning("Opps !!!!",message="Username bạn nhập đã tồn tại !!!!", parent = self) return if cellValue in listTemp: self.numberTempUserAdd -= 1 else: messagebox.showwarning("Empty !!!", "Username không thể để trống",parent = self) self.treeUser.insert("", str(self.treeUser.index(self.treeUser.focus())), temp ,text = temp, values = curUser["values"]) self.treeUser.delete(self.treeUser.focus()) return if col == "#1": temp = simpledialog.askstring("Đổi Password", "Nhập Password mới: ", parent = self) if temp != None: if len(temp) > 0: cellValue = curUser["values"][0] self.data.updatePassOfUser(curUser["text"],temp) curUser["values"][0] = temp print(curUser["text"]) self.treeUser.item(curUser["text"], values = curUser["values"]) self.treeUser.update() else: messagebox.showwarning("Empty !!!", "Password không thể để trống",parent = self) return if col == "#2": temp = simpledialog.askstring("Đổi tên", "Nhập tên mới",parent = self) if temp != None: if len(temp) > 0: cellValue = curUser["values"][1] self.data.updateNameOfuser(curUser["text"],temp) curUser["values"][1] = temp print(curUser["text"]) self.treeUser.item(curUser["text"], values = curUser["values"]) self.treeUser.update() else: messagebox.showwarning("Empty !!!", "Họ tên không thể để trống",parent = self) return print("Cell Values = ", cellValue) def onDeleteUser(self, event = None): curItem = self.treeUser.selection() accept = messagebox.askokcancel("Xóa User này","Bạn thật sự muốn xóa!!! Bạn không thể hoàn tác hành động này", parent = self) if accept == True: if len(curItem) == 0: messagebox.showwarning(title = "Empty !!!", message = "Xin hãy chọn User bạn muốn xóa", parent = self) return for choose in curItem: treeItem = self.treeUser.item(choose) self.data.deleteUser(treeItem["text"]) self.treeUser.delete(treeItem["text"]) def onDestroy(self, event): # ask = """ # Bạn thực sự muốn đóng ứng dụng # """ # messagebox.askokcancel("Closing!!!",) self.parent.linkAccelerator() self.destroy() # if __name__ == "__main__": # root = Tk() # ShowListUserDialog(root) # root.mainloop()
class Main_Page: """Displaying available books and user's borrowed books""" def __init__(self, master, logged_userid): self.user_app = master self.user_app.title('Library') self.user_app.geometry('850x350') self.users_list = Treeview(self.user_app, columns=BORROWINGS_COLUMNS, show='headings', height=10) self.books_list = Treeview(self.user_app, columns=LIBRARY_COLUMNS, show='headings', height=10) self.logged_userid = logged_userid self.borrowed_books_counter = 0 self.dbd = db.DatesDB('library.db') self.udb = db.UsersDB('library.db') self.bdb = db.BooksDB('library.db') self.search_text = tk.StringVar() self.search_entry = None def logout(self): """Log out""" if messagebox.askyesno("Logging out", "Do you want to log out?"): self.user_app.destroy() self.user_app = tk.Tk() login_window = Login(self.user_app) login_window.welcome_screen() self.user_app.mainloop() def populate_list(self): """Displays books available to borrow""" self.books_list.delete(*self.books_list.get_children()) for row in self.bdb.fetch(1): self.books_list.insert('', tk.END, values=row[0:6]) def user_list(self): """Daysplays books borrowed by user""" self.users_list.delete(*self.users_list.get_children()) for row in self.dbd.fetch_users(self.logged_userid): self.borrowed_books_counter += 1 self.users_list.insert('', tk.END, values=row[0:4]) def borrow_book(self): """Borrowing selected book""" if self.books_list.selection(): if self.borrowed_books_counter < 3: selected_book = self.books_list.set(self.books_list.selection()) id = selected_book.get('Id') self.dbd.insert(self.logged_userid, id) self.bdb.status_update(id, 0) self.user_list() self.populate_list() self.borrowed_books_counter += 1 else: messagebox.showerror('Limit', 'You can have up to 3 books borrowed!') return def return_book(self): """Returning seleted book""" if self.users_list.selection(): selected_book = self.users_list.set(self.users_list.selection()) id = selected_book.get('Id') self.bdb.status_update(id, 1) self.dbd.remove(id) self.populate_list() self.user_list() self.borrowed_books_counter -= 1 def menu_screen(self): """Prepares labels, listboxes, buttons""" # Available book treeview self.books_list.grid(row=1, column=0, padx=8) for column_name, width in zip(LIBRARY_COLUMNS, LIBRARY_COLUMNS_SIZE): self.books_list.column(column_name, width=width, anchor=tk.CENTER) self.books_list.heading(column_name, text=column_name) scrollbar = tk.Scrollbar(self.user_app, orient=tk.VERTICAL) scrollbar.configure(command=self.books_list.set) self.books_list.configure(yscrollcommand=scrollbar) # User's borrowed books treeview self.users_list.grid(row=1, column=1, padx=8) for column_name, width in zip(BORROWINGS_COLUMNS, BORROWINGS_COLUMNS_SIZE): self.users_list.column(column_name, width=width, anchor=tk.CENTER) self.users_list.heading(column_name, text=column_name) scrollbar = tk.Scrollbar(self.user_app, orient=tk.VERTICAL) scrollbar.configure(command=self.users_list.set) self.users_list.configure(yscrollcommand=scrollbar) borrow_btn = tk.Button(self.user_app, text='Borrow book', width=12, command=self.borrow_book) borrow_btn.grid(row=9, column=0, pady=10) return_btn = tk.Button(self.user_app, text='Return book', width=12, command=self.return_book) return_btn.grid(row=9, column=1, pady=10) logout_btn = tk.Button(self.user_app, text='Logout', width=12, command=self.logout) logout_btn.grid(row=0, column=1, pady=10) self.populate_list() self.user_list()
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 CollectionOfFilms(Frame): # ГЛАВНЫЙ КЛАСС; def __init__(self, root, x, y): super().__init__(root) self.x = x self.y = y self.init_main() self.db = db self.update_records() def init_main(self): # СТИЛЬ ШРИФТА; font_style = ('Consolas', '12') # СТИЛЬ ТАБЛИЦ и КНОПОК в ВСПЛЫВАЮЩИХ ОКНАХ; Style().configure("Treeview.Heading", font=font_style, foreground='#425370') Style().configure('Treeview', font=font_style, foreground='#425370') Style().configure('TButton', font=('Consolas', '10')) Style().configure('TLabel', font=font_style, background='#EDF0F5', foreground='#425370') # ВЕРХНЯЯ и НИЖНЯЯ ПАНЕЛЬ для КНОПОК и ПОДСКАЗОК; TB_TOP = Frame(self, bg='#EDF0F5', bd=1) TB_BOT = Frame(self, bg='#EDF0F5', bd=1) TB_TOP.pack(side=TOP, fill=X) TB_BOT.pack(side=BOTTOM, fill=X) # КНОПКИ на ВЕРХНЕЙ ПАНЕЛИ; btn_add_ = Button(TB_TOP, text='Добавить', compound=TOP, command=self.open_dlg_add, bg='#EDF0F5', activebackground='#425370', activeforeground='white', width=15, height=1, font=font_style, relief=GROOVE, overrelief=GROOVE) btn_edit = Button(TB_TOP, text='Редактировать', compound=TOP, command=self.open_dlg_edit, bg='#EDF0F5', activebackground='#425370', activeforeground='white', width=15, height=1, font=font_style, relief=GROOVE, overrelief=GROOVE) btn_del_ = Button(TB_TOP, text='Удалить', compound=TOP, command=self.open_dlg_del, bg='#EDF0F5', activebackground='#425370', activeforeground='white', width=15, height=1, font=font_style, relief=GROOVE, overrelief=GROOVE) btn_view = Button(TB_BOT, text='Просмотр', compound=TOP, command=self.open_dlg_view, bg='#EDF0F5', activebackground='#425370', activeforeground='white', width=10, height=1, font=font_style, relief=FLAT, overrelief=FLAT) btn_add_.pack(side=LEFT) btn_edit.pack(side=LEFT) btn_del_.pack(side=RIGHT) btn_view.pack(side=RIGHT) btn_add_.bind('<Enter>', lambda event: btn_add_.configure(bg='#8E9FBD', fg='white')) btn_add_.bind('<Leave>', lambda event: btn_add_.configure(bg='#EDF0F5', fg='black')) btn_edit.bind('<Enter>', lambda event: btn_edit.configure(bg='#8E9FBD', fg='white')) btn_edit.bind('<Leave>', lambda event: btn_edit.configure(bg='#EDF0F5', fg='black')) btn_del_.bind('<Enter>', lambda event: btn_del_.configure(bg='#8E9FBD', fg='white')) btn_del_.bind('<Leave>', lambda event: btn_del_.configure(bg='#EDF0F5', fg='black')) btn_view.bind('<Enter>', lambda event: btn_view.configure(bg='#8E9FBD', fg='white')) btn_view.bind('<Leave>', lambda event: btn_view.configure(bg='#EDF0F5', fg='black')) # ВЫВОД ОШИБОК и КОЛ-ВА ЗАПИСЕЙ на НИЖНЮЮ ПАНЕЛЬ; self.error = Label(TB_BOT, foreground='red') self.count = Label(TB_BOT) self.error.pack(side=RIGHT) self.count.pack(side=LEFT) # ПОЛОСА ПРОКРУТКИ и ТАБЛИЦА; _yscroll_ = Scrollbar(self) self.tree = Treeview(self, columns=('id', 'name', 'mors', 'janr', 'flag'), height=20, show='headings', yscrollcommand=_yscroll_.set) _yscroll_.config(command=self.tree.yview) self.tree.column('id', width=50, minwidth=50, anchor=CENTER) self.tree.column('name', width=370, minwidth=370) self.tree.column('mors', width=110, minwidth=110) self.tree.column('janr', width=110, minwidth=110) self.tree.column('flag', width=140, minwidth=140) self.tree.heading('id', text='№') self.tree.heading('name', text='Название') self.tree.heading('mors', text='Кино/Сериал') self.tree.heading('janr', text='Жанр') self.tree.heading('flag', 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) def update_records(self): # ОБНОВЛЕНИЕ ДАННЫХ; num = 0 self.db.c.execute(''' SELECT * FROM films ''') [ 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) def add_record(self, name, mors, janr, flag): # ДОБАВЛЕНИЕ; if (name != ''): self.error['text'] = '' flag_record = False self.db.c.execute(''' SELECT name FROM films ''') for row in self.db.c.fetchall(): # Проверка на схожую запись; if (name == row[0]): flag_record = True if (not flag_record): self.db.add_data(name, mors, janr, flag) else: self.error['text'] = 'Такая запись уже существует! ' else: self.error['text'] = 'Данные не введены в поле! ' self.update_records() def edit_record(self, name, mors, janr, flag): # РЕДАКТИРОВАНИЕ; if (name != ''): self.error['text'] = '' self.db.c.execute(''' UPDATE films SET name=?, mors=?, janr=?, flag=? WHERE id=? ''', (name, mors, janr, flag, self.tree.set(self.tree.selection()[0], '#1'))) else: self.error['text'] = 'Данные не введены в поле! ' self.db.conn.commit() self.update_records() def delete_record(self): # УДАЛЕНИЕ; for item in self.tree.selection(): self.db.c.execute(''' DELETE FROM films WHERE id=? ''', (self.tree.set(item)['id'],)) self.db.conn.commit() self.update_records() def open_dlg_add(self): # ОТКРЫВАЕМ ОКНО ДОБАВЛЕНИЯ; self.error['text'] = '' AddData(self.x, self.y) def open_dlg_edit(self): # ОТКРЫВАЕМ ОКНО РЕДАКТИРОВАНИЯ; if self.tree.selection(): self.error['text'] = '' EditData(self.x, self.y, self.tree.item(self.tree.selection()[0])['values'][1]) else: self.error['text'] = 'Не выбрана запись для редактирования! ' def open_dlg_del(self): # ОТКРЫВАЕМ ОКНО УДАЛЕНИЯ; if (self.tree.selection()): self.error['text'] = '' DeleteData(self.x, self.y) else: self.error['text'] = 'Нет записей для удаления! ' def open_dlg_view(self): # ОТКРЫВАЕМ ОКНО ПРОСМОТРА; self.error['text'] = '' SelectForViewRecords(self.x, self.y)
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 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 Multicolumn_Listbox(Frame): _style_index = 0 class List_Of_Rows(object): def __init__(self, multicolumn_listbox): self._multicolumn_listbox = multicolumn_listbox def data(self, index): return self._multicolumn_listbox.row_data(index) def get(self, index): return Row(self._multicolumn_listbox, index) def insert(self, data, index=None): self._multicolumn_listbox.insert_row(data, index) def delete(self, index): self._multicolumn_listbox.delete_row(index) def update(self, index, data): self._multicolumn_listbox.update_row(index, data) def select(self, index): self._multicolumn_listbox.select_row(index) def deselect(self, index): self._multicolumn_listbox.deselect_row(index) def set_selection(self, indices): self._multicolumn_listbox.set_selection(indices) def __getitem__(self, index): return self.get(index) def __setitem__(self, index, value): return self._multicolumn_listbox.update_row(index, value) def __delitem__(self, index): self._multicolumn_listbox.delete_row(index) def __len__(self): return self._multicolumn_listbox.number_of_rows class List_Of_Columns(object): def __init__(self, multicolumn_listbox): self._multicolumn_listbox = multicolumn_listbox def data(self, index): return self._multicolumn_listbox.get_column(index) def get(self, index): return Column(self._multicolumn_listbox, index) def delete(self, index): self._multicolumn_listbox.delete_column(index) def update(self, index, data): self._multicolumn_listbox.update_column(index, data) def __getitem__(self, index): return self.get(index) def __setitem__(self, index, value): return self._multicolumn_listbox.update_column(index, value) def __delitem__(self, index): self._multicolumn_listbox.delete_column(index) def __len__(self): return self._multicolumn_listbox.number_of_columns def __init__(self, master, columns, data=None, command=None, sort=True, select_mode=None, heading_anchor=CENTER, cell_anchor=W, style=None, height=None, padding=None, adjust_heading_to_content=False, stripped_rows=None, selection_background=None, selection_foreground=None, field_background=None, heading_font=None, heading_background=None, heading_foreground=None, cell_pady=2, cell_background=None, cell_foreground=None, cell_font=None, headers=True): self._stripped_rows = stripped_rows self._columns = columns self._number_of_rows = 0 self._number_of_columns = len(columns) self.row = self.List_Of_Rows(self) self.column = self.List_Of_Columns(self) s = Style() if style is None: style_name = "Multicolumn_Listbox%s.Treeview" % self._style_index self._style_index += 1 else: style_name = style style_map = {} if selection_background is not None: style_map["background"] = [('selected', selection_background)] if selection_foreground is not None: style_map["foeground"] = [('selected', selection_foreground)] if style_map: s.map(style_name, **style_map) style_config = {} if cell_background is not None: style_config["background"] = cell_background if cell_foreground is not None: style_config["foreground"] = cell_foreground if cell_font is None: font_name = s.lookup(style_name, "font") cell_font = nametofont(font_name) else: if not isinstance(cell_font, Font): if isinstance(cell_font, basestring): cell_font = nametofont(cell_font) else: if len(Font) == 1: cell_font = Font(family=cell_font[0]) elif len(Font) == 2: cell_font = Font(family=cell_font[0], size=cell_font[1]) elif len(Font) == 3: cell_font = Font(family=cell_font[0], size=cell_font[1], weight=cell_font[2]) else: raise ValueError( "Not possible more than 3 values for font") style_config["font"] = cell_font self._cell_font = cell_font self._rowheight = cell_font.metrics("linespace") + cell_pady style_config["rowheight"] = self._rowheight if field_background is not None: style_config["fieldbackground"] = field_background s.configure(style_name, **style_config) heading_style_config = {} if heading_font is not None: heading_style_config["font"] = heading_font if heading_background is not None: heading_style_config["background"] = heading_background if heading_foreground is not None: heading_style_config["foreground"] = heading_foreground heading_style_name = style_name + ".Heading" s.configure(heading_style_name, **heading_style_config) treeview_kwargs = {"style": style_name} if height is not None: treeview_kwargs["height"] = height if padding is not None: treeview_kwargs["padding"] = padding if headers: treeview_kwargs["show"] = "headings" else: treeview_kwargs["show"] = "" if select_mode is not None: treeview_kwargs["selectmode"] = select_mode self.interior = Treeview(master, columns=columns, **treeview_kwargs) if command is not None: self._command = command self.interior.bind("<<TreeviewSelect>>", self._on_select) for i in range(0, self._number_of_columns): if sort: self.interior.heading( i, text=columns[i], anchor=heading_anchor, command=lambda col=i: self.sort_by(col, descending=False)) else: self.interior.heading(i, text=columns[i], anchor=heading_anchor) if adjust_heading_to_content: self.interior.column(i, width=Font().measure(columns[i])) self.interior.column(i, anchor=cell_anchor) if data is not None: for row in data: self.insert_row(row) @property def row_height(self): return self._rowheight @property def font(self): return self._cell_font def configure_column(self, index, width=None, minwidth=None, anchor=None, stretch=None): kwargs = {} for config_name in ("width", "anchor", "stretch", "minwidth"): config_value = locals()[config_name] if config_value is not None: kwargs[config_name] = config_value self.interior.column('#%s' % (index + 1), **kwargs) def row_data(self, index): try: item_ID = self.interior.get_children()[index] except IndexError: raise ValueError("Row index out of range: %d" % index) return self.item_ID_to_row_data(item_ID) def update_row(self, index, data): try: item_ID = self.interior.get_children()[index] except IndexError: raise ValueError("Row index out of range: %d" % index) if len(data) == len(self._columns): self.interior.item(item_ID, values=data) else: raise ValueError("The multicolumn listbox has only %d columns" % self._number_of_columns) def delete_row(self, index): list_of_items = self.interior.get_children() try: item_ID = list_of_items[index] except IndexError: raise ValueError("Row index out of range: %d" % index) self.interior.delete(item_ID) self._number_of_rows -= 1 if self._stripped_rows: for i in range(index, self._number_of_rows): self.interior.tag_configure(list_of_items[i + 1], background=self._stripped_rows[i % 2]) def insert_row(self, data, index=None): if len(data) != self._number_of_columns: raise ValueError("The multicolumn listbox has only %d columns" % self._number_of_columns) if index is None: index = self._number_of_rows - 1 item_ID = self.interior.insert('', index, values=data) self.interior.item(item_ID, tags=item_ID) self._number_of_rows += 1 if self._stripped_rows: list_of_items = self.interior.get_children() self.interior.tag_configure(item_ID, background=self._stripped_rows[index % 2]) for i in range(index + 1, self._number_of_rows): self.interior.tag_configure(list_of_items[i], background=self._stripped_rows[i % 2]) def column_data(self, index): return [ self.interior.set(child_ID, index) for child_ID in self.interior.get_children('') ] def update_column(self, index, data): for i, item_ID in enumerate(self.interior.get_children()): data_row = self.item_ID_to_row_data(item_ID) data_row[index] = data[i] self.interior.item(item_ID, values=data_row) return data def clear(self): # Another possibility: # self.interior.delete(*self.interior.get_children()) for row in self.interior.get_children(): self.interior.delete(row) self._number_of_rows = 0 def update(self, data): self.clear() for row in data: self.insert_row(row) def focus(self, index=None): if index is None: return self.interior.item(self.interior.focus()) else: item = self.interior.get_children()[index] self.interior.focus(item) def state(self, state=None): if state is None: return self.interior.state() else: self.interior.state(state) @property def number_of_rows(self): return self._number_of_rows @property def number_of_columns(self): return self._number_of_columns def toogle_selection(self, index): list_of_items = self.interior.get_children() try: item_ID = list_of_items[index] except IndexError: raise ValueError("Row index out of range: %d" % index) self.interior.selection_toggle(item_ID) def select_row(self, index): list_of_items = self.interior.get_children() try: item_ID = list_of_items[index] except IndexError: raise ValueError("Row index out of range: %d" % index) self.interior.selection_add(item_ID) def deselect_row(self, index): list_of_items = self.interior.get_children() try: item_ID = list_of_items[index] except IndexError: raise ValueError("Row index out of range: %d" % index) self.interior.selection_remove(item_ID) def deselect_all(self): self.interior.selection_remove(self.interior.selection()) def set_selection(self, indices): list_of_items = self.interior.get_children() self.interior.selection_set(" ".join(list_of_items[row_index] for row_index in indices)) @property def selected_rows(self): data = [] for item_ID in self.interior.selection(): data_row = self.item_ID_to_row_data(item_ID) data.append(data_row) return data @property def indices_of_selected_rows(self): list_of_indices = [] for index, item_ID in enumerate(self.interior.get_children()): if item_ID in self.interior.selection(): list_of_indices.append(index) return list_of_indices def delete_all_selected_rows(self): selected_items = self.interior.selection() for item_ID in selected_items: self.interior.delete(item_ID) number_of_deleted_rows = len(selected_items) self._number_of_rows -= number_of_deleted_rows return number_of_deleted_rows def _on_select(self, event): for item_ID in event.widget.selection(): data_row = self.item_ID_to_row_data(item_ID) self._command(data_row) def item_ID_to_row_data(self, item_ID): item = self.interior.item(item_ID) return item["values"] @property def table_data(self): data = [] for item_ID in self.interior.get_children(): data_row = self.item_ID_to_row_data(item_ID) data.append(data_row) return data @table_data.setter def table_data(self, data): self.update(data) def cell_data(self, row, column): """Get the value of a table cell""" try: item = self.interior.get_children()[row] except IndexError: raise ValueError("Row index out of range: %d" % row) return self.interior.set(item, column) def update_cell(self, row, column, value): """Set the value of a table cell""" item_ID = self.interior.get_children()[row] data = self.item_ID_to_row_data(item_ID) data[column] = value self.interior.item(item_ID, values=data) def __getitem__(self, index): if isinstance(index, tuple): row, column = index return self.cell_data(row, column) else: raise Exception("Row and column indices are required") def __setitem__(self, index, value): if isinstance(index, tuple): row, column = index self.update_cell(row, column, value) else: raise Exception("Row and column indices are required") def bind(self, event, handler): self.interior.bind(event, handler) def sort_by(self, col, descending): """ sort tree contents when a column header is clicked """ # grab values to sort data = [(self.interior.set(child_ID, col), child_ID) for child_ID in self.interior.get_children('')] # if the data to be sorted is numeric change to float try: data = [(float(number), child_ID) for number, child_ID in data] except ValueError: pass # now sort the data in place data.sort(reverse=descending) for idx, item in enumerate(data): self.interior.move(item[1], '', idx) # switch the heading so that it will sort in the opposite direction self.interior.heading( col, command=lambda col=col: self.sort_by(col, not descending)) if self._stripped_rows: list_of_items = self.interior.get_children('') for i in range(len(list_of_items)): self.interior.tag_configure(list_of_items[i], background=self._stripped_rows[i % 2]) def destroy(self): self.interior.destroy() def item_ID(self, index): return self.interior.get_children()[index]
class CustomerApp: """Main customer window.""" def __init__(self, master): """Initializes main customer window.""" self.master = master self.master.geometry(CUSTOMER_WINDOW_SIZE) self.master.configure(bg=my_config.BACKGROUND) self.master.title(my_config.APP_NAME) # main frames self.frame = tk.Frame(self.master, bg=my_config.BACKGROUND) self.function_frame = tk.Frame(self.master, bg=my_config.BACKGROUND) self.function_frame2 = tk.Frame(self.master, bg=my_config.BACKGROUND) self.function_frame3 = tk.Frame(self.master, bg=my_config.BACKGROUND) # it contains error messages, for example not all entry are filled. self.error_label = tk.Label() self.product_tree = None self.my_orders_tree = None self.location_entry = None self.quantity_entry = None self.id_product_entry = None def initialize_main_buttons(self): """Initializes main buttons. Used in other functions repeatedly, that's why it's not in __init__""" if self.frame: self.frame.destroy() if self.function_frame: self.function_frame.destroy() if self.function_frame2: self.function_frame2.destroy() if self.function_frame3: self.function_frame3.destroy() self.frame = tk.Frame(self.master, bg=my_config.BACKGROUND) search_button = tk.Button(self.frame, text='List of products', bg=my_config.FOREGROUND, command=self.list_products, width=16) search_button.grid(row=0, column=0, pady=(10, 3)) edit_button = tk.Button(self.frame, text='Edit account', bg=my_config.FOREGROUND, command=self.account_edit, width=16) edit_button.grid(row=1, column=0, pady=(0, 3)) orders_button = tk.Button(self.frame, text='My Orders', bg=my_config.FOREGROUND, command=self.my_orders, width=16) orders_button.grid(row=2, column=0, pady=(0, 3)) logoff_button = tk.Button(self.frame, text='Logoff', bg=my_config.FOREGROUND, command=self.log_off, width=16) logoff_button.grid(row=3, column=0, pady=(0, 3)) self.frame.pack() def list_products(self): """Lists all of the customer products under menu.""" self.initialize_main_buttons() # frame for listbox self.function_frame = tk.Frame(self.master, bg=my_config.BACKGROUND) self.function_frame.pack() self.function_frame2 = tk.Frame(self.master, bg=my_config.BACKGROUND) self.function_frame2.pack() list_label = tk.Label(self.function_frame, text='list of products', width=100, bg=my_config.BACKGROUND) list_label.grid(row=0, column=0, pady=(10, 0)) # creating treeview for customers self.product_tree = Treeview(self.function_frame, columns=PRODUCT_COLUMNS, show='headings', height=10) self.product_tree.grid(row=1, column=0, padx=8) for column_name, width in zip(PRODUCT_COLUMNS, PRODUCT_COLUMNS_SIZE): self.product_tree.column(column_name, width=width, anchor=tk.CENTER) self.product_tree.heading(column_name, text=column_name) scrollbar = tk.Scrollbar(self.function_frame, orient=tk.VERTICAL) scrollbar.configure(command=self.product_tree.set) self.product_tree.configure(yscrollcommand=scrollbar) self.product_tree.bind('<ButtonRelease-1>', self.product_selection) # adding records from DB to Listbox records = db.return_products() for record in records: self.product_tree.insert('', tk.END, values=[record[0], record[1], record[2], record[3]]) # crating labels id_product_label = tk.Label(self.function_frame2, text='Product ID:', bg=my_config.BACKGROUND) id_product_label.grid(row=0, column=0, sticky=tk.E) quantity_label = tk.Label(self.function_frame2, text='Quantity:', bg=my_config.BACKGROUND) quantity_label.grid(row=1, column=0, sticky=tk.E) location_label = tk.Label(self.function_frame2, text='Order location:', bg=my_config.BACKGROUND) location_label.grid(row=2, column=0, sticky=tk.E) # creating entry boxes self.id_product_entry = tk.Entry(self.function_frame2, width=30, bg=my_config.FOREGROUND) self.id_product_entry.grid(row=0, column=1) self.quantity_entry = tk.Entry(self.function_frame2, width=30, bg=my_config.FOREGROUND) self.quantity_entry.grid(row=1, column=1) self.location_entry = tk.Entry(self.function_frame2, width=30, bg=my_config.FOREGROUND) self.location_entry.grid(row=2, column=1) # buttons place_order_button = tk.Button(self.function_frame2, text='Place order', bg=my_config.FOREGROUND, command=self.place_order, width=16) place_order_button.grid(row=4, column=0) details_button = tk.Button(self.function_frame2, text='details', bg=my_config.FOREGROUND, command=self.product_details, width=16) details_button.grid(row=4, column=1, ) def place_order(self): """Place new order, if all required entries are filled.""" if self.error_label: self.error_label.destroy() # checking if all required entries are filled properly if not self.id_product_entry.get(): self.error_message("'id product' missing") elif not my_config.is_integer(self.quantity_entry.get()) or int(self.quantity_entry.get()) < 1: self.error_message("'quantity' Must be an positive integer") elif not self.location_entry.get(): self.error_message("'location' missing") # checking if customer and product exists elif not db.is_customer_id_exist(my_config.MY_ID) or not db.is_product_id_exists( self.id_product_entry.get()): self.error_message("product or customer id not Exists") # function itself check if there is enough products, and count total price (quantity*price) elif db.add_order(my_config.MY_ID, self.id_product_entry.get(), self.quantity_entry.get(), self.location_entry.get()): messagebox.showinfo("Mendiona bytes", 'successfully added.') self.list_products() else: self.error_message("not enough products in stock.") def product_details(self): """show details of selected product.""" if self.error_label: self.error_label.destroy() if self.function_frame3: self.function_frame3.destroy() if not self.id_product_entry.get(): self.error_message("select product.") elif db.is_product_id_exists(self.id_product_entry.get()): self.function_frame3 = tk.Frame(self.master, bg=my_config.BACKGROUND) self.function_frame3.pack(side=tk.TOP) # creating Message instead of Label (description might be long) description = db.return_product(self.id_product_entry.get())[4] self.error_label = tk.Message(self.function_frame3, text="Description: {}".format(description), bg=my_config.BACKGROUND, width=300) self.error_label.grid(row=5, column=0) else: self.error_message("Product not exist.") def product_selection(self, event): """Adds id of selected product to designated entry.""" try: if self.product_tree.selection(): record = self.product_tree.set(self.product_tree.selection()) self.id_product_entry.delete(0, tk.END) self.id_product_entry.insert(tk.END, record[PRODUCT_COLUMNS[0]]) except KeyError: pass def order_selection(self, event): """Shows details of selected order.""" if self.my_orders_tree.selection(): record = self.my_orders_tree.set(self.my_orders_tree.selection()) record = db.return_order(record[PRODUCT_COLUMNS[0]]) if self.function_frame2: self.function_frame2.destroy() self.function_frame2 = tk.Frame(self.master, bg=my_config.BACKGROUND) self.function_frame2.pack(side=tk.TOP) # creating Message instead of Label (might be long) order_info = ("quantity: \t{}\ntotal_price: \t{}\npayment_status: \t{}\n" "send_status: \t{}\noder_date: \t{}\nlocation: \t{}\n" ).format(record[3], record[4], record[5], record[6], record[7], record[8]) self.error_label = tk.Message(self.function_frame2, text=order_info, bg=my_config.BACKGROUND, width=300) self.error_label.grid(row=0, column=0) def account_edit(self): """Runs new window for editing account.""" if self.frame: self.frame.destroy() if self.function_frame: self.function_frame.destroy() if self.function_frame2: self.function_frame2.destroy() if self.function_frame3: self.function_frame3.destroy() AccountEdit(self.master) def my_orders(self): """Creates menu with list of user orders.""" self.initialize_main_buttons() self.function_frame = tk.Frame(self.master, bg=my_config.BACKGROUND) self.function_frame.pack() # creating listbox for customers list_label = tk.Label(self.function_frame, text='my orders:', width=100, bg=my_config.BACKGROUND) list_label.grid(row=0, column=0, pady=(10, 0)) # creating treeview for customers self.my_orders_tree = Treeview(self.function_frame, columns=MY_ORDERS_COLUMNS, show='headings', height=10) self.my_orders_tree.grid(row=1, column=0) for column_name, width in zip(MY_ORDERS_COLUMNS, MY_ORDERS_COLUMNS_SIZE): self.my_orders_tree.column(column_name, width=width, anchor=tk.CENTER) self.my_orders_tree.heading(column_name, text=column_name) scrollbar = tk.Scrollbar(self.function_frame, orient=tk.VERTICAL) scrollbar.configure(command=self.my_orders_tree.set) self.my_orders_tree.configure(yscrollcommand=scrollbar) self.my_orders_tree.bind('<ButtonRelease-1>', self.order_selection) # adding records from DB to treeview records = db.orders_product_info(my_config.MY_ID) for record in records: self.my_orders_tree.insert('', tk.END, values=[record[0], record[1], record[2], record[3]]) def error_message(self, name): """Shows passed message in designated place Used to clear code and make it more readable as it is called multiple times.""" # deleting missing label from last add_order call if it exists if self.error_label: self.error_label.destroy() self.error_label = tk.Label(self.function_frame2, text=name, bg=my_config.BACKGROUND, fg=my_config.ERROR_FOREGROUND) self.error_label.grid(row=3, column=1) def log_off(self): """Returns User to logging window.""" if self.frame: self.frame.destroy() if self.function_frame: self.function_frame.destroy() if self.function_frame2: self.function_frame2.destroy() if self.function_frame3: self.function_frame3.destroy() application = login_window.LoginWindow(self.master) application.initialize_login_window()
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 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 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 PWD or prior taxonomy package zip file, or a taxonomy manifest (.taxonomyPackage.xml) within an unzipped taxonomy package. "), wraplength=360) 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 PWD or prior taxonomy package zip file, or a taxonomy manifest (.taxonomyPackage.xml) within an unzipped taxonomy package. "), wraplength=360) manifestNameButton = Button(buttonFrame, text=_("Manifest"), command=self.manifestName) ToolTip(manifestNameButton, text=_("Provide pre-PWD non-standard archive manifest file name pattern (e.g., *taxonomyPackage.xml). " "Uses unix file name pattern matching. " "Multiple manifest files are supported in pre-PWD archives (such as oasis catalogs). " "(Replaces pre-PWD search for either .taxonomyPackage.xml or catalog.xml). "), wraplength=480) 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) 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 packages."), wraplength=240) disableAllButton = Button(enableDisableFrame, text=_("Disable All"), command=self.disableAll) ToolTip(disableAllButton, text=_("Disable all packages."), 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.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"), (_("PWD Manifest (taxonomyPackage.xml)"), "taxonomyPackage.xml"), (_("pre-PWD Manifest (*.taxonomyPackage.xml)"), "*.taxonomyPackage.xml"), (_("pre-PWD 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(self.cntlr, 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(self.cntlr, 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.cntlr) 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.cntlr) 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.cntlr) 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.cntlr) 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(self.cntlr, url, reload=True, packageManifestName=packageInfo.get("manifestName")) if packageInfo: self.addPackageInfo(packageInfo) PackageManager.rebuildRemappings(self.cntlr) 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.cntlr) self.loadTreeViews() def enableAll(self): self.enableDisableAll(True) def disableAll(self): self.enableDisableAll(False) def enableDisableAll(self, doEnable): for iPkg in range(len(self.packagesConfig["packages"])): packageInfo = self.packagesConfig["packages"][iPkg] if doEnable: packageInfo["status"] = "enabled" self.packageEnableButton['text'] = self.DISABLE else: packageInfo["status"] = "disabled" self.packageEnableButton['text'] = self.ENABLE self.packagesConfigChanged = True PackageManager.rebuildRemappings(self.cntlr) self.loadTreeViews()
class EventScheduler(Tk): def __init__(self): Tk.__init__(self, className='Scheduler') logging.info('Start') self.protocol("WM_DELETE_WINDOW", self.hide) self._visible = BooleanVar(self, False) self.withdraw() self.icon_img = PhotoImage(master=self, file=ICON48) self.iconphoto(True, self.icon_img) # --- systray icon self.icon = TrayIcon(ICON, fallback_icon_path=ICON_FALLBACK) # --- menu self.menu_widgets = SubMenu(parent=self.icon.menu) self.menu_eyes = Eyes(self.icon.menu, self) self.icon.menu.add_checkbutton(label=_('Manager'), command=self.display_hide) self.icon.menu.add_cascade(label=_('Widgets'), menu=self.menu_widgets) self.icon.menu.add_cascade(label=_("Eyes' rest"), menu=self.menu_eyes) self.icon.menu.add_command(label=_('Settings'), command=self.settings) self.icon.menu.add_separator() self.icon.menu.add_command(label=_('About'), command=lambda: About(self)) self.icon.menu.add_command(label=_('Quit'), command=self.exit) self.icon.bind_left_click(lambda: self.display_hide(toggle=True)) add_trace(self._visible, 'write', self._visibility_trace) self.menu = Menu(self, tearoff=False) self.menu.add_command(label=_('Edit'), command=self._edit_menu) self.menu.add_command(label=_('Delete'), command=self._delete_menu) self.right_click_iid = None self.menu_task = Menu(self.menu, tearoff=False) self._task_var = StringVar(self) menu_in_progress = Menu(self.menu_task, tearoff=False) for i in range(0, 110, 10): prog = '{}%'.format(i) menu_in_progress.add_radiobutton(label=prog, value=prog, variable=self._task_var, command=self._set_progress) for state in ['Pending', 'Completed', 'Cancelled']: self.menu_task.add_radiobutton(label=_(state), value=state, variable=self._task_var, command=self._set_progress) self._img_dot = tkPhotoImage(master=self) self.menu_task.insert_cascade(1, menu=menu_in_progress, compound='left', label=_('In Progress'), image=self._img_dot) self.title('Scheduler') self.rowconfigure(1, weight=1) self.columnconfigure(0, weight=1) self.scheduler = BackgroundScheduler(coalesce=False, misfire_grace_time=86400) self.scheduler.add_jobstore('sqlalchemy', url='sqlite:///%s' % JOBSTORE) self.scheduler.add_jobstore('memory', alias='memo') # --- style self.style = Style(self) self.style.theme_use("clam") self.style.configure('title.TLabel', font='TkdefaultFont 10 bold') self.style.configure('title.TCheckbutton', font='TkdefaultFont 10 bold') self.style.configure('subtitle.TLabel', font='TkdefaultFont 9 bold') self.style.configure('white.TLabel', background='white') self.style.configure('border.TFrame', background='white', border=1, relief='sunken') self.style.configure("Treeview.Heading", font="TkDefaultFont") bgc = self.style.lookup("TButton", "background") fgc = self.style.lookup("TButton", "foreground") bga = self.style.lookup("TButton", "background", ("active", )) self.style.map('TCombobox', fieldbackground=[('readonly', 'white'), ('readonly', 'focus', 'white')], background=[("disabled", "active", "readonly", bgc), ("!disabled", "active", "readonly", bga)], foreground=[('readonly', '!disabled', fgc), ('readonly', '!disabled', 'focus', fgc), ('readonly', 'disabled', 'gray40'), ('readonly', 'disabled', 'focus', 'gray40') ], arrowcolor=[("disabled", "gray40")]) self.style.configure('menu.TCombobox', foreground=fgc, background=bgc, fieldbackground=bgc) self.style.map('menu.TCombobox', fieldbackground=[('readonly', bgc), ('readonly', 'focus', bgc)], background=[("disabled", "active", "readonly", bgc), ("!disabled", "active", "readonly", bga)], foreground=[('readonly', '!disabled', fgc), ('readonly', '!disabled', 'focus', fgc), ('readonly', 'disabled', 'gray40'), ('readonly', 'disabled', 'focus', 'gray40') ], arrowcolor=[("disabled", "gray40")]) self.style.map('DateEntry', arrowcolor=[("disabled", "gray40")]) self.style.configure('cal.TFrame', background='#424242') self.style.configure('month.TLabel', background='#424242', foreground='white') self.style.configure('R.TButton', background='#424242', arrowcolor='white', bordercolor='#424242', lightcolor='#424242', darkcolor='#424242') self.style.configure('L.TButton', background='#424242', arrowcolor='white', bordercolor='#424242', lightcolor='#424242', darkcolor='#424242') active_bg = self.style.lookup('TEntry', 'selectbackground', ('focus', )) self.style.map('R.TButton', background=[('active', active_bg)], bordercolor=[('active', active_bg)], darkcolor=[('active', active_bg)], lightcolor=[('active', active_bg)]) self.style.map('L.TButton', background=[('active', active_bg)], bordercolor=[('active', active_bg)], darkcolor=[('active', active_bg)], lightcolor=[('active', active_bg)]) self.style.configure('txt.TFrame', background='white') self.style.layout('down.TButton', [('down.TButton.downarrow', { 'side': 'right', 'sticky': 'ns' })]) self.style.map('TRadiobutton', indicatorforeground=[('disabled', 'gray40')]) self.style.map('TCheckbutton', indicatorforeground=[('disabled', 'gray40')], indicatorbackground=[ ('pressed', '#dcdad5'), ('!disabled', 'alternate', 'white'), ('disabled', 'alternate', '#a0a0a0'), ('disabled', '#dcdad5') ]) self.style.map('down.TButton', arrowcolor=[("disabled", "gray40")]) self.style.map('TMenubutton', arrowcolor=[('disabled', self.style.lookup('TMenubutton', 'foreground', ['disabled']))]) bg = self.style.lookup('TFrame', 'background', default='#ececec') self.configure(bg=bg) self.option_add('*Toplevel.background', bg) self.option_add('*Menu.background', bg) self.option_add('*Menu.tearOff', False) # toggle text self._open_image = PhotoImage(name='img_opened', file=IM_OPENED, master=self) self._closed_image = PhotoImage(name='img_closed', file=IM_CLOSED, master=self) self._open_image_sel = PhotoImage(name='img_opened_sel', file=IM_OPENED_SEL, master=self) self._closed_image_sel = PhotoImage(name='img_closed_sel', file=IM_CLOSED_SEL, master=self) self.style.element_create( "toggle", "image", "img_closed", ("selected", "!disabled", "img_opened"), ("active", "!selected", "!disabled", "img_closed_sel"), ("active", "selected", "!disabled", "img_opened_sel"), border=2, sticky='') self.style.map('Toggle', background=[]) self.style.layout('Toggle', [('Toggle.border', { 'children': [('Toggle.padding', { 'children': [('Toggle.toggle', { 'sticky': 'nswe' })], 'sticky': 'nswe' })], 'sticky': 'nswe' })]) # toggle sound self._im_sound = PhotoImage(master=self, file=IM_SOUND) self._im_mute = PhotoImage(master=self, file=IM_MUTE) self._im_sound_dis = PhotoImage(master=self, file=IM_SOUND_DIS) self._im_mute_dis = PhotoImage(master=self, file=IM_MUTE_DIS) self.style.element_create( 'mute', 'image', self._im_sound, ('selected', '!disabled', self._im_mute), ('selected', 'disabled', self._im_mute_dis), ('!selected', 'disabled', self._im_sound_dis), border=2, sticky='') self.style.layout('Mute', [('Mute.border', { 'children': [('Mute.padding', { 'children': [('Mute.mute', { 'sticky': 'nswe' })], 'sticky': 'nswe' })], 'sticky': 'nswe' })]) self.style.configure('Mute', relief='raised') # widget scrollbar self._im_trough = {} self._im_slider_vert = {} self._im_slider_vert_prelight = {} self._im_slider_vert_active = {} self._slider_alpha = Image.open(IM_SCROLL_ALPHA) for widget in ['Events', 'Tasks']: bg = CONFIG.get(widget, 'background', fallback='gray10') fg = CONFIG.get(widget, 'foreground') widget_bg = self.winfo_rgb(bg) widget_fg = tuple( round(c * 255 / 65535) for c in self.winfo_rgb(fg)) active_bg = active_color(*widget_bg) active_bg2 = active_color(*active_color(*widget_bg, 'RGB')) slider_vert = Image.new('RGBA', (13, 28), active_bg) slider_vert_active = Image.new('RGBA', (13, 28), widget_fg) slider_vert_prelight = Image.new('RGBA', (13, 28), active_bg2) self._im_trough[widget] = tkPhotoImage(width=15, height=15, master=self) self._im_trough[widget].put(" ".join( ["{" + " ".join([bg] * 15) + "}"] * 15)) self._im_slider_vert_active[widget] = PhotoImage( slider_vert_active, master=self) self._im_slider_vert[widget] = PhotoImage(slider_vert, master=self) self._im_slider_vert_prelight[widget] = PhotoImage( slider_vert_prelight, master=self) self.style.element_create('%s.Vertical.Scrollbar.trough' % widget, 'image', self._im_trough[widget]) self.style.element_create( '%s.Vertical.Scrollbar.thumb' % widget, 'image', self._im_slider_vert[widget], ('pressed', '!disabled', self._im_slider_vert_active[widget]), ('active', '!disabled', self._im_slider_vert_prelight[widget]), border=6, sticky='ns') self.style.layout( '%s.Vertical.TScrollbar' % widget, [('%s.Vertical.Scrollbar.trough' % widget, { 'children': [('%s.Vertical.Scrollbar.thumb' % widget, { 'expand': '1' })], 'sticky': 'ns' })]) # --- tree columns = { _('Summary'): ({ 'stretch': True, 'width': 300 }, lambda: self._sort_by_desc(_('Summary'), False)), _('Place'): ({ 'stretch': True, 'width': 200 }, lambda: self._sort_by_desc(_('Place'), False)), _('Start'): ({ 'stretch': False, 'width': 150 }, lambda: self._sort_by_date(_('Start'), False)), _('End'): ({ 'stretch': False, 'width': 150 }, lambda: self._sort_by_date(_("End"), False)), _('Category'): ({ 'stretch': False, 'width': 100 }, lambda: self._sort_by_desc(_('Category'), False)) } self.tree = Treeview(self, show="headings", columns=list(columns)) for label, (col_prop, cmd) in columns.items(): self.tree.column(label, **col_prop) self.tree.heading(label, text=label, anchor="w", command=cmd) self.tree.tag_configure('0', background='#ececec') self.tree.tag_configure('1', background='white') self.tree.tag_configure('outdated', foreground='red') scroll = AutoScrollbar(self, orient='vertical', command=self.tree.yview) self.tree.configure(yscrollcommand=scroll.set) # --- toolbar toolbar = Frame(self) self.img_plus = PhotoImage(master=self, file=IM_ADD) Button(toolbar, image=self.img_plus, padding=1, command=self.add).pack(side="left", padx=4) Label(toolbar, text=_("Filter by")).pack(side="left", padx=4) # --- TODO: add filter by start date (after date) self.filter_col = Combobox( toolbar, state="readonly", # values=("",) + self.tree.cget('columns')[1:], values=("", _("Category")), exportselection=False) self.filter_col.pack(side="left", padx=4) self.filter_val = Combobox(toolbar, state="readonly", exportselection=False) self.filter_val.pack(side="left", padx=4) Button(toolbar, text=_('Delete All Outdated'), padding=1, command=self.delete_outdated_events).pack(side="right", padx=4) # --- grid toolbar.grid(row=0, columnspan=2, sticky='we', pady=4) self.tree.grid(row=1, column=0, sticky='eswn') scroll.grid(row=1, column=1, sticky='ns') # --- restore data data = {} self.events = {} self.nb = 0 try: with open(DATA_PATH, 'rb') as file: dp = Unpickler(file) data = dp.load() except Exception: l = [ f for f in os.listdir(os.path.dirname(BACKUP_PATH)) if f.startswith('data.backup') ] if l: l.sort(key=lambda x: int(x[11:])) shutil.copy(os.path.join(os.path.dirname(BACKUP_PATH), l[-1]), DATA_PATH) with open(DATA_PATH, 'rb') as file: dp = Unpickler(file) data = dp.load() self.nb = len(data) backup() now = datetime.now() for i, prop in enumerate(data): iid = str(i) self.events[iid] = Event(self.scheduler, iid=iid, **prop) self.tree.insert('', 'end', iid, values=self.events[str(i)].values()) tags = [str(self.tree.index(iid) % 2)] self.tree.item(iid, tags=tags) if not prop['Repeat']: for rid, d in list(prop['Reminders'].items()): if d < now: del self.events[iid]['Reminders'][rid] self.after_id = self.after(15 * 60 * 1000, self.check_outdated) # --- bindings self.bind_class("TCombobox", "<<ComboboxSelected>>", self.clear_selection, add=True) self.bind_class("TCombobox", "<Control-a>", self.select_all) self.bind_class("TEntry", "<Control-a>", self.select_all) self.tree.bind('<3>', self._post_menu) self.tree.bind('<1>', self._select) self.tree.bind('<Double-1>', self._edit_on_click) self.menu.bind('<FocusOut>', lambda e: self.menu.unpost()) self.filter_col.bind("<<ComboboxSelected>>", self.update_filter_val) self.filter_val.bind("<<ComboboxSelected>>", self.apply_filter) # --- widgets self.widgets = {} prop = { op: CONFIG.get('Calendar', op) for op in CONFIG.options('Calendar') } self.widgets['Calendar'] = CalendarWidget(self, locale=CONFIG.get( 'General', 'locale'), **prop) self.widgets['Events'] = EventWidget(self) self.widgets['Tasks'] = TaskWidget(self) self.widgets['Timer'] = Timer(self) self.widgets['Pomodoro'] = Pomodoro(self) self._setup_style() for item, widget in self.widgets.items(): self.menu_widgets.add_checkbutton( label=_(item), command=lambda i=item: self.display_hide_widget(i)) self.menu_widgets.set_item_value(_(item), widget.variable.get()) add_trace(widget.variable, 'write', lambda *args, i=item: self._menu_widgets_trace(i)) self.icon.loop(self) self.tk.eval(""" apply {name { set newmap {} foreach {opt lst} [ttk::style map $name] { if {($opt eq "-foreground") || ($opt eq "-background")} { set newlst {} foreach {st val} $lst { if {($st eq "disabled") || ($st eq "selected")} { lappend newlst $st $val } } if {$newlst ne {}} { lappend newmap $opt $newlst } } else { lappend newmap $opt $lst } } ttk::style map $name {*}$newmap }} Treeview """) # react to scheduler --update-date in command line signal.signal(signal.SIGUSR1, self.update_date) # update selected date in calendar and event list every day self.scheduler.add_job(self.update_date, CronTrigger(hour=0, minute=0, second=1), jobstore='memo') self.scheduler.start() def _setup_style(self): # scrollbars for widget in ['Events', 'Tasks']: bg = CONFIG.get(widget, 'background', fallback='gray10') fg = CONFIG.get(widget, 'foreground', fallback='white') widget_bg = self.winfo_rgb(bg) widget_fg = tuple( round(c * 255 / 65535) for c in self.winfo_rgb(fg)) active_bg = active_color(*widget_bg) active_bg2 = active_color(*active_color(*widget_bg, 'RGB')) slider_vert = Image.new('RGBA', (13, 28), active_bg) slider_vert.putalpha(self._slider_alpha) slider_vert_active = Image.new('RGBA', (13, 28), widget_fg) slider_vert_active.putalpha(self._slider_alpha) slider_vert_prelight = Image.new('RGBA', (13, 28), active_bg2) slider_vert_prelight.putalpha(self._slider_alpha) self._im_trough[widget].put(" ".join( ["{" + " ".join([bg] * 15) + "}"] * 15)) self._im_slider_vert_active[widget].paste(slider_vert_active) self._im_slider_vert[widget].paste(slider_vert) self._im_slider_vert_prelight[widget].paste(slider_vert_prelight) for widget in self.widgets.values(): widget.update_style() def report_callback_exception(self, *args): err = ''.join(traceback.format_exception(*args)) logging.error(err) showerror('Exception', str(args[1]), err, parent=self) def save(self): logging.info('Save event database') data = [ev.to_dict() for ev in self.events.values()] with open(DATA_PATH, 'wb') as file: pick = Pickler(file) pick.dump(data) def update_date(self, *args): """Update Calendar's selected day and Events' list.""" self.widgets['Calendar'].update_date() self.widgets['Events'].display_evts() self.update_idletasks() # --- bindings def _select(self, event): if not self.tree.identify_row(event.y): self.tree.selection_remove(*self.tree.selection()) def _edit_on_click(self, event): sel = self.tree.selection() if sel: sel = sel[0] self.edit(sel) # --- class bindings @staticmethod def clear_selection(event): combo = event.widget combo.selection_clear() @staticmethod def select_all(event): event.widget.selection_range(0, "end") return "break" # --- show / hide def _menu_widgets_trace(self, item): self.menu_widgets.set_item_value(_(item), self.widgets[item].variable.get()) def display_hide_widget(self, item): value = self.menu_widgets.get_item_value(_(item)) if value: self.widgets[item].show() else: self.widgets[item].hide() def hide(self): self._visible.set(False) self.withdraw() self.save() def show(self): self._visible.set(True) self.deiconify() def _visibility_trace(self, *args): self.icon.menu.set_item_value(_('Manager'), self._visible.get()) def display_hide(self, toggle=False): value = self.icon.menu.get_item_value(_('Manager')) if toggle: value = not value self.icon.menu.set_item_value(_('Manager'), value) self._visible.set(value) if not value: self.withdraw() self.save() else: self.deiconify() # --- event management def event_add(self, event): self.nb += 1 iid = str(self.nb) self.events[iid] = event self.tree.insert('', 'end', iid, values=event.values()) self.tree.item(iid, tags=str(self.tree.index(iid) % 2)) self.widgets['Calendar'].add_event(event) self.widgets['Events'].display_evts() self.widgets['Tasks'].display_tasks() self.save() def event_configure(self, iid): self.tree.item(iid, values=self.events[iid].values()) self.widgets['Calendar'].add_event(self.events[iid]) self.widgets['Events'].display_evts() self.widgets['Tasks'].display_tasks() self.save() def add(self, date=None): iid = str(self.nb + 1) if date is not None: event = Event(self.scheduler, iid=iid, Start=date) else: event = Event(self.scheduler, iid=iid) Form(self, event, new=True) def delete(self, iid): index = self.tree.index(iid) self.tree.delete(iid) for k, item in enumerate(self.tree.get_children('')[index:]): tags = [ t for t in self.tree.item(item, 'tags') if t not in ['1', '0'] ] tags.append(str((index + k) % 2)) self.tree.item(item, tags=tags) self.events[iid].reminder_remove_all() self.widgets['Calendar'].remove_event(self.events[iid]) del (self.events[iid]) self.widgets['Events'].display_evts() self.widgets['Tasks'].display_tasks() self.save() def edit(self, iid): self.widgets['Calendar'].remove_event(self.events[iid]) Form(self, self.events[iid]) def check_outdated(self): """Check for outdated events every 15 min.""" now = datetime.now() for iid, event in self.events.items(): if not event['Repeat'] and event['Start'] < now: tags = list(self.tree.item(iid, 'tags')) if 'outdated' not in tags: tags.append('outdated') self.tree.item(iid, tags=tags) self.after_id = self.after(15 * 60 * 1000, self.check_outdated) def delete_outdated_events(self): now = datetime.now() outdated = [] for iid, prop in self.events.items(): if prop['End'] < now: if not prop['Repeat']: outdated.append(iid) elif prop['Repeat']['Limit'] != 'always': end = prop['End'] enddate = datetime.fromordinal( prop['Repeat']['EndDate'].toordinal()) enddate.replace(hour=end.hour, minute=end.minute) if enddate < now: outdated.append(iid) for item in outdated: self.delete(item) logging.info('Deleted outdated events') def refresh_reminders(self): """ Reschedule all reminders. Required when APScheduler is updated. """ for event in self.events.values(): reminders = [date for date in event['Reminders'].values()] event.reminder_remove_all() for date in reminders: event.reminder_add(date) logging.info('Refreshed reminders') # --- sorting def _move_item(self, item, index): self.tree.move(item, "", index) tags = [t for t in self.tree.item(item, 'tags') if t not in ['1', '0']] tags.append(str(index % 2)) self.tree.item(item, tags=tags) @staticmethod def to_datetime(date): date_format = get_date_format("short", CONFIG.get("General", "locale")).pattern dayfirst = date_format.startswith("d") yearfirst = date_format.startswith("y") return parse(date, dayfirst=dayfirst, yearfirst=yearfirst) def _sort_by_date(self, col, reverse): l = [(self.to_datetime(self.tree.set(k, col)), k) for k in self.tree.get_children('')] l.sort(reverse=reverse) # rearrange items in sorted positions for index, (val, k) in enumerate(l): self._move_item(k, index) # reverse sort next time self.tree.heading(col, command=lambda: self._sort_by_date(col, not reverse)) def _sort_by_desc(self, col, reverse): l = [(self.tree.set(k, col), k) for k in self.tree.get_children('')] l.sort(reverse=reverse, key=lambda x: x[0].lower()) # rearrange items in sorted positions for index, (val, k) in enumerate(l): self._move_item(k, index) # reverse sort next time self.tree.heading(col, command=lambda: self._sort_by_desc(col, not reverse)) # --- filter def update_filter_val(self, event): col = self.filter_col.get() self.filter_val.set("") if col: l = set() for k in self.events: l.add(self.tree.set(k, col)) self.filter_val.configure(values=tuple(l)) else: self.filter_val.configure(values=[]) self.apply_filter(event) def apply_filter(self, event): col = self.filter_col.get() val = self.filter_val.get() items = list(self.events.keys()) if not col: for item in items: self._move_item(item, int(item)) else: i = 0 for item in items: if self.tree.set(item, col) == val: self._move_item(item, i) i += 1 else: self.tree.detach(item) # --- manager's menu def _post_menu(self, event): self.right_click_iid = self.tree.identify_row(event.y) self.tree.selection_remove(*self.tree.selection()) self.tree.selection_add(self.right_click_iid) if self.right_click_iid: try: self.menu.delete(_('Progress')) except TclError: pass state = self.events[self.right_click_iid]['Task'] if state: self._task_var.set(state) if '%' in state: self._img_dot = PhotoImage(master=self, file=IM_DOT) else: self._img_dot = tkPhotoImage(master=self) self.menu_task.entryconfigure(1, image=self._img_dot) self.menu.insert_cascade(0, menu=self.menu_task, label=_('Progress')) self.menu.tk_popup(event.x_root, event.y_root) def _delete_menu(self): if self.right_click_iid: self.delete(self.right_click_iid) def _edit_menu(self): if self.right_click_iid: self.edit(self.right_click_iid) def _set_progress(self): if self.right_click_iid: self.events[self.right_click_iid]['Task'] = self._task_var.get() self.widgets['Tasks'].display_tasks() if '%' in self._task_var.get(): self._img_dot = PhotoImage(master=self, file=IM_DOT) else: self._img_dot = tkPhotoImage(master=self) self.menu_task.entryconfigure(1, image=self._img_dot) # --- icon menu def exit(self): self.save() rep = self.widgets['Pomodoro'].stop(self.widgets['Pomodoro'].on) if not rep: return self.menu_eyes.quit() self.after_cancel(self.after_id) try: self.scheduler.shutdown() except SchedulerNotRunningError: pass self.destroy() def settings(self): splash_supp = CONFIG.get('General', 'splash_supported', fallback=True) dialog = Settings(self) self.wait_window(dialog) self._setup_style() if splash_supp != CONFIG.get('General', 'splash_supported'): for widget in self.widgets.values(): widget.update_position() # --- week schedule def get_next_week_events(self): """Return events scheduled for the next 7 days """ locale = CONFIG.get("General", "locale") next_ev = {} today = datetime.now().date() for d in range(7): day = today + timedelta(days=d) evts = self.widgets['Calendar'].get_events(day) if evts: evts = [self.events[iid] for iid in evts] evts.sort(key=lambda ev: ev.get_start_time()) desc = [] for ev in evts: if ev["WholeDay"]: date = "" else: date = "%s - %s " % ( format_time(ev['Start'], locale=locale), format_time(ev['End'], locale=locale)) place = "(%s)" % ev['Place'] if place == "()": place = "" desc.append(("%s%s %s\n" % (date, ev['Summary'], place), ev['Description'])) next_ev[day.strftime('%A')] = desc return next_ev # --- tasks def get_tasks(self): # TODO: find events with repetition in the week # TODO: better handling of events on several days tasks = [] for event in self.events.values(): if event['Task']: tasks.append(event) return tasks
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()