jenkins-bot has submitted this change and it was merged.

Change subject: Bug 68645: ported gui.py to core
......................................................................


Bug 68645: ported gui.py to core

Change-Id: Ib9371bbb21e688f0679664b3723aad3514263a28
---
A pywikibot/userinterfaces/gui.py
1 file changed, 436 insertions(+), 0 deletions(-)

Approvals:
  John Vandenberg: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/pywikibot/userinterfaces/gui.py b/pywikibot/userinterfaces/gui.py
new file mode 100644
index 0000000..25f68d4
--- /dev/null
+++ b/pywikibot/userinterfaces/gui.py
@@ -0,0 +1,436 @@
+# -*- coding: utf-8  -*-
+"""
+A window with a unicode textfield where the user can edit.
+
+Useful for editing the contents of an article.
+"""
+
+#
+# (C) Rob W.W. Hooft, 2003
+# (C) Daniel Herding, 2004
+#     Wikiwichtel
+# (C) pywikibot team, 2008-2014
+#
+# Distributed under the terms of the MIT license.
+#
+__version__ = '$Id$'
+#
+
+import Tkinter
+from ScrolledText import ScrolledText
+import tkSimpleDialog
+
+from idlelib import SearchDialog, ReplaceDialog, configDialog
+from idlelib.configHandler import idleConf
+from idlelib.MultiCall import MultiCallCreator
+
+
+class TextEditor(ScrolledText):
+    """A text widget with some editing enhancements.
+
+    A lot of code here is copied or adapted from the idlelib/EditorWindow.py
+    file in the standard Python distribution.
+
+    """
+    def __init__(self, master=None, **kwargs):
+        # get default settings from user's IDLE configuration
+        currentTheme = idleConf.CurrentTheme()
+        textcf = dict(padx=5, wrap='word', undo='True',
+                      foreground=idleConf.GetHighlight(currentTheme,
+                                                       'normal', fgBg='fg'),
+                      background=idleConf.GetHighlight(currentTheme,
+                                                       'normal', fgBg='bg'),
+                      highlightcolor=idleConf.GetHighlight(currentTheme,
+                                                           'hilite', 
fgBg='fg'),
+                      highlightbackground=idleConf.GetHighlight(currentTheme,
+                                                                'hilite',
+                                                                fgBg='bg'),
+                      insertbackground=idleConf.GetHighlight(currentTheme,
+                                                             'cursor',
+                                                             fgBg='fg'),
+                      width=idleConf.GetOption('main', 'EditorWindow', 
'width'),
+                      height=idleConf.GetOption('main', 'EditorWindow',
+                                                'height')
+                      )
+        fontWeight = 'normal'
+        if idleConf.GetOption('main', 'EditorWindow', 'font-bold', 
type='bool'):
+            fontWeight = 'bold'
+        textcf['font'] = (idleConf.GetOption('main', 'EditorWindow', 'font'),
+                          idleConf.GetOption('main', 'EditorWindow',
+                                             'font-size'),
+                          fontWeight)
+        # override defaults with any user-specified settings
+        textcf.update(kwargs)
+        ScrolledText.__init__(self, master, **textcf)
+
+    def add_bindings(self):
+        # due to IDLE dependencies, this can't be called from __init__
+        # add key and event bindings
+        self.bind("<<cut>>", self.cut)
+        self.bind("<<copy>>", self.copy)
+        self.bind("<<paste>>", self.paste)
+        self.bind("<<select-all>>", self.select_all)
+        self.bind("<<remove-selection>>", self.remove_selection)
+        self.bind("<<find>>", self.find_event)
+        self.bind("<<find-again>>", self.find_again_event)
+        self.bind("<<find-selection>>", self.find_selection_event)
+        self.bind("<<replace>>", self.replace_event)
+        self.bind("<<goto-line>>", self.goto_line_event)
+        self.bind("<<del-word-left>>", self.del_word_left)
+        self.bind("<<del-word-right>>", self.del_word_right)
+        keydefs = {'<<copy>>': ['<Control-Key-c>', '<Control-Key-C>'],
+                   '<<cut>>': ['<Control-Key-x>', '<Control-Key-X>'],
+                   '<<del-word-left>>': ['<Control-Key-BackSpace>'],
+                   '<<del-word-right>>': ['<Control-Key-Delete>'],
+                   '<<end-of-file>>': ['<Control-Key-d>', '<Control-Key-D>'],
+                   '<<find-again>>': ['<Control-Key-g>', '<Key-F3>'],
+                   '<<find-selection>>': ['<Control-Key-F3>'],
+                   '<<find>>': ['<Control-Key-f>', '<Control-Key-F>'],
+                   '<<goto-line>>': ['<Alt-Key-g>', '<Meta-Key-g>'],
+                   '<<paste>>': ['<Control-Key-v>', '<Control-Key-V>'],
+                   '<<redo>>': ['<Control-Shift-Key-Z>'],
+                   '<<remove-selection>>': ['<Key-Escape>'],
+                   '<<replace>>': ['<Control-Key-h>', '<Control-Key-H>'],
+                   '<<select-all>>': ['<Control-Key-a>'],
+                   '<<undo>>': ['<Control-Key-z>', '<Control-Key-Z>'],
+                   }
+
+        for event, keylist in keydefs.iteritems():
+            if keylist:
+                self.event_add(event, *keylist)
+
+    def cut(self, event):
+        if self.tag_ranges("sel"):
+            self.event_generate("<<Cut>>")
+        return "break"
+
+    def copy(self, event):
+        if self.tag_ranges("sel"):
+            self.event_generate("<<Copy>>")
+        return "break"
+
+    def paste(self, event):
+        self.event_generate("<<Paste>>")
+        return "break"
+
+    def select_all(self, event=None):
+        self.tag_add("sel", "1.0", "end-1c")
+        self.mark_set("insert", "1.0")
+        self.see("insert")
+        return "break"
+
+    def remove_selection(self, event=None):
+        self.tag_remove("sel", "1.0", "end")
+        self.see("insert")
+
+    def del_word_left(self, event):
+        self.event_generate('<Meta-Delete>')
+        return "break"
+
+    def del_word_right(self, event=None):
+        self.event_generate('<Meta-d>')
+        return "break"
+
+    def find_event(self, event=None):
+        if not self.tag_ranges("sel"):
+            found = self.tag_ranges("found")
+            if found:
+                self.tag_add("sel", found[0], found[1])
+            else:
+                self.tag_add("sel", "1.0", "1.0+1c")
+        SearchDialog.find(self)
+        return "break"
+
+    def find_again_event(self, event=None):
+        SearchDialog.find_again(self)
+        return "break"
+
+    def find_selection_event(self, event=None):
+        SearchDialog.find_selection(self)
+        return "break"
+
+    def replace_event(self, event=None):
+        ReplaceDialog.replace(self)
+        return "break"
+
+    def find_all(self, s):
+        '''
+        Highlight all occurrences of string s, and select the first one. If
+        the string has already been highlighted, jump to the next occurrence
+        after the current selection. (You cannot go backwards using the
+        button, but you can manually place the cursor anywhere in the
+        document to start searching from that point.)
+
+        '''
+        if hasattr(self, "_highlight") and self._highlight == s:
+            try:
+                if self.get(Tkinter.SEL_FIRST, Tkinter.SEL_LAST) == s:
+                    return self.find_selection_event(None)
+                else:
+                    # user must have changed the selection
+                    found = self.tag_nextrange('found', Tkinter.SEL_LAST)
+            except Tkinter.TclError:
+                # user must have unset the selection
+                found = self.tag_nextrange('found', Tkinter.INSERT)
+            if not found:
+                # at last occurrence, scroll back to the top
+                found = self.tag_nextrange('found', 1.0)
+            if found:
+                self.do_highlight(found[0], found[1])
+        else:
+            # find all occurrences of string s;
+            # adapted from O'Reilly's Python in a Nutshell
+            # remove previous uses of tag 'found', if any
+            self.tag_remove('found', '1.0', Tkinter.END)
+            if s:
+                self._highlight = s
+                # start from the beginning (and when we come to the end, stop)
+                idx = '1.0'
+                while True:
+                    # find next occurence, exit loop if no more
+                    idx = self.search(s, idx, nocase=1, stopindex=Tkinter.END)
+                    if not idx:
+                        break
+                    # index right after the end of the occurence
+                    lastidx = '%s+%dc' % (idx, len(s))
+                    # tag the whole occurence (start included, stop excluded)
+                    self.tag_add('found', idx, lastidx)
+                    # prepare to search for next occurence
+                    idx = lastidx
+                # use a red foreground for all the tagged occurences
+                self.tag_config('found', foreground='red')
+                found = self.tag_nextrange('found', 1.0)
+                if found:
+                    self.do_highlight(found[0], found[1])
+
+    def do_highlight(self, start, end):
+        """Select and show the text from index start to index end."""
+        self.see(start)
+        self.tag_remove(Tkinter.SEL, '1.0', Tkinter.END)
+        self.tag_add(Tkinter.SEL, start, end)
+        self.focus_set()
+
+    def goto_line_event(self, event):
+        lineno = tkSimpleDialog.askinteger("Goto", "Go to line number:",
+                                           parent=self)
+        if lineno is None:
+            return "break"
+        if lineno <= 0:
+            self.bell()
+            return "break"
+        self.mark_set("insert", "%d.0" % lineno)
+        self.see("insert")
+
+
+class EditBoxWindow(Tkinter.Frame):
+
+    def __init__(self, parent=None, **kwargs):
+        if parent is None:
+            # create a new window
+            parent = Tkinter.Tk()
+        self.parent = parent
+        Tkinter.Frame.__init__(self, parent)
+        self.editbox = MultiCallCreator(TextEditor)(self, **kwargs)
+        self.editbox.pack(side=Tkinter.TOP)
+        self.editbox.add_bindings()
+        self.bind("<<open-config-dialog>>", self.config_dialog)
+
+        bottom = Tkinter.Frame(parent)
+        # lower left subframe which will contain a textfield and a Search 
button
+        bottom_left_frame = Tkinter.Frame(bottom)
+        self.textfield = Tkinter.Entry(bottom_left_frame)
+        self.textfield.pack(side=Tkinter.LEFT, fill=Tkinter.X, expand=1)
+
+        buttonSearch = Tkinter.Button(bottom_left_frame, text='Find next',
+                                      command=self.find)
+        buttonSearch.pack(side=Tkinter.RIGHT)
+        bottom_left_frame.pack(side=Tkinter.LEFT, expand=1)
+
+        # lower right subframe which will contain OK and Cancel buttons
+        bottom_right_frame = Tkinter.Frame(bottom)
+
+        buttonOK = Tkinter.Button(bottom_right_frame, text='OK',
+                                  command=self.pressedOK)
+        buttonCancel = Tkinter.Button(bottom_right_frame, text='Cancel',
+                                      command=parent.destroy)
+        buttonOK.pack(side=Tkinter.LEFT, fill=Tkinter.X)
+        buttonCancel.pack(side=Tkinter.RIGHT, fill=Tkinter.X)
+        bottom_right_frame.pack(side=Tkinter.RIGHT, expand=1)
+
+        bottom.pack(side=Tkinter.TOP)
+
+        # create a toplevel menu
+        menubar = Tkinter.Menu(self.parent)
+
+        findmenu = Tkinter.Menu(menubar)
+        findmenu.add_command(label="Find",
+                             command=self.editbox.find_event,
+                             accelerator="Ctrl+F",
+                             underline=0)
+        findmenu.add_command(label="Find again",
+                             command=self.editbox.find_again_event,
+                             accelerator="Ctrl+G",
+                             underline=6)
+        findmenu.add_command(label="Find all",
+                             command=self.find_all,
+                             underline=5)
+        findmenu.add_command(label="Find selection",
+                             command=self.editbox.find_selection_event,
+                             accelerator="Ctrl+F3",
+                             underline=5)
+        findmenu.add_command(label="Replace",
+                             command=self.editbox.replace_event,
+                             accelerator="Ctrl+H",
+                             underline=0)
+        menubar.add_cascade(label="Find", menu=findmenu, underline=0)
+
+        editmenu = Tkinter.Menu(menubar)
+        editmenu.add_command(label="Cut",
+                             command=self.editbox.cut,
+                             accelerator="Ctrl+X",
+                             underline=2)
+        editmenu.add_command(label="Copy",
+                             command=self.editbox.copy,
+                             accelerator="Ctrl+C",
+                             underline=0)
+        editmenu.add_command(label="Paste",
+                             command=self.editbox.paste,
+                             accelerator="Ctrl+V",
+                             underline=0)
+        editmenu.add_separator()
+        editmenu.add_command(label="Select all",
+                             command=self.editbox.select_all,
+                             accelerator="Ctrl+A",
+                             underline=7)
+        editmenu.add_command(label="Clear selection",
+                             command=self.editbox.remove_selection,
+                             accelerator="Esc")
+        menubar.add_cascade(label="Edit", menu=editmenu, underline=0)
+
+        optmenu = Tkinter.Menu(menubar)
+        optmenu.add_command(label="Settings...",
+                            command=self.config_dialog,
+                            underline=0)
+        menubar.add_cascade(label="Options", menu=optmenu, underline=0)
+
+        # display the menu
+        self.parent.config(menu=menubar)
+        self.pack()
+
+    def edit(self, text, jumpIndex=None, highlight=None):
+        """
+        Parameters:
+            * text      - a Unicode string
+            * jumpIndex - an integer: position at which to put the caret
+            * highlight - a substring; each occurence will be highlighted
+        """
+        self.text = None
+        # put given text into our textarea
+        self.editbox.insert(Tkinter.END, text)
+        # wait for user to push a button which will destroy (close) the window
+        # enable word wrap
+        self.editbox.tag_add('all', '1.0', Tkinter.END)
+        self.editbox.tag_config('all', wrap=Tkinter.WORD)
+        # start search if required
+        if highlight:
+            self.find_all(highlight)
+        if jumpIndex:
+            print jumpIndex
+            # lines are indexed starting at 1
+            line = text[:jumpIndex].count('\n') + 1
+            column = jumpIndex - (text[:jumpIndex].rfind('\n') + 1)
+            # don't know how to place the caret, but scrolling to the right 
line
+            # should already be helpful.
+            self.editbox.see('%d.%d' % (line, column))
+        # wait for user to push a button which will destroy (close) the window
+        self.parent.mainloop()
+        return self.text
+
+    def find_all(self, target):
+        self.textfield.insert(Tkinter.END, target)
+        self.editbox.find_all(target)
+
+    def find(self):
+        # get text to search for
+        s = self.textfield.get()
+        if s:
+            self.editbox.find_all(s)
+
+    def config_dialog(self, event=None):
+        configDialog.ConfigDialog(self, 'Settings')
+
+    def pressedOK(self):
+        # called when user pushes the OK button.
+        # saves the buffer into a variable, and closes the window.
+        self.text = self.editbox.get('1.0', Tkinter.END)
+        # if the editbox contains ASCII characters only, get() will
+        # return string, otherwise unicode (very annoying). We only want
+        # it to return unicode, so we work around this.
+        if isinstance(self.text, str):
+            self.text = unicode(self.text)
+        self.parent.destroy()
+
+    def debug(self, event=None):
+        self.quit()
+        return "break"
+
+
+# the following class isn't used anywhere in the framework: ####
+class ListBoxWindow:
+
+    # called when user pushes the OK button.
+    # closes the window.
+    def pressedOK(self):
+        # ok closes listbox
+        self.parent.destroy()
+
+    def __init__(self, parent=None):
+        if parent is None:
+            # create a new window
+            parent = Tkinter.Tk()
+        self.parent = parent
+
+        # selectable: only one item
+        self.listbox = Tkinter.Listbox(parent, selectmode=Tkinter.SINGLE)
+        # put list into main frame, using all available space
+        self.listbox.pack(anchor=Tkinter.CENTER, fill=Tkinter.BOTH)
+
+        # lower subframe which will contain one button
+        self.bottom_frame = Tkinter.Frame(parent)
+        self.bottom_frame.pack(side=Tkinter.BOTTOM)
+
+        buttonOK = Tkinter.Button(self.bottom_frame, text='OK', 
command=self.pressedOK)
+        buttonOK.pack(side=Tkinter.LEFT, fill=Tkinter.X)
+        # idea: set title to cur_disambiguation
+
+    def list(self, list):
+        # put list of alternatives into listbox
+        self.list = list
+        # find required area
+        laenge = len(list)
+        maxbreite = 0
+        for i in range(laenge):
+            # cycle through all listitems to find maxlength
+            if len(list[i]) + len(str(i)) > maxbreite:
+                maxbreite = len(list[i]) + len(str(i))
+            # show list as formerly in DOS-window
+            self.listbox.insert(Tkinter.END, str(i) + ' - ' + list[i])
+        # set optimized height & width
+        self.listbox.config(height=laenge, width=maxbreite + 2)
+        # wait for user to push a button which will destroy (close) the window
+        return self.list
+
+
+if __name__ == "__main__":
+    import pywikibot
+    try:
+        root = Tkinter.Tk()
+        root.resizable(width=Tkinter.FALSE, height=Tkinter.FALSE)
+        root.title("pywikibot GUI")
+        page = pywikibot.Page(pywikibot.Site(), u'Main Page')
+        content = page.get()
+        myapp = EditBoxWindow(root)
+        myapp.bind("<Control-d>", myapp.debug)
+        v = myapp.edit(content, highlight=page.title())
+    finally:
+        pywikibot.stopme()

-- 
To view, visit https://gerrit.wikimedia.org/r/151973
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Ib9371bbb21e688f0679664b3723aad3514263a28
Gerrit-PatchSet: 4
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Mpaa <[email protected]>
Gerrit-Reviewer: John Vandenberg <[email protected]>
Gerrit-Reviewer: Ladsgroup <[email protected]>
Gerrit-Reviewer: Merlijn van Deen <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
Pywikibot-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/pywikibot-commits

Reply via email to