def create_gui(app): f1 = PanedWindow( app, orient='horizontal', width=800, height=600, sashrelief='ridge', sashwidth=4, ) f1.grid( row=0, column=0, sticky="nesw", ) f2 = PanedWindow(f1, orient='vertical') canvW = Canvas(f2, bg='#113333') f2.add(canvW) logW = scrolledtext.ScrolledText(f2, bg='#223399', fg='#cccc99', font=font.nametofont('TkDefaultFont')) f2.add(logW) bodyW = makeBodyW(f1) f1.add(f2) f1.add(bodyW) return f1, f2, bodyW, canvW, logW
def resize_paned_window(self, tk_paned_window: tkinter.PanedWindow): column, row = tk_paned_window.grid_info()['column'], tk_paned_window.grid_info()['row'] tk_paned_window.grid_forget() p_pane_list = [] for pane_index in range(len(tk_paned_window.panes())): p_pane_list.append(self.widgets.find_pyte_widget_from_tk(str(tk_paned_window.panes()[0]))) tk_paned_window.forget(p_pane_list[-1].tk_name) tk_widget_list = [] tk_widget_row = [] # tk_widget_column = [] for tk_widget in p_pane_list[-1].tk_name.winfo_children(): tk_widget_list.append(tk_widget) # print(tk_widget) tk_widget_row.append(tk_widget.grid_info()['row']) # print(p_pane_list[-1]) for p_pane in p_pane_list: tk_paned_window.add(p_pane.tk_name) tk_paned_window.grid(row=row, column=column)
def __init__(self, master, appWidth, appHeight): self.master = master geo = "%dx%d" % (appWidth, appHeight) master.geometry(geo) master.title("Smacket") self.videoPath = "" bcg = "#0A8049" topLeft = PanedWindow(master, orient=tk.VERTICAL) topLeft.config(bg = bcg) topLeft.grid(row = 0, column = 0) topLeftTitle = tk.Message(master, text = "Timestamps") topLeftTitle.config(anchor = "center", aspect = 500, font = ("consolas", 20, "bold"), bg = bcg, relief = "sunken") timeStamps = TextScrollCombo(master) timeStamps.config(width=appWidth // 10, height= appHeight // 2) timeStamps.txt.config(font=("consolas", 12), undo=True, wrap='word', borderwidth=3, relief="sunken") topLeft.add(topLeftTitle) topLeft.add(timeStamps) bottomLeft = PanedWindow(master, orient=tk.VERTICAL) bottomLeft.config(bg = bcg) bottomLeft.grid(row = 1, column = 0) bottomLeftTitle = tk.Message(master, text = "Matches") bottomLeftTitle.config(anchor = "center", aspect = 500, font = ("consolas", 20, "bold"), bg = bcg, relief = "sunken") matches = TextScrollCombo(master) matches.config(width= appWidth// 10 , height=appHeight // 2) matches.txt.config(font=("consolas", 12), undo=True, wrap='word', borderwidth=3, relief="sunken") bottomLeft.add(bottomLeftTitle) bottomLeft.add(matches) videoPlayer = Label(master, height = 20, width = appWidth - (appWidth // 10), bg = 'red', text = "VideoPlayer",) videoPlayer.grid(row=0, column=1) videoSkimmer = Label(master, height = 20, width = appWidth - (appWidth // 10), bg = 'yellow', text = "VideoSkimmer",) videoSkimmer.grid(row=1, column=1)
def show_paned_window_parent(self, tk_paned_window: tkinter.PanedWindow): p_paned_window = self.widgets.find_pyte_parent(tk_paned_window) tk_paned_window.grid(row=p_paned_window.row, column=p_paned_window.column)
class PyLatency: """Ping tool visualization with tkinter""" def __init__(self, root): """Setup window geometry & widgets + layout, init counters""" self.master = root self.master.title("pyLatency") self.appdata_dir = getenv("APPDATA") + "/pyLatency" self.options_path = self.appdata_dir + "/options.json" self.log_dir = self.appdata_dir + "/logs" self.logfile = None self.options_logging = BooleanVar() self.options_geometry = "" self.options = self.init_options() if self.options: self.options_geometry = self.options["geometry"] self.options_logging.set(self.options["logging"]) if self.options_geometry: self.master.geometry(self.options_geometry) else: self.master.geometry("400x200") self.master.minsize(width=400, height=200) self.master.update() self.running = False self.hostname = None self.RECT_SCALE_FACTOR = 2 self.TIMEOUT = 5000 self.minimum = self.TIMEOUT self.maximum = 0 self.average = 0 self.SAMPLE_SIZE = 1000 self.sample = deque(maxlen=self.SAMPLE_SIZE) self.pcount = 0 self.max_bar = None self.min_bar = None # Widgets: self.frame = Frame(self.master) self.lbl_entry = Label(self.frame, text="Host:") self.lbl_status_1 = Label(self.frame, text="Ready") self.lbl_status_2 = Label(self.frame, fg="red") self.entry = Entry(self.frame) self.btn_start = Button(self.frame, text="Start", command=self.start) self.btn_stop = Button(self.frame, text="Stop", command=self.stop) self.chk_log = Checkbutton(self.frame, text="Enable log", variable=self.options_logging) self.delay_scale = Scale( self.frame, label="Interval (ms)", orient="horizontal", from_=100, to=self.TIMEOUT, resolution=100, ) self.delay_scale.set(1000) self.paneview = PanedWindow(self.master, sashwidth=5, bg="#cccccc") self.left_pane = PanedWindow(self.paneview) self.right_pane = PanedWindow(self.paneview) self.paneview.add(self.left_pane) self.paneview.add(self.right_pane) self.canvas_scroll_y = Scrollbar(self.left_pane) self.canvas = Canvas(self.left_pane, bg="#FFFFFF", yscrollcommand=self.canvas_scroll_y.set) self.canvas_scroll_y.config(command=self.canvas.yview) self.left_pane.add(self.canvas_scroll_y) self.ping_list_scroll = Scrollbar(self.master) self.ping_list = Listbox(self.right_pane, highlightthickness=0, font=14, selectmode="disabled", yscrollcommand=self.ping_list_scroll.set) self.ping_list_scroll.config(command=self.ping_list.yview) self.right_pane.add(self.ping_list_scroll) self.left_pane.add(self.canvas) self.right_pane.add(self.ping_list) # Layout: self.master.columnconfigure(0, weight=1) self.master.rowconfigure(1, weight=1) self.frame.columnconfigure(1, weight=1) self.frame.grid(row=0, column=0, sticky="nsew") self.lbl_entry.grid(row=0, column=0) self.lbl_status_1.grid(row=1, column=0, columnspan=4) self.lbl_status_2.grid(row=2, column=0, columnspan=4) self.entry.grid(row=0, column=1, sticky="ew") self.btn_start.grid(row=0, column=2) self.btn_stop.grid(row=0, column=3) self.chk_log.grid(row=1, column=2, columnspan=2) self.delay_scale.grid(row=0, column=4, rowspan=2) # self.canvas_scroll_y.grid(row=1, column=2, sticky="ns") self.paneview.grid(row=1, column=0, sticky="nsew") # self.ping_list_scroll.grid(row=1, column=1, sticky="ns") self.paneview.paneconfigure( self.left_pane, width=(self.master.winfo_width() - self.delay_scale.winfo_reqwidth()), ) #Bindings: self.canvas.bind("<MouseWheel>", self.scroll_canvas) self.master.bind("<Return>", self.start) self.master.bind("<Escape>", self.stop) self.master.bind("<Control-w>", lambda event: self.master.destroy()) self.master.bind( "<Up>", lambda event: self.delay_scale.set(self.delay_scale.get() + 100)) self.master.bind( "<Down>", lambda event: self.delay_scale.set(self.delay_scale.get() - 100)) self.master.protocol("WM_DELETE_WINDOW", self.master_close) def __str__(self): """Return own address""" return f"pyLatency GUI @ {hex(id(self))}" def start(self, event=None): """ Reset the GUI, create & start a thread so we don't block the mainloop during each poll. """ if not self.running: self.hostname = self.entry.get() if self.hostname: self.ping_list.delete(0, "end") self.canvas.delete("all") self.lbl_status_1.config(text="Running", fg="green") self.lbl_status_2.config(text="") self.sample.clear() (self.minimum, self.maximum, self.average, self.pcount) = self.TIMEOUT, 0, 0, 0 self.running = True self.thread = Thread(target=self.run, daemon=True) self.thread.start() else: self.lbl_status_2.config(text="Missing Hostname") def logged(fn): """ decorates self.run(), create a log directory if one doesn't exist, create a filename with a date & timestamp, call self.run() with logging enabled or disabled """ @wraps(fn) def inner(self): if self.options_logging.get(): if not exists(self.log_dir): mkdir(self.log_dir) timestamp = datetime.now() fname = timestamp.strftime("%a %b %d @ %H-%M-%S") with open(self.log_dir + f"/{fname}.txt", "w+") as self.logfile: self.logfile.write(f"pyLatency {fname}\n") self.logfile.write(f"Host: {self.hostname}\n") self.logfile.write("-" * 40 + "\n") start = default_timer() fn(self) end = default_timer() elapsed = end - start self.logfile.write("-" * 40 + "\n") self.logfile.write( f"Logged {self.pcount} pings over {int(elapsed)} seconds" ) else: fn(self) return inner @logged def run(self): """ Continuously shell out to ping, get an integer result, update the GUI, and wait. """ while self.running: latency = self.ping(self.hostname) self.pcount += 1 if latency is None: self.stop() self.lbl_status_2.config(text="Unable to ping host") return if latency > self.maximum: self.maximum = latency if latency < self.minimum: self.minimum = latency self.sample.append(latency) self.average = sum(self.sample) / len(self.sample) if self.logfile: self.logfile.write(str(latency) + "\n") self.update_gui(latency) sleep(self.delay_scale.get() / 1000) def update_gui(self, latency): """ Update the listbox, shift all existing rectangles, draw the latest result from self.ping(), cleanup unused rectangles, update the mainloop """ if self.ping_list.size() >= self.SAMPLE_SIZE: self.ping_list.delete(self.SAMPLE_SIZE - 1, "end") self.ping_list.insert(0, str(latency) + "ms") self.canvas.move("rect", 10, 0) self.canvas.create_rectangle(0, 0, 10, int(latency * self.RECT_SCALE_FACTOR), fill="#333333", tags="rect", width=0) self.canvas.delete(self.max_bar) self.max_bar = self.canvas.create_line( 0, self.maximum * self.RECT_SCALE_FACTOR, self.canvas.winfo_width(), self.maximum * self.RECT_SCALE_FACTOR, fill="red", ) self.canvas.delete(self.min_bar) self.min_bar = self.canvas.create_line( 0, self.minimum * self.RECT_SCALE_FACTOR, self.canvas.winfo_width(), self.minimum * self.RECT_SCALE_FACTOR, fill="green", ) # canvas scrollable region is not updated automatically self.canvas.configure(scrollregion=self.canvas.bbox("all")) self.lbl_status_2.config(fg="#000000", text=f"Min: {self.minimum} " f"Max: {self.maximum} " f"Avg: {round(self.average,2):.2f}") self.cleanup_rects() self.master.update() def scroll_canvas(self, event): """ Bound to <MouseWheel> tkinter event on self.canvas. Respond to Linux or Windows mousewheel event, and scroll the canvas accordingly """ count = None if event.num == 5 or event.delta == -120: count = 1 if event.num == 4 or event.delta == 120: count = -1 self.canvas.yview_scroll(count, "units") def cleanup_rects(self): """Delete rectangles that are outside the bbox of the canvas""" for rect in self.canvas.find_withtag("rect"): if self.canvas.coords(rect)[0] > self.canvas.winfo_width(): self.canvas.delete(rect) def stop(self, event=None): """Satisfy the condition in which self.thread exits""" if self.running: self.running = False self.lbl_status_1.config(text="Stopped", fg="red") def master_close(self, event=None): """Writes window geometry/options to the disk.""" options = dumps({ "geometry": self.master.geometry(), "logging": self.options_logging.get() }) if not exists(self.appdata_dir): mkdir(self.appdata_dir) with open(self.options_path, "w+") as options_file: options_file.write(options) self.master.destroy() def init_options(self): """Called on startup, loads, parses, and returns options from json.""" if exists(self.options_path): with open(self.options_path, "r") as options_file: options_json = options_file.read() return loads(options_json) else: return None @staticmethod def ping(url): """ Shell out to ping and return an integer result. Returns None if ping fails for any reason: timeout, bad hostname, etc. """ flag = "-n" if platform == "win32" else "-c" result = run(["ping", flag, "1", "-w", "5000", url], capture_output=True, creationflags=DETACHED_PROCESS) output = result.stdout.decode("utf-8") try: duration = findall("\\d+ms", output)[0] return int(duration[:-2]) except IndexError: return None
class PreferencesFrame(FrameTemplate): """Used to set decision maker's preferences for use in prioritization.""" # Label used for button to select frame in the main program. buttonLabel = 'Prioritization' # Image used on button to select frame, when frame is active. activeIcon = 'icons/Prioritization_ON.gif' # Image used on button to select frame, when frame is inactive. inactiveIcon = 'icons/Prioritization_OFF.gif' # Help text to be displayed when screen is active. helpText = ("Select a decision maker from the column at left by clicking " "its 'Edit' button. Enter preferred conditions using the " "inputs to the right. Preferred conditions for the selected " "decision maker are shown at the far right, from most " "important at the top, to least important at the bottom.") # ######################## INITIALIZATION ################################ def __init__(self, master, conflict, *args): """Initialize the Frame. Does not build widgets.""" FrameTemplate.__init__(self, master, conflict, self.buttonLabel, self.activeIcon, self.inactiveIcon, self.helpText) self.lastBuildDMs = None self.lastBuildOptions = None self.lastBuildInfeasibles = None self.lastBuildRanking = None # ############################ METHODS ################################### def hasRequiredData(self): """Check that minimum data required to render the frame exists.""" if len(self.conflict.decisionMakers) < 1: return False if len(self.conflict.options) < 1: return False if len(self.conflict.feasibles) < 1: self.conflict.recalculateFeasibleStates() if len(self.conflict.feasibles) < 1: return False else: return True def dataChanged(self): """Check if data has changed since the last build of the Frame.""" if self.lastBuildDMs != self.conflict.decisionMakers.export_rep(): return True if self.lastBuildOptions != self.conflict.options.export_rep(): return True if self.lastBuildInfeasibles != self.conflict.infeasibles.export_rep(): return True if self.lastBuildRanking != self.conflict.useManualPreferenceRanking: return True else: return False def buildFrame(self): """Contruct frame widgets and initialize data.""" if self.built: return # Ensure all required parts of the conflict model are properly set-up. self.conflict.reorderOptionsByDM() self.conflict.options.set_indexes() self.conflict.infeasibles.validate() self.conflict.recalculateFeasibleStates() for dm in self.conflict.decisionMakers: dm.calculatePerceived() dm.calculatePreferences() self.lastBuildDMs = self.conflict.decisionMakers.export_rep() self.lastBuildOptions = self.conflict.options.export_rep() self.lastBuildInfeasibles = self.conflict.infeasibles.export_rep() self.lastBuildRanking = self.conflict.useManualPreferenceRanking # Define variables that will display in the infoFrame numD = len(self.conflict.decisionMakers) self.infoText = StringVar( value='Valid Preferences set for {0}/{0} DMs.'.format(numD)) # Define frame-specific variables self.dm = None # infoFrame: frame and label definitions (with master 'self.infoFrame') self.infoLabel = ttk.Label(self.infoFrame, textvariable=self.infoText) # helpFrame: frame and label definitions (with master 'self.helpFrame') self.helpLabel = ttk.Label(self.helpFrame, textvariable=self.helpVar, wraplength=150) # Define frame-specific input widgets (with 'self' as master) self.paneMaster = PanedWindow(self, orient=VERTICAL, sashwidth=5, sashrelief="raised", sashpad=2, relief="sunken") self.paneTop = ttk.Frame(self.paneMaster) self.rankings = PreferenceRankingMaster(self.paneTop, self.conflict) self.editor = RadiobuttonEntry(self.paneTop, self.conflict) self.paneTopRightMaster = PanedWindow(self.paneTop, orient=HORIZONTAL, sashwidth=5, sashrelief="raised", sashpad=2, relief="sunken") self.staging = PreferenceStaging(self.paneTopRightMaster, self.conflict) self.preferenceDisp = PreferenceListDisplay(self.paneTopRightMaster, self.conflict) self.paneBottom = ttk.Frame(self.paneMaster) self.optionTable = OptionFormTable(self.paneBottom, self.conflict) self.usePrioritizationButton = ttk.Button( self, text=("Use preference prioritization. Any manually set " "preference rankings will be lost."), command=self.usePrioritization) # ######## preliminary gridding and option configuration # configuring the input frame self.grid(column=0, row=0, rowspan=5, sticky=NSEW) self.columnconfigure(0, weight=1) self.rowconfigure(1, weight=1) self.grid_remove() # configuring infoFrame & infoFrame widgets self.infoFrame.grid(column=2, row=0, sticky=NSEW, padx=3, pady=3) self.infoFrame.grid_remove() self.infoLabel.grid(column=0, row=1, sticky=NSEW) # configuring helpFrame & helpFrame widgets self.helpFrame.grid(column=2, row=1, sticky=NSEW, padx=3, pady=3) self.helpFrame.grid_remove() self.helpLabel.grid(column=0, row=0, sticky=NSEW) # configuring frame-specific options self.paneMaster.grid(column=0, row=1, sticky=NSEW) self.paneMaster.add(self.paneTop, minsize=200) self.rankings.grid(column=0, row=1, sticky=NSEW) ttk.Separator(self.paneTop, orient=VERTICAL).grid(column=1, row=1, sticky=NSEW, padx=3) self.editor.grid(column=2, row=1, sticky=NSEW) ttk.Separator(self.paneTop, orient=VERTICAL).grid(column=3, row=1, sticky=NSEW, padx=3) self.paneTopRightMaster.grid(column=4, row=1, sticky=NSEW) self.paneTop.columnconfigure(0, weight=0) self.paneTop.columnconfigure(2, weight=0) self.paneTop.columnconfigure(4, weight=1) self.paneTop.rowconfigure(1, weight=1) self.usePrioritizationButton.grid(column=0, row=0, columnspan=5, sticky=NSEW) self.paneTopRightMaster.add(self.staging) self.paneTopRightMaster.add(self.preferenceDisp) self.paneMaster.add(self.paneBottom) self.optionTable.grid(column=0, row=0, sticky=NSEW) self.paneBottom.columnconfigure(0, weight=1) self.paneBottom.rowconfigure(0, weight=1) # bindings self.rankings.bind('<<DMchg>>', self.dmChgHandler) self.editor.bind('<<AddPref>>', self.addPref) self.editor.bind('<<StagePref>>', self.stagePref) self.staging.bind('<<SelCond>>', self.selCondChg) self.staging.bind('<<PullFromStage>>', self.pullFromStage) self.preferenceDisp.bind('<<SelPref>>', self.selPrefChg) self.preferenceDisp.bind('<<ValueChange>>', self.refresh) self.dmChgHandler() self.built = True def enter(self, *args): """Re-grid the screen into the master. Perform required updates.""" if self.dataChanged(): self.clearFrame() FrameTemplate.enter(self) def refresh(self, *args): """Refresh data in all active display widgets.""" for dm in self.conflict.decisionMakers: dm.calculatePreferences() self.editor.reloadOpts() self.rankings.refresh() self.preferenceDisp.refresh() self.optionTable.buildTable(self.dm) self.checkIfUsingRankings() def checkIfUsingRankings(self, event=None): """Disable screen if Manual Ranking has been assigned.""" if self.conflict.useManualPreferenceRanking: self.usePrioritizationButton.grid() self.rankings.disable() self.editor.disable() self.staging.disable() self.preferenceDisp.disable() else: self.usePrioritizationButton.grid_remove() def usePrioritization(self): """Reactivate the screen if user decides to use prioritization.""" self.conflict.useManualPreferenceRanking = False self.conflict.preferenceErrors = None self.refresh() def dmChgHandler(self, event=None): """Bound to <<DMchg>>.""" self.dm = self.rankings.dm self.preferenceDisp.changeDM(self.dm) self.optionTable.buildTable(self.dm) self.staging.clear() self.editor.setStates('clear') if self.dm is None: self.preferenceDisp.disable() self.editor.disable() self.staging.disable() else: self.preferenceDisp.enable() self.editor.enable() def addPref(self, event=None): """Add a preference for the active decision maker.""" pref = self.editor.getStates() self.dm.preferences.append(pref) self.refresh() def stagePref(self, event=None): """Send a condition to the staging area.""" if self.editor.hasValidIf: for cond in self.editor.ifCond: self.staging.addCondition(cond) else: condData = self.editor.getStates() newCond = self.conflict.newCondition(condData) self.staging.addCondition(newCond) self.editor.setStates('clear') def pullFromStage(self, event=None): """Move a compound condition from Staging to Preferences.""" newPref = self.staging.conditionList self.staging.clear() self.dm.preferences.append(newPref) self.refresh() def selCondChg(self, event=None): """Triggered when a condition is selected in staging.""" condition = self.staging.conditionList[event.x] self.editor.setStates(condition.ynd()) def selPrefChg(self, event=None): """Triggered when a preference is select from preferences.""" condition = self.dm.preferences[event.x] self.staging.setList(condition)
class StabilityFrame(FrameTemplate): """Frame used to for Stability Analysis.""" # Label used for button to select frame in the main program. buttonLabel = 'Post Analysis' # Image used on button to select frame, when frame is active. activeIcon = 'icons/Post_Analysis_ON.gif' # Image used on button to select frame, when frame is inactive. inactiveIcon = 'icons/Post_Analysis_OFF.gif' # Help text to be displayed when screen is active. helpText = ("Selecting coalitions, status quo and goals at the top left" "will generate a reachability tree below the status quo, and " "detail the patterns that preferences must match for the goal " "state to be reached.") # ######################## INITIALIZATION ################################ def __init__(self, master, conflict, *args): """Initialize the Frame. Does not build widgets.""" FrameTemplate.__init__(self, master, conflict, self.buttonLabel, self.activeIcon, self.inactiveIcon, self.helpText) self.lastBuildConflict = None # ############################ METHODS ################################### def hasRequiredData(self): """Check that minimum data required to render the frame exists.""" if len(self.conflict.decisionMakers) < 1: return False if len(self.conflict.options) < 1: return False if len(self.conflict.feasibles) < 1: return False if self.conflict.preferenceErrors: return False else: return True def dataChanged(self): """Check if data has changed since the last build of the Frame.""" if self.lastBuildConflict != self.conflict.export_rep(): return True else: return False def buildFrame(self): """Contruct frame widgets and initialize data.""" if self.built: return # Ensure all required parts of the conflict model are properly set-up. self.conflict.reorderOptionsByDM() self.conflict.options.set_indexes() self.conflict.infeasibles.validate() self.conflict.recalculateFeasibleStates() for dm in self.conflict.decisionMakers: dm.calculatePerceived() dm.calculatePreferences() self.lastBuildConflict = self.conflict.export_rep() # Define frame-specific variables self.sol = GoalSeeker(self.conflict) # infoFrame: frame and label definitions (with master 'self.infoFrame') self.infoLabel = ttk.Label(self.infoFrame, text="") # helpFrame: frame and label definitions (with master 'self.helpFrame') self.helpLabel = ttk.Label(self.helpFrame, textvariable=self.helpVar, wraplength=150) # Define frame-specific input widgets (with 'self' as master) self.paneMaster = PanedWindow(self, orient=HORIZONTAL, sashwidth=10, sashrelief="raised", sashpad=3, relief="sunken") self.paneLeft = PanedWindow(self.paneMaster, orient=VERTICAL, sashwidth=10, sashrelief="raised", sashpad=3, relief="sunken") self.paneLeftTop = ttk.Frame(self.paneLeft) self.coalitionSelector = CoalitionSelector(self.paneLeftTop, self.conflict, self) self.statusQuoAndGoals = StatusQuoAndGoals(self.paneLeftTop, self.conflict) self.reachableTree = ReachableTreeViewer(self.paneLeftTop, self.conflict, self) self.paneLeftBottom = ttk.Frame(self.paneLeft) self.optionFormTable = OptionFormTable(self.paneLeftBottom, self.conflict) self.paneRight = ttk.Frame(self.paneMaster) self.patternNarrator = PatternNarrator(self.paneRight, self.conflict, self) # ######## preliminary gridding and option configuration # configuring the input frame self.grid(column=0, row=0, rowspan=5, sticky=NSEW) self.grid_remove() self.columnconfigure(0, weight=1) self.rowconfigure(0, weight=1) # configuring infoFrame & infoFrame widgets self.infoFrame.grid(column=2, row=0, sticky=NSEW, padx=3, pady=3) self.infoFrame.grid_remove() self.infoLabel.grid(column=0, row=1, sticky=NSEW) # configuring helpFrame & helpFrame widgets self.helpFrame.grid(column=2, row=1, sticky=NSEW, padx=3, pady=3) self.helpFrame.grid_remove() self.helpLabel.grid(column=0, row=0, sticky=NSEW) # configuring frame-specific options self.paneMaster.grid(row=0, column=0, sticky=NSEW) self.paneMaster.add(self.paneLeft) self.paneLeft.add(self.paneLeftTop) self.paneLeftTop.columnconfigure(1, weight=1) self.paneLeftTop.rowconfigure(2, weight=1) self.coalitionSelector.grid(row=0, column=0, sticky=NSEW) ttk.Separator(self, orient=HORIZONTAL).grid(row=1, column=0, sticky=NSEW, pady=3) self.statusQuoAndGoals.grid(row=2, column=0, sticky=NSEW) self.reachableTree.grid(row=0, column=1, rowspan=3, sticky=NSEW) self.paneLeft.add(self.paneLeftBottom) self.paneLeftBottom.rowconfigure(0, weight=1) self.paneLeftBottom.columnconfigure(0, weight=1) self.optionFormTable.grid(row=0, column=0, sticky=NSEW) self.paneMaster.add(self.paneRight) self.paneRight.rowconfigure(0, weight=1) self.paneRight.columnconfigure(0, weight=1) self.patternNarrator.grid(row=0, column=0, sticky=NSEW) # bindings self.statusQuoAndGoals.bind("<<StatusQuoChanged>>", self.refresh) self.statusQuoAndGoals.bind("<<GoalChanged>>", self.refresh) self.coalitionSelector.bind("<<CoalitionsChanged>>", self.refresh) self.built = True def refresh(self): """Refresh data in all active display widgets.""" sq = self.statusQuoAndGoals.statusQuoSelector.current() goals = self.statusQuoAndGoals.getGoals() if len(goals) > 0: self.sol = GoalSeeker(self.conflict, goals) else: self.sol = GoalSeeker(self.conflict) self.reachableTree.buildTree(sq, watchFor=[x[0] for x in goals]) self.patternNarrator.updateNarration( goalInfo=self.reachableTree.goalInfo())
class PicasaView(): """ Interface to the view class """ def __init__(self, controller): self.controller = controller # function to call self.root = Tk() # create main window called root self.root.state('zoomed') # have root be full screen self.root.title('Picasa') # name the display self.root.protocol( 'WM_DELETE_WINDOW', lambda: self.controller('stop')) # Trap close window self.menu = PicasaMenu(self.root, controller) # create main menu self.root.config(menu=self.menu) # add menu to window self.TAB = PanedWindow(self.root, orient=VERTICAL) # Top And Bottom self.TAB.grid(sticky='nsew') self.LAR = PanedWindow(self.root, orient=HORIZONTAL) # Left And Right self.LAR.grid(sticky='nsew') self.TAB.add(self.LAR) self.tree = Tree(self.root, controller) # create tree self.LAR.add(self.tree) self.gallery = Gallery(self.root, controller) # create all pics self.LAR.add(self.gallery) self.status = Status(self.TAB, controller) # create status text self.TAB.add(self.status) self.TAB.config(sashrelief='raised') # make sash visible self.LAR.config(sashrelief='raised') self.root.grid_columnconfigure(0, weight=1) # make all resizeable self.root.grid_rowconfigure(0, weight=1) self.TAB.sash_place(0, 1, 1000) #self.TAB.sash('dragto', 0, 1, 1000) def start(self): """ start the main window """ self.root.mainloop() # display root def stop(self): """ quit Picasa and close down cleanly """ self.root.destroy() # Quit the application #self.root.quit() def addstatus(self, text): """ add a comment to the status log """ txt = "" for msg in text: txt += msg self.status.text.insert((1.0), txt + '\n') def clearstatus(self): """ clear the contents of the status area """ self.status.text.delete((1.0), 'end') def getdir(self): """ Get the user to choose a folder to watch """ return filedialog.askdirectory() def build(self, album): """ build tree structure and gallery from list of folders """ self.tree.build(album) self.gallery.build(album)
def main(fname): tstart = time.monotonic() #@+others #@+node:vitalije.20180518114840.1: *3* 1. load xml ltm = load_leo(fname) ltm.selectedPosition = ltm.data.positions[1] ltmbytes = ltm.to_bytes() #@+node:vitalije.20180518114847.1: *3* 2. create app app = Tk() app.columnconfigure(0, weight=1) app.rowconfigure(0, weight=1) #@+node:vitalije.20180518114857.1: *3* 3. adjust fonts font.nametofont('TkFixedFont').config(size=18) font.nametofont('TkTextFont').config(size=18) font.nametofont('TkDefaultFont').config(size=18) #@+node:vitalije.20180518114953.1: *3* 4. create gui f1 = PanedWindow(app, orient='horizontal', width=800, height=600, sashrelief='ridge', sashwidth=4) f1.grid( row=0, column=0, sticky="nesw", ) f2 = PanedWindow(f1, orient='vertical') canvW = Canvas(f2, bg='#113333') f2.add(canvW) logW = scrolledtext.ScrolledText(f2, bg='#223399', fg='#cccc99', font=font.nametofont('TkDefaultFont')) f2.add(logW) bodyW = makeBodyW(f1) f1.add(f2) f1.add(bodyW) #@+node:vitalije.20180518115000.1: *3* 5. f_later def f_later(): f1.sash_place(0, 270, 1) f2.sash_place(0, 1, 350) app.geometry("800x600+720+50") app.wm_title(fname) app.after_idle(update_model) #@+node:vitalije.20180518115003.1: *3* 6. loadex def loadex(): ltm2 = LeoTreeModel.from_bytes(ltmbytes) loaddir = os.path.dirname(fname) load_external_files(ltm2, loaddir) G.q.put(ltm2) #@+node:vitalije.20180518115010.1: *3* 7. update_model def update_model(): try: m = G.q.get(False) ltm.data = m.data draw_tree(G.tree, ltm) tend = time.monotonic() t1 = (tend - tstart) logW.insert('end', 'External files loaded in %.3fs\n' % t1) except queue.Empty: app.after(100, update_model) #@+node:vitalije.20180518115038.1: *3* 8. start loading thread threading.Thread(target=loadex, name='externals-loader').start() app.after_idle(f_later) #@-others return bunch(ltm=ltm, app=app, tree=canvW, body=bodyW, log=logW, q=queue.Queue(1), topIndex=IntVar(), search=SearchState(str_to_fa(''), ltm.data.positions[1], 0, 0, False))
font=('calibri', '11'), bg='#ffffff', relief=FLAT, bd=0) profiles_adjust_ROI_text.pack(side=TOP, padx=(0, 0)) global profiles_redefine_film_ROI_frame profiles_redefine_film_ROI_frame = tk.Frame(profiles_adjust_ROI_canvas) profiles_redefine_film_ROI_frame.pack(side=BOTTOM, padx=(0, 0)) profiles_redefine_film_ROI_frame.config(bg='#ffffff') global profiles_adjust_button_left global profiles_adjust_button_right global profiles_adjust_button_down global profiles_adjust_button_up global profiles_film_panedwindow profiles_film_panedwindow = \ PanedWindow(profiles_view_film_doseplan_ROI, orient='horizontal') profiles_film_panedwindow.grid(row=1, column=0, \ columnspan=3, rowspan=5, sticky=N+W) profiles_film_panedwindow.configure(sashrelief=RAISED, showhandle=True) global profiles_scanned_image_text_image global profiles_film_dose_map_text_image global profiles_doseplan_text_image global doseplan_write_image global film_write_image global doseplan_write_image_width global doseplan_write_image_height global doseplan_write_image_var_x doseplan_write_image_var_x = 0 global doseplan_write_image_var_y doseplan_write_image_var_y = 0 global profiles_coordinate_in_dataset
class StabilityFrame(FrameTemplate): """Frame used to for Stability Analysis.""" # Label used for button to select frame in the main program. buttonLabel = 'Post Analysis' # Image used on button to select frame, when frame is active. activeIcon = 'icons/Post_Analysis_ON.gif' # Image used on button to select frame, when frame is inactive. inactiveIcon = 'icons/Post_Analysis_OFF.gif' # Help text to be displayed when screen is active. helpText = ("Selecting coalitions, status quo and goals at the top left" "will generate a reachability tree below the status quo, and " "detail the patterns that preferences must match for the goal " "state to be reached.") # ######################## INITIALIZATION ################################ def __init__(self, master, conflict, *args): """Initialize the Frame. Does not build widgets.""" FrameTemplate.__init__(self, master, conflict, self.buttonLabel, self.activeIcon, self.inactiveIcon, self.helpText) self.lastBuildConflict = None # ############################ METHODS ################################### def hasRequiredData(self): """Check that minimum data required to render the frame exists.""" if len(self.conflict.decisionMakers) < 1: return False if len(self.conflict.options) < 1: return False if len(self.conflict.feasibles) < 1: return False if self.conflict.preferenceErrors: return False else: return True def dataChanged(self): """Check if data has changed since the last build of the Frame.""" if self.lastBuildConflict != self.conflict.export_rep(): return True else: return False def buildFrame(self): """Contruct frame widgets and initialize data.""" if self.built: return # Ensure all required parts of the conflict model are properly set-up. self.conflict.reorderOptionsByDM() self.conflict.options.set_indexes() self.conflict.infeasibles.validate() self.conflict.recalculateFeasibleStates() for dm in self.conflict.decisionMakers: dm.calculatePerceived() dm.calculatePreferences() self.lastBuildConflict = self.conflict.export_rep() # Define frame-specific variables self.sol = GoalSeeker(self.conflict) # infoFrame: frame and label definitions (with master 'self.infoFrame') self.infoLabel = ttk.Label(self.infoFrame, text="") # helpFrame: frame and label definitions (with master 'self.helpFrame') self.helpLabel = ttk.Label(self.helpFrame, textvariable=self.helpVar, wraplength=150) # Define frame-specific input widgets (with 'self' as master) self.paneMaster = PanedWindow(self, orient=HORIZONTAL, sashwidth=10, sashrelief="raised", sashpad=3, relief="sunken") self.paneLeft = PanedWindow(self.paneMaster, orient=VERTICAL, sashwidth=10, sashrelief="raised", sashpad=3, relief="sunken") self.paneLeftTop = ttk.Frame(self.paneLeft) self.coalitionSelector = CoalitionSelector(self.paneLeftTop, self.conflict, self) self.statusQuoAndGoals = StatusQuoAndGoals(self.paneLeftTop, self.conflict) self.reachableTree = ReachableTreeViewer(self.paneLeftTop, self.conflict, self) self.paneLeftBottom = ttk.Frame(self.paneLeft) self.optionFormTable = OptionFormTable(self.paneLeftBottom, self.conflict) self.paneRight = ttk.Frame(self.paneMaster) self.patternNarrator = PatternNarrator(self.paneRight, self.conflict, self) # ######## preliminary gridding and option configuration # configuring the input frame self.grid(column=0, row=0, rowspan=5, sticky=NSEW) self.grid_remove() self.columnconfigure(0, weight=1) self.rowconfigure(0, weight=1) # configuring infoFrame & infoFrame widgets self.infoFrame.grid(column=2, row=0, sticky=NSEW, padx=3, pady=3) self.infoFrame.grid_remove() self.infoLabel.grid(column=0, row=1, sticky=NSEW) # configuring helpFrame & helpFrame widgets self.helpFrame.grid(column=2, row=1, sticky=NSEW, padx=3, pady=3) self.helpFrame.grid_remove() self.helpLabel.grid(column=0, row=0, sticky=NSEW) # configuring frame-specific options self.paneMaster.grid(row=0, column=0, sticky=NSEW) self.paneMaster.add(self.paneLeft) self.paneLeft.add(self.paneLeftTop) self.paneLeftTop.columnconfigure(1, weight=1) self.paneLeftTop.rowconfigure(2, weight=1) self.coalitionSelector.grid(row=0, column=0, sticky=NSEW) ttk.Separator(self, orient=HORIZONTAL).grid(row=1, column=0, sticky=NSEW, pady=3) self.statusQuoAndGoals.grid(row=2, column=0, sticky=NSEW) self.reachableTree.grid(row=0, column=1, rowspan=3, sticky=NSEW) self.paneLeft.add(self.paneLeftBottom) self.paneLeftBottom.rowconfigure(0, weight=1) self.paneLeftBottom.columnconfigure(0, weight=1) self.optionFormTable.grid(row=0, column=0, sticky=NSEW) self.paneMaster.add(self.paneRight) self.paneRight.rowconfigure(0, weight=1) self.paneRight.columnconfigure(0, weight=1) self.patternNarrator.grid(row=0, column=0, sticky=NSEW) # bindings self.statusQuoAndGoals.bind("<<StatusQuoChanged>>", self.refresh) self.statusQuoAndGoals.bind("<<GoalChanged>>", self.refresh) self.coalitionSelector.bind("<<CoalitionsChanged>>", self.refresh) self.built = True def refresh(self, *args): """Refresh data in all active display widgets.""" sq = self.statusQuoAndGoals.statusQuoSelector.current() goals = self.statusQuoAndGoals.getGoals() if len(goals) > 0: self.sol = GoalSeeker(self.conflict, goals) else: self.sol = GoalSeeker(self.conflict) self.reachableTree.buildTree(sq, watchFor=[x[0] for x in goals]) self.patternNarrator.updateNarration( goalInfo=self.reachableTree.goalInfo())
def BuildMainFrame(self): from tkinter import Menu, IntVar, StringVar, Toplevel, Listbox, Frame, PanedWindow, Text, Scrollbar, Entry from tkinter import X, N, S, W, E, VERTICAL, TOP, END, DISABLED, RAISED menu = Menu(self.master,activeborderwidth=0,bd=0) self.master.config(menu=menu) filemenu = Menu(menu,tearoff=0,bd=1,activeborderwidth=0) menu.add_cascade(label="File", underline=0, menu=filemenu) filemenu.add_command(label="New", accelerator='Ctrl+N',command=self.NewCommand) filemenu.add_command(label="Open...",accelerator='Ctrl+O', command=self.OpenCommand) filemenu.add_command(label="Save as...",accelerator='Ctrl+S', command=self.SaveCommand) filemenu.add_separator() filemenu.add_command(label="Quit",accelerator='Ctrl+Q', command=self.QuitCommand) self.log_on = IntVar() self.log_on.set(1) self.output_to_file = StringVar() self.output_to_file.set('n') scriptmenu = Menu(menu,tearoff=0,bd=1,activeborderwidth=0) modulenames = ['vmtkscripts'] for modulename in modulenames: scriptsubmenu = self.BuildScriptMenu(menu,modulename) if scriptsubmenu: scriptmenu.add_cascade(label=modulename,menu=scriptsubmenu) editmenu = Menu(menu,tearoff=0,bd=1,activeborderwidth=0) menu.add_cascade(label="Edit",underline=0, menu=editmenu) editmenu.add_cascade(label="Insert script",menu=scriptmenu) editmenu.add_command(label="Insert file name", accelerator='Ctrl+F',command=self.InsertFileName) editmenu.add_separator() editmenu.add_command(label="Clear input", command=self.ClearInputCommand) editmenu.add_command(label="Clear output", command=self.ClearOutputCommand) editmenu.add_command(label="Clear all", command=self.ClearAllCommand) editmenu.add_separator() editmenu.add_checkbutton(label="Log", variable=self.log_on) editmenu.add_separator() editmenu.add_radiobutton(label="No output to file", variable=self.output_to_file,value='n') editmenu.add_radiobutton(label="Write output to file", variable=self.output_to_file,value='w') editmenu.add_radiobutton(label="Append output to file", variable=self.output_to_file,value='a') editmenu.add_command(label="Output file...", command=self.OutputFileCommand) runmenu = Menu(menu,tearoff=0,bd=1,activeborderwidth=0) menu.add_cascade(label="Run", underline=0, menu=runmenu) runmenu.add_command(label="Run all", command=self.RunAllCommand) runmenu.add_command(label="Run current line", command=self.RunLineCommand) runmenu.add_command(label="Run selection", command=self.RunSelectionCommand) helpmenu = Menu(menu,tearoff=0,bd=1,activeborderwidth=0) menu.add_cascade(label="Help", underline=0, menu=helpmenu) helpmenu.add_command(label="Help", underline=0, accelerator='F1',command=self.ShowHelpCommand) helpmenu.add_command(label="About", underline=0, command=self.AboutCommand) self.master.bind("<Control-KeyPress-q>", self.QuitHandler) self.master.bind("<Control-KeyPress-n>", self.NewHandler) self.master.bind("<Control-KeyPress-o>", self.OpenHandler) self.master.bind("<Control-KeyPress-s>", self.SaveHandler) self.master.bind("<Control-KeyPress-f>", self.InsertFileNameHandler) self.master.bind("<KeyPress-F1>", self.ShowHelpHandler) self.master.bind("<KeyPress>", self.KeyPressHandler) self.wordIndex = ['1.0','1.0'] self.suggestionswindow = Toplevel(bg='#ffffff',bd=0,height=50,width=600,highlightthickness=0,takefocus=True) self.suggestionswindow.overrideredirect(1) self.suggestionslist = Listbox(self.suggestionswindow,bg='#ffffff',bd=1,fg='#336699',activestyle='none',highlightthickness=0,height=9) self.suggestionslist.insert(END,"foo") self.suggestionslist.pack(side=TOP,fill=X) self.suggestionswindow.bind("<KeyPress>", self.TopKeyPressHandler) self.suggestionswindow.withdraw() self.master.rowconfigure(0,weight=1) self.master.columnconfigure(0,weight=1) content = Frame(self.master,bd=0,padx=2,pady=2) content.grid(row=0,column=0,sticky=N+S+W+E) content.rowconfigure(0,weight=1,minsize=50) content.rowconfigure(1,weight=0) content.columnconfigure(0,weight=1) panes = PanedWindow(content,orient=VERTICAL,bd=1,sashwidth=8,sashpad=0,sashrelief=RAISED,showhandle=True) panes.grid(row=0,column=0,sticky=N+S+W+E) frame1 = Frame(panes,bd=0) frame1.grid(row=0,column=0,sticky=N+S+W+E) frame1.columnconfigure(0,weight=1) frame1.columnconfigure(1,weight=0) frame1.rowconfigure(0,weight=1) panes.add(frame1,height=300,minsize=20) frame2 = Frame(panes,bd=0) frame2.grid(row=1,column=0,sticky=N+S+W+E) frame2.columnconfigure(0,weight=1) frame2.columnconfigure(1,weight=0) frame2.rowconfigure(0,weight=1) panes.add(frame2,minsize=20) self.text_input = Text(frame1, bg='#ffffff',bd=1,highlightthickness=0) self.text_input.bind("<KeyPress>", self.KeyPressHandler) self.text_input.bind("<Button-3>", self.PopupHandler) self.text_input.bind("<Control-Return>", self.RunKeyboardHandler) self.input_scrollbar = Scrollbar(frame1,orient=VERTICAL,command=self.text_input.yview) self.text_input["yscrollcommand"] = self.input_scrollbar.set self.text_output = Text(frame2,state=DISABLED,bd=1,bg='#ffffff',highlightthickness=0) self.output_scrollbar = Scrollbar(frame2,orient=VERTICAL,command=self.text_output.yview) self.text_output["yscrollcommand"] = self.output_scrollbar.set self.text_entry = Entry(content,bd=1,bg='#ffffff',state=DISABLED,highlightthickness=0) self.text_input.focus_set() self.text_input.grid(row=0,column=0,sticky=N+S+W+E) self.input_scrollbar.grid(row=0,column=1,sticky=N+S+W+E) self.text_output.grid(row=0,column=0,sticky=N+S+W+E) self.output_scrollbar.grid(row=0,column=1,sticky=N+S+W+E) self.text_entry.grid(row=1,column=0,sticky=N+S+W+E) self.popupmenu = Menu(self.text_input, tearoff=1, bd=0) self.popupmenu.add_command(label="Context help", command=self.ShowHelpCommand) self.popupmenu.add_cascade(label="Insert script",menu=scriptmenu) self.popupmenu.add_command(label="Insert file name...", command=self.InsertFileName) self.popupmenu.add_separator() self.popupmenu.add_command(label="Run all", command=self.RunAllCommand) self.popupmenu.add_command(label="Run current line", command=self.RunLineCommand) self.popupmenu.add_command(label="Run selection", command=self.RunSelectionCommand) self.output_stream = TkPadOutputStream(self.text_output) self.input_stream = TkPadInputStream(self.text_entry,self.output_stream)
class PreferencesFrame(FrameTemplate): """Used to set decision maker's preferences for use in prioritization.""" # Label used for button to select frame in the main program. buttonLabel = 'Prioritization' # Image used on button to select frame, when frame is active. activeIcon = 'icons/Prioritization_ON.gif' # Image used on button to select frame, when frame is inactive. inactiveIcon = 'icons/Prioritization_OFF.gif' # Help text to be displayed when screen is active. helpText = ("Select a decision maker from the column at left by clicking " "its 'Edit' button. Enter preferred conditions using the " "inputs to the right. Preferred conditions for the selected " "decision maker are shown at the far right, from most " "important at the top, to least important at the bottom.") # ######################## INITIALIZATION ################################ def __init__(self, master, conflict, *args): """Initialize the Frame. Does not build widgets.""" FrameTemplate.__init__(self, master, conflict, self.buttonLabel, self.activeIcon, self.inactiveIcon, self.helpText) self.lastBuildDMs = None self.lastBuildOptions = None self.lastBuildInfeasibles = None self.lastBuildRanking = None # ############################ METHODS ################################### def hasRequiredData(self): """Check that minimum data required to render the frame exists.""" if len(self.conflict.decisionMakers) < 1: return False if len(self.conflict.options) < 1: return False if len(self.conflict.feasibles) < 1: self.conflict.recalculateFeasibleStates() if len(self.conflict.feasibles) < 1: return False else: return True def dataChanged(self): """Check if data has changed since the last build of the Frame.""" if self.lastBuildDMs != self.conflict.decisionMakers.export_rep(): return True if self.lastBuildOptions != self.conflict.options.export_rep(): return True if self.lastBuildInfeasibles != self.conflict.infeasibles.export_rep(): return True if self.lastBuildRanking != self.conflict.useManualPreferenceRanking: return True else: return False def buildFrame(self): """Contruct frame widgets and initialize data.""" if self.built: return # Ensure all required parts of the conflict model are properly set-up. self.conflict.reorderOptionsByDM() self.conflict.options.set_indexes() self.conflict.infeasibles.validate() self.conflict.recalculateFeasibleStates() for dm in self.conflict.decisionMakers: dm.calculatePerceived() dm.calculatePreferences() self.lastBuildDMs = self.conflict.decisionMakers.export_rep() self.lastBuildOptions = self.conflict.options.export_rep() self.lastBuildInfeasibles = self.conflict.infeasibles.export_rep() self.lastBuildRanking = self.conflict.useManualPreferenceRanking # Define variables that will display in the infoFrame numD = len(self.conflict.decisionMakers) self.infoText = StringVar( value='Valid Preferences set for {0}/{0} DMs.'.format(numD)) # Define frame-specific variables self.dm = None # infoFrame: frame and label definitions (with master 'self.infoFrame') self.infoLabel = ttk.Label(self.infoFrame, textvariable=self.infoText) # helpFrame: frame and label definitions (with master 'self.helpFrame') self.helpLabel = ttk.Label(self.helpFrame, textvariable=self.helpVar, wraplength=150) # Define frame-specific input widgets (with 'self' as master) self.paneMaster = PanedWindow(self, orient=VERTICAL, sashwidth=5, sashrelief="raised", sashpad=2, relief="sunken") self.paneTop = ttk.Frame(self.paneMaster) self.rankings = PreferenceRankingMaster(self.paneTop, self.conflict) self.editor = RadiobuttonEntry(self.paneTop, self.conflict) self.paneTopRightMaster = PanedWindow(self.paneTop, orient=HORIZONTAL, sashwidth=5, sashrelief="raised", sashpad=2, relief="sunken") self.staging = PreferenceStaging(self.paneTopRightMaster, self.conflict) self.preferenceDisp = PreferenceListDisplay(self.paneTopRightMaster, self.conflict) self.paneBottom = ttk.Frame(self.paneMaster) self.optionTable = OptionFormTable(self.paneBottom, self.conflict) self.usePrioritizationButton = ttk.Button( self, text=("Use preference prioritization. Any manually set " "preference rankings will be lost."), command=self.usePrioritization) # ######## preliminary gridding and option configuration # configuring the input frame self.grid(column=0, row=0, rowspan=5, sticky=NSEW) self.columnconfigure(0, weight=1) self.rowconfigure(1, weight=1) self.grid_remove() # configuring infoFrame & infoFrame widgets self.infoFrame.grid(column=2, row=0, sticky=NSEW, padx=3, pady=3) self.infoFrame.grid_remove() self.infoLabel.grid(column=0, row=1, sticky=NSEW) # configuring helpFrame & helpFrame widgets self.helpFrame.grid(column=2, row=1, sticky=NSEW, padx=3, pady=3) self.helpFrame.grid_remove() self.helpLabel.grid(column=0, row=0, sticky=NSEW) # configuring frame-specific options self.paneMaster.grid(column=0, row=1, sticky=NSEW) self.paneMaster.add(self.paneTop, minsize=200) self.rankings.grid(column=0, row=1, sticky=NSEW) ttk.Separator(self.paneTop, orient=VERTICAL).grid( column=1, row=1, sticky=NSEW, padx=3) self.editor.grid(column=2, row=1, sticky=NSEW) ttk.Separator(self.paneTop, orient=VERTICAL).grid( column=3, row=1, sticky=NSEW, padx=3) self.paneTopRightMaster.grid(column=4, row=1, sticky=NSEW) self.paneTop.columnconfigure(0, weight=0) self.paneTop.columnconfigure(2, weight=0) self.paneTop.columnconfigure(4, weight=1) self.paneTop.rowconfigure(1, weight=1) self.usePrioritizationButton.grid(column=0, row=0, columnspan=5, sticky=NSEW) self.paneTopRightMaster.add(self.staging) self.paneTopRightMaster.add(self.preferenceDisp) self.paneMaster.add(self.paneBottom) self.optionTable.grid(column=0, row=0, sticky=NSEW) self.paneBottom.columnconfigure(0, weight=1) self.paneBottom.rowconfigure(0, weight=1) # bindings self.rankings.bind('<<DMchg>>', self.dmChgHandler) self.editor.bind('<<AddPref>>', self.addPref) self.editor.bind('<<StagePref>>', self.stagePref) self.staging.bind('<<SelCond>>', self.selCondChg) self.staging.bind('<<PullFromStage>>', self.pullFromStage) self.preferenceDisp.bind('<<SelPref>>', self.selPrefChg) self.preferenceDisp.bind('<<ValueChange>>', self.refresh) self.dmChgHandler() self.built = True def enter(self, *args): """Re-grid the screen into the master. Perform required updates.""" if self.dataChanged(): self.clearFrame() FrameTemplate.enter(self) def refresh(self, *args): """Refresh data in all active display widgets.""" for dm in self.conflict.decisionMakers: dm.calculatePreferences() self.editor.reloadOpts() self.rankings.refresh() self.preferenceDisp.refresh() self.optionTable.buildTable(self.dm) self.checkIfUsingRankings() def checkIfUsingRankings(self, event=None): """Disable screen if Manual Ranking has been assigned.""" if self.conflict.useManualPreferenceRanking: self.usePrioritizationButton.grid() self.rankings.disable() self.editor.disable() self.staging.disable() self.preferenceDisp.disable() else: self.usePrioritizationButton.grid_remove() def usePrioritization(self): """Reactivate the screen if user decides to use prioritization.""" self.conflict.useManualPreferenceRanking = False self.conflict.preferenceErrors = None self.refresh() def dmChgHandler(self, event=None): """Bound to <<DMchg>>.""" self.dm = self.rankings.dm self.preferenceDisp.changeDM(self.dm) self.optionTable.buildTable(self.dm) self.staging.clear() self.editor.setStates('clear') if self.dm is None: self.preferenceDisp.disable() self.editor.disable() self.staging.disable() else: self.preferenceDisp.enable() self.editor.enable() def addPref(self, event=None): """Add a preference for the active decision maker.""" pref = self.editor.getStates() self.dm.preferences.append(pref) self.refresh() def stagePref(self, event=None): """Send a condition to the staging area.""" if self.editor.hasValidIf: for cond in self.editor.ifCond: self.staging.addCondition(cond) else: condData = self.editor.getStates() newCond = self.conflict.newCondition(condData) self.staging.addCondition(newCond) self.editor.setStates('clear') def pullFromStage(self, event=None): """Move a compound condition from Staging to Preferences.""" newPref = self.staging.conditionList self.staging.clear() self.dm.preferences.append(newPref) self.refresh() def selCondChg(self, event=None): """Triggered when a condition is selected in staging.""" condition = self.staging.conditionList[event.x] self.editor.setStates(condition.ynd()) def selPrefChg(self, event=None): """Triggered when a preference is select from preferences.""" condition = self.dm.preferences[event.x] self.staging.setList(condition)
class CopyToMoveTo: """ Minimalist file manager intended to be used independently or alongside Windows Explorer """ def __init__(self, root): """ Setup window geometry, init settings, define widgets + layout """ self.master = root self.master.title("CopyTo-MoveTo") self.master.iconbitmap(f"{dirname(__file__)}/icon.ico") if system() != "Windows": self.master.withdraw() messagebox.showwarning( "Incompatible platform", "CopyTo-MoveTo currently supports Windows platforms only.") raise SystemExit #Settings: self.settings_show_hidden = BooleanVar() self.settings_include_files = BooleanVar(value=True) self.settings_ask_overwrite = BooleanVar() self.settings_ask_overwrite.trace("w", self.settings_exclusives) self.settings_rename_dupes = BooleanVar(value=True) self.settings_rename_dupes.trace("w", self.settings_exclusives) self.settings_multiselect = BooleanVar(value=True) self.settings_select_dirs = BooleanVar(value=True) self.settings_select_dirs.trace("w", self.settings_mutuals) self.settings_select_files = BooleanVar(value=True) self.settings_select_files.trace("w", self.settings_mutuals) self.settings_geometry = None self.appdata_dir = getenv("APPDATA") + "/CopyTo-MoveTo" self.appdata_path = self.appdata_dir + "/settings.json" self.settings = self.init_settings() if self.settings: self.settings_geometry = self.settings["geometry"] self.settings_show_hidden.set(self.settings["show_hidden"]) self.settings_include_files.set(self.settings["include_files"]) self.settings_ask_overwrite.set(self.settings["ask_overwrite"]) self.settings_rename_dupes.set(self.settings["rename_dupes"]) self.settings_multiselect.set(self.settings["multiselect"]) self.settings_select_dirs.set(self.settings["select_dirs"]) self.settings_select_files.set(self.settings["select_files"]) self.dialog_showing = BooleanVar() self.help_showing = BooleanVar() self.about_showing = BooleanVar() self.master.protocol("WM_DELETE_WINDOW", self.master_close) self.master.bind("<Control-w>", self.master_close) #Geometry: self.master.minsize(width=450, height=200) if self.settings_geometry: self.master.geometry(self.settings_geometry) self.master.update() else: self.master.geometry("600x400") self.master.update_idletasks() (width_offset, height_offset) = Ufd.get_offset(self.master) self.master.geometry(f"+{width_offset}+{height_offset}") self.master.update_idletasks() # Menu: self.main_menu = Menu(self.master) self.master.config(menu=self.main_menu) self.file_menu = Menu(self.main_menu, tearoff=0) self.settings_menu = Menu(self.main_menu, tearoff=0) self.main_menu.add_cascade(label="File", menu=self.file_menu) self.main_menu.add_cascade(label="Settings", menu=self.settings_menu) self.file_menu.add_command(label="Open Source(s)", accelerator="Ctrl+O", command=lambda: self.show_ufd(source=True)) self.master.bind("<Control-o>", lambda event: self.show_ufd(source=True)) self.file_menu.add_command(label="Open Destination(s)", accelerator="Ctrl+K+O", command=lambda: self.show_ufd(source=False)) self.master.bind("<Control-k>o", lambda event: self.show_ufd(source=False)) self.file_menu.add_separator() self.file_menu.add_command(label="Help / Commands", command=self.show_help) self.file_menu.add_command(label="About", command=self.show_about) #Settings menu: self.settings_menu.add_checkbutton(label="Show Hidden Files & Folders", variable=self.settings_show_hidden, onvalue=True, offvalue=False) self.settings_menu.add_checkbutton( label="Include Files in Tree", variable=self.settings_include_files, onvalue=True, offvalue=False) self.settings_menu.add_separator() self.settings_menu.add_checkbutton( label="Ask Overwrite", variable=self.settings_ask_overwrite, onvalue=True, offvalue=False) self.settings_menu.add_checkbutton(label="Rename Duplicates", variable=self.settings_rename_dupes, onvalue=True, offvalue=False) self.settings_menu.add_separator() self.settings_menu.add_checkbutton(label="Multiselect", variable=self.settings_multiselect, onvalue=True, offvalue=False) self.settings_menu.add_checkbutton(label="Select Folders", variable=self.settings_select_dirs, onvalue=True, offvalue=False) self.settings_menu.add_checkbutton(label="Select Files", variable=self.settings_select_files, onvalue=True, offvalue=False) self.main_menu.add_separator() #Menu commands: self.main_menu.add_command(label="Swap Selected", command=self.swap_selected) self.master.bind("<Control-s>", lambda event: self.swap_selected()) self.main_menu.add_command(label="Clear Selected", command=self.clear_selected) self.master.bind("<Control-x>", lambda event: self.clear_selected()) self.main_menu.add_command(label="Clear All", command=self.clear_all) self.master.bind("<Control-Shift-X>", lambda event: self.clear_all()) self.main_menu.add_separator() self.main_menu.add_command(label="COPY", command=lambda: self._submit(copy=True)) self.master.bind("<Control-Shift-Return>", lambda event: self._submit(copy=True)) self.main_menu.add_command(label="MOVE", command=lambda: self._submit(copy=False)) self.master.bind("<Control-Return>", lambda event: self._submit(copy=False)) # Body: self.paneview = PanedWindow(self.master, sashwidth=7, bg="#cccccc", bd=0, orient="vertical") self.top_pane = PanedWindow(self.paneview) self.bottom_pane = PanedWindow(self.paneview) self.paneview.add(self.top_pane) self.paneview.add(self.bottom_pane) self.label_source = Label(self.top_pane, text="Source(s):") self.label_dest = Label(self.bottom_pane, text="Destination(s):") self.y_scrollbar_source = Scrollbar(self.top_pane, orient="vertical") self.x_scrollbar_source = Scrollbar(self.top_pane, orient="horizontal") self.y_scrollbar_dest = Scrollbar(self.bottom_pane, orient="vertical") self.x_scrollbar_dest = Scrollbar(self.bottom_pane, orient="horizontal") self.list_box_source = Listbox( self.top_pane, selectmode="extended", yscrollcommand=self.y_scrollbar_source.set, xscrollcommand=self.x_scrollbar_source.set) self.list_box_dest = Listbox(self.bottom_pane, selectmode="extended", yscrollcommand=self.y_scrollbar_dest.set, xscrollcommand=self.x_scrollbar_dest.set) self.x_scrollbar_source.config(command=self.list_box_source.xview) self.y_scrollbar_source.config(command=self.list_box_source.yview) self.x_scrollbar_dest.config(command=self.list_box_dest.xview) self.y_scrollbar_dest.config(command=self.list_box_dest.yview) # Layout: self.master.rowconfigure(0, weight=1) self.master.columnconfigure(0, weight=1) self.top_pane.rowconfigure(1, weight=1) self.top_pane.columnconfigure(0, weight=1) self.bottom_pane.rowconfigure(1, weight=1) self.bottom_pane.columnconfigure(0, weight=1) self.paneview.paneconfigure(self.top_pane, minsize=100) self.paneview.paneconfigure(self.bottom_pane, minsize=100) self.paneview.grid(row=0, column=0, sticky="nsew") self.label_source.grid(row=0, column=0, sticky="w") self.list_box_source.grid(row=1, column=0, sticky="nsew") self.y_scrollbar_source.grid(row=1, column=1, sticky="ns") self.x_scrollbar_source.grid(row=2, column=0, sticky="ew") self.label_dest.grid(row=0, column=0, sticky="w", columnspan=2) self.list_box_dest.grid(row=1, column=0, sticky="nsew") self.y_scrollbar_dest.grid(row=1, column=1, sticky="ns") self.x_scrollbar_dest.grid(row=2, column=0, sticky="ew") def __str__(self): """Return own address""" return f"CopyTo-MoveTo @ {hex(id(self))}" def init_settings(self): """Called on startup, loads, parses, and returns json settings.""" if exists(self.appdata_path): with open(self.appdata_path, "r") as settings_file: settings_json = settings_file.read() settings = loads(settings_json) return settings else: return None def settings_exclusives(self, *args): """ Callback assigned to settings that are mutually exclusive, to prevent logical/runtime errors or unexpected behavior. """ if args[0] == "PY_VAR2": if self.settings_ask_overwrite.get() == 1: self.settings_rename_dupes.set(0) return elif args[0] == "PY_VAR3": if self.settings_rename_dupes.get() == 1: self.settings_ask_overwrite.set(0) return def settings_mutuals(self, *args): """ Prevent select folders & select files from being disabled concurrently If both are unselected, reselect the one we didn't just deselect on. """ if self.settings_select_dirs.get() == 0 \ and self.settings_select_files.get() == 0: if args[0] == "PY_VAR5": self.settings_select_files.set(1) elif args[0] == "PY_VAR6": self.settings_select_dirs.set(1) def master_close(self, event=None): """ Similar to utils.toplevel_close(). writes settings to the disk as json. """ settings = { "geometry": self.master.geometry(), "show_hidden": self.settings_show_hidden.get(), "include_files": self.settings_include_files.get(), "ask_overwrite": self.settings_ask_overwrite.get(), "rename_dupes": self.settings_rename_dupes.get(), "multiselect": self.settings_multiselect.get(), "select_dirs": self.settings_select_dirs.get(), "select_files": self.settings_select_files.get(), } settings_json = dumps(settings) if not exists(self.appdata_dir): mkdir(self.appdata_dir) with open(self.appdata_path, "w+") as settings_file: settings_file.write(settings_json) if self.dialog_showing.get() == 1: self.ufd.cancel() self.master.destroy() def toplevel_close(self, dialog, boolean): """ This callback flips the value for a given toplevel_showing boolean to false, before disposing of the toplevel. """ boolean.set(0) dialog.destroy() def swap_selected(self): """Swap list entries between source & destination""" source_selection = list(self.list_box_source.curselection()) dest_selection = list(self.list_box_dest.curselection()) for i in reversed(source_selection): item = self.list_box_source.get(i) self.list_box_source.delete(i) self.list_box_dest.insert("0", item) for i in reversed(dest_selection): item = self.list_box_dest.get(i) self.list_box_dest.delete(i) self.list_box_source.insert("0", item) def clear_selected(self): """Removes selected (highlighted) item(s) from a given listbox""" source_selection = list(self.list_box_source.curselection()) dest_selection = list(self.list_box_dest.curselection()) if source_selection: for i in reversed(source_selection): self.list_box_source.delete(i) self.list_box_source.selection_set(source_selection[0]) if dest_selection: for i in reversed(dest_selection): self.list_box_dest.delete(i) self.list_box_dest.selection_set(dest_selection[0]) def clear_all(self): """Clears both listboxes in the main UI, resetting the form.""" self.list_box_source.delete(0, "end") self.list_box_dest.delete(0, "end") def handled(fn): """Filesystem operations are wrapped here for error handling""" @wraps(fn) def inner(self, *args, **kwargs): try: fn(self, *args, **kwargs) return True except (PermissionError, FileNotFoundError) as err: self.skipped_err.append(f"{err.args[1]}:\n" + (" => ".join(args))) return False return inner @handled def _copy(self, path, destination): """Wrapper for shutil.copy2() || shutil.copytree()""" if isfile(path): copy2(path, destination) else: copytree(path, destination) @handled def _move(self, path, destination): """Wrapper for shutil.move()""" move(path, destination) @handled def _delete(self, path): """Wrapper for os.remove() || shutil.rmtree()""" if isfile(path): remove(path) elif isdir(path): rmtree(path) def disabled_ui(fn): """Menubar is disabled during operations""" @wraps(fn) def inner(self, *args, **kwargs): self.main_menu.entryconfig("File", state="disabled") self.main_menu.entryconfig("Settings", state="disabled") self.main_menu.entryconfig("Clear Selected", state="disabled") self.main_menu.entryconfig("Clear All", state="disabled") self.main_menu.entryconfig("COPY", state="disabled") self.main_menu.entryconfig("MOVE", state="disabled") fn(self, *args, **kwargs) self.main_menu.entryconfig("File", state="normal") self.main_menu.entryconfig("Settings", state="normal") self.main_menu.entryconfig("Clear Selected", state="normal") self.main_menu.entryconfig("Clear All", state="normal") self.main_menu.entryconfig("COPY", state="normal") self.main_menu.entryconfig("MOVE", state="normal") return inner def _submit(self, copy): """Thread/wrapper for submit() so we don't block the UI during operations""" self.thread = Thread(target=self.submit, args=(copy, ), daemon=True) self.thread.start() @disabled_ui def submit(self, copy): """ Move or copy each item in the origin list to the path in the destination list. Supports no more than one destination directory where copy == False. Ask Overwrite and Rename Dupes will alter the way we handle existing data standing in the way. By default, duplicates are renamed with an index. A messagebox can complain to the user if shutil raises a PermissionError, and the operation is skipped. """ if (self.list_box_dest.size() > 1) and not copy: messagebox.showwarning( "Invalid Operation", "Move operation only supports a single destination directory.") return sources = self.list_box_source.get(0, "end") destinations = self.list_box_dest.get(0, "end") self.skipped_err = [] for j, destination in enumerate(destinations): if isfile(destination): self.skipped_err.append(f"Invalid destination: {destination}") continue for i, source in enumerate(sources): self.progress(i, j) (_, filename) = split(source) future_destination = join(destination + sep, filename) if exists(future_destination): if not self.settings_ask_overwrite.get() \ and not self.settings_rename_dupes.get(): if not self._delete(future_destination): continue if self.settings_ask_overwrite.get(): if self.ask_overwrite(future_destination): if not self._delete(future_destination): continue else: continue if self.settings_rename_dupes.get(): future_destination = self.name_dupe(future_destination) if copy: if not self._copy(source, future_destination): continue else: if not self._move(source, future_destination): continue self.list_box_source.delete(0, "end") self.list_box_dest.delete(0, "end") if self.skipped_err: messagebox.showerror(title="Error(s)", message="\n\n".join(self.skipped_err)) @staticmethod def name_dupe(path): """ Renames the file or directory until it doesn't exist in the destination with that name anymore, by appending the filename with an index wrapped in parenthesis. (Windows platforms) file.txt => file (1).txt => file (2).txt """ if system() != "Windows": raise OSError("For use with Windows filesystems.") path_ = path (root, filename) = split(path_) if isdir(path_): title = filename ext = None else: (title, ext) = splitext(filename) filecount = 0 while exists(path_): filecount += 1 new_title = title + " (" + str(filecount) + ")" if ext: new_title = new_title + ext path_ = join(root, new_title) return path_ def ask_overwrite(self, future_destination): """Messagebox result returned as truth value""" return messagebox.askyesno( title="Path Conflict", message=f"Overwrite:\n\n{future_destination}?\n\n" \ f"YES - Overwrite\nNO - Skip" ) def progress(self, i, j): """ Visualize operands in GUI during operations i = current source operand index j = current destination operand index """ for y, _ in enumerate(self.list_box_source.get(0, "end")): if y != i: self.list_box_source.itemconfigure(y, bg="#FFFFFF", fg="#000000") else: self.list_box_source.itemconfigure(y, bg="#cccccc", fg="#000000") for x, _ in enumerate(self.list_box_dest.get(0, "end")): if x != j: self.list_box_dest.itemconfigure(x, bg="#FFFFFF", fg="#000000") else: self.list_box_dest.itemconfigure(x, bg="#cccccc", fg="#000000") self.master.update() #Toplevels: def show_about(self): """ Displays a static dialog that doesn't allow additional instances of itself to be created while showing. """ if self.about_showing.get() == 0: self.about_showing.set(1) try: with open(f"{dirname(__file__)}/about.txt", "r") as aboutfile: about_info = aboutfile.read() except FileNotFoundError: messagebox.showerror("Error", "File not found") self.about_showing.set(0) return else: self.about = Toplevel() self.about.title("About") self.about.iconbitmap(f"{dirname(__file__)}/icon.ico") self.about.geometry("600x400") self.about.resizable(0, 0) self.about.update_idletasks() (width_offset, height_offset) = Ufd.get_offset(self.about) self.about.geometry(f"+{width_offset-75}+{height_offset-75}") self.about.update_idletasks() self.about_message = Label( self.about, text=about_info, justify="left", wraplength=(self.about.winfo_width() - 25)) self.about_message.grid(sticky="nsew") self.about.protocol( "WM_DELETE_WINDOW", lambda: self.toplevel_close( self.about, self.about_showing)) def show_help(self): """ Displays a scrollable dialog that doesn't allow additional instances of itself to be created while showing. """ if self.help_showing.get() == 0: self.help_showing.set(1) try: with open(f"{dirname(__file__)}/help.txt", "r") as helpfile: help_info = helpfile.read() except FileNotFoundError: messagebox.showerror("Error", "File not found") self.help_showing.set(0) return else: self.help_window = Toplevel() self.help_window.title("Help") self.help_window.iconbitmap(f"{dirname(__file__)}/icon.ico") self.help_window.geometry("500x300") self.help_window.update_idletasks() (width_offset, height_offset) = Ufd.get_offset(self.help_window) self.help_window.geometry( f"+{width_offset+75}+{height_offset-75}") self.help_window.update_idletasks() self.message_y_scrollbar = Scrollbar(self.help_window, orient="vertical") self.help_text = Text( self.help_window, wrap="word", yscrollcommand=self.message_y_scrollbar.set) self.help_window.rowconfigure(0, weight=1) self.help_window.columnconfigure(0, weight=1) self.help_text.grid(row=0, column=0, sticky="nsew") self.message_y_scrollbar.grid(row=0, column=1, sticky="nse") self.message_y_scrollbar.config(command=self.help_text.yview) self.help_text.insert("end", help_info) self.help_text.config(state="disabled") self.help_window.protocol( "WM_DELETE_WINDOW", lambda: self.toplevel_close( self.help_window, self.help_showing)) def show_ufd(self, source=True): """ Display Ufd w/ appropriate kwargs => Populate GUI w/ result""" if self.dialog_showing.get() == 0: self.dialog_showing.set(1) self.ufd = Ufd(title="Add Items", show_hidden=self.settings_show_hidden.get(), include_files=self.settings_include_files.get(), multiselect=self.settings_multiselect.get(), select_dirs=self.settings_select_dirs.get(), select_files=self.settings_select_files.get(), unix_delimiter=False, stdout=False) for result in self.ufd(): if source: self.list_box_source.insert("end", result) else: self.list_box_dest.insert("end", result) self.dialog_showing.set(0)
class ResultFrame(FrameTemplate): """Frame for display of equilibria in the conflict.""" # Label used for button to select frame in the main program. buttonLabel = 'Equilibria Results' # Image used on button to select frame, when frame is active. activeIcon = 'icons/Equilibria_Results_ON.gif' # Image used on button to select frame, when frame is inactive. inactiveIcon = 'icons/Equilibria_Results_OFF.gif' # Help text to be displayed when screen is active. helpText = ("The stability of each state in the conflict is shown in the " "table on the left, giving results under a number of different" " stability criterion. The display on the right allows the " "logic which defines the stability or instability of each " "option to be examined.") # ######################## INITIALIZATION ################################ def __init__(self, master, conflict, *args): """Initialize the Frame. Does not build widgets.""" FrameTemplate.__init__(self, master, conflict, self.buttonLabel, self.activeIcon, self.inactiveIcon, self.helpText) self.lastBuildConflict = None # ############################ METHODS ################################### def hasRequiredData(self): """Check that minimum data required to render the frame exists.""" if len(self.conflict.decisionMakers) < 1: return False if len(self.conflict.options) < 1: return False if len(self.conflict.feasibles) < 1: return False if self.conflict.preferenceErrors: return False else: return True def dataChanged(self): """Check if data has changed since the last build of the Frame.""" if self.lastBuildConflict != self.conflict.export_rep(): return True else: return False def buildFrame(self): """Contruct frame widgets and initialize data.""" if self.built: return # Ensure all required parts of the conflict model are properly set-up. self.conflict.reorderOptionsByDM() self.conflict.options.set_indexes() self.conflict.infeasibles.validate() self.conflict.recalculateFeasibleStates() self.conflict.coalitions.validate() for dm in self.conflict.decisionMakers: dm.calculatePerceived() dm.calculatePreferences() self.lastBuildConflict = self.conflict.export_rep() # Define variables that will display in the infoFrame self.infoText = StringVar(value='') # Define frame-specific variables self.sol = LogicalSolver(self.conflict) self.sol.findEquilibria() # infoFrame: frame and label definitions (with master 'self.infoFrame') self.infoLabel = ttk.Label(self.infoFrame, textvariable=self.infoText) # helpFrame: frame and label definitions (with master 'self.helpFrame') self.helpLabel = ttk.Label(self.helpFrame, textvariable=self.helpVar, wraplength=150) # Define frame-specific input widgets (with 'self' as master) self.paneMaster = PanedWindow(self, orient=HORIZONTAL, sashwidth=10, sashrelief="raised", sashpad=3, relief="sunken") self.pane1 = ttk.Frame(self.paneMaster) self.coalitionSelector = CoalitionSelector(self.pane1, self.conflict, self) self.solutionTable = OptionFormSolutionTable(self.pane1, self.conflict, self) self.exporter = Exporter(self.pane1, self.conflict, self) self.pane2 = ttk.Frame(self.paneMaster) self.narrator = LogNarrator(self.pane2, self.conflict, self) # ######## preliminary gridding and option configuration # configuring the input frame self.grid(column=0, row=0, rowspan=5, sticky=NSEW) self.grid_remove() self.columnconfigure(0, weight=1) self.rowconfigure(1, weight=1) # configuring infoFrame & infoFrame widgets self.infoFrame.grid(column=2, row=0, sticky=NSEW, padx=3, pady=3) self.infoFrame.grid_remove() self.infoLabel.grid(column=0, row=1, sticky=NSEW) # configuring helpFrame & helpFrame widgets self.helpFrame.grid(column=2, row=1, sticky=NSEW, padx=3, pady=3) self.helpFrame.grid_remove() self.helpLabel.grid(column=0, row=0, sticky=NSEW) # configuring frame-specific options self.paneMaster.grid(column=0, row=1, sticky=NSEW) self.paneMaster.add(self.pane1, width=600, stretch='always') self.pane1.rowconfigure(1, weight=1) self.pane1.columnconfigure(0, weight=1) self.coalitionSelector.grid(row=0, column=0, sticky=NSEW) self.solutionTable.grid(row=1, column=0, sticky=NSEW) self.exporter.grid(row=2, column=0, sticky=NSEW) self.paneMaster.add(self.pane2, width=250, stretch='always') self.pane2.rowconfigure(0, weight=1) self.pane2.columnconfigure(0, weight=1) self.narrator.grid(row=0, column=0, sticky=NSEW) # bindings self.coalitionSelector.bind("<<CoalitionsChanged>>", self.refresh) self.built = True def refresh(self, *args): """Refresh data in all active display widgets.""" self.sol = LogicalSolver(self.conflict) self.sol.findEquilibria() self.coalitionSelector.refresh() self.solutionTable.refresh() self.narrator.refresh()
self.config(bg='blue') bp_label = Label(self) bp_label.config(text='Barplot Controls') bp_label.grid(row=0, column=0, sticky='ew') window_prop = Frame(self) window_prop.config(width=10, height=150, bg='cyan') window_prop.grid(row=0, column=1, sticky='nsew') EDA_Bottom_Pane = PanedWindow(root, orient=VERTICAL, bd=0, bg='yellow', sashwidth=4) EDA_Bottom_Pane.grid(sticky='nsew') EDA_Top_Pane = PanedWindow(EDA_Bottom_Pane, orient=HORIZONTAL, bd=0, bg='black', sashwidth=4) EDA_Top_Pane.grid(sticky='nsew') EDA_Bottom_Pane.add(EDA_Top_Pane, stretch='always') EDA_Controls_Frame = Frame(EDA_Bottom_Pane) EDA_Controls_Frame.config(bg='red', width=200, height=100) EDA_Controls_Frame.grid(sticky='nsew') EDA_Controls_Frame.grid_columnconfigure(0, weight=1) EDA_Controls_Frame.grid_columnconfigure(2, weight=1) EDA_Bottom_Pane.add(EDA_Controls_Frame, stretch='never')
menu2.add_command(label="Help") menu_area.add_cascade(label="Help", menu=menu2) label_frame = LabelFrame(win, text='label frame') label_frame.grid(row=0, column=0, padx=5, pady=5) lbl_name = Label(label_frame, text="성명: ") lbl_phone = Label(label_frame, text="전화번호: ") lbl_email = Label(label_frame, text="이메일: ") entry_name = Entry(label_frame) entry_phone = Entry(label_frame) entry_email = Entry(label_frame) lbl_name.grid(row=0, column=0) entry_name.grid(row=0, column=1, padx=5, pady=5) lbl_phone.grid(row=1, column=0) entry_phone.grid(row=1, column=1, padx=5, pady=5) lbl_email.grid(row=2, column=0) entry_email.grid(row=2, column=1, padx=5, pady=5) penedwindow = PanedWindow(relief="raised", bd=0) penedwindow.grid(row=3, column=0, columnspan=2) btn_ok = Button(penedwindow, text="확인") btn_cancel = Button(penedwindow, text="취소") penedwindow.add(btn_ok) penedwindow.add(btn_cancel) if __name__ == '__main__': win.mainloop()
class Ufd: """ Universal File Dialog - "UFD" Unopinionated, minimalist, reusable, slightly configurable, general-purpose file-dialog. """ def __init__(self, title: str = "Universal File Dialog", icon: str = "", show_hidden: bool = False, include_files: bool = True, multiselect: bool = True, select_dirs: bool = True, select_files: bool = True, unix_delimiter: bool = True, stdout: bool = False): """ Init kwargs as object attributes, save references to Tk PhotoImages, & define the widgets + layout """ if not isinstance(title, str): raise TypeError("Argument title must be type string.") self.title = title if icon: if not isinstance(icon, str): raise TypeError("Argument icon must be type string.") if not isfile(icon): raise FileNotFoundError(f"File not found: {icon}") self.icon = icon else: self.icon = "" if show_hidden: self.show_hidden = True else: self.show_hidden = False if include_files: self.include_files = True else: self.include_files = False if multiselect: self.multiselect = True else: self.multiselect = False if select_dirs: self.select_dirs = True else: self.select_dirs = False if select_files: self.select_files = True else: self.select_files = False if unix_delimiter: self.unix_delimiter = True else: self.unix_delimiter = False if stdout: self.stdout = True else: self.stdout = False # Tkinter: self.dialog = Tk() self.dialog.withdraw() self.dialog.title(self.title) self.dialog.minsize(width=300, height=200) self.dialog.geometry("500x300") self.dialog.update_idletasks() self.file_icon = PhotoImage(file=f"{dirname(__file__)}/file.gif", master=self.dialog).subsample(50) self.folder_icon = PhotoImage(file=f"{dirname(__file__)}/folder.gif", master=self.dialog).subsample(15) self.disk_icon = PhotoImage(file=f"{dirname(__file__)}/disk.gif", master=self.dialog).subsample(15) if self.icon: self.dialog.iconbitmap(self.icon) else: self.dialog.iconbitmap(f"{dirname(__file__)}/icon.ico") # Widgets: self.paneview = PanedWindow( self.dialog, sashwidth=7, bg="#cccccc", bd=0, ) self.left_pane = PanedWindow(self.paneview) self.right_pane = PanedWindow(self.paneview) self.paneview.add(self.left_pane) self.paneview.add(self.right_pane) self.treeview_x_scrollbar = Scrollbar(self.left_pane, orient="horizontal") self.treeview_y_scrollbar = Scrollbar(self.left_pane, orient="vertical") self.list_box_x_scrollbar = Scrollbar(self.right_pane, orient="horizontal") self.list_box_y_scrollbar = Scrollbar(self.right_pane, orient="vertical") # tstyle = Style().configure(".", ) self.treeview = Treeview( self.left_pane, xscrollcommand=self.treeview_x_scrollbar.set, yscrollcommand=self.treeview_y_scrollbar.set, show="tree", selectmode="browse", # style=tstyle ) self.list_box = Listbox(self.right_pane, xscrollcommand=self.list_box_x_scrollbar.set, yscrollcommand=self.list_box_y_scrollbar.set, width=34, highlightthickness=0, bd=2, relief="ridge") if self.multiselect: self.list_box.config(selectmode="extended") else: self.list_box.config(selectmode="browse") self.cancel_button = Button(self.left_pane, text="Cancel", command=self.cancel) self.submit_button = Button(self.right_pane, text="Submit", command=self.submit) self.treeview_x_scrollbar.config(command=self.treeview.xview) self.treeview_y_scrollbar.config(command=self.treeview.yview) self.list_box_x_scrollbar.config(command=self.list_box.xview) self.list_box_y_scrollbar.config(command=self.list_box.yview) #Layout: self.dialog.rowconfigure(0, weight=1) self.dialog.columnconfigure(0, weight=1) self.left_pane.grid_rowconfigure(0, weight=1) self.left_pane.grid_columnconfigure(0, weight=1) self.right_pane.grid_rowconfigure(0, weight=1) self.right_pane.grid_columnconfigure(0, weight=1) self.paneview.paneconfigure( self.left_pane, minsize=100, #Start off w/ the sash centered in the GUI: width=(self.dialog.winfo_width() / 2) - ceil( (self.paneview.cget("sashwidth") * 1.5)), ) self.paneview.paneconfigure(self.right_pane, minsize=100) self.paneview.grid(row=0, column=0, sticky="nsew") self.treeview.grid(row=0, column=0, sticky="nsew") self.treeview_y_scrollbar.grid(row=0, column=1, sticky="ns") self.treeview_x_scrollbar.grid(row=1, column=0, columnspan=2, sticky="ew") self.list_box.grid(row=0, column=0, sticky="nsew") self.list_box_y_scrollbar.grid(row=0, column=1, sticky="ns") self.list_box_x_scrollbar.grid(row=1, column=0, columnspan=2, sticky="ew") self.cancel_button.grid(row=2, column=0, sticky="w", padx=10, pady=10) self.submit_button.grid(row=2, column=0, columnspan=2, sticky="e", padx=10, pady=10) #Bindings, Protocols, & Misc: self.dialog.bind("<Control-w>", self.cancel) self.treeview.bind("<<TreeviewSelect>>", self.treeview_select) self.treeview.bind("<Double-Button-1>", self.dialog_populate) self.treeview.bind("<Return>", self.dialog_populate) self.treeview.bind("<Right>", self.dialog_populate) self.list_box.bind("<<ListboxSelect>>", self.list_box_select) self.list_box.bind("<Return>", self.submit) self.dialog.protocol("WM_DELETE_WINDOW", self.cancel) self.dialog_selection = deque() self.selection_paths = deque() for disk in self.get_disks(): self.treeview.insert( "", index="end", text=disk, image=self.disk_icon, ) self.dialog.focus() def __call__(self): """ Display dialog & return selection """ (width_offset, height_offset) = self.get_offset(self.dialog) self.dialog.geometry(f"+{width_offset}+{height_offset}") self.dialog.update_idletasks() self.dialog.deiconify() self.dialog.wait_window() for i, path in enumerate(self.dialog_selection): if self.unix_delimiter: self.dialog_selection[i] = sub("\\\\", "/", path) else: self.dialog_selection[i] = sub("/", "\\\\", path) if self.stdout: [print(item) for item in self.dialog_selection] return list(self.dialog_selection) def __str__(self): """ Return own address """ return "Universal File Dialog"\ f" @ {hex(id(self))}" def __repr__(self): """ Return full string representation of constructor signature """ return f"Ufd("\ f"title=\"{self.title}\","\ f" icon=\"{self.icon}\","\ f" show_hidden={self.show_hidden},"\ f" include_files={self.include_files},"\ f" multiselect={self.multiselect},"\ f" select_dirs={self.select_dirs},"\ f" select_files={self.select_files},"\ f" unix_delimiter={self.unix_delimiter})"\ f" stdout={self.stdout})"\ f" @ {hex(id(self))}" @staticmethod def get_offset(tk_window): """ Returns an appropriate offset for a given tkinter toplevel, such that it always is created center screen on the primary display. """ width_offset = int((tk_window.winfo_screenwidth() / 2) - (tk_window.winfo_width() / 2)) height_offset = int((tk_window.winfo_screenheight() / 2) - (tk_window.winfo_height() / 2)) return (width_offset, height_offset) @staticmethod def get_disks(): """ Returns all mounted disks (for Windows) >> ["A:", "B:", "C:"] """ if system() != "Windows": raise OSError("For use with Windows platforms.") logicaldisks = run(["wmic", "logicaldisk", "get", "name"], capture_output=True) return findall("[A-Z]:", str(logicaldisks.stdout)) @staticmethod def list_dir(path, force=False): """ Reads a directory with a shell call to dir. Truthiness of bool force determines whether hidden items are returned or not. (For Windows) """ path = sub("/", "\\\\", path) if force: dir_listing = run(["dir", path, "/b", "/a"], shell=True, capture_output=True) else: dir_listing = run(["dir", path, "/b"], shell=True, capture_output=True) output = dir_listing.stdout err = dir_listing.stderr if not output: return [] if err: err = err.decode("utf-8") raise Exception(err) str_output = output.decode("utf-8") list_output = re_split("\r\n", str_output) return sorted([item for item in list_output if item]) def climb(self, item): """ Builds & returns a complete path to root directory, including the item name itself as the path tail. An extra delimiter is appeneded for the subsequent child node, which is normalized in dialog_populate() """ item_text = self.treeview.item(item)["text"] parent = self.treeview.parent(item) path = "" parents = deque() while parent: parents.append(self.treeview.item(parent)["text"] + "/") parent = self.treeview.parent(parent) for parent in reversed(parents): path += parent path += item_text + "/" return path def dialog_populate(self, event=None): """ Dynamically populates & updates the treeview, listbox, and keeps track of the full paths corresponding to each item in the listbox """ if not self.treeview.focus(): return self.treeview.column("#0", width=1000) existing_children = self.treeview.get_children(self.treeview.focus()) [self.treeview.delete(child) for child in existing_children] self.list_box.delete(0, "end") self.selection_paths.clear() focus_item = self.treeview.focus() path = self.climb(focus_item) if self.show_hidden: children = self.list_dir(path, force=True) else: children = self.list_dir(path) for child in children: if isdir(path + child): self.treeview.insert(focus_item, index="end", text=child, image=self.folder_icon) if self.select_dirs: self.list_box.insert("end", child) self.selection_paths.append(path + child) elif isfile(path + child): if self.include_files: self.treeview.insert(focus_item, index="end", text=child, image=self.file_icon) if self.select_files: self.list_box.insert("end", child) self.list_box.itemconfig("end", {"bg": "#EAEAEA"}) self.selection_paths.append(path + child) if isfile(normpath(path)): (head, tail) = path_split(normpath(path)) head = sub("\\\\", "/", head) self.list_box.insert("end", tail) self.selection_paths.append(head + "/" + tail) self.list_box.itemconfig("end", {"bg": "#EAEAEA"}) def list_box_select(self, event=None): """ Dynamically refresh the dialog selection with what's selected in the listbox (Callback for <<ListboxSelect>>). """ self.dialog_selection.clear() for i in self.list_box.curselection(): self.dialog_selection.append(self.selection_paths[i]) def treeview_select(self, event=None): """ Dynamically refresh the dialog selection with what's selected in the treeview (Callback for <<TreeviewSelect>>). """ for i in self.list_box.curselection(): self.list_box.selection_clear(i) self.dialog_selection.clear() item = normpath(self.climb(self.treeview.focus())) self.dialog_selection.append(item) def submit(self, event=None): """ Satisfies wait_window() in self.__call__() and validates selection (Callback for <Return>, <Button-1> on file_list, submit_button) """ if self.select_dirs == False: for item in self.dialog_selection: if isdir(item): messagebox.showwarning( "Error - Invalid Selection", "Unable to select directory. Please select a file(s).") return if self.select_files == False: for item in self.dialog_selection: if isfile(item): messagebox.showwarning( "Error - Invalid Selection", "Unable to select file. Please select a folder(s)") return self.dialog.destroy() def cancel(self, event=None): """ Satisfies wait_window() in self.__call__() (Callback for <Button-1> on cancel_button) (Callback for protocol "WM_DELETE_WINDOW" on self.dialog) """ self.dialog_selection.clear() self.dialog.destroy()