def add(self, itemType, cnf = {}, **kw): # handle variable text only for items which have such parameters if itemType in [ # "radiobutton" - TODO: check it "cascade", "command", "checkbutton" # "separator" - does not have such parameters ]: for param in ["label", "accelerator"]: if param in cnf: var = cnf.pop(param) elif param in kw: var = kw.pop(param) else: var = "" if not isinstance(var, variables): var = StringVar(self, var) binding = MenuVarBinding(self, var, self.count, param) var.trace_variable("w", binding.on_var_changed) if cnf: cnf[param] = var.get() else: kw[param] = var.get() self.count = self.count + 1 Menu.add(self, itemType, cnf or kw)
class VarLabel(Label): def __init__(self, *args, **kw): # "textvariable" argument has same effect to VarLabel as "text" try: var = kw.pop("textvariable") except KeyError: pass else: if "text" in kw: raise RuntimeError('"text" and "textvariable" ' 'arguments cannot be passed together to a VarLabel' ) kw["text"] = var if "text" in kw: self.text_var = kw.pop("text") kw["text"] = self.text_var.get() else: self.text_var = StringVar("") Label.__init__(self, *args, **kw) self.text_var.trace_variable("w", self.on_var_changed) def on_var_changed(self, *args): Label.config(self, text = self.text_var.get())
def __refresh__(self): desc = self.desc for attr, info in desc.__attribute_info__.items(): try: _input = info["input"] except KeyError: _input = None cur_val = getattr(desc, attr) v = getattr(self, "_var_" + attr) if _input is PCIId: if not PCIId.db.built and cur_val is None: # use string values only without database cur_val = "" # use appropriate widget/variable pair if isinstance(cur_val, str): if not isinstance(v, StringVar): v = StringVar() setattr(self, "_var_" + attr, v) # Fill frame with appropriate widget frame = getattr(self, "_w_" + attr) for w in frame.winfo_children(): w.destroy() w = HKEntry(frame, textvariable = v) w.grid(row = 0, column = 0, sticky = "NEWS") elif cur_val is None or isinstance(cur_val, PCIId): if not isinstance(v, ObjRefVar): v = ObjRefVar() setattr(self, "_var_" + attr, v) frame = getattr(self, "_w_" + attr) for w in frame.winfo_children(): w.destroy() w = PCIIdWidget(v, frame) w.grid(row = 0, column = 0, sticky = "NEWS") widget_val = v.get() if _input is int: try: widget_val = int(widget_val, base = 0) except ValueError: widget_val = None if widget_val != cur_val: v.set(cur_val) for cb in self._all_highlights: cb()
class VarButton(Button): def __init__(self, *args, **kw): if "text" in kw: self.text_var = kw.pop("text") kw["text"] = self.text_var.get() else: self.text_var = StringVar("") Button.__init__(self, *args, **kw) self.text_var.trace_variable("w", self.on_var_changed) def on_var_changed(self, *args): Button.config(self, text=self.text_var.get())
class VarLabelFrame(LabelFrame): def __init__(self, *args, **kw): if "text" in kw: self.text_var = kw.pop("text") kw["text"] = self.text_var.get() else: self.text_var = StringVar("") LabelFrame.__init__(self, *args, **kw) self.text_var.trace_variable("w", self.on_var_changed) def on_var_changed(self, *args): Label.config(self, text=self.text_var.get())
class QDCGUIWindow(GUITk, QDCGUISignalHelper): def __init__(self, project=None): GUITk.__init__(self, wait_msec=1) for signame in ["qvc_dirtied", "qvd_failed", "qvc_available"]: s = CoSignal() s.attach(self.signal_dispatcher) setattr(self, "sig_" + signame, s) self.title_suffix = _("Qemu device creator GUI") self.title_suffix.trace_variable("w", self.__on_title_suffix_write__) self.title_not_saved_asterisk = StringVar() self.title_not_saved_asterisk.trace_variable( "w", self.__on_title_suffix_write__) self.saved_operation = None self.var_title = StringVar() self.title(self.var_title) # Hot keys, accelerators self.hk = hotkeys = HotKey(self) hotkeys.add_bindings([ HotKeyBinding( self.invert_history_window, key_code=43, description=_("If editing history window is hidden then \ show it else hide it."), symbol="H"), HotKeyBinding(self.on_load, key_code=32, description=_("Load project from file."), symbol="O"), HotKeyBinding(self.on_new_project, key_code=57, description=_("Create new project."), symbol="N"), HotKeyBinding(self.on_add_description, key_code=40, description=_("Add description to the project"), symbol="D"), HotKeyBinding(self.on_set_qemu_build_path, key_code=56, description=_("Set Qemu build path for the project"), symbol="B"), HotKeyBinding( self.on_sel_tgt_qemu_version, key_code=28, description=_("Select target Qemu version for the project"), symbol="T"), HotKeyBinding(self.on_generate, key_code=42, description=_("Launch code generation"), symbol="G"), HotKeyBinding(self.on_delete, key_code=24, description=_("Shutdown the application."), symbol="Q"), HotKeyBinding(self.undo, key_code=52, description=_("Revert previous editing."), symbol="Z"), HotKeyBinding(self.redo, key_code=29, description=_("Make reverted editing again."), symbol="Y"), HotKeyBinding(self.on_save, key_code=39, description=_("Save project."), symbol="S"), HotKeyBinding(self.on_reload, key_code=27, description=_("Reload current project from file."), symbol="R") ]) # see `set_user_settings` self._user_settings = None # Menu bar menubar = VarMenu(self) self.filemenu = filemenu = VarMenu(menubar, tearoff=False) filemenu.add_command(label=_("Add description"), command=self.on_add_description, accelerator=hotkeys.get_keycode_string( self.on_add_description)) filemenu.add_command(label=_("Set Qemu build path"), command=self.on_set_qemu_build_path, accelerator=hotkeys.get_keycode_string( self.on_set_qemu_build_path)) filemenu.add_command(label=_("Select target Qemu version"), command=self.on_sel_tgt_qemu_version, accelerator=hotkeys.get_keycode_string( self.on_sel_tgt_qemu_version)) filemenu.add_command(label=_("Generate"), command=self.on_generate, accelerator=hotkeys.get_keycode_string( self.on_generate)) filemenu.add_separator() filemenu.add_command(label=_("New project"), command=self.on_new_project, accelerator=hotkeys.get_keycode_string( self.on_new_project)), filemenu.add_command(label=_("Save"), command=self.on_save, accelerator=hotkeys.get_keycode_string( self.on_save)), filemenu.add_command(label=_("Save project as..."), command=self.on_save_as) filemenu.add_command(label=_("Load"), command=self.on_load, accelerator=hotkeys.get_keycode_string( self.on_load)), self.reload_idx = filemenu.count filemenu.add_command(label=_("Reload"), command=self.on_reload, accelerator=hotkeys.get_keycode_string( self.on_reload)), self.recentmenu = recentmenu = VarMenu(filemenu, tearoff=False) filemenu.add_cascade( label=_("Recent projects"), menu=recentmenu, state=DISABLED # a user settings instance is required ) filemenu.add_separator() filemenu.add_command(label=_("Quit"), command=self.quit, accelerator=hotkeys.get_keycode_string( self.on_delete)) menubar.add_cascade(label=_("File"), menu=filemenu) self.editmenu = editmenu = VarMenu(menubar, tearoff=False) editmenu.add_command(label=_("Undo"), command=self.undo, accelerator=hotkeys.get_keycode_string(self.undo)) self.undo_idx = editmenu.count - 1 editmenu.add_command(label=_("Redo"), command=self.redo, accelerator=hotkeys.get_keycode_string(self.redo)) self.redo_idx = editmenu.count - 1 editmenu.add_separator() editmenu.add_command(label=_("Rebuild Cache"), command=self.rebuild_cache, accelerator=hotkeys.get_keycode_string( self.rebuild_cache)) editmenu.add_separator() v = self.var_history_window = BooleanVar() v.set(False) self.__on_var_history_window = v.trace_variable( "w", self.__on_var_history_window__) editmenu.add_checkbutton(label=_("Editing history window"), variable=v, accelerator=hotkeys.get_keycode_string( self.invert_history_window)) menubar.add_cascade(label=_("Edit"), menu=editmenu) self.optionsmenu = optionsmenu = VarMenu(menubar, tearoff=False) v = self.var_schedule_generation = BooleanVar() v.set(False) self.__on_var_schedule_generation = v.trace_variable( "w", self.__on_var_schedule_generation__) optionsmenu.add_checkbutton( label=_("Schedule generation after cache loading"), variable=v) v = self.var_gen_chunk_graphs = BooleanVar() v.set(False) self.__on_var_gen_chunk_graphs = v.trace_variable( "w", self.__on_var_gen_chunk_graphs__) optionsmenu.add_checkbutton(label=_("Generate chunk graphs"), variable=v) menubar.add_cascade(label=_("Options"), menu=optionsmenu) self.config(menu=menubar) # Widget layout self.grid() self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(0, weight=1) # Status bar self.grid_rowconfigure(1, weight=0) self.sb = sb = Statusbar(self) sb.grid(row=1, column=0, sticky="NEWS") # Target Qemu version in the status bar self._target_qemu = Variable(None) # This complicated scheme is required because the status must also # be updated on language change. @as_variable(self._target_qemu, _("No target"), _("Target Qemu: %s")) def var_target_qemu(target, no_target, target_qemu): if target is None: return no_target else: return target_qemu % target sb.left(var_target_qemu) # QEMU build path displaying self.var_qemu_build_path = StringVar() sb.left(self.var_qemu_build_path) # Task counters in status bar sb.right(_("Background tasks: ")) sb.repack(CoStatusView(sb), RIGHT) self.signal_dispatcher.watch_failed(self.__on_listener_failed) self.protocol("WM_DELETE_WINDOW", self.on_delete) self.set_project(GUIProject() if project is None else project) self.__update_title__() self.__check_saved_asterisk__() self.qsig_watch("qvc_available", self.__on_qvc_available) def set_user_settings(self, val): if self._user_settings is val: return self._user_settings = val self._update_recent_projects() self._update_options() def _update_recent_projects(self): settings = self._user_settings menu = self.recentmenu # first, clear recent menu last = menu.index(END) if last is not None: menu.delete(0, last) added = 0 if settings is not None: for f in reversed(list(settings.recent_projects)): # a simple check if not isfile(f): settings.recent_projects.discard(f) continue def do_load(file_name=f): self._do_load(file_name) menu.add_command(label=f, command=do_load) added += 1 self.filemenu.entryconfigure(self.filemenu.index( _("Recent projects").get()), state=NORMAL if added else DISABLED) def _update_options(self): settings = self._user_settings if settings is None: return self.var_schedule_generation.set(settings.schedule_generation) self.var_gen_chunk_graphs.set(settings.gen_chunk_graphs) def __on_listener_failed(self, e, tb): sys.stderr.write("Listener failed - %s" % "".join(format_exception(type(e), e, tb))) def __on_history_window_destroy__(self, *args, **kw): self.var_history_window.trace_vdelete("w", self.__on_var_history_window) self.var_history_window.set(False) self.__on_var_history_window = self.var_history_window.trace_variable( "w", self.__on_var_history_window__) def __on_var_history_window__(self, *args): if self.var_history_window.get(): self._history_window = HistoryWindow(self.pht, self) self._history_window.bind("<Destroy>", self.__on_history_window_destroy__, "+") else: try: self._history_window.destroy() except AttributeError: pass else: del self._history_window def invert_history_window(self): self.var_history_window.set(not self.var_history_window.get()) def __on_var_schedule_generation__(self, *args): settings = self._user_settings if settings is not None: settings.schedule_generation = self.var_schedule_generation.get() def __on_var_gen_chunk_graphs__(self, *args): settings = self._user_settings if settings is not None: settings.gen_chunk_graphs = self.var_gen_chunk_graphs.get() def __on_title_suffix_write__(self, *args, **kw): self.__update_title__() def __update_title__(self): try: title_prefix = str(self.current_file_name) except AttributeError: title_prefix = "[New project]" self.var_title.set(title_prefix + self.title_not_saved_asterisk.get() + " - " + self.title_suffix.get()) def check_undo_redo(self): can_do = self.pht.can_do() self.hk.set_enabled(self.redo, can_do) if can_do: self.editmenu.entryconfig(self.redo_idx, state=NORMAL) else: self.editmenu.entryconfig(self.redo_idx, state=DISABLED) can_undo = self.pht.can_undo() self.hk.set_enabled(self.undo, can_undo) if can_undo: self.editmenu.entryconfig(self.undo_idx, state=NORMAL) else: self.editmenu.entryconfig(self.undo_idx, state=DISABLED) def set_current_file_name(self, file_name=None): if file_name is None: try: del self.current_file_name except AttributeError: pass # already deleted else: self.filemenu.entryconfig(self.reload_idx, state=DISABLED) else: # TODO: watch the file in FS and auto ask user to reload self.current_file_name = file_name self.filemenu.entryconfig(self.reload_idx, state=NORMAL) self.__update_title__() def set_project(self, project): try: pht = self.pht except AttributeError: # Project was never been set pass else: pht.unwatch_changed(self.on_changed) try: self.pw.destroy() except AttributeError: # project widget was never been created pass # Close history window if self.var_history_window.get(): self.var_history_window.set(False) self.proj = project self.pht = GUIProjectHistoryTracker(self.proj, self.proj.history) DescNameWatcher(self.pht) self.pw = ProjectWidget(self.proj, self) self.pw.grid(column=0, row=0, sticky="NEWS") self.update_qemu_build_path(project.build_path) self.update_target_qemu() self.pht.watch_changed(self.on_changed) self.check_undo_redo() def __saved_asterisk__(self, saved=True): if saved: if self.title_not_saved_asterisk.get() != "": self.title_not_saved_asterisk.set("") else: if self.title_not_saved_asterisk.get() != "*": self.title_not_saved_asterisk.set("*") def __check_saved_asterisk__(self): if self.saved_operation == self.pht.pos: self.__saved_asterisk__(True) else: self.__saved_asterisk__(False) def on_changed(self, op, *args, **kw): self.check_undo_redo() self.__check_saved_asterisk__() if isinstance(op, GUIPOp_SetBuildPath): proj = self.proj if op.p is proj: self.update_qemu_build_path(proj.build_path) # Note that target Qemu version info will be update when QVC # will be ready. def undo(self): self.pht.undo_sequence() def redo(self): self.pht.do_sequence() def rebuild_cache(self): try: qvd = qvd_get(self.proj.build_path, version=self.proj.target_version) except BadBuildPath as e: showerror( title=_("Cache rebuilding is impossible").get(), message=(_("Selected Qemu build path is bad. Reason: %s") % (e.message)).get()) return # if 'reload_build_path_task' is not failed if hasattr(self.pw, 'reload_build_path_task'): # reload_build_path_task always run at program start that is why # it's in process when it's not in finished_tasks and not failed if self.pw.reload_build_path_task not in self.pw.tm.finished_tasks: ans = askyesno(self, title=_("Cache rebuilding"), message=_("Cache building is already \ in process. Do you want to start cache rebuilding?")) if not ans: return qvd.remove_cache() self.pw.reload_build_path() def on_delete(self): if self.title_not_saved_asterisk.get() == "*": resp = askyesnocancel( title=self.title_suffix.get(), message=_("Current project has unsaved changes." " Would you like to save it?" "\n\nNote that a backup is always saved with name" " project.py in current working directory.").get()) if resp is None: return if resp: self.on_save() if self.title_not_saved_asterisk.get() == "*": # user canceled saving during on_save return try: """ TODO: Note that it is possible to prevent window to close if a generation task is in process. But is it really needed? """ self.task_manager.remove(self._project_generation_task) except AttributeError: pass else: del self._project_generation_task self.signal_dispatcher.unwatch_failed(self.__on_listener_failed) self.quit() def on_add_description(self): d = AddDescriptionDialog(self.pht, self) def on_set_qemu_build_path(self): dir = askdirectory(self, title=_("Select Qemu build path")) if not dir: return self.pht.set_build_path(dir) def on_sel_tgt_qemu_version(self): try: qvd = qvd_get(self.proj.build_path) except: repo = None else: repo = qvd.repo new_target = GitVerSelDialog(self, repo).wait() if new_target is None: return self.pht.set_target(new_target) def on_generate(self): try: t = self._project_generation_task except AttributeError: pass else: if not t.finished: showerror(title=_("Generation is cancelled").get(), message=_("At least one generation task is already \ in process.").get()) return if not self.proj.build_path: showerror( title=_("Generation is impossible").get(), message=_("No Qemu build path is set for the project.").get()) return try: qvd = qvd_get(self.proj.build_path, version=self.proj.target_version) except BadBuildPath as e: showerror( title=_("Generation is impossible").get(), message=(_("Selected Qemu build path is bad. Reason: %s") % (e.message)).get()) return if not hasattr(self.pw, "reload_build_path_task"): showerror(title=_("Generation is impossible").get(), message=_("Qemu version cache loading failed.").get()) return if not qvd.qvc_is_ready and not self.var_schedule_generation.get(): showerror(title=_("Generation is cancelled").get(), message=_("Qemu version cache is not ready yet. Try \ later.").get()) return self._project_generation_task = ProjectGeneration( self.proj, qvd.src_path, self.sig_qvc_dirtied, self.pw.reload_build_path_task, self.var_gen_chunk_graphs.get()) self.task_manager.enqueue(self._project_generation_task) def load_project_from_file(self, file_name): loaded_variables = dict(qdt.__dict__) try: execfile(file_name, loaded_variables) except Exception as e: raise e else: qproj = None for v in loaded_variables.values(): if isinstance(v, GUIProject): break elif qproj is None and isinstance(v, QProject): qproj = v else: if qproj: v = GUIProject.from_qproject(qproj) else: raise Exception("No project object was loaded") self.set_project(v) self.set_current_file_name(file_name) self.saved_operation = self.pht.pos self.__check_saved_asterisk__() if self._user_settings: self._user_settings.account_project(file_name) self._update_recent_projects() def save_project_to_file(self, file_name): self.pw.refresh_layouts() project = self.proj # Ensure that all machine nodes are in corresponding lists for d in project.descriptions: if isinstance(d, MachineNode): d.link(handle_system_bus=False) pythonize(project, file_name) self.set_current_file_name(file_name) self.saved_operation = self.pht.pos self.__check_saved_asterisk__() def try_save_project_to_file(self, file_name): try: open(file_name, "wb").close() except IOError as e: if not e.errno == 13: # Do not remove read-only files try: remove(file_name) except: pass showerror(title=_("Cannot save project").get(), message=str(e)) return self.save_project_to_file(file_name) if self._user_settings: self._user_settings.account_project(file_name) self._update_recent_projects() def on_save_as(self): fname = asksaveas(self, [(_("QDC GUI Project defining script"), ".py")], title=_("Save project")) if not fname: return self.try_save_project_to_file(fname) def on_save(self): try: fname = self.current_file_name except AttributeError: self.on_save_as() else: self.try_save_project_to_file(fname) def check_unsaved(self): if self.title_not_saved_asterisk.get() == "*": return askyesno( self, title=self.title_suffix, message= _("Current project has unsaved changes. They will be lost. Continue?" )) else: return True def on_new_project(self): if not self.check_unsaved(): return self.set_project(GUIProject()) self.set_current_file_name() """ There is nothing to save in just created project. So declare that all changes are saved. """ self.saved_operation = self.pht.pos self.__check_saved_asterisk__() def on_load(self): if not self.check_unsaved(): return fname = askopen(self, [(_("QDC GUI Project defining script"), ".py")], title=_("Load project")) if fname: self._do_load(fname) def _do_load(self, fname): try: self.load_project_from_file(fname) except Exception as e: showerror(title=_("Project loading failed").get(), message=str(e)) def on_reload(self): try: fname = self.current_file_name except AttributeError: # No file is selected for the project yet return if not self.check_unsaved(): return self._do_load(fname) def update_qemu_build_path(self, bp): if bp is None: self.var_qemu_build_path.set( _("No QEMU build path selected").get()) else: self.var_qemu_build_path.set("QEMU: " + bp) def update_target_qemu(self): p, qvd = self.proj, QemuVersionDescription.current self._target_qemu.set(p and p.target_version or qvd and qvd.commit_sha) def __on_qvc_available(self): self.update_target_qemu()
class MemorySettingsWidget(SettingsWidget): def __init__(self, mem, *args, **kw): SettingsWidget.__init__(self, mem, *args, **kw) self.mem = mem self.mem_fr = fr = GUIFrame(self) fr.pack(fill=BOTH, expand=False) fr.columnconfigure(0, weight=0) fr.columnconfigure(1, weight=1) fr.rowconfigure(0, weight=0) row = 0 l = VarLabel(fr, text=_("Region type")) l.grid(row=row, column=0, sticky="NES") memtype2str = { MemoryNode: _("Container"), MemorySASNode: _("SAS"), MemoryAliasNode: _("Alias"), MemoryRAMNode: _("RAM"), MemoryROMNode: _("ROM") } l = VarLabel(fr, text=memtype2str[type(mem)]) l.grid(row=row, column=1, sticky="NEWS") row += 1 if not isinstance(mem, MemorySASNode): l = VarLabel(fr, text=_("Parent region")) l.grid(row=row, column=0, sticky="NES") self.var_parent = StringVar() self.cb_parent = Combobox(fr, textvariable=self.var_parent, state="readonly") self.cb_parent.grid(row=row, column=1, sticky="NEWS") row += 1 self.fields = [(_("Name"), "name", CConst), (_("Size"), "size", CConst), (_("Offset"), "offset", CConst), (_("May overlap"), "may_overlap", bool), (_("Priority"), "priority", CConst)] if type(mem) is MemoryAliasNode: self.fields.extend([(_("Alias offset"), "alias_offset", CConst)]) if isinstance(mem, MemorySASNode): self.fields = [(_("Name"), "name", str)] for text, field, _type in self.fields: if _type is bool: l = None v = BooleanVar() w = VarCheckbutton(fr, text=text, variable=v) else: l = VarLabel(fr, text=text) v = StringVar() w = HKEntry(fr, textvariable=v) fr.rowconfigure(row, weight=0) if l is None: w.grid(row=row, column=0, sticky="NWS", columnspan=2) else: l.grid(row=row, column=0, sticky="NES") l.gi = l.grid_info() w.grid(row=row, column=1, sticky="NEWS") w.gi = w.grid_info() row += 1 if l: setattr(self, "l_" + field, l) setattr(self, "w_" + field, w) setattr(self, "var_" + field, v) self.var_name.trace_variable("w", self.__on_name_var_changed) if type(mem) is MemoryAliasNode: l = VarLabel(fr, text=_("Alias region")) l.grid(row=row, column=0, sticky="NES") self.var_alias_to = StringVar() self.cb_alias_to = Combobox(fr, textvariable=self.var_alias_to, state="readonly") self.cb_alias_to.grid(row=row, column=1, sticky="NEWS") if not isinstance(mem, MemorySASNode): if not mem.parent: self.l_offset.grid_forget() self.w_offset.grid_forget() def __apply_internal__(self): if not isinstance(self.mem, MemorySASNode): new_parent = self.find_node_by_link_text(self.var_parent.get()) cur_parent = self.mem.parent if new_parent is None: new_parent_id = -1 else: new_parent_id = new_parent.id if cur_parent is None: cur_parent_id = -1 else: cur_parent_id = cur_parent.id if not new_parent_id == cur_parent_id: if not cur_parent_id == -1: self.mht.stage(MOp_RemoveMemChild, self.mem.id, cur_parent_id) if not new_parent_id == -1: self.mht.stage(MOp_AddMemChild, self.mem.id, new_parent_id) for text, field, _type in self.fields: new_val = getattr(self, "var_" + field).get() if _type is CConst: try: new_val = CConst.parse(new_val) except: continue cur_val = getattr(self.mem, field) if new_val == cur_val: continue self.mht.stage(MOp_SetMemNodeAttr, field, new_val, self.mem.id) if type(self.mem) is MemoryAliasNode: new_alias_to = self.find_node_by_link_text(self.var_alias_to.get()) cur_alias_to = self.mem.alias_to if not new_alias_to == cur_alias_to: self.mht.stage(MOp_SetMemNodeAlias, "alias_to", new_alias_to, self.mem.id) self.mht.set_sequence_description( _("Memory '%s' (%d) configuration.") % (self.mem.name, self.mem.id)) def refresh(self): SettingsWidget.refresh(self) if not isinstance(self.mem, MemorySASNode): values = [ DeviceSettingsWidget.gen_node_link_text(mem) for mem in ([ mem for mem in self.mach.mems if (not isinstance(mem, MemoryLeafNode) and mem != self.mem) ] + [None]) ] self.cb_parent.config(values=values) self.var_parent.set( DeviceSettingsWidget.gen_node_link_text(self.mem.parent)) for text, field, _type in self.fields: var = getattr(self, "var_" + field) cur_val = getattr(self.mem, field) var.set(cur_val) if type(self.mem) is MemoryAliasNode: values = [ DeviceSettingsWidget.gen_node_link_text(mem) for mem in ( [mem for mem in self.mach.mems if (mem != self.mem)]) ] self.cb_alias_to.config(values=values) self.var_alias_to.set( DeviceSettingsWidget.gen_node_link_text(self.mem.alias_to)) if not isinstance(self.mem, MemorySASNode): if self.mem.parent is None: self.l_offset.grid_forget() self.w_offset.grid_forget() else: self.l_offset.grid(self.l_offset.gi) self.w_offset.grid(self.w_offset.gi) def on_changed(self, op, *args, **kw): if not isinstance(op, MachineNodeOperation): return if op.writes_node() and self.mem.id == -1: self.destroy() else: self.refresh() def __on_name_var_changed(self, *args): vvb = self.v_var_base vb = vvb.get() try: prev_n = self.__prev_name except AttributeError: # name was not edited yet prev_n = self.mem.name.v if vb == "mem" or vb == name_to_var_base(prev_n): """ If current variable name base is default or corresponds to previous name then auto suggest new variable name base with account of just entered name. """ n = self.var_name.get() vvb.set(name_to_var_base(n)) self.__prev_name = n
class CollocationsView: _BACKGROUND_COLOUR = "#FFF" # white def __init__(self): self.queue = q.Queue() self.model = CollocationsModel(self.queue) self.top = Tk() self._init_top(self.top) self._init_menubar() self._init_widgets(self.top) self.load_corpus(self.model.DEFAULT_CORPUS) self.after = self.top.after(POLL_INTERVAL, self._poll) def _init_top(self, top): top.geometry("550x650+50+50") top.title("NLTK Collocations List") top.bind("<Control-q>", self.destroy) top.protocol("WM_DELETE_WINDOW", self.destroy) top.minsize(550, 650) def _init_widgets(self, parent): self.main_frame = Frame( parent, dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1)) self._init_corpus_select(self.main_frame) self._init_results_box(self.main_frame) self._init_paging(self.main_frame) self._init_status(self.main_frame) self.main_frame.pack(fill="both", expand=True) def _init_corpus_select(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.var = StringVar(innerframe) self.var.set(self.model.DEFAULT_CORPUS) Label( innerframe, justify=LEFT, text=" Corpus: ", background=self._BACKGROUND_COLOUR, padx=2, pady=1, border=0, ).pack(side="left") other_corpora = list(self.model.CORPORA.keys()).remove( self.model.DEFAULT_CORPUS) om = OptionMenu(innerframe, self.var, self.model.DEFAULT_CORPUS, command=self.corpus_selected, *self.model.non_default_corpora()) om["borderwidth"] = 0 om["highlightthickness"] = 1 om.pack(side="left") innerframe.pack(side="top", fill="x", anchor="n") def _init_status(self, parent): self.status = Label( parent, justify=LEFT, relief=SUNKEN, background=self._BACKGROUND_COLOUR, border=0, padx=1, pady=0, ) self.status.pack(side="top", anchor="sw") def _init_menubar(self): self._result_size = IntVar(self.top) menubar = Menu(self.top) filemenu = Menu(menubar, tearoff=0, borderwidth=0) filemenu.add_command(label="Exit", underline=1, command=self.destroy, accelerator="Ctrl-q") menubar.add_cascade(label="File", underline=0, menu=filemenu) editmenu = Menu(menubar, tearoff=0) rescntmenu = Menu(editmenu, tearoff=0) rescntmenu.add_radiobutton( label="20", variable=self._result_size, underline=0, value=20, command=self.set_result_size, ) rescntmenu.add_radiobutton( label="50", variable=self._result_size, underline=0, value=50, command=self.set_result_size, ) rescntmenu.add_radiobutton( label="100", variable=self._result_size, underline=0, value=100, command=self.set_result_size, ) rescntmenu.invoke(1) editmenu.add_cascade(label="Result Count", underline=0, menu=rescntmenu) menubar.add_cascade(label="Edit", underline=0, menu=editmenu) self.top.config(menu=menubar) def set_result_size(self, **kwargs): self.model.result_count = self._result_size.get() def _init_results_box(self, parent): innerframe = Frame(parent) i1 = Frame(innerframe) i2 = Frame(innerframe) vscrollbar = Scrollbar(i1, borderwidth=1) hscrollbar = Scrollbar(i2, borderwidth=1, orient="horiz") self.results_box = Text( i1, font=Font(family="courier", size="16"), state="disabled", borderwidth=1, yscrollcommand=vscrollbar.set, xscrollcommand=hscrollbar.set, wrap="none", width="40", height="20", exportselection=1, ) self.results_box.pack(side="left", fill="both", expand=True) vscrollbar.pack(side="left", fill="y", anchor="e") vscrollbar.config(command=self.results_box.yview) hscrollbar.pack(side="left", fill="x", expand=True, anchor="w") hscrollbar.config(command=self.results_box.xview) # there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!! Label(i2, text=" ", background=self._BACKGROUND_COLOUR).pack(side="left", anchor="e") i1.pack(side="top", fill="both", expand=True, anchor="n") i2.pack(side="bottom", fill="x", anchor="s") innerframe.pack(side="top", fill="both", expand=True) def _init_paging(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.prev = prev = Button( innerframe, text="Previous", command=self.previous, width="10", borderwidth=1, highlightthickness=1, state="disabled", ) prev.pack(side="left", anchor="center") self.next = next = Button( innerframe, text="Next", command=self.__next__, width="10", borderwidth=1, highlightthickness=1, state="disabled", ) next.pack(side="right", anchor="center") innerframe.pack(side="top", fill="y") self.reset_current_page() def reset_current_page(self): self.current_page = -1 def _poll(self): try: event = self.queue.get(block=False) except q.Empty: pass else: if event == CORPUS_LOADED_EVENT: self.handle_corpus_loaded(event) elif event == ERROR_LOADING_CORPUS_EVENT: self.handle_error_loading_corpus(event) self.after = self.top.after(POLL_INTERVAL, self._poll) def handle_error_loading_corpus(self, event): self.status["text"] = "Error in loading " + self.var.get() self.unfreeze_editable() self.clear_results_box() self.freeze_editable() self.reset_current_page() def handle_corpus_loaded(self, event): self.status["text"] = self.var.get() + " is loaded" self.unfreeze_editable() self.clear_results_box() self.reset_current_page() # self.next() collocations = self.model.next(self.current_page + 1) self.write_results(collocations) self.current_page += 1 def corpus_selected(self, *args): new_selection = self.var.get() self.load_corpus(new_selection) def previous(self): self.freeze_editable() collocations = self.model.prev(self.current_page - 1) self.current_page = self.current_page - 1 self.clear_results_box() self.write_results(collocations) self.unfreeze_editable() def __next__(self): self.freeze_editable() collocations = self.model.next(self.current_page + 1) self.clear_results_box() self.write_results(collocations) self.current_page += 1 self.unfreeze_editable() def load_corpus(self, selection): if self.model.selected_corpus != selection: self.status["text"] = "Loading " + selection + "..." self.freeze_editable() self.model.load_corpus(selection) def freeze_editable(self): self.prev["state"] = "disabled" self.next["state"] = "disabled" def clear_results_box(self): self.results_box["state"] = "normal" self.results_box.delete("1.0", END) self.results_box["state"] = "disabled" def fire_event(self, event): # Firing an event so that rendering of widgets happen in the mainloop thread self.top.event_generate(event, when="tail") def destroy(self, *e): if self.top is None: return self.top.after_cancel(self.after) self.top.destroy() self.top = None def mainloop(self, *args, **kwargs): if in_idle(): return self.top.mainloop(*args, **kwargs) def unfreeze_editable(self): self.set_paging_button_states() def set_paging_button_states(self): if self.current_page == -1 or self.current_page == 0: self.prev["state"] = "disabled" else: self.prev["state"] = "normal" if self.model.is_last_page(self.current_page): self.next["state"] = "disabled" else: self.next["state"] = "normal" def write_results(self, results): self.results_box["state"] = "normal" row = 1 for each in results: self.results_box.insert( str(row) + ".0", each[0] + " " + each[1] + "\n") row += 1 self.results_box["state"] = "disabled"
class DeviceSettingsWidget(SettingsWidget): EVENT_BUS_SELECTED = "<<DSWBusSelected>>" prop_type_name_map = { QOMPropertyTypeInteger: ("Integer", ), QOMPropertyTypeLink: ("Link", ), QOMPropertyTypeString: ("String", ), QOMPropertyTypeBoolean: ("Boolean", ) } prop_name_type_map = { "Integer": QOMPropertyTypeInteger, "Link": QOMPropertyTypeLink, "String": QOMPropertyTypeString, "Boolean": QOMPropertyTypeBoolean } def __init__(self, device, *args, **kw): SettingsWidget.__init__(self, device, *args, **kw) self.dev = device common_fr = GUIFrame(self) common_fr.pack(fill = BOTH, expand = False) common_fr.columnconfigure(0, weight = 0) common_fr.columnconfigure(1, weight = 1) common_fr.rowconfigure(0, weight = 0) l = VarLabel(common_fr, text = _("QOM type")) v = self.qom_type_var = StringVar() e = HKEntry(common_fr, textvariable = v) v.trace_variable("w", self.__on_qom_type_var_changed) l.grid(row = 0, column = 0, sticky = "W") e.grid(row = 0, column = 1, sticky = "EW") b = VarButton(common_fr, text = _("Select"), command = self.on_press_select_qom_type ) b.grid(row = 0, column = 2, sticky = "EW") # Check for device tree bp = self.mach.project.build_path if bp is None: b["state"] = "disabled" else: qvd = qvd_get(bp, version = self.mach.project.target_version) if qvd.qvc is None or qvd.qvc.device_tree is None: # TODO: watch "qvc_available" signal b["state"] = "disabled" # parent bus editing widgets l = VarLabel(common_fr, text = _("Parent bus")) self.bus_var = StringVar() self.bus_var.trace_variable("w", self.on_parent_bus_var_changed) self.bus_cb = Combobox( common_fr, textvariable = self.bus_var, state = "readonly" ) l.grid(row = 1, column = 0, sticky = "W") self.bus_cb.grid(row = 1, column = 1, sticky = "EW") common_fr.rowconfigure(1, weight = 0) self.buses_lf = lf = VarLabelFrame( common_fr, text = _("Child buses") ) lf.grid(row = 2, column = 0, columns = 3, sticky = "NEWS") self.rowconfigure(2, weight = 1) lf.columnconfigure(0, weight = 1) self.child_buses_rows = [] # pack properties inside a frame with scrolling outer_frame = VarLabelFrame(self, text = _("Properties")) outer_frame.pack(fill = BOTH, expand = True) self.props_lf = add_scrollbars(outer_frame, GUIFrame) self.props_lf.columnconfigure(0, weight = 1) self.props_lf.columnconfigure(1, weight = 0) self.props_lf.columnconfigure(2, weight = 1) self.props_lf.columnconfigure(3, weight = 0) self.prop2field = {} self.bt_add_prop = VarButton( self.props_lf, text = _("Add"), command = self.on_prop_add ) self.bt_add_prop.grid( column = 3, sticky = "NEWS" ) def __on_destroy__(self, *args): for bld in self.child_buses_rows: bld.delete() SettingsWidget.__on_destroy__(self, *args) def __on_qom_type_var_changed(self, *args): vvb = self.v_var_base vb = vvb.get() try: prev_qt = self.__prev_qom_type except AttributeError: # QOM type was not edited yet prev_qt = self.node.qom_type if vb == "dev" or vb == qom_type_to_var_base(prev_qt): """ If current variable name base is default or corresponds to previous QOM type name then auto suggest new variable name base with account of just entered QOM type. """ qt = self.qom_type_var.get() vvb.set(qom_type_to_var_base(qt)) self.__prev_qom_type = qt def on_parent_bus_var_changed(self, *args): self.event_generate(DeviceSettingsWidget.EVENT_BUS_SELECTED) def on_press_select_qom_type(self): DeviceTreeWidget(self) def gen_uniq_prop_name(self): for x in count(0, 1): name = "name-of-new-property-" + str(x) for prop in self.prop2field: if name == prop.prop_name: name = None break if name: return name def on_prop_add(self): p = QOMPropertyValue( QOMPropertyTypeLink, self.gen_uniq_prop_name(), None ) lpd = PropLineDesc(self, p) row = len(self.prop2field) lpd.gen_row(row) self.prop2field[p] = lpd # Move add button bottom self.bt_add_prop.grid(row = row + 1) def on_changed(self, op, *args, **kw): if isinstance(op, MOp_SetChildBus): self.event_generate(DeviceSettingsWidget.EVENT_BUS_SELECTED) if isinstance(op, MOp_SetDevParentBus): self.event_generate(DeviceSettingsWidget.EVENT_BUS_SELECTED) if isinstance(op, MachineNodeOperation): if op.writes_node() and self.dev.id == -1: self.destroy() elif op.node_id == self.dev.id: self.refresh() @staticmethod def gen_prop_type_optionmenu(parent, current = None): var = StringVar() keys = [] for ptn in DeviceSettingsWidget.prop_type_name_map.values(): keys.append(ptn[0]) om = OptionMenu(parent, var, *keys) if current: current = DeviceSettingsWidget.prop_type_name_map[current][0] else: current = next(itervalues(DeviceSettingsWidget.prop_type_name_map)) var.set(current) return om, var @staticmethod def gen_node_link_text(node): # TODO: localize? if node is None: return "-1: NULL" ret = "%s: " % node.id if isinstance(node, BusNode): ret = ret + "Bus, %s" % node.gen_child_name_for_bus() elif isinstance(node, IRQLine): ret = ret + "IRQ: %s" \ % DeviceSettingsWidget.gen_node_link_text(node.src[0]) \ + " -> %s" \ % DeviceSettingsWidget.gen_node_link_text(node.dst[0]) elif isinstance(node, IRQHub): ret = ret + "IRQ Hub" elif isinstance(node, DeviceNode): ret = ret + "Device, %s" % node.qom_type elif isinstance(node, MemoryNode): ret = ret + "Memory, %s" % node.name return ret def refresh(self): SettingsWidget.refresh(self) self.qom_type_var.set(self.dev.qom_type) for p, desc in self.prop2field.items(): desc.e_name.destroy() desc.om_type.destroy() desc.w_val.destroy() desc.bt_del.destroy() self.prop2field = {} # If self.dev.properties is empty the row variable will remain # undefined. row = -1 for row, p in enumerate(self.dev.properties): lpd = PropLineDesc(self, p) lpd.gen_row(row) # Do not use different QOMPropertyValue as the key for the # PropLineDesc of corresponding device-stored QOMPropertyValue # The QOMPropertyValue is used to apply deletion of device # property. self.prop2field[p] = lpd self.bt_add_prop.grid(row = row + 1) # refresh parent bus buses = [ DeviceSettingsWidget.gen_node_link_text(None) ] for n in self.mach.id2node.values(): if not isinstance(n, BusNode): continue buses.append(DeviceSettingsWidget.gen_node_link_text(n)) self.bus_cb.config(values = buses) self.bus_var.set( DeviceSettingsWidget.gen_node_link_text(self.dev.parent_bus) ) bus_row_count = len(self.child_buses_rows) bus_count = len(self.dev.buses) + 1 if bus_row_count < bus_count: if bus_row_count: bld = self.child_buses_rows[-1] bld.v.trace_vdelete("w", bld.obs) del bld.obs for idx in xrange(bus_row_count, bus_count): bld = BusLineDesc(self, idx) self.child_buses_rows.append(bld) bld.gen_row() bld.obs = bld.v.trace_variable("w", self.on_last_child_bus_changed) if bus_count < bus_row_count: for idx in xrange(bus_count, bus_row_count): bld = self.child_buses_rows.pop() bld.delete() bld = self.child_buses_rows[-1] bld.obs = bld.v.trace_variable("w", self.on_last_child_bus_changed) for bld in self.child_buses_rows: bld.update() def on_last_child_bus_changed(self, *args): bld = self.child_buses_rows[-1] bus = self.find_node_by_link_text(bld.v.get()) if not bus is None: # Selecting not NULL child bus means that a child bus was added. # Add new NULL bus string for consequent bus addition. bld.v.trace_vdelete("w", bld.obs) del bld.obs bld = BusLineDesc(self, len(self.child_buses_rows)) self.child_buses_rows.append(bld) bld.gen_row() bld.update() bld.obs = bld.v.trace_variable("w", self.on_last_child_bus_changed) def get_selected_child_buses(self): child_buses = [ bld.v.get() for bld in self.child_buses_rows ] ret = [ self.find_node_by_link_text(t) for t in child_buses if t ] return [ b.id for b in ret if not b is None ] def get_selected_buses(self): ret = self.get_selected_child_buses() parent_bus = self.find_node_by_link_text(self.bus_var.get()) if not parent_bus is None: parent_bus = parent_bus.id if not parent_bus in ret: ret.append(parent_bus) return ret def __apply_internal__(self): # apply parent bus new_bus_text = self.bus_var.get() new_bus = self.find_node_by_link_text(new_bus_text) if not self.dev.parent_bus == new_bus: self.mht.stage(MOp_SetDevParentBus, new_bus, self.dev.id) qom = self.qom_type_var.get() if not self.dev.qom_type == qom: self.mht.stage(MOp_SetDevQOMType, qom, self.dev.id) # Do property removing before addition to prevent conflicts of # property recreation. for p in self.dev.properties: if not p in self.prop2field: self.mht.stage(MOp_DelDevProp, p, self.dev.id) for p, desc in list(self.prop2field.items()): cur_name, cur_type, cur_val = desc.get_current_name(), \ desc.get_current_type(), desc.get_current_val() if p in self.dev.properties.values(): if cur_name != p.prop_name: # Name of property was changed. Recreate it. new_p = QOMPropertyValue(cur_type, cur_name, cur_val) self.mht.stage(MOp_DelDevProp, p, self.dev.id) self.mht.stage(MOp_AddDevProp, new_p, self.dev.id) del self.prop2field[p] self.prop2field[new_p] = desc desc.prop = new_p elif not ( p.prop_type == cur_type and p.prop_val == cur_val ): self.mht.stage( MOp_SetDevProp, cur_type, cur_val, p, self.dev.id ) else: # A completely new property. It was added using # the 'Add' button. new_p = QOMPropertyValue(cur_type, cur_name, cur_val) self.mht.stage(MOp_AddDevProp, new_p, self.dev.id) new_buses = self.get_selected_child_buses() # Changing of buses is made in two steps to allow reordering of buses # during single iteration. step2 = [] # The child bus list is reversed to remove buses from the end to to the # begin. After removing bus from middle consequent indexes becomes # incorrect. for i, bus in reversed([ x for x in enumerate(self.dev.buses) ]): try: new_bus = new_buses.pop(i) except IndexError: # remove i-th bus self.mht.stage( MOp_SetChildBus, self.dev.id, i, -1 ) else: if bus.id == new_bus: continue # change i-th bus (1-st step: remove) self.mht.stage( MOp_SetChildBus, self.dev.id, i, -1 ) # step 2 should be done in increasing index order step2.insert(0, (i, new_bus)) adding = [ x for x in zip(count(len(self.dev.buses)), new_buses) ] for i, new_bus in step2 + adding: # add i-th bus self.mht.stage( MOp_SetChildBus, self.dev.id, i, new_bus ) self.mht.set_sequence_description(_("Device configuration."))
class Task(Frame): def __init__(self, master=None, work=WORK_TM, rest=REST_TM, server=HOST, port=PORT, mode='fascist'): """create the task widget and get things started""" self.lid_state = "open" self.lid_time = time.time() self.interrupt_count = 0 self.then = 0.0 self.old_work = 0.0 self.state = "working" self.cancel_rest = 0 self.log = logging.getLogger(__name__) Frame.__init__(*(self, master)) self.mode = StringVar() self.mode.set(mode) # "fascist" or "friendly" # create main interactor window self.workmeter = Meter(self, background="grey50") self.workmeter.pack() self.work_frame = Frame(self) self.work_frame.pack() self.work_label = Label(self.work_frame, text="Work time:") self.work_label.pack(side=LEFT) self.work_scl = Scale(self.work_frame, orient=HORIZONTAL, from_=1, to=max(45, work), command=self.reset_duration) self.work_scl.set(work) self.work_scl.pack(side=LEFT) self.rest_frame = Frame(self) self.rest_frame.pack() self.rest_label = Label(self.rest_frame, text="Rest time:") self.rest_label.pack(side=LEFT) self.rest_scl = Scale(self.rest_frame, orient=HORIZONTAL, from_=1, to=max(15, rest), command=self.reset_duration) self.rest_scl.set(rest) self.rest_scl.pack(side=LEFT) self.radio_frame = Frame(self) self.radio_frame.pack() self.dictator = Radiobutton(self.radio_frame, text="Fascist", variable=self.mode, value="fascist") self.friend = Radiobutton(self.radio_frame, text="Friendly", variable=self.mode, value="friendly") self.dictator.pack(side=LEFT) self.friend.pack(side=LEFT) self.button_frame = Frame(self) self.button_frame.pack() self.restb = Button(self.button_frame, text="Rest", command=self.rest) self.restb.pack(side=LEFT) self.stopb = Button(self.button_frame, text="Quit", command=self.quit) self.stopb.pack(side=LEFT) self.helpb = Button(self.button_frame, text="Help", command=self.help) self.helpb.pack(side=LEFT) # create the cover window self.cover = Toplevel(background="black") self.cover.withdraw() # user can't resize it self.cover.resizable(0, 0) # when displayed, it should cover all displays (w, h) = (self.winfo_vrootwidth(), self.winfo_vrootheight()) if self.log.getEffectiveLevel() <= logging.DEBUG: # but we reduce it while debugging w, h = (w / 5, h / 5) self.cover.geometry("%dx%d+0+0" % (w, h)) # and it's undecorated self.cover.overrideredirect(1) # cover contains a frame with rest message and meter f = Frame(self.cover, background="black") self.restnote = Label(f, background="black", foreground="white") self.restmeter = Meter(f, background="grey50", height=10, width=200) self.restnote.pack(pady=2) self.restmeter.pack(fill="x", expand=1, pady=2) self.cancel_button = Button(f, text="Cancel Rest", command=self.cancel) f.pack() # initialize interrupt information self.linux_interrupts = 0 # used by the default activity checker self.mouse = None self.setup_server(server, port) # self.last_int is the last time the server was alerted to activity # self.now is the server's notion of the current time # idle time is therefore max(0, self.now-self.last_int) (self.last_int, _w_time, _r_time, self.now) = self.server.get() self.idle = max(0, self.now - self.last_int) self.bgcolor = self["background"] # start the ball rolling self.after(CHK_INT, self.tick) self.work() def setup_server(self, server, port): if server is None: self.server = collector.Collector() self.log.debug("using private Collector()") return self.server = xmlrpc_client.ServerProxy("http://%s:%d" % (server, port)) try: self.server.get() self.log.debug("found existing server") except socket.error: if server in ["", "localhost", "127.0.0.1"]: cmd = "watch-server.py" args = ["-p", "%d" % port] pid = os.spawnvp(os.P_NOWAIT, cmd, args) # wait briefly for server to start time.sleep(0.2) self.server = xmlrpc_client.ServerProxy("http://%s:%d" % (server, port), allow_none=True) # try connecting for _i in range(10): try: self.server.get() atexit.register(os.kill, pid, signal.SIGHUP) self.log.debug("spawned server") return except socket.error: time.sleep(0.1) # nothing else worked, so fall back to an embedded collector self.server = collector.Collector() self.log.debug("using private Collector()") def reset_duration(self, _dummy=None): """reset work/rest interval lengths to current scale values""" wtime = self.work_scl.get() self.workmeter.set_range(self.workmeter.min_val, self.workmeter.min_val + wtime * 60) self.restmeter.set_range( self.restmeter.min_val, self.restmeter.min_val + self.rest_scl.get() * 60) # only time the user can fiddle the work/rest meters is during # the work phase, so the only scale change that matters for extending # or contracting the end of the interval is the work scale try: delta = wtime - self.old_work except AttributeError: delta = 0 self.log.debug(__("then: {} delta: {}m", hhmm(self.then), delta)) self.then = self.then + delta * 60 self.old_work = wtime self.server.put(self.work_scl.get(), self.rest_scl.get()) def work(self): """start the work period""" self.reset_warning() self.restmeter.reset() self.state = "working" self.then = self.now + self.work_scl.get() * 60 self.log.debug(__("work: then: {}", hhmm(self.then))) self.workmeter.set_range(self.now, self.then) self.workmeter.reset() self.cover.withdraw() def warn_work_end(self): """alert user that work period is almost up""" self.set_background("yellow") def reset_warning(self): """back to plain old grey bg""" self.set_background(self.bgcolor) def set_background(self, color): for w in (self, self.work_scl, self.rest_scl, self.dictator, self.friend, self.button_frame, self.stopb, self.restb, self.helpb, self.work_frame, self.rest_frame, self.radio_frame, self.work_label, self.rest_label): w["background"] = color def rest(self): """overlay the screen with a window, preventing typing""" self.cancel_rest = 0 self.workmeter.reset() self.state = "resting" # the user may not have been typing or mousing right up to the # work/rest threshold - give credit for whatever rest time has # already been accumulated resttime = self.rest_scl.get() * 60 - (self.now - self.last_int) if resttime < 0: self.work() return mins, secs = divmod(resttime, 60) self.then = self.now + resttime self.cover.deiconify() self.cover.tkraise() resttext = ("Rest for %dm%02ds please..." % (mins, secs)) self.restnote.configure(text=resttext) self.restmeter.set_range(self.now, self.then) self.restmeter.reset() if self.mode.get() == "friendly": self.cancel_button.pack(pady=2) else: self.cancel_button.pack_forget() self.log.debug( __("rest: state: {} now: {} then: {} active? {}", self.state, hhmm(self.now), hhmm(self.then), self.check_activity())) def help(self): Dialog(self.master, title="Help", content=usageText()) ### ---- define activity checkers here ---- ### # keyed by sys.platform or "default" to return a method that checks # for mouse/keyboard activity _dispatch = {} def check_activity(self): """check mouse/keyboard activity info where possible, call platform-dependent routine to get mouse and keyboard info, otherwise, just return mouse info in all cases, the value returned should evaluate to False if no activity was detected. """ active = False if self.lid_state == "open": dflt = self._dispatch["default"] checker = self._dispatch.get(sys.platform, dflt) active = checker(self) if active: self.server.tick() return active def check_mouse(self): """default checker, just compares current w/ saved mouse pos""" mouse = self.winfo_pointerxy() try: return mouse != self.mouse finally: self.mouse = mouse _dispatch["default"] = check_mouse def get_linux_interrupts(self): count = 0 # Can't seem to find mouse interrupts, so for now, just watch # keyboard and mix add get_mouseinfo() output as a substitute for # mouse interrupts. last_count = self.interrupt_count for line in open("/proc/interrupts"): fields = line.split() if fields[0] == "1:": count = sum(int(fields[n]) for n in range(1, 8)) self.interrupt_count = count break return self.check_mouse() or self.interrupt_count > last_count _dispatch["linux"] = get_linux_interrupts def check_lid_state(self): if os.path.exists(LID_STATE): for line in open(LID_STATE): fields = line.strip().split() if fields[0] == "state:": return self.maybe_change_lid_state(fields[1]) else: self.lid_state = "open" return 0 def maybe_change_lid_state(self, state): """Take necessary action when lid state changes. Return True if lid state changed. """ idle = 0 if state != self.lid_state: self.log.info(__("lid state changed: {}", state)) lid_time = time.time() if state == "open": idle = lid_time - self.lid_time self.log.info(__("idle for {}s", idle)) self.lid_state = state self.lid_time = lid_time return idle def tick(self): """perform periodic checks for activity or state switch""" try: self._tick() finally: self.after(CHK_INT, self.tick) def _tick(self): (self.last_int, work_time, rest_time, self.now) = self.server.get() # check for mouse or keyboard activity idle_time = self.check_lid_state() if idle_time > rest_time * 60: self.log.info( __("The lid is up! Back to work: {}", hhmm(idle_time))) self.work() return active = self.check_activity() idle = max(0, self.now - self.last_int) # check to see if the work/rest scales got adjusted by another # watch instance if (self.work_scl.get() != work_time or self.rest_scl.get() != rest_time): self.work_scl.set(work_time) self.rest_scl.set(rest_time) self.log.debug( __("work: state: {} now: {} then: {} active? {}", self.state, hhmm(self.now), hhmm(self.then), active)) if self.state == "resting": # user explicitly cancelled the rest or the idle period # exceeded the desired rest time if self.cancel_rest or idle > rest_time * 60: self.work() return # make sure the rest window is as far up the window stack as # possible self.cover.tkraise() if idle <= self.idle: # user moved something - extend rest by a second self.then += 1 self.restmeter.set_range(self.restmeter.min_val, self.then) self.restmeter.set(self.now) self.idle = idle # update message to reflect rest time remaining timeleft = int(round(self.then - self.now)) minleft, secleft = divmod(timeleft, 60) resttext = ("Rest for %dm%02ds please..." % (minleft, secleft)) self.restnote.configure(text=resttext) else: # if it's been at least the length of the rest interval # since last interrupt, reset the work interval if idle >= rest_time * 60: self.work() return self.idle = idle if self.now > self.then: # work period expired self.rest() return if self.now + 60 > self.then: # work period nearly expired - warn user self.warn_work_end() else: self.reset_warning() self.restmeter.set(self.now) self.workmeter.set(self.now) def cancel(self): self.cancel_rest = 1
class CollocationsView: _BACKGROUND_COLOUR='#FFF' #white def __init__(self): self.queue = q.Queue() self.model = CollocationsModel(self.queue) self.top = Tk() self._init_top(self.top) self._init_menubar() self._init_widgets(self.top) self.load_corpus(self.model.DEFAULT_CORPUS) self.after = self.top.after(POLL_INTERVAL, self._poll) def _init_top(self, top): top.geometry('550x650+50+50') top.title('NLTK Collocations List') top.bind('<Control-q>', self.destroy) top.protocol('WM_DELETE_WINDOW', self.destroy) top.minsize(550,650) def _init_widgets(self, parent): self.main_frame = Frame(parent, dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1)) self._init_corpus_select(self.main_frame) self._init_results_box(self.main_frame) self._init_paging(self.main_frame) self._init_status(self.main_frame) self.main_frame.pack(fill='both', expand=True) def _init_corpus_select(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.var = StringVar(innerframe) self.var.set(self.model.DEFAULT_CORPUS) Label(innerframe, justify=LEFT, text=' Corpus: ', background=self._BACKGROUND_COLOUR, padx = 2, pady = 1, border = 0).pack(side='left') other_corpora = list(self.model.CORPORA.keys()).remove(self.model.DEFAULT_CORPUS) om = OptionMenu(innerframe, self.var, self.model.DEFAULT_CORPUS, command=self.corpus_selected, *self.model.non_default_corpora()) om['borderwidth'] = 0 om['highlightthickness'] = 1 om.pack(side='left') innerframe.pack(side='top', fill='x', anchor='n') def _init_status(self, parent): self.status = Label(parent, justify=LEFT, relief=SUNKEN, background=self._BACKGROUND_COLOUR, border=0, padx = 1, pady = 0) self.status.pack(side='top', anchor='sw') def _init_menubar(self): self._result_size = IntVar(self.top) menubar = Menu(self.top) filemenu = Menu(menubar, tearoff=0, borderwidth=0) filemenu.add_command(label='Exit', underline=1, command=self.destroy, accelerator='Ctrl-q') menubar.add_cascade(label='File', underline=0, menu=filemenu) editmenu = Menu(menubar, tearoff=0) rescntmenu = Menu(editmenu, tearoff=0) rescntmenu.add_radiobutton(label='20', variable=self._result_size, underline=0, value=20, command=self.set_result_size) rescntmenu.add_radiobutton(label='50', variable=self._result_size, underline=0, value=50, command=self.set_result_size) rescntmenu.add_radiobutton(label='100', variable=self._result_size, underline=0, value=100, command=self.set_result_size) rescntmenu.invoke(1) editmenu.add_cascade(label='Result Count', underline=0, menu=rescntmenu) menubar.add_cascade(label='Edit', underline=0, menu=editmenu) self.top.config(menu=menubar) def set_result_size(self, **kwargs): self.model.result_count = self._result_size.get() def _init_results_box(self, parent): innerframe = Frame(parent) i1 = Frame(innerframe) i2 = Frame(innerframe) vscrollbar = Scrollbar(i1, borderwidth=1) hscrollbar = Scrollbar(i2, borderwidth=1, orient='horiz') self.results_box = Text(i1, font=Font(family='courier', size='16'), state='disabled', borderwidth=1, yscrollcommand=vscrollbar.set, xscrollcommand=hscrollbar.set, wrap='none', width='40', height = '20', exportselection=1) self.results_box.pack(side='left', fill='both', expand=True) vscrollbar.pack(side='left', fill='y', anchor='e') vscrollbar.config(command=self.results_box.yview) hscrollbar.pack(side='left', fill='x', expand=True, anchor='w') hscrollbar.config(command=self.results_box.xview) #there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!! Label(i2, text=' ', background=self._BACKGROUND_COLOUR).pack(side='left', anchor='e') i1.pack(side='top', fill='both', expand=True, anchor='n') i2.pack(side='bottom', fill='x', anchor='s') innerframe.pack(side='top', fill='both', expand=True) def _init_paging(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.prev = prev = Button(innerframe, text='Previous', command=self.previous, width='10', borderwidth=1, highlightthickness=1, state='disabled') prev.pack(side='left', anchor='center') self.next = next = Button(innerframe, text='Next', command=self.__next__, width='10', borderwidth=1, highlightthickness=1, state='disabled') next.pack(side='right', anchor='center') innerframe.pack(side='top', fill='y') self.reset_current_page() def reset_current_page(self): self.current_page = -1 def _poll(self): try: event = self.queue.get(block=False) except q.Empty: pass else: if event == CORPUS_LOADED_EVENT: self.handle_corpus_loaded(event) elif event == ERROR_LOADING_CORPUS_EVENT: self.handle_error_loading_corpus(event) self.after = self.top.after(POLL_INTERVAL, self._poll) def handle_error_loading_corpus(self, event): self.status['text'] = 'Error in loading ' + self.var.get() self.unfreeze_editable() self.clear_results_box() self.freeze_editable() self.reset_current_page() def handle_corpus_loaded(self, event): self.status['text'] = self.var.get() + ' is loaded' self.unfreeze_editable() self.clear_results_box() self.reset_current_page() #self.next() collocations = self.model.next(self.current_page + 1) self.write_results(collocations) self.current_page += 1 def corpus_selected(self, *args): new_selection = self.var.get() self.load_corpus(new_selection) def previous(self): self.freeze_editable() collocations = self.model.prev(self.current_page - 1) self.current_page= self.current_page - 1 self.clear_results_box() self.write_results(collocations) self.unfreeze_editable() def __next__(self): self.freeze_editable() collocations = self.model.next(self.current_page + 1) self.clear_results_box() self.write_results(collocations) self.current_page += 1 self.unfreeze_editable() def load_corpus(self, selection): if self.model.selected_corpus != selection: self.status['text'] = 'Loading ' + selection + '...' self.freeze_editable() self.model.load_corpus(selection) def freeze_editable(self): self.prev['state'] = 'disabled' self.next['state'] = 'disabled' def clear_results_box(self): self.results_box['state'] = 'normal' self.results_box.delete("1.0", END) self.results_box['state'] = 'disabled' def fire_event(self, event): #Firing an event so that rendering of widgets happen in the mainloop thread self.top.event_generate(event, when='tail') def destroy(self, *e): if self.top is None: return self.top.after_cancel(self.after) self.top.destroy() self.top = None def mainloop(self, *args, **kwargs): if in_idle(): return self.top.mainloop(*args, **kwargs) def unfreeze_editable(self): self.set_paging_button_states() def set_paging_button_states(self): if self.current_page == -1 or self.current_page == 0: self.prev['state'] = 'disabled' else: self.prev['state'] = 'normal' if self.model.is_last_page(self.current_page): self.next['state'] = 'disabled' else: self.next['state'] = 'normal' def write_results(self, results): self.results_box['state'] = 'normal' row = 1 for each in results: self.results_box.insert(str(row) + '.0', each[0] + " " + each[1] + "\n") row += 1 self.results_box['state'] = 'disabled'
class CollocationsView: _BACKGROUND_COLOUR = '#FFF' # white def __init__(self): self.queue = q.Queue() self.model = CollocationsModel(self.queue) self.top = Tk() self._init_top(self.top) self._init_menubar() self._init_widgets(self.top) self.load_corpus(self.model.DEFAULT_CORPUS) self.after = self.top.after(POLL_INTERVAL, self._poll) def _init_top(self, top): top.geometry('550x650+50+50') top.title('NLTK Collocations List') top.bind('<Control-q>', self.destroy) top.protocol('WM_DELETE_WINDOW', self.destroy) top.minsize(550, 650) def _init_widgets(self, parent): self.main_frame = Frame( parent, dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1)) self._init_corpus_select(self.main_frame) self._init_results_box(self.main_frame) self._init_paging(self.main_frame) self._init_status(self.main_frame) self.main_frame.pack(fill='both', expand=True) def _init_corpus_select(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.var = StringVar(innerframe) self.var.set(self.model.DEFAULT_CORPUS) Label(innerframe, justify=LEFT, text=' Corpus: ', background=self._BACKGROUND_COLOUR, padx=2, pady=1, border=0).pack(side='left') other_corpora = list(self.model.CORPORA.keys()).remove( self.model.DEFAULT_CORPUS) om = OptionMenu(innerframe, self.var, self.model.DEFAULT_CORPUS, command=self.corpus_selected, *self.model.non_default_corpora()) om['borderwidth'] = 0 om['highlightthickness'] = 1 om.pack(side='left') innerframe.pack(side='top', fill='x', anchor='n') def _init_status(self, parent): self.status = Label(parent, justify=LEFT, relief=SUNKEN, background=self._BACKGROUND_COLOUR, border=0, padx=1, pady=0) self.status.pack(side='top', anchor='sw') def _init_menubar(self): self._result_size = IntVar(self.top) menubar = Menu(self.top) filemenu = Menu(menubar, tearoff=0, borderwidth=0) filemenu.add_command(label='Exit', underline=1, command=self.destroy, accelerator='Ctrl-q') menubar.add_cascade(label='File', underline=0, menu=filemenu) editmenu = Menu(menubar, tearoff=0) rescntmenu = Menu(editmenu, tearoff=0) rescntmenu.add_radiobutton(label='20', variable=self._result_size, underline=0, value=20, command=self.set_result_size) rescntmenu.add_radiobutton(label='50', variable=self._result_size, underline=0, value=50, command=self.set_result_size) rescntmenu.add_radiobutton(label='100', variable=self._result_size, underline=0, value=100, command=self.set_result_size) rescntmenu.invoke(1) editmenu.add_cascade(label='Result Count', underline=0, menu=rescntmenu) menubar.add_cascade(label='Edit', underline=0, menu=editmenu) self.top.config(menu=menubar) def set_result_size(self, **kwargs): self.model.result_count = self._result_size.get() def _init_results_box(self, parent): innerframe = Frame(parent) i1 = Frame(innerframe) i2 = Frame(innerframe) vscrollbar = Scrollbar(i1, borderwidth=1) hscrollbar = Scrollbar(i2, borderwidth=1, orient='horiz') self.results_box = Text(i1, font=Font(family='courier', size='16'), state='disabled', borderwidth=1, yscrollcommand=vscrollbar.set, xscrollcommand=hscrollbar.set, wrap='none', width='40', height='20', exportselection=1) self.results_box.pack(side='left', fill='both', expand=True) vscrollbar.pack(side='left', fill='y', anchor='e') vscrollbar.config(command=self.results_box.yview) hscrollbar.pack(side='left', fill='x', expand=True, anchor='w') hscrollbar.config(command=self.results_box.xview) # there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!! Label(i2, text=' ', background=self._BACKGROUND_COLOUR).pack(side='left', anchor='e') i1.pack(side='top', fill='both', expand=True, anchor='n') i2.pack(side='bottom', fill='x', anchor='s') innerframe.pack(side='top', fill='both', expand=True) def _init_paging(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.prev = prev = Button(innerframe, text='Previous', command=self.previous, width='10', borderwidth=1, highlightthickness=1, state='disabled') prev.pack(side='left', anchor='center') self.next = next = Button(innerframe, text='Next', command=self.__next__, width='10', borderwidth=1, highlightthickness=1, state='disabled') next.pack(side='right', anchor='center') innerframe.pack(side='top', fill='y') self.reset_current_page() def reset_current_page(self): self.current_page = -1 def _poll(self): try: event = self.queue.get(block=False) except q.Empty: pass else: if event == CORPUS_LOADED_EVENT: self.handle_corpus_loaded(event) elif event == ERROR_LOADING_CORPUS_EVENT: self.handle_error_loading_corpus(event) self.after = self.top.after(POLL_INTERVAL, self._poll) def handle_error_loading_corpus(self, event): self.status['text'] = 'Error in loading ' + self.var.get() self.unfreeze_editable() self.clear_results_box() self.freeze_editable() self.reset_current_page() def handle_corpus_loaded(self, event): self.status['text'] = self.var.get() + ' is loaded' self.unfreeze_editable() self.clear_results_box() self.reset_current_page() # self.next() collocations = self.model.next(self.current_page + 1) self.write_results(collocations) self.current_page += 1 def corpus_selected(self, *args): new_selection = self.var.get() self.load_corpus(new_selection) def previous(self): self.freeze_editable() collocations = self.model.prev(self.current_page - 1) self.current_page = self.current_page - 1 self.clear_results_box() self.write_results(collocations) self.unfreeze_editable() def __next__(self): self.freeze_editable() collocations = self.model.next(self.current_page + 1) self.clear_results_box() self.write_results(collocations) self.current_page += 1 self.unfreeze_editable() def load_corpus(self, selection): if self.model.selected_corpus != selection: self.status['text'] = 'Loading ' + selection + '...' self.freeze_editable() self.model.load_corpus(selection) def freeze_editable(self): self.prev['state'] = 'disabled' self.next['state'] = 'disabled' def clear_results_box(self): self.results_box['state'] = 'normal' self.results_box.delete("1.0", END) self.results_box['state'] = 'disabled' def fire_event(self, event): # Firing an event so that rendering of widgets happen in the mainloop thread self.top.event_generate(event, when='tail') def destroy(self, *e): if self.top is None: return self.top.after_cancel(self.after) self.top.destroy() self.top = None def mainloop(self, *args, **kwargs): if in_idle(): return self.top.mainloop(*args, **kwargs) def unfreeze_editable(self): self.set_paging_button_states() def set_paging_button_states(self): if self.current_page == -1 or self.current_page == 0: self.prev['state'] = 'disabled' else: self.prev['state'] = 'normal' if self.model.is_last_page(self.current_page): self.next['state'] = 'disabled' else: self.next['state'] = 'normal' def write_results(self, results): self.results_box['state'] = 'normal' row = 1 for each in results: self.results_box.insert( str(row) + '.0', each[0] + " " + each[1] + "\n") row += 1 self.results_box['state'] = 'disabled'
class ConcordanceSearchView(object): _BACKGROUND_COLOUR='#FFF' #white #Colour of highlighted results _HIGHLIGHT_WORD_COLOUR='#F00' #red _HIGHLIGHT_WORD_TAG='HL_WRD_TAG' _HIGHLIGHT_LABEL_COLOUR='#C0C0C0' # dark grey _HIGHLIGHT_LABEL_TAG='HL_LBL_TAG' #Percentage of text left of the scrollbar position _FRACTION_LEFT_TEXT=0.30 def __init__(self): self.queue = q.Queue() self.model = ConcordanceSearchModel(self.queue) self.top = Tk() self._init_top(self.top) self._init_menubar() self._init_widgets(self.top) self.load_corpus(self.model.DEFAULT_CORPUS) self.after = self.top.after(POLL_INTERVAL, self._poll) def _init_top(self, top): top.geometry('950x680+50+50') top.title('NLTK Concordance Search') top.bind('<Control-q>', self.destroy) top.protocol('WM_DELETE_WINDOW', self.destroy) top.minsize(950,680) def _init_widgets(self, parent): self.main_frame = Frame(parent, dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1)) self._init_corpus_select(self.main_frame) self._init_query_box(self.main_frame) self._init_results_box(self.main_frame) self._init_paging(self.main_frame) self._init_status(self.main_frame) self.main_frame.pack(fill='both', expand=True) def _init_menubar(self): self._result_size = IntVar(self.top) self._cntx_bf_len = IntVar(self.top) self._cntx_af_len = IntVar(self.top) menubar = Menu(self.top) filemenu = Menu(menubar, tearoff=0, borderwidth=0) filemenu.add_command(label='Exit', underline=1, command=self.destroy, accelerator='Ctrl-q') menubar.add_cascade(label='File', underline=0, menu=filemenu) editmenu = Menu(menubar, tearoff=0) rescntmenu = Menu(editmenu, tearoff=0) rescntmenu.add_radiobutton(label='20', variable=self._result_size, underline=0, value=20, command=self.set_result_size) rescntmenu.add_radiobutton(label='50', variable=self._result_size, underline=0, value=50, command=self.set_result_size) rescntmenu.add_radiobutton(label='100', variable=self._result_size, underline=0, value=100, command=self.set_result_size) rescntmenu.invoke(1) editmenu.add_cascade(label='Result Count', underline=0, menu=rescntmenu) cntxmenu = Menu(editmenu, tearoff=0) cntxbfmenu = Menu(cntxmenu, tearoff=0) cntxbfmenu.add_radiobutton(label='60 characters', variable=self._cntx_bf_len, underline=0, value=60, command=self.set_cntx_bf_len) cntxbfmenu.add_radiobutton(label='80 characters', variable=self._cntx_bf_len, underline=0, value=80, command=self.set_cntx_bf_len) cntxbfmenu.add_radiobutton(label='100 characters', variable=self._cntx_bf_len, underline=0, value=100, command=self.set_cntx_bf_len) cntxbfmenu.invoke(1) cntxmenu.add_cascade(label='Before', underline=0, menu=cntxbfmenu) cntxafmenu = Menu(cntxmenu, tearoff=0) cntxafmenu.add_radiobutton(label='70 characters', variable=self._cntx_af_len, underline=0, value=70, command=self.set_cntx_af_len) cntxafmenu.add_radiobutton(label='90 characters', variable=self._cntx_af_len, underline=0, value=90, command=self.set_cntx_af_len) cntxafmenu.add_radiobutton(label='110 characters', variable=self._cntx_af_len, underline=0, value=110, command=self.set_cntx_af_len) cntxafmenu.invoke(1) cntxmenu.add_cascade(label='After', underline=0, menu=cntxafmenu) editmenu.add_cascade(label='Context', underline=0, menu=cntxmenu) menubar.add_cascade(label='Edit', underline=0, menu=editmenu) self.top.config(menu=menubar) def set_result_size(self, **kwargs): self.model.result_count = self._result_size.get() def set_cntx_af_len(self, **kwargs): self._char_after = self._cntx_af_len.get() def set_cntx_bf_len(self, **kwargs): self._char_before = self._cntx_bf_len.get() def _init_corpus_select(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.var = StringVar(innerframe) self.var.set(self.model.DEFAULT_CORPUS) Label(innerframe, justify=LEFT, text=' Corpus: ', background=self._BACKGROUND_COLOUR, padx = 2, pady = 1, border = 0).pack(side='left') other_corpora = list(self.model.CORPORA.keys()).remove(self.model.DEFAULT_CORPUS) om = OptionMenu(innerframe, self.var, self.model.DEFAULT_CORPUS, command=self.corpus_selected, *self.model.non_default_corpora()) om['borderwidth'] = 0 om['highlightthickness'] = 1 om.pack(side='left') innerframe.pack(side='top', fill='x', anchor='n') def _init_status(self, parent): self.status = Label(parent, justify=LEFT, relief=SUNKEN, background=self._BACKGROUND_COLOUR, border=0, padx = 1, pady = 0) self.status.pack(side='top', anchor='sw') def _init_query_box(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) another = Frame(innerframe, background=self._BACKGROUND_COLOUR) self.query_box = Entry(another, width=60) self.query_box.pack(side='left', fill='x', pady=25, anchor='center') self.search_button = Button(another, text='Search', command=self.search, borderwidth=1, highlightthickness=1) self.search_button.pack(side='left', fill='x', pady=25, anchor='center') self.query_box.bind('<KeyPress-Return>', self.search_enter_keypress_handler) another.pack() innerframe.pack(side='top', fill='x', anchor='n') def search_enter_keypress_handler(self, *event): self.search() def _init_results_box(self, parent): innerframe = Frame(parent) i1 = Frame(innerframe) i2 = Frame(innerframe) vscrollbar = Scrollbar(i1, borderwidth=1) hscrollbar = Scrollbar(i2, borderwidth=1, orient='horiz') self.results_box = Text(i1, font=Font(family='courier', size='16'), state='disabled', borderwidth=1, yscrollcommand=vscrollbar.set, xscrollcommand=hscrollbar.set, wrap='none', width='40', height = '20', exportselection=1) self.results_box.pack(side='left', fill='both', expand=True) self.results_box.tag_config(self._HIGHLIGHT_WORD_TAG, foreground=self._HIGHLIGHT_WORD_COLOUR) self.results_box.tag_config(self._HIGHLIGHT_LABEL_TAG, foreground=self._HIGHLIGHT_LABEL_COLOUR) vscrollbar.pack(side='left', fill='y', anchor='e') vscrollbar.config(command=self.results_box.yview) hscrollbar.pack(side='left', fill='x', expand=True, anchor='w') hscrollbar.config(command=self.results_box.xview) #there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!! Label(i2, text=' ', background=self._BACKGROUND_COLOUR).pack(side='left', anchor='e') i1.pack(side='top', fill='both', expand=True, anchor='n') i2.pack(side='bottom', fill='x', anchor='s') innerframe.pack(side='top', fill='both', expand=True) def _init_paging(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.prev = prev = Button(innerframe, text='Previous', command=self.previous, width='10', borderwidth=1, highlightthickness=1, state='disabled') prev.pack(side='left', anchor='center') self.next = next = Button(innerframe, text='Next', command=self.__next__, width='10', borderwidth=1, highlightthickness=1, state='disabled') next.pack(side='right', anchor='center') innerframe.pack(side='top', fill='y') self.current_page = 0 def previous(self): self.clear_results_box() self.freeze_editable() self.model.prev(self.current_page - 1) def __next__(self): self.clear_results_box() self.freeze_editable() self.model.next(self.current_page + 1) def about(self, *e): ABOUT = ("NLTK Concordance Search Demo\n") TITLE = 'About: NLTK Concordance Search Demo' try: from six.moves.tkinter_messagebox import Message Message(message=ABOUT, title=TITLE, parent=self.main_frame).show() except: ShowText(self.top, TITLE, ABOUT) def _bind_event_handlers(self): self.top.bind(CORPUS_LOADED_EVENT, self.handle_corpus_loaded) self.top.bind(SEARCH_TERMINATED_EVENT, self.handle_search_terminated) self.top.bind(SEARCH_ERROR_EVENT, self.handle_search_error) self.top.bind(ERROR_LOADING_CORPUS_EVENT, self.handle_error_loading_corpus) def _poll(self): try: event = self.queue.get(block=False) except q.Empty: pass else: if event == CORPUS_LOADED_EVENT: self.handle_corpus_loaded(event) elif event == SEARCH_TERMINATED_EVENT: self.handle_search_terminated(event) elif event == SEARCH_ERROR_EVENT: self.handle_search_error(event) elif event == ERROR_LOADING_CORPUS_EVENT: self.handle_error_loading_corpus(event) self.after = self.top.after(POLL_INTERVAL, self._poll) def handle_error_loading_corpus(self, event): self.status['text'] = 'Error in loading ' + self.var.get() self.unfreeze_editable() self.clear_all() self.freeze_editable() def handle_corpus_loaded(self, event): self.status['text'] = self.var.get() + ' is loaded' self.unfreeze_editable() self.clear_all() self.query_box.focus_set() def handle_search_terminated(self, event): #todo: refactor the model such that it is less state sensitive results = self.model.get_results() self.write_results(results) self.status['text'] = '' if len(results) == 0: self.status['text'] = 'No results found for ' + self.model.query else: self.current_page = self.model.last_requested_page self.unfreeze_editable() self.results_box.xview_moveto(self._FRACTION_LEFT_TEXT) def handle_search_error(self, event): self.status['text'] = 'Error in query ' + self.model.query self.unfreeze_editable() def corpus_selected(self, *args): new_selection = self.var.get() self.load_corpus(new_selection) def load_corpus(self, selection): if self.model.selected_corpus != selection: self.status['text'] = 'Loading ' + selection + '...' self.freeze_editable() self.model.load_corpus(selection) def search(self): self.current_page = 0 self.clear_results_box() self.model.reset_results() query = self.query_box.get() if (len(query.strip()) == 0): return self.status['text'] = 'Searching for ' + query self.freeze_editable() self.model.search(query, self.current_page + 1, ) def write_results(self, results): self.results_box['state'] = 'normal' row = 1 for each in results: sent, pos1, pos2 = each[0].strip(), each[1], each[2] if len(sent) != 0: if (pos1 < self._char_before): sent, pos1, pos2 = self.pad(sent, pos1, pos2) sentence = sent[pos1-self._char_before:pos1+self._char_after] if not row == len(results): sentence += '\n' self.results_box.insert(str(row) + '.0', sentence) word_markers, label_markers = self.words_and_labels(sent, pos1, pos2) for marker in word_markers: self.results_box.tag_add(self._HIGHLIGHT_WORD_TAG, str(row) + '.' + str(marker[0]), str(row) + '.' + str(marker[1])) for marker in label_markers: self.results_box.tag_add(self._HIGHLIGHT_LABEL_TAG, str(row) + '.' + str(marker[0]), str(row) + '.' + str(marker[1])) row += 1 self.results_box['state'] = 'disabled' def words_and_labels(self, sentence, pos1, pos2): search_exp = sentence[pos1:pos2] words, labels = [], [] labeled_words = search_exp.split(' ') index = 0 for each in labeled_words: if each == '': index += 1 else: word, label = each.split('/') words.append((self._char_before + index, self._char_before + index + len(word))) index += len(word) + 1 labels.append((self._char_before + index, self._char_before + index + len(label))) index += len(label) index += 1 return words, labels def pad(self, sent, hstart, hend): if hstart >= self._char_before: return sent, hstart, hend d = self._char_before - hstart sent = ''.join([' '] * d) + sent return sent, hstart + d, hend + d def destroy(self, *e): if self.top is None: return self.top.after_cancel(self.after) self.top.destroy() self.top = None def clear_all(self): self.query_box.delete(0, END) self.model.reset_query() self.clear_results_box() def clear_results_box(self): self.results_box['state'] = 'normal' self.results_box.delete("1.0", END) self.results_box['state'] = 'disabled' def freeze_editable(self): self.query_box['state'] = 'disabled' self.search_button['state'] = 'disabled' self.prev['state'] = 'disabled' self.next['state'] = 'disabled' def unfreeze_editable(self): self.query_box['state'] = 'normal' self.search_button['state'] = 'normal' self.set_paging_button_states() def set_paging_button_states(self): if self.current_page == 0 or self.current_page == 1: self.prev['state'] = 'disabled' else: self.prev['state'] = 'normal' if self.model.has_more_pages(self.current_page): self.next['state'] = 'normal' else: self.next['state'] = 'disabled' def fire_event(self, event): #Firing an event so that rendering of widgets happen in the mainloop thread self.top.event_generate(event, when='tail') def mainloop(self, *args, **kwargs): if in_idle(): return self.top.mainloop(*args, **kwargs)
class ConcordanceSearchView(object): _BACKGROUND_COLOUR = "#FFF" # white # Colour of highlighted results _HIGHLIGHT_WORD_COLOUR = "#F00" # red _HIGHLIGHT_WORD_TAG = "HL_WRD_TAG" _HIGHLIGHT_LABEL_COLOUR = "#C0C0C0" # dark grey _HIGHLIGHT_LABEL_TAG = "HL_LBL_TAG" # Percentage of text left of the scrollbar position _FRACTION_LEFT_TEXT = 0.30 def __init__(self): self.queue = q.Queue() self.model = ConcordanceSearchModel(self.queue) self.top = Tk() self._init_top(self.top) self._init_menubar() self._init_widgets(self.top) self.load_corpus(self.model.DEFAULT_CORPUS) self.after = self.top.after(POLL_INTERVAL, self._poll) def _init_top(self, top): top.geometry("950x680+50+50") top.title("NLTK Concordance Search") top.bind("<Control-q>", self.destroy) top.protocol("WM_DELETE_WINDOW", self.destroy) top.minsize(950, 680) def _init_widgets(self, parent): self.main_frame = Frame( parent, dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1)) self._init_corpus_select(self.main_frame) self._init_query_box(self.main_frame) self._init_results_box(self.main_frame) self._init_paging(self.main_frame) self._init_status(self.main_frame) self.main_frame.pack(fill="both", expand=True) def _init_menubar(self): self._result_size = IntVar(self.top) self._cntx_bf_len = IntVar(self.top) self._cntx_af_len = IntVar(self.top) menubar = Menu(self.top) filemenu = Menu(menubar, tearoff=0, borderwidth=0) filemenu.add_command(label="Exit", underline=1, command=self.destroy, accelerator="Ctrl-q") menubar.add_cascade(label="File", underline=0, menu=filemenu) editmenu = Menu(menubar, tearoff=0) rescntmenu = Menu(editmenu, tearoff=0) rescntmenu.add_radiobutton( label="20", variable=self._result_size, underline=0, value=20, command=self.set_result_size, ) rescntmenu.add_radiobutton( label="50", variable=self._result_size, underline=0, value=50, command=self.set_result_size, ) rescntmenu.add_radiobutton( label="100", variable=self._result_size, underline=0, value=100, command=self.set_result_size, ) rescntmenu.invoke(1) editmenu.add_cascade(label="Result Count", underline=0, menu=rescntmenu) cntxmenu = Menu(editmenu, tearoff=0) cntxbfmenu = Menu(cntxmenu, tearoff=0) cntxbfmenu.add_radiobutton( label="60 characters", variable=self._cntx_bf_len, underline=0, value=60, command=self.set_cntx_bf_len, ) cntxbfmenu.add_radiobutton( label="80 characters", variable=self._cntx_bf_len, underline=0, value=80, command=self.set_cntx_bf_len, ) cntxbfmenu.add_radiobutton( label="100 characters", variable=self._cntx_bf_len, underline=0, value=100, command=self.set_cntx_bf_len, ) cntxbfmenu.invoke(1) cntxmenu.add_cascade(label="Before", underline=0, menu=cntxbfmenu) cntxafmenu = Menu(cntxmenu, tearoff=0) cntxafmenu.add_radiobutton( label="70 characters", variable=self._cntx_af_len, underline=0, value=70, command=self.set_cntx_af_len, ) cntxafmenu.add_radiobutton( label="90 characters", variable=self._cntx_af_len, underline=0, value=90, command=self.set_cntx_af_len, ) cntxafmenu.add_radiobutton( label="110 characters", variable=self._cntx_af_len, underline=0, value=110, command=self.set_cntx_af_len, ) cntxafmenu.invoke(1) cntxmenu.add_cascade(label="After", underline=0, menu=cntxafmenu) editmenu.add_cascade(label="Context", underline=0, menu=cntxmenu) menubar.add_cascade(label="Edit", underline=0, menu=editmenu) self.top.config(menu=menubar) def set_result_size(self, **kwargs): self.model.result_count = self._result_size.get() def set_cntx_af_len(self, **kwargs): self._char_after = self._cntx_af_len.get() def set_cntx_bf_len(self, **kwargs): self._char_before = self._cntx_bf_len.get() def _init_corpus_select(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.var = StringVar(innerframe) self.var.set(self.model.DEFAULT_CORPUS) Label( innerframe, justify=LEFT, text=" Corpus: ", background=self._BACKGROUND_COLOUR, padx=2, pady=1, border=0, ).pack(side="left") other_corpora = list(self.model.CORPORA.keys()).remove( self.model.DEFAULT_CORPUS) om = OptionMenu(innerframe, self.var, self.model.DEFAULT_CORPUS, command=self.corpus_selected, *self.model.non_default_corpora()) om["borderwidth"] = 0 om["highlightthickness"] = 1 om.pack(side="left") innerframe.pack(side="top", fill="x", anchor="n") def _init_status(self, parent): self.status = Label( parent, justify=LEFT, relief=SUNKEN, background=self._BACKGROUND_COLOUR, border=0, padx=1, pady=0, ) self.status.pack(side="top", anchor="sw") def _init_query_box(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) another = Frame(innerframe, background=self._BACKGROUND_COLOUR) self.query_box = Entry(another, width=60) self.query_box.pack(side="left", fill="x", pady=25, anchor="center") self.search_button = Button( another, text="Search", command=self.search, borderwidth=1, highlightthickness=1, ) self.search_button.pack(side="left", fill="x", pady=25, anchor="center") self.query_box.bind("<KeyPress-Return>", self.search_enter_keypress_handler) another.pack() innerframe.pack(side="top", fill="x", anchor="n") def search_enter_keypress_handler(self, *event): self.search() def _init_results_box(self, parent): innerframe = Frame(parent) i1 = Frame(innerframe) i2 = Frame(innerframe) vscrollbar = Scrollbar(i1, borderwidth=1) hscrollbar = Scrollbar(i2, borderwidth=1, orient="horiz") self.results_box = Text( i1, font=Font(family="courier", size="16"), state="disabled", borderwidth=1, yscrollcommand=vscrollbar.set, xscrollcommand=hscrollbar.set, wrap="none", width="40", height="20", exportselection=1, ) self.results_box.pack(side="left", fill="both", expand=True) self.results_box.tag_config(self._HIGHLIGHT_WORD_TAG, foreground=self._HIGHLIGHT_WORD_COLOUR) self.results_box.tag_config(self._HIGHLIGHT_LABEL_TAG, foreground=self._HIGHLIGHT_LABEL_COLOUR) vscrollbar.pack(side="left", fill="y", anchor="e") vscrollbar.config(command=self.results_box.yview) hscrollbar.pack(side="left", fill="x", expand=True, anchor="w") hscrollbar.config(command=self.results_box.xview) # there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!! Label(i2, text=" ", background=self._BACKGROUND_COLOUR).pack(side="left", anchor="e") i1.pack(side="top", fill="both", expand=True, anchor="n") i2.pack(side="bottom", fill="x", anchor="s") innerframe.pack(side="top", fill="both", expand=True) def _init_paging(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.prev = prev = Button( innerframe, text="Previous", command=self.previous, width="10", borderwidth=1, highlightthickness=1, state="disabled", ) prev.pack(side="left", anchor="center") self.next = next = Button( innerframe, text="Next", command=self.__next__, width="10", borderwidth=1, highlightthickness=1, state="disabled", ) next.pack(side="right", anchor="center") innerframe.pack(side="top", fill="y") self.current_page = 0 def previous(self): self.clear_results_box() self.freeze_editable() self.model.prev(self.current_page - 1) def __next__(self): self.clear_results_box() self.freeze_editable() self.model.next(self.current_page + 1) def about(self, *e): ABOUT = "NLTK Concordance Search Demo\n" TITLE = "About: NLTK Concordance Search Demo" try: from six.moves.tkinter_messagebox import Message Message(message=ABOUT, title=TITLE, parent=self.main_frame).show() except: ShowText(self.top, TITLE, ABOUT) def _bind_event_handlers(self): self.top.bind(CORPUS_LOADED_EVENT, self.handle_corpus_loaded) self.top.bind(SEARCH_TERMINATED_EVENT, self.handle_search_terminated) self.top.bind(SEARCH_ERROR_EVENT, self.handle_search_error) self.top.bind(ERROR_LOADING_CORPUS_EVENT, self.handle_error_loading_corpus) def _poll(self): try: event = self.queue.get(block=False) except q.Empty: pass else: if event == CORPUS_LOADED_EVENT: self.handle_corpus_loaded(event) elif event == SEARCH_TERMINATED_EVENT: self.handle_search_terminated(event) elif event == SEARCH_ERROR_EVENT: self.handle_search_error(event) elif event == ERROR_LOADING_CORPUS_EVENT: self.handle_error_loading_corpus(event) self.after = self.top.after(POLL_INTERVAL, self._poll) def handle_error_loading_corpus(self, event): self.status["text"] = "Error in loading " + self.var.get() self.unfreeze_editable() self.clear_all() self.freeze_editable() def handle_corpus_loaded(self, event): self.status["text"] = self.var.get() + " is loaded" self.unfreeze_editable() self.clear_all() self.query_box.focus_set() def handle_search_terminated(self, event): # todo: refactor the model such that it is less state sensitive results = self.model.get_results() self.write_results(results) self.status["text"] = "" if len(results) == 0: self.status["text"] = "No results found for " + self.model.query else: self.current_page = self.model.last_requested_page self.unfreeze_editable() self.results_box.xview_moveto(self._FRACTION_LEFT_TEXT) def handle_search_error(self, event): self.status["text"] = "Error in query " + self.model.query self.unfreeze_editable() def corpus_selected(self, *args): new_selection = self.var.get() self.load_corpus(new_selection) def load_corpus(self, selection): if self.model.selected_corpus != selection: self.status["text"] = "Loading " + selection + "..." self.freeze_editable() self.model.load_corpus(selection) def search(self): self.current_page = 0 self.clear_results_box() self.model.reset_results() query = self.query_box.get() if len(query.strip()) == 0: return self.status["text"] = "Searching for " + query self.freeze_editable() self.model.search(query, self.current_page + 1) def write_results(self, results): self.results_box["state"] = "normal" row = 1 for each in results: sent, pos1, pos2 = each[0].strip(), each[1], each[2] if len(sent) != 0: if pos1 < self._char_before: sent, pos1, pos2 = self.pad(sent, pos1, pos2) sentence = sent[pos1 - self._char_before:pos1 + self._char_after] if not row == len(results): sentence += "\n" self.results_box.insert(str(row) + ".0", sentence) word_markers, label_markers = self.words_and_labels( sent, pos1, pos2) for marker in word_markers: self.results_box.tag_add( self._HIGHLIGHT_WORD_TAG, str(row) + "." + str(marker[0]), str(row) + "." + str(marker[1]), ) for marker in label_markers: self.results_box.tag_add( self._HIGHLIGHT_LABEL_TAG, str(row) + "." + str(marker[0]), str(row) + "." + str(marker[1]), ) row += 1 self.results_box["state"] = "disabled" def words_and_labels(self, sentence, pos1, pos2): search_exp = sentence[pos1:pos2] words, labels = [], [] labeled_words = search_exp.split(" ") index = 0 for each in labeled_words: if each == "": index += 1 else: word, label = each.split("/") words.append((self._char_before + index, self._char_before + index + len(word))) index += len(word) + 1 labels.append((self._char_before + index, self._char_before + index + len(label))) index += len(label) index += 1 return words, labels def pad(self, sent, hstart, hend): if hstart >= self._char_before: return sent, hstart, hend d = self._char_before - hstart sent = "".join([" "] * d) + sent return sent, hstart + d, hend + d def destroy(self, *e): if self.top is None: return self.top.after_cancel(self.after) self.top.destroy() self.top = None def clear_all(self): self.query_box.delete(0, END) self.model.reset_query() self.clear_results_box() def clear_results_box(self): self.results_box["state"] = "normal" self.results_box.delete("1.0", END) self.results_box["state"] = "disabled" def freeze_editable(self): self.query_box["state"] = "disabled" self.search_button["state"] = "disabled" self.prev["state"] = "disabled" self.next["state"] = "disabled" def unfreeze_editable(self): self.query_box["state"] = "normal" self.search_button["state"] = "normal" self.set_paging_button_states() def set_paging_button_states(self): if self.current_page == 0 or self.current_page == 1: self.prev["state"] = "disabled" else: self.prev["state"] = "normal" if self.model.has_more_pages(self.current_page): self.next["state"] = "normal" else: self.next["state"] = "disabled" def fire_event(self, event): # Firing an event so that rendering of widgets happen in the mainloop thread self.top.event_generate(event, when="tail") def mainloop(self, *args, **kwargs): if in_idle(): return self.top.mainloop(*args, **kwargs)
class LintGui(object): """Build and control a window to interact with pylint""" def __init__(self, root=None): """init""" self.root = root or Tk() self.root.title('Pylint') #reporter self.reporter = None #message queue for output from reporter self.msg_queue = six.moves.queue.Queue() self.msgs = [] self.visible_msgs = [] self.filenames = [] self.rating = StringVar() self.tabs = {} self.report_stream = BasicStream(self) #gui objects self.lb_messages = None self.showhistory = None self.results = None self.btnRun = None self.information_box = None self.convention_box = None self.refactor_box = None self.warning_box = None self.error_box = None self.fatal_box = None self.txtModule = None self.status = None self.msg_type_dict = None self.init_gui() def init_gui(self): """init helper""" window = PanedWindow(self.root, orient="vertical") window.pack(side=TOP, fill=BOTH, expand=True) top_pane = Frame(window) window.add(top_pane) mid_pane = Frame(window) window.add(mid_pane) bottom_pane = Frame(window) window.add(bottom_pane) #setting up frames top_frame = Frame(top_pane) mid_frame = Frame(top_pane) history_frame = Frame(top_pane) radio_frame = Frame(mid_pane) rating_frame = Frame(mid_pane) res_frame = Frame(mid_pane) check_frame = Frame(bottom_pane) msg_frame = Frame(bottom_pane) btn_frame = Frame(bottom_pane) top_frame.pack(side=TOP, fill=X) mid_frame.pack(side=TOP, fill=X) history_frame.pack(side=TOP, fill=BOTH, expand=True) radio_frame.pack(side=TOP, fill=X) rating_frame.pack(side=TOP, fill=X) res_frame.pack(side=TOP, fill=BOTH, expand=True) check_frame.pack(side=TOP, fill=X) msg_frame.pack(side=TOP, fill=BOTH, expand=True) btn_frame.pack(side=TOP, fill=X) # Binding F5 application-wide to run lint self.root.bind('<F5>', self.run_lint) #Message ListBox rightscrollbar = Scrollbar(msg_frame) rightscrollbar.pack(side=RIGHT, fill=Y) bottomscrollbar = Scrollbar(msg_frame, orient=HORIZONTAL) bottomscrollbar.pack(side=BOTTOM, fill=X) self.lb_messages = Listbox(msg_frame, yscrollcommand=rightscrollbar.set, xscrollcommand=bottomscrollbar.set, bg="white") self.lb_messages.bind("<Double-Button-1>", self.show_sourcefile) self.lb_messages.pack(expand=True, fill=BOTH) rightscrollbar.config(command=self.lb_messages.yview) bottomscrollbar.config(command=self.lb_messages.xview) #History ListBoxes rightscrollbar2 = Scrollbar(history_frame) rightscrollbar2.pack(side=RIGHT, fill=Y) bottomscrollbar2 = Scrollbar(history_frame, orient=HORIZONTAL) bottomscrollbar2.pack(side=BOTTOM, fill=X) self.showhistory = Listbox(history_frame, yscrollcommand=rightscrollbar2.set, xscrollcommand=bottomscrollbar2.set, bg="white") self.showhistory.pack(expand=True, fill=BOTH) rightscrollbar2.config(command=self.showhistory.yview) bottomscrollbar2.config(command=self.showhistory.xview) self.showhistory.bind('<Double-Button-1>', self.select_recent_file) self.set_history_window() #status bar self.status = Label(self.root, text="", bd=1, relief=SUNKEN, anchor=W) self.status.pack(side=BOTTOM, fill=X) #labelbl_ratingls lbl_rating_label = Label(rating_frame, text='Rating:') lbl_rating_label.pack(side=LEFT) lbl_rating = Label(rating_frame, textvariable=self.rating) lbl_rating.pack(side=LEFT) Label(mid_frame, text='Recently Used:').pack(side=LEFT) Label(top_frame, text='Module or package').pack(side=LEFT) #file textbox self.txt_module = Entry(top_frame, background='white') self.txt_module.bind('<Return>', self.run_lint) self.txt_module.pack(side=LEFT, expand=True, fill=X) #results box rightscrollbar = Scrollbar(res_frame) rightscrollbar.pack(side=RIGHT, fill=Y) bottomscrollbar = Scrollbar(res_frame, orient=HORIZONTAL) bottomscrollbar.pack(side=BOTTOM, fill=X) self.results = Listbox(res_frame, yscrollcommand=rightscrollbar.set, xscrollcommand=bottomscrollbar.set, bg="white", font="Courier") self.results.pack(expand=True, fill=BOTH, side=BOTTOM) rightscrollbar.config(command=self.results.yview) bottomscrollbar.config(command=self.results.xview) #buttons Button(top_frame, text='Open', command=self.file_open).pack(side=LEFT) Button(top_frame, text='Open Package', command=(lambda: self.file_open(package=True))).pack(side=LEFT) self.btnRun = Button(top_frame, text='Run', command=self.run_lint) self.btnRun.pack(side=LEFT) Button(btn_frame, text='Quit', command=self.quit).pack(side=BOTTOM) #radio buttons self.information_box = IntVar() self.convention_box = IntVar() self.refactor_box = IntVar() self.warning_box = IntVar() self.error_box = IntVar() self.fatal_box = IntVar() i = Checkbutton(check_frame, text="Information", fg=COLORS['(I)'], variable=self.information_box, command=self.refresh_msg_window) c = Checkbutton(check_frame, text="Convention", fg=COLORS['(C)'], variable=self.convention_box, command=self.refresh_msg_window) r = Checkbutton(check_frame, text="Refactor", fg=COLORS['(R)'], variable=self.refactor_box, command=self.refresh_msg_window) w = Checkbutton(check_frame, text="Warning", fg=COLORS['(W)'], variable=self.warning_box, command=self.refresh_msg_window) e = Checkbutton(check_frame, text="Error", fg=COLORS['(E)'], variable=self.error_box, command=self.refresh_msg_window) f = Checkbutton(check_frame, text="Fatal", fg=COLORS['(F)'], variable=self.fatal_box, command=self.refresh_msg_window) i.select() c.select() r.select() w.select() e.select() f.select() i.pack(side=LEFT) c.pack(side=LEFT) r.pack(side=LEFT) w.pack(side=LEFT) e.pack(side=LEFT) f.pack(side=LEFT) #check boxes self.box = StringVar() # XXX should be generated report = Radiobutton(radio_frame, text="Report", variable=self.box, value="Report", command=self.refresh_results_window) raw_met = Radiobutton(radio_frame, text="Raw metrics", variable=self.box, value="Raw metrics", command=self.refresh_results_window) dup = Radiobutton(radio_frame, text="Duplication", variable=self.box, value="Duplication", command=self.refresh_results_window) ext = Radiobutton(radio_frame, text="External dependencies", variable=self.box, value="External dependencies", command=self.refresh_results_window) stat = Radiobutton(radio_frame, text="Statistics by type", variable=self.box, value="Statistics by type", command=self.refresh_results_window) msg_cat = Radiobutton(radio_frame, text="Messages by category", variable=self.box, value="Messages by category", command=self.refresh_results_window) msg = Radiobutton(radio_frame, text="Messages", variable=self.box, value="Messages", command=self.refresh_results_window) source_file = Radiobutton(radio_frame, text="Source File", variable=self.box, value="Source File", command=self.refresh_results_window) report.select() report.grid(column=0, row=0, sticky=W) raw_met.grid(column=1, row=0, sticky=W) dup.grid(column=2, row=0, sticky=W) msg.grid(column=3, row=0, sticky=W) stat.grid(column=0, row=1, sticky=W) msg_cat.grid(column=1, row=1, sticky=W) ext.grid(column=2, row=1, sticky=W) source_file.grid(column=3, row=1, sticky=W) #dictionary for check boxes and associated error term self.msg_type_dict = { 'I': lambda: self.information_box.get() == 1, 'C': lambda: self.convention_box.get() == 1, 'R': lambda: self.refactor_box.get() == 1, 'E': lambda: self.error_box.get() == 1, 'W': lambda: self.warning_box.get() == 1, 'F': lambda: self.fatal_box.get() == 1 } self.txt_module.focus_set() def select_recent_file(self, event): # pylint: disable=unused-argument """adds the selected file in the history listbox to the Module box""" if not self.showhistory.size(): return selected = self.showhistory.curselection() item = self.showhistory.get(selected) #update module self.txt_module.delete(0, END) self.txt_module.insert(0, item) def refresh_msg_window(self): """refresh the message window with current output""" #clear the window self.lb_messages.delete(0, END) self.visible_msgs = [] for msg in self.msgs: if self.msg_type_dict.get(msg.C)(): self.visible_msgs.append(msg) msg_str = convert_to_string(msg) self.lb_messages.insert(END, msg_str) fg_color = COLORS.get(msg_str[:3], 'black') self.lb_messages.itemconfigure(END, fg=fg_color) def refresh_results_window(self): """refresh the results window with current output""" #clear the window self.results.delete(0, END) try: for res in self.tabs[self.box.get()]: self.results.insert(END, res) except KeyError: pass def process_incoming(self): """process the incoming messages from running pylint""" while self.msg_queue.qsize(): try: msg = self.msg_queue.get(0) if msg == "DONE": self.report_stream.output_contents() return False #adding message to list of msgs self.msgs.append(msg) #displaying msg if message type is selected in check box if self.msg_type_dict.get(msg.C)(): self.visible_msgs.append(msg) msg_str = convert_to_string(msg) self.lb_messages.insert(END, msg_str) fg_color = COLORS.get(msg_str[:3], 'black') self.lb_messages.itemconfigure(END, fg=fg_color) except six.moves.queue.Empty: pass return True def periodic_call(self): """determine when to unlock the run button""" if self.process_incoming(): self.root.after(100, self.periodic_call) else: #enabling button so it can be run again self.btnRun.config(state=NORMAL) def mainloop(self): """launch the mainloop of the application""" self.root.mainloop() def quit(self, _=None): """quit the application""" self.root.quit() def halt(self): # pylint: disable=no-self-use """program halt placeholder""" return def file_open(self, package=False, _=None): """launch a file browser""" if not package: filename = askopenfilename(parent=self.root, filetypes=[('pythonfiles', '*.py'), ('allfiles', '*')], title='Select Module') else: filename = askdirectory(title="Select A Folder", mustexist=1) if filename == (): return self.txt_module.delete(0, END) self.txt_module.insert(0, filename) def update_filenames(self): """update the list of recent filenames""" filename = self.txt_module.get() if not filename: filename = os.getcwd() if filename + '\n' in self.filenames: index = self.filenames.index(filename + '\n') self.filenames.pop(index) #ensure only 10 most recent are stored if len(self.filenames) == 10: self.filenames.pop() self.filenames.insert(0, filename + '\n') def set_history_window(self): """update the history window with info from the history file""" #clear the window self.showhistory.delete(0, END) # keep the last 10 most recent files try: view_history = open(HOME + HISTORY, 'r') for hist in view_history.readlines(): if hist not in self.filenames: self.filenames.append(hist) self.showhistory.insert(END, hist.split('\n')[0]) view_history.close() except IOError: # do nothing since history file will be created later return def run_lint(self, _=None): """launches pylint""" self.update_filenames() self.root.configure(cursor='watch') self.reporter = GUIReporter(self, output=self.report_stream) module = self.txt_module.get() if not module: module = os.getcwd() #cleaning up msgs and windows self.msgs = [] self.visible_msgs = [] self.lb_messages.delete(0, END) self.tabs = {} self.results.delete(0, END) self.btnRun.config(state=DISABLED) #setting up a worker thread to run pylint worker = Thread(target=lint_thread, args=( module, self.reporter, self, )) self.periodic_call() worker.start() # Overwrite the .pylint-gui-history file with all the new recently added files # in order from filenames but only save last 10 files write_history = open(HOME + HISTORY, 'w') write_history.writelines(self.filenames) write_history.close() self.set_history_window() self.root.configure(cursor='') def show_sourcefile(self, event=None): # pylint: disable=unused-argument selected = self.lb_messages.curselection() if not selected: return msg = self.visible_msgs[int(selected[0])] scroll = msg.line - 3 if scroll < 0: scroll = 0 self.tabs["Source File"] = open(msg.path, "r").readlines() self.box.set("Source File") self.refresh_results_window() self.results.yview(scroll) self.results.select_set(msg.line - 1)
class LintGui(object): """Build and control a window to interact with pylint""" def __init__(self, root=None): """init""" self.root = root or Tk() self.root.title('Pylint') #reporter self.reporter = None #message queue for output from reporter self.msg_queue = six.moves.queue.Queue() self.msgs = [] self.visible_msgs = [] self.filenames = [] self.rating = StringVar() self.tabs = {} self.report_stream = BasicStream(self) #gui objects self.lb_messages = None self.showhistory = None self.results = None self.btnRun = None self.information_box = None self.convention_box = None self.refactor_box = None self.warning_box = None self.error_box = None self.fatal_box = None self.txtModule = None self.status = None self.msg_type_dict = None self.init_gui() def init_gui(self): """init helper""" window = PanedWindow(self.root, orient="vertical") window.pack(side=TOP, fill=BOTH, expand=True) top_pane = Frame(window) window.add(top_pane) mid_pane = Frame(window) window.add(mid_pane) bottom_pane = Frame(window) window.add(bottom_pane) #setting up frames top_frame = Frame(top_pane) mid_frame = Frame(top_pane) history_frame = Frame(top_pane) radio_frame = Frame(mid_pane) rating_frame = Frame(mid_pane) res_frame = Frame(mid_pane) check_frame = Frame(bottom_pane) msg_frame = Frame(bottom_pane) btn_frame = Frame(bottom_pane) top_frame.pack(side=TOP, fill=X) mid_frame.pack(side=TOP, fill=X) history_frame.pack(side=TOP, fill=BOTH, expand=True) radio_frame.pack(side=TOP, fill=X) rating_frame.pack(side=TOP, fill=X) res_frame.pack(side=TOP, fill=BOTH, expand=True) check_frame.pack(side=TOP, fill=X) msg_frame.pack(side=TOP, fill=BOTH, expand=True) btn_frame.pack(side=TOP, fill=X) # Binding F5 application-wide to run lint self.root.bind('<F5>', self.run_lint) #Message ListBox rightscrollbar = Scrollbar(msg_frame) rightscrollbar.pack(side=RIGHT, fill=Y) bottomscrollbar = Scrollbar(msg_frame, orient=HORIZONTAL) bottomscrollbar.pack(side=BOTTOM, fill=X) self.lb_messages = Listbox( msg_frame, yscrollcommand=rightscrollbar.set, xscrollcommand=bottomscrollbar.set, bg="white") self.lb_messages.bind("<Double-Button-1>", self.show_sourcefile) self.lb_messages.pack(expand=True, fill=BOTH) rightscrollbar.config(command=self.lb_messages.yview) bottomscrollbar.config(command=self.lb_messages.xview) #History ListBoxes rightscrollbar2 = Scrollbar(history_frame) rightscrollbar2.pack(side=RIGHT, fill=Y) bottomscrollbar2 = Scrollbar(history_frame, orient=HORIZONTAL) bottomscrollbar2.pack(side=BOTTOM, fill=X) self.showhistory = Listbox( history_frame, yscrollcommand=rightscrollbar2.set, xscrollcommand=bottomscrollbar2.set, bg="white") self.showhistory.pack(expand=True, fill=BOTH) rightscrollbar2.config(command=self.showhistory.yview) bottomscrollbar2.config(command=self.showhistory.xview) self.showhistory.bind('<Double-Button-1>', self.select_recent_file) self.set_history_window() #status bar self.status = Label(self.root, text="", bd=1, relief=SUNKEN, anchor=W) self.status.pack(side=BOTTOM, fill=X) #labelbl_ratingls lbl_rating_label = Label(rating_frame, text='Rating:') lbl_rating_label.pack(side=LEFT) lbl_rating = Label(rating_frame, textvariable=self.rating) lbl_rating.pack(side=LEFT) Label(mid_frame, text='Recently Used:').pack(side=LEFT) Label(top_frame, text='Module or package').pack(side=LEFT) #file textbox self.txt_module = Entry(top_frame, background='white') self.txt_module.bind('<Return>', self.run_lint) self.txt_module.pack(side=LEFT, expand=True, fill=X) #results box rightscrollbar = Scrollbar(res_frame) rightscrollbar.pack(side=RIGHT, fill=Y) bottomscrollbar = Scrollbar(res_frame, orient=HORIZONTAL) bottomscrollbar.pack(side=BOTTOM, fill=X) self.results = Listbox( res_frame, yscrollcommand=rightscrollbar.set, xscrollcommand=bottomscrollbar.set, bg="white", font="Courier") self.results.pack(expand=True, fill=BOTH, side=BOTTOM) rightscrollbar.config(command=self.results.yview) bottomscrollbar.config(command=self.results.xview) #buttons Button(top_frame, text='Open', command=self.file_open).pack(side=LEFT) Button(top_frame, text='Open Package', command=(lambda: self.file_open(package=True))).pack(side=LEFT) self.btnRun = Button(top_frame, text='Run', command=self.run_lint) self.btnRun.pack(side=LEFT) Button(btn_frame, text='Quit', command=self.quit).pack(side=BOTTOM) #radio buttons self.information_box = IntVar() self.convention_box = IntVar() self.refactor_box = IntVar() self.warning_box = IntVar() self.error_box = IntVar() self.fatal_box = IntVar() i = Checkbutton(check_frame, text="Information", fg=COLORS['(I)'], variable=self.information_box, command=self.refresh_msg_window) c = Checkbutton(check_frame, text="Convention", fg=COLORS['(C)'], variable=self.convention_box, command=self.refresh_msg_window) r = Checkbutton(check_frame, text="Refactor", fg=COLORS['(R)'], variable=self.refactor_box, command=self.refresh_msg_window) w = Checkbutton(check_frame, text="Warning", fg=COLORS['(W)'], variable=self.warning_box, command=self.refresh_msg_window) e = Checkbutton(check_frame, text="Error", fg=COLORS['(E)'], variable=self.error_box, command=self.refresh_msg_window) f = Checkbutton(check_frame, text="Fatal", fg=COLORS['(F)'], variable=self.fatal_box, command=self.refresh_msg_window) i.select() c.select() r.select() w.select() e.select() f.select() i.pack(side=LEFT) c.pack(side=LEFT) r.pack(side=LEFT) w.pack(side=LEFT) e.pack(side=LEFT) f.pack(side=LEFT) #check boxes self.box = StringVar() # XXX should be generated report = Radiobutton( radio_frame, text="Report", variable=self.box, value="Report", command=self.refresh_results_window) raw_met = Radiobutton( radio_frame, text="Raw metrics", variable=self.box, value="Raw metrics", command=self.refresh_results_window) dup = Radiobutton( radio_frame, text="Duplication", variable=self.box, value="Duplication", command=self.refresh_results_window) ext = Radiobutton( radio_frame, text="External dependencies", variable=self.box, value="External dependencies", command=self.refresh_results_window) stat = Radiobutton( radio_frame, text="Statistics by type", variable=self.box, value="Statistics by type", command=self.refresh_results_window) msg_cat = Radiobutton( radio_frame, text="Messages by category", variable=self.box, value="Messages by category", command=self.refresh_results_window) msg = Radiobutton( radio_frame, text="Messages", variable=self.box, value="Messages", command=self.refresh_results_window) source_file = Radiobutton( radio_frame, text="Source File", variable=self.box, value="Source File", command=self.refresh_results_window) report.select() report.grid(column=0, row=0, sticky=W) raw_met.grid(column=1, row=0, sticky=W) dup.grid(column=2, row=0, sticky=W) msg.grid(column=3, row=0, sticky=W) stat.grid(column=0, row=1, sticky=W) msg_cat.grid(column=1, row=1, sticky=W) ext.grid(column=2, row=1, sticky=W) source_file.grid(column=3, row=1, sticky=W) #dictionary for check boxes and associated error term self.msg_type_dict = { 'I': lambda: self.information_box.get() == 1, 'C': lambda: self.convention_box.get() == 1, 'R': lambda: self.refactor_box.get() == 1, 'E': lambda: self.error_box.get() == 1, 'W': lambda: self.warning_box.get() == 1, 'F': lambda: self.fatal_box.get() == 1 } self.txt_module.focus_set() def select_recent_file(self, event): # pylint: disable=unused-argument """adds the selected file in the history listbox to the Module box""" if not self.showhistory.size(): return selected = self.showhistory.curselection() item = self.showhistory.get(selected) #update module self.txt_module.delete(0, END) self.txt_module.insert(0, item) def refresh_msg_window(self): """refresh the message window with current output""" #clear the window self.lb_messages.delete(0, END) self.visible_msgs = [] for msg in self.msgs: if self.msg_type_dict.get(msg.C)(): self.visible_msgs.append(msg) msg_str = convert_to_string(msg) self.lb_messages.insert(END, msg_str) fg_color = COLORS.get(msg_str[:3], 'black') self.lb_messages.itemconfigure(END, fg=fg_color) def refresh_results_window(self): """refresh the results window with current output""" #clear the window self.results.delete(0, END) try: for res in self.tabs[self.box.get()]: self.results.insert(END, res) except KeyError: pass def process_incoming(self): """process the incoming messages from running pylint""" while self.msg_queue.qsize(): try: msg = self.msg_queue.get(0) if msg == "DONE": self.report_stream.output_contents() return False #adding message to list of msgs self.msgs.append(msg) #displaying msg if message type is selected in check box if self.msg_type_dict.get(msg.C)(): self.visible_msgs.append(msg) msg_str = convert_to_string(msg) self.lb_messages.insert(END, msg_str) fg_color = COLORS.get(msg_str[:3], 'black') self.lb_messages.itemconfigure(END, fg=fg_color) except six.moves.queue.Empty: pass return True def periodic_call(self): """determine when to unlock the run button""" if self.process_incoming(): self.root.after(100, self.periodic_call) else: #enabling button so it can be run again self.btnRun.config(state=NORMAL) def mainloop(self): """launch the mainloop of the application""" self.root.mainloop() def quit(self, _=None): """quit the application""" self.root.quit() def halt(self): # pylint: disable=no-self-use """program halt placeholder""" return def file_open(self, package=False, _=None): """launch a file browser""" if not package: filename = askopenfilename(parent=self.root, filetypes=[('pythonfiles', '*.py'), ('allfiles', '*')], title='Select Module') else: filename = askdirectory(title="Select A Folder", mustexist=1) if filename == (): return self.txt_module.delete(0, END) self.txt_module.insert(0, filename) def update_filenames(self): """update the list of recent filenames""" filename = self.txt_module.get() if not filename: filename = os.getcwd() if filename+'\n' in self.filenames: index = self.filenames.index(filename+'\n') self.filenames.pop(index) #ensure only 10 most recent are stored if len(self.filenames) == 10: self.filenames.pop() self.filenames.insert(0, filename+'\n') def set_history_window(self): """update the history window with info from the history file""" #clear the window self.showhistory.delete(0, END) # keep the last 10 most recent files try: view_history = open(HOME+HISTORY, 'r') for hist in view_history.readlines(): if not hist in self.filenames: self.filenames.append(hist) self.showhistory.insert(END, hist.split('\n')[0]) view_history.close() except IOError: # do nothing since history file will be created later return def run_lint(self, _=None): """launches pylint""" self.update_filenames() self.root.configure(cursor='watch') self.reporter = GUIReporter(self, output=self.report_stream) module = self.txt_module.get() if not module: module = os.getcwd() #cleaning up msgs and windows self.msgs = [] self.visible_msgs = [] self.lb_messages.delete(0, END) self.tabs = {} self.results.delete(0, END) self.btnRun.config(state=DISABLED) #setting up a worker thread to run pylint worker = Thread(target=lint_thread, args=(module, self.reporter, self,)) self.periodic_call() worker.start() # Overwrite the .pylint-gui-history file with all the new recently added files # in order from filenames but only save last 10 files write_history = open(HOME+HISTORY, 'w') write_history.writelines(self.filenames) write_history.close() self.set_history_window() self.root.configure(cursor='') def show_sourcefile(self, event=None): # pylint: disable=unused-argument selected = self.lb_messages.curselection() if not selected: return msg = self.visible_msgs[int(selected[0])] scroll = msg.line - 3 if scroll < 0: scroll = 0 self.tabs["Source File"] = open(msg.path, "r").readlines() self.box.set("Source File") self.refresh_results_window() self.results.yview(scroll) self.results.select_set(msg.line - 1)
class BusSettingsWidget(SettingsWidget): def __init__(self, bus, *args, **kw): SettingsWidget.__init__(self, bus, *args, **kw) self.bus = bus self.bus_fr = fr = GUIFrame(self) fr.pack(fill=BOTH, expand=False) fr.columnconfigure(0, weight=0) fr.columnconfigure(1, weight=1) fr.rowconfigure(0, weight=0) l = VarLabel(fr, text=_("Parent device")) l.grid(row=0, column=0, sticky="NES") self.var_parent = StringVar() self.cb_parent = Combobox(fr, textvariable=self.var_parent, state="readonly") self.cb_parent.grid(row=0, column=1, sticky="NEWS") self.fields = [] if type(bus) is BusNode: self.fields.extend([(_("C type"), "c_type", str), (_("Casting macro"), "cast", str), (_("Child name"), "child_name", str), (_("Always show index"), "force_index", bool)]) # Common bus type for row, (text, field, _type) in enumerate(self.fields, start=1): if _type is str: l = VarLabel(fr, text=text) v = StringVar() w = HKEntry(fr, textvariable=v) elif _type is bool: l = None v = BooleanVar() w = VarCheckbutton(fr, text=text, variable=v) fr.rowconfigure(row, weight=0) if l is None: w.grid(row=row, column=0, sticky="NEWS", columnspan=2) else: l.grid(row=row, column=0, sticky="NES") w.grid(row=row, column=1, sticky="NEWS") setattr(self, "w_" + field, w) setattr(self, "var_" + field, v) def __apply_internal__(self): new_parent = self.find_node_by_link_text(self.var_parent.get()) cur_parent = self.bus.parent_device if new_parent is None: new_parent_id = -1 else: new_parent_id = new_parent.id if cur_parent is None: cur_parent_id = -1 else: cur_parent_id = cur_parent.id if not new_parent_id == cur_parent_id: if new_parent_id == -1: if not cur_parent_id == -1: self.mht.disconnect_child_bus(self.bus.id) else: self.mht.append_child_bus(new_parent_id, self.bus.id) for (text, field, _type) in self.fields: new_val = getattr(self, "var_" + field).get() cur_val = getattr(self.bus, field) if new_val == cur_val: continue self.mht.stage(MOp_SetBusAttr, field, new_val, self.bus.id) self.mht.set_sequence_description( _("Bus %d configuration.") % self.bus.id) def refresh(self): SettingsWidget.refresh(self) values = [ DeviceSettingsWidget.gen_node_link_text(dev) for dev \ in ( self.mach.devices + [ None ] ) ] self.cb_parent.config(values=values) self.var_parent.set( DeviceSettingsWidget.gen_node_link_text(self.bus.parent_device)) for (text, field, _type) in self.fields: var = getattr(self, "var_" + field) cur_val = getattr(self.bus, field) var.set(cur_val) def on_changed(self, op, *args, **kw): if isinstance(op, MOp_SetChildBus): if self.bus.id in [op.prev_bus_id, op.bus_id]: self.refresh() return if not isinstance(op, MachineNodeOperation): return if op.writes_node(): if not self.bus.id in self.mach.id2node: self.destroy() else: self.refresh()
class ConcordanceSearchView(object): _BACKGROUND_COLOUR = '#FFF' #white #Colour of highlighted results _HIGHLIGHT_WORD_COLOUR = '#F00' #red _HIGHLIGHT_WORD_TAG = 'HL_WRD_TAG' _HIGHLIGHT_LABEL_COLOUR = '#C0C0C0' # dark grey _HIGHLIGHT_LABEL_TAG = 'HL_LBL_TAG' #Percentage of text left of the scrollbar position _FRACTION_LEFT_TEXT = 0.30 def __init__(self): self.queue = q.Queue() self.model = ConcordanceSearchModel(self.queue) self.top = Tk() self._init_top(self.top) self._init_menubar() self._init_widgets(self.top) self.load_corpus(self.model.DEFAULT_CORPUS) self.after = self.top.after(POLL_INTERVAL, self._poll) def _init_top(self, top): top.geometry('950x680+50+50') top.title('NLTK Concordance Search') top.bind('<Control-q>', self.destroy) top.protocol('WM_DELETE_WINDOW', self.destroy) top.minsize(950, 680) def _init_widgets(self, parent): self.main_frame = Frame( parent, dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1)) self._init_corpus_select(self.main_frame) self._init_query_box(self.main_frame) self._init_results_box(self.main_frame) self._init_paging(self.main_frame) self._init_status(self.main_frame) self.main_frame.pack(fill='both', expand=True) def _init_menubar(self): self._result_size = IntVar(self.top) self._cntx_bf_len = IntVar(self.top) self._cntx_af_len = IntVar(self.top) menubar = Menu(self.top) filemenu = Menu(menubar, tearoff=0, borderwidth=0) filemenu.add_command(label='Exit', underline=1, command=self.destroy, accelerator='Ctrl-q') menubar.add_cascade(label='File', underline=0, menu=filemenu) editmenu = Menu(menubar, tearoff=0) rescntmenu = Menu(editmenu, tearoff=0) rescntmenu.add_radiobutton(label='20', variable=self._result_size, underline=0, value=20, command=self.set_result_size) rescntmenu.add_radiobutton(label='50', variable=self._result_size, underline=0, value=50, command=self.set_result_size) rescntmenu.add_radiobutton(label='100', variable=self._result_size, underline=0, value=100, command=self.set_result_size) rescntmenu.invoke(1) editmenu.add_cascade(label='Result Count', underline=0, menu=rescntmenu) cntxmenu = Menu(editmenu, tearoff=0) cntxbfmenu = Menu(cntxmenu, tearoff=0) cntxbfmenu.add_radiobutton(label='60 characters', variable=self._cntx_bf_len, underline=0, value=60, command=self.set_cntx_bf_len) cntxbfmenu.add_radiobutton(label='80 characters', variable=self._cntx_bf_len, underline=0, value=80, command=self.set_cntx_bf_len) cntxbfmenu.add_radiobutton(label='100 characters', variable=self._cntx_bf_len, underline=0, value=100, command=self.set_cntx_bf_len) cntxbfmenu.invoke(1) cntxmenu.add_cascade(label='Before', underline=0, menu=cntxbfmenu) cntxafmenu = Menu(cntxmenu, tearoff=0) cntxafmenu.add_radiobutton(label='70 characters', variable=self._cntx_af_len, underline=0, value=70, command=self.set_cntx_af_len) cntxafmenu.add_radiobutton(label='90 characters', variable=self._cntx_af_len, underline=0, value=90, command=self.set_cntx_af_len) cntxafmenu.add_radiobutton(label='110 characters', variable=self._cntx_af_len, underline=0, value=110, command=self.set_cntx_af_len) cntxafmenu.invoke(1) cntxmenu.add_cascade(label='After', underline=0, menu=cntxafmenu) editmenu.add_cascade(label='Context', underline=0, menu=cntxmenu) menubar.add_cascade(label='Edit', underline=0, menu=editmenu) self.top.config(menu=menubar) def set_result_size(self, **kwargs): self.model.result_count = self._result_size.get() def set_cntx_af_len(self, **kwargs): self._char_after = self._cntx_af_len.get() def set_cntx_bf_len(self, **kwargs): self._char_before = self._cntx_bf_len.get() def _init_corpus_select(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.var = StringVar(innerframe) self.var.set(self.model.DEFAULT_CORPUS) Label(innerframe, justify=LEFT, text=' Corpus: ', background=self._BACKGROUND_COLOUR, padx=2, pady=1, border=0).pack(side='left') other_corpora = list(self.model.CORPORA.keys()).remove( self.model.DEFAULT_CORPUS) om = OptionMenu(innerframe, self.var, self.model.DEFAULT_CORPUS, command=self.corpus_selected, *self.model.non_default_corpora()) om['borderwidth'] = 0 om['highlightthickness'] = 1 om.pack(side='left') innerframe.pack(side='top', fill='x', anchor='n') def _init_status(self, parent): self.status = Label(parent, justify=LEFT, relief=SUNKEN, background=self._BACKGROUND_COLOUR, border=0, padx=1, pady=0) self.status.pack(side='top', anchor='sw') def _init_query_box(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) another = Frame(innerframe, background=self._BACKGROUND_COLOUR) self.query_box = Entry(another, width=60) self.query_box.pack(side='left', fill='x', pady=25, anchor='center') self.search_button = Button(another, text='Search', command=self.search, borderwidth=1, highlightthickness=1) self.search_button.pack(side='left', fill='x', pady=25, anchor='center') self.query_box.bind('<KeyPress-Return>', self.search_enter_keypress_handler) another.pack() innerframe.pack(side='top', fill='x', anchor='n') def search_enter_keypress_handler(self, *event): self.search() def _init_results_box(self, parent): innerframe = Frame(parent) i1 = Frame(innerframe) i2 = Frame(innerframe) vscrollbar = Scrollbar(i1, borderwidth=1) hscrollbar = Scrollbar(i2, borderwidth=1, orient='horiz') self.results_box = Text(i1, font=Font(family='courier', size='16'), state='disabled', borderwidth=1, yscrollcommand=vscrollbar.set, xscrollcommand=hscrollbar.set, wrap='none', width='40', height='20', exportselection=1) self.results_box.pack(side='left', fill='both', expand=True) self.results_box.tag_config(self._HIGHLIGHT_WORD_TAG, foreground=self._HIGHLIGHT_WORD_COLOUR) self.results_box.tag_config(self._HIGHLIGHT_LABEL_TAG, foreground=self._HIGHLIGHT_LABEL_COLOUR) vscrollbar.pack(side='left', fill='y', anchor='e') vscrollbar.config(command=self.results_box.yview) hscrollbar.pack(side='left', fill='x', expand=True, anchor='w') hscrollbar.config(command=self.results_box.xview) #there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!! Label(i2, text=' ', background=self._BACKGROUND_COLOUR).pack(side='left', anchor='e') i1.pack(side='top', fill='both', expand=True, anchor='n') i2.pack(side='bottom', fill='x', anchor='s') innerframe.pack(side='top', fill='both', expand=True) def _init_paging(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.prev = prev = Button(innerframe, text='Previous', command=self.previous, width='10', borderwidth=1, highlightthickness=1, state='disabled') prev.pack(side='left', anchor='center') self.next = next = Button(innerframe, text='Next', command=self.__next__, width='10', borderwidth=1, highlightthickness=1, state='disabled') next.pack(side='right', anchor='center') innerframe.pack(side='top', fill='y') self.current_page = 0 def previous(self): self.clear_results_box() self.freeze_editable() self.model.prev(self.current_page - 1) def __next__(self): self.clear_results_box() self.freeze_editable() self.model.next(self.current_page + 1) def about(self, *e): ABOUT = ("NLTK Concordance Search Demo\n") TITLE = 'About: NLTK Concordance Search Demo' try: from six.moves.tkinter_messagebox import Message Message(message=ABOUT, title=TITLE, parent=self.main_frame).show() except: ShowText(self.top, TITLE, ABOUT) def _bind_event_handlers(self): self.top.bind(CORPUS_LOADED_EVENT, self.handle_corpus_loaded) self.top.bind(SEARCH_TERMINATED_EVENT, self.handle_search_terminated) self.top.bind(SEARCH_ERROR_EVENT, self.handle_search_error) self.top.bind(ERROR_LOADING_CORPUS_EVENT, self.handle_error_loading_corpus) def _poll(self): try: event = self.queue.get(block=False) except q.Empty: pass else: if event == CORPUS_LOADED_EVENT: self.handle_corpus_loaded(event) elif event == SEARCH_TERMINATED_EVENT: self.handle_search_terminated(event) elif event == SEARCH_ERROR_EVENT: self.handle_search_error(event) elif event == ERROR_LOADING_CORPUS_EVENT: self.handle_error_loading_corpus(event) self.after = self.top.after(POLL_INTERVAL, self._poll) def handle_error_loading_corpus(self, event): self.status['text'] = 'Error in loading ' + self.var.get() self.unfreeze_editable() self.clear_all() self.freeze_editable() def handle_corpus_loaded(self, event): self.status['text'] = self.var.get() + ' is loaded' self.unfreeze_editable() self.clear_all() self.query_box.focus_set() def handle_search_terminated(self, event): #todo: refactor the model such that it is less state sensitive results = self.model.get_results() self.write_results(results) self.status['text'] = '' if len(results) == 0: self.status['text'] = 'No results found for ' + self.model.query else: self.current_page = self.model.last_requested_page self.unfreeze_editable() self.results_box.xview_moveto(self._FRACTION_LEFT_TEXT) def handle_search_error(self, event): self.status['text'] = 'Error in query ' + self.model.query self.unfreeze_editable() def corpus_selected(self, *args): new_selection = self.var.get() self.load_corpus(new_selection) def load_corpus(self, selection): if self.model.selected_corpus != selection: self.status['text'] = 'Loading ' + selection + '...' self.freeze_editable() self.model.load_corpus(selection) def search(self): self.current_page = 0 self.clear_results_box() self.model.reset_results() query = self.query_box.get() if (len(query.strip()) == 0): return self.status['text'] = 'Searching for ' + query self.freeze_editable() self.model.search( query, self.current_page + 1, ) def write_results(self, results): self.results_box['state'] = 'normal' row = 1 for each in results: sent, pos1, pos2 = each[0].strip(), each[1], each[2] if len(sent) != 0: if (pos1 < self._char_before): sent, pos1, pos2 = self.pad(sent, pos1, pos2) sentence = sent[pos1 - self._char_before:pos1 + self._char_after] if not row == len(results): sentence += '\n' self.results_box.insert(str(row) + '.0', sentence) word_markers, label_markers = self.words_and_labels( sent, pos1, pos2) for marker in word_markers: self.results_box.tag_add(self._HIGHLIGHT_WORD_TAG, str(row) + '.' + str(marker[0]), str(row) + '.' + str(marker[1])) for marker in label_markers: self.results_box.tag_add(self._HIGHLIGHT_LABEL_TAG, str(row) + '.' + str(marker[0]), str(row) + '.' + str(marker[1])) row += 1 self.results_box['state'] = 'disabled' def words_and_labels(self, sentence, pos1, pos2): search_exp = sentence[pos1:pos2] words, labels = [], [] labeled_words = search_exp.split(' ') index = 0 for each in labeled_words: if each == '': index += 1 else: word, label = each.split('/') words.append((self._char_before + index, self._char_before + index + len(word))) index += len(word) + 1 labels.append((self._char_before + index, self._char_before + index + len(label))) index += len(label) index += 1 return words, labels def pad(self, sent, hstart, hend): if hstart >= self._char_before: return sent, hstart, hend d = self._char_before - hstart sent = ''.join([' '] * d) + sent return sent, hstart + d, hend + d def destroy(self, *e): if self.top is None: return self.top.after_cancel(self.after) self.top.destroy() self.top = None def clear_all(self): self.query_box.delete(0, END) self.model.reset_query() self.clear_results_box() def clear_results_box(self): self.results_box['state'] = 'normal' self.results_box.delete("1.0", END) self.results_box['state'] = 'disabled' def freeze_editable(self): self.query_box['state'] = 'disabled' self.search_button['state'] = 'disabled' self.prev['state'] = 'disabled' self.next['state'] = 'disabled' def unfreeze_editable(self): self.query_box['state'] = 'normal' self.search_button['state'] = 'normal' self.set_paging_button_states() def set_paging_button_states(self): if self.current_page == 0 or self.current_page == 1: self.prev['state'] = 'disabled' else: self.prev['state'] = 'normal' if self.model.has_more_pages(self.current_page): self.next['state'] = 'normal' else: self.next['state'] = 'disabled' def fire_event(self, event): #Firing an event so that rendering of widgets happen in the mainloop thread self.top.event_generate(event, when='tail') def mainloop(self, *args, **kwargs): if in_idle(): return self.top.mainloop(*args, **kwargs)
class QDCGUIWindow(GUITk): def __init__(self, project=None): GUITk.__init__(self, wait_msec=1) for signame in ["qvc_dirtied", "qvd_failed", "qvc_available"]: s = CoSignal() s.attach(self.signal_dispatcher) setattr(self, "sig_" + signame, s) self.title_suffix = _("Qemu device creator GUI") self.title_suffix.trace_variable("w", self.__on_title_suffix_write__) self.title_not_saved_asterisk = StringVar() self.title_not_saved_asterisk.trace_variable( "w", self.__on_title_suffix_write__) self.saved_operation = None self.var_title = StringVar() self.title(self.var_title) # Hot keys, accelerators self.hk = hotkeys = HotKey(self) hotkeys.add_bindings([ HotKeyBinding( self.invert_history_window, key_code=43, # H description=_("If editing history window is hidden then \ show it else hide it.")), HotKeyBinding( self.on_load, key_code=32, # O description=_("Load project from file.")), HotKeyBinding( self.on_new_project, key_code=57, # N description=_("Create new project.")), HotKeyBinding( self.on_add_description, key_code=40, # D description=_("Add description to the project")), HotKeyBinding( self.on_set_qemu_build_path, key_code=56, # B description=_("Set Qemu build path for the project")), HotKeyBinding( self.on_generate, key_code=42, # G description=_("Launch code generation")), HotKeyBinding( self.on_delete, key_code=24, # Q description=_("Shutdown the application.")), HotKeyBinding( self.undo, key_code=52, # Z description=_("Revert previous editing.")), HotKeyBinding( self.redo, key_code=29, # Y description=_("Make reverted editing again.")), HotKeyBinding( self.on_save, key_code=39, # S description=_("Save project.")), HotKeyBinding( self.rebuild_cache, key_code=27, # R description=_("Rebuild Cache.")) ]) hotkeys.add_key_symbols({ 27: "R", 43: "H", 32: "O", 57: "N", 40: "D", 56: "B", 42: "G", 24: "Q", 52: "Z", 29: "Y", 39: "S" }) # Menu bar menubar = VarMenu(self) filemenu = VarMenu(menubar, tearoff=False) filemenu.add_command(label=_("Add description"), command=self.on_add_description, accelerator=hotkeys.get_keycode_string( self.on_add_description)) filemenu.add_command(label=_("Set Qemu build path"), command=self.on_set_qemu_build_path, accelerator=hotkeys.get_keycode_string( self.on_set_qemu_build_path)) filemenu.add_command(label=_("Generate"), command=self.on_generate, accelerator=hotkeys.get_keycode_string( self.on_generate)) filemenu.add_separator() filemenu.add_command(label=_("New project"), command=self.on_new_project, accelerator=hotkeys.get_keycode_string( self.on_new_project)), filemenu.add_command(label=_("Save"), command=self.on_save, accelerator=hotkeys.get_keycode_string( self.on_save)), filemenu.add_command(label=_("Save project as..."), command=self.on_save_as) filemenu.add_command(label=_("Load"), command=self.on_load, accelerator=hotkeys.get_keycode_string( self.on_load)), filemenu.add_separator() filemenu.add_command(label=_("Quit"), command=self.quit, accelerator=hotkeys.get_keycode_string( self.on_delete)) menubar.add_cascade(label=_("File"), menu=filemenu) self.editmenu = editmenu = VarMenu(menubar, tearoff=False) editmenu.add_command(label=_("Undo"), command=self.undo, accelerator=hotkeys.get_keycode_string(self.undo)) self.undo_idx = editmenu.count - 1 editmenu.add_command(label=_("Redo"), command=self.redo, accelerator=hotkeys.get_keycode_string(self.redo)) self.redo_idx = editmenu.count - 1 editmenu.add_separator() editmenu.add_command(label=_("Rebuild Cache"), command=self.rebuild_cache, accelerator=hotkeys.get_keycode_string( self.rebuild_cache)) editmenu.add_separator() v = self.var_history_window = BooleanVar() v.set(False) self.__on_var_history_window = v.trace_variable( "w", self.__on_var_history_window__) editmenu.add_checkbutton(label=_("Editing history window"), variable=v, accelerator=hotkeys.get_keycode_string( self.invert_history_window)) menubar.add_cascade(label=_("Edit"), menu=editmenu) self.config(menu=menubar) # Widget layout self.grid() self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(0, weight=1) # Status bar self.grid_rowconfigure(1, weight=0) self.sb = sb = Statusbar(self) sb.grid(row=1, column=0, sticky="NEWS") # QEMU build path displaying self.var_qemu_build_path = StringVar() sb.left(self.var_qemu_build_path) # Task counters in status bar self.var_tasks = vt = IntVar() self.var_callers = vc = IntVar() self.var_active_tasks = vat = IntVar() self.var_finished_tasks = vft = IntVar() sb.right(_("Background tasks: ")) sb.right(FormatVar(value="%u") % vt, fg="red") sb.right(FormatVar(value="%u") % vc, fg="orange") sb.right(FormatVar(value="%u") % vat) sb.right(FormatVar(value="%u") % vft, fg="grey") self.task_manager.watch_activated(self.__on_task_state_changed) self.task_manager.watch_finished(self.__on_task_state_changed) self.task_manager.watch_failed(self.__on_task_state_changed) self.task_manager.watch_removed(self.__on_task_state_changed) self.protocol("WM_DELETE_WINDOW", self.on_delete) self.set_project(GUIProject() if project is None else project) self.__update_title__() self.__check_saved_asterisk__() def __on_task_state_changed(self, task): for group in ["tasks", "callers", "active_tasks", "finished_tasks"]: var = getattr(self, "var_" + group) cur_val = len(getattr(self.task_manager, group)) if cur_val != var.get(): var.set(cur_val) def __on_history_window_destroy__(self, *args, **kw): self.var_history_window.trace_vdelete("w", self.__on_var_history_window) self.var_history_window.set(False) self.__on_var_history_window = self.var_history_window.trace_variable( "w", self.__on_var_history_window__) def __on_var_history_window__(self, *args): if self.var_history_window.get(): self._history_window = HistoryWindow(self.pht, self) self._history_window.bind("<Destroy>", self.__on_history_window_destroy__, "+") else: try: self._history_window.destroy() except AttributeError: pass else: del self._history_window def invert_history_window(self): self.var_history_window.set(not self.var_history_window.get()) def __on_title_suffix_write__(self, *args, **kw): self.__update_title__() def __update_title__(self): try: title_prefix = str(self.current_file_name) except AttributeError: title_prefix = "[New project]" self.var_title.set(title_prefix + self.title_not_saved_asterisk.get() + " - " + self.title_suffix.get()) def check_undo_redo(self): can_do = self.pht.can_do() self.hk.set_enabled(self.redo, can_do) if can_do: self.editmenu.entryconfig(self.redo_idx, state="normal") else: self.editmenu.entryconfig(self.redo_idx, state="disabled") can_undo = self.pht.can_undo() self.hk.set_enabled(self.undo, can_undo) if can_undo: self.editmenu.entryconfig(self.undo_idx, state="normal") else: self.editmenu.entryconfig(self.undo_idx, state="disabled") def set_current_file_name(self, file_name=None): if file_name is None: try: del self.current_file_name except AttributeError: pass else: self.current_file_name = file_name self.__update_title__() def set_project(self, project): try: pht = self.pht except AttributeError: # Project was never been set pass else: pht.unwatch_changed(self.on_changed) try: self.pw.destroy() except AttributeError: # project widget was never been created pass # Close history window if self.var_history_window.get(): self.var_history_window.set(False) self.proj = project self.pht = GUIProjectHistoryTracker(self.proj, self.proj.history) self.pw = ProjectWidget(self.proj, self) self.pw.grid(column=0, row=0, sticky="NEWS") self.update_qemu_build_path(project.build_path) self.pht.watch_changed(self.on_changed) self.check_undo_redo() def __saved_asterisk__(self, saved=True): if saved: if self.title_not_saved_asterisk.get() != "": self.title_not_saved_asterisk.set("") else: if self.title_not_saved_asterisk.get() != "*": self.title_not_saved_asterisk.set("*") def __check_saved_asterisk__(self): if self.saved_operation == self.pht.pos: self.__saved_asterisk__(True) else: self.__saved_asterisk__(False) def on_changed(self, op, *args, **kw): self.check_undo_redo() self.__check_saved_asterisk__() if isinstance(op, GUIPOp_SetBuildPath): proj = self.proj if op.p is proj: self.update_qemu_build_path(proj.build_path) def undo(self): self.pht.undo_sequence() def redo(self): self.pht.do_sequence() def rebuild_cache(self): try: qvd = qvd_get(self.proj.build_path) except BadBuildPath as e: showerror( title=_("Cache rebuilding is impossible").get(), message=(_("Selected Qemu build path is bad. Reason: %s") % (e.message)).get()) return # if 'reload_build_path_task' is not failed if hasattr(self.pw, 'reload_build_path_task'): # reload_build_path_task always run at program start that is why # it's in process when it's not in finished_tasks and not failed if self.pw.reload_build_path_task not in self.pw.tm.finished_tasks: ans = askyesno(self, title=_("Cache rebuilding"), message=_("Cache building is already \ in process. Do you want to start cache rebuilding?")) if not ans: return qvd.remove_cache() self.pw.reload_build_path() def on_delete(self): if self.title_not_saved_asterisk.get() == "*": resp = askyesnocancel( title=self.title_suffix.get(), message=_("Current project has unsaved changes." " Would you like to save it?" "\n\nNote that a backup is always saved with name" " project.py in current working directory.").get()) if resp is None: return if resp: self.on_save() if self.title_not_saved_asterisk.get() == "*": # user canceled saving during on_save return try: """ TODO: Note that it is possible to prevent window to close if a generation task is in process. But is it really needed? """ self.task_manager.remove(self._project_generation_task) except AttributeError: pass else: del self._project_generation_task self.task_manager.unwatch_activated(self.__on_task_state_changed) self.task_manager.unwatch_finished(self.__on_task_state_changed) self.task_manager.unwatch_failed(self.__on_task_state_changed) self.task_manager.unwatch_removed(self.__on_task_state_changed) self.quit() def on_add_description(self): d = AddDescriptionDialog(self.pht, self) def on_set_qemu_build_path(self): dir = askdirectory(self, title=_("Select Qemu build path")) if not dir: return self.pht.set_build_path(dir) def on_generate(self): try: t = self._project_generation_task except AttributeError: pass else: if not t.finished: showerror(title=_("Generation is cancelled").get(), message=_("At least one generation task is already \ in process.").get()) return if not self.proj.build_path: showerror( title=_("Generation is impossible").get(), message=_("No Qemu build path is set for the project.").get()) return try: qvd = qvd_get(self.proj.build_path) except BadBuildPath as e: showerror( title=_("Generation is impossible").get(), message=(_("Selected Qemu build path is bad. Reason: %s") % (e.message)).get()) return if not qvd.qvc_is_ready: showerror(title=_("Generation is cancelled").get(), message=_("Qemu version cache is not ready yet. Try \ later.").get()) return self._project_generation_task = ProjectGeneration( self.proj, qvd.src_path, self.sig_qvc_dirtied) self.task_manager.enqueue(self._project_generation_task) def load_project_from_file(self, file_name): loaded_variables = {} try: execfile(file_name, qdt.__dict__, loaded_variables) except Exception as e: raise e else: qproj = None for v in loaded_variables.values(): if isinstance(v, GUIProject): break elif qproj is None and isinstance(v, QProject): qproj = v else: if qproj: v = GUIProject.from_qproject(qproj) else: raise Exception("No project object was loaded") self.set_project(v) self.set_current_file_name(file_name) self.saved_operation = self.pht.pos self.__check_saved_asterisk__() def save_project_to_file(self, file_name): self.pw.refresh_layouts() project = self.proj # Ensure that all machine nodes are in corresponding lists for d in project.descriptions: if isinstance(d, MachineNode): d.link(handle_system_bus=False) PyGenerator().serialize(open(file_name, "wb"), project) self.set_current_file_name(file_name) self.saved_operation = self.pht.pos self.__check_saved_asterisk__() def try_save_project_to_file(self, file_name): try: open(file_name, "wb").close() except IOError as e: if not e.errno == 13: # Do not remove read-only files try: remove(file_name) except: pass showerror(title=_("Cannot save project").get(), message=str(e)) return self.save_project_to_file(file_name) def on_save_as(self): fname = asksaveas(self, [(_("QDC GUI Project defining script"), ".py")], title=_("Save project")) if not fname: return self.try_save_project_to_file(fname) def on_save(self): try: fname = self.current_file_name except AttributeError: self.on_save_as() else: self.try_save_project_to_file(fname) def check_unsaved(self): if self.title_not_saved_asterisk.get() == "*": return askyesno( self, title=self.title_suffix, message= _("Current project has unsaved changes. They will be lost. Continue?" )) else: return True def on_new_project(self): if not self.check_unsaved(): return self.set_project(GUIProject()) self.set_current_file_name() """ There is nothing to save in just created project. So declare that all changes are saved. """ self.saved_operation = self.pht.pos self.__check_saved_asterisk__() def on_load(self): if not self.check_unsaved(): return fname = askopen(self, [(_("QDC GUI Project defining script"), ".py")], title=_("Load project")) if not fname: return try: self.load_project_from_file(fname) except Exception as e: showerror(title=_("Project loading failed").get(), message=str(e)) def update_qemu_build_path(self, bp): if bp is None: self.var_qemu_build_path.set( _("No QEMU build path selected").get()) else: self.var_qemu_build_path.set("QEMU: " + bp)