Exemplo n.º 1
0
    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)
Exemplo n.º 2
0
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())
Exemplo n.º 3
0
    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()
Exemplo n.º 4
0
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())
Exemplo n.º 5
0
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())
Exemplo n.º 6
0
Arquivo: qdc-gui.py Projeto: ufwt/qdt
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()
Exemplo n.º 7
0
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
Exemplo n.º 8
0
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"
Exemplo n.º 9
0
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."))
Exemplo n.º 10
0
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'
Exemplo n.º 12
0
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)
Exemplo n.º 14
0
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)
Exemplo n.º 15
0
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)
Exemplo n.º 16
0
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)
Exemplo n.º 17
0
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()
Exemplo n.º 18
0
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)
Exemplo n.º 19
0
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)