[Pywikipedia-l] SVN: [5412] trunk/pywikipedia

russblau at svn.wikimedia.org russblau at svn.wikimedia.org
Tue May 20 18:14:52 UTC 2008


Revision: 5412
Author:   russblau
Date:     2008-05-20 18:14:52 +0000 (Tue, 20 May 2008)

Log Message:
-----------
Since we have our own text editor, we might as well give it some basic functionality.  Remove duplicate code in tkinter_interface.py

Modified Paths:
--------------
    trunk/pywikipedia/gui.py
    trunk/pywikipedia/userinterfaces/tkinter_interface.py

Modified: trunk/pywikipedia/gui.py
===================================================================
--- trunk/pywikipedia/gui.py	2008-05-20 13:27:38 UTC (rev 5411)
+++ trunk/pywikipedia/gui.py	2008-05-20 18:14:52 UTC (rev 5412)
@@ -1,5 +1,3 @@
-#coding: utf-8
-
 '''
 A window with a unicode textfield where the user can e.g. edit
 the contents of an article
@@ -9,64 +7,247 @@
 # (C) Rob W.W. Hooft, 2003
 # (C) Daniel Herding, 2004
 #     Wikiwichtel
+# (C) the PyWikipediabot team, 2008
 #
 # Distributed under the terms of the MIT license.
 #
 __version__='$Id$'
 
 from Tkinter import *
+from ScrolledText import ScrolledText
+import tkSimpleDialog
+
+from idlelib.configHandler import idleConf
+from idlelib import SearchDialog, ReplaceDialog
+
 import wikipedia
 
-class EditBoxWindow:
 
-    # called when user pushes the OK button.
-    # saves the buffer into a variable, and closes the window.
-    def pressedOK(self):
-        self.text = self.editbox.get('1.0', END)
-        # if the editbox contains ASCII characters only, editbox.get() will
-        # return string, otherwise unicode (very annoying). We only want
-        # it to return unicode, so we work around this.  
-        if type(self.text) == type(''):
-            self.text = unicode(self.text, 'ascii')
-        self.myParent.destroy()
+class TextEditor(ScrolledText):
+    """A text widget with some editing enhancements.
 
-    def __init__(self, parent = None):
+    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',
+                      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)
+
+        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.items():
+            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):
+        self.event_generate('<Meta-d>')
+        return "break"
+
+    def find_event(self, event):
+        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):
+        SearchDialog.find_again(self)
+        return "break"
+
+    def find_selection_event(self, event):
+        SearchDialog.find_selection(self)
+        return "break"
+
+    def replace_event(self, event):
+        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(SEL_FIRST, SEL_LAST) == s:
+                    return self.find_selection_event(None)
+                else:
+                    # user must have changed the selection
+                    found = self.tag_nextrange('found', SEL_LAST)
+            except TclError:
+                # user must have unset the selection
+                found = self.tag_nextrange('found', 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', 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=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(SEL, '1.0', END)
+        self.tag_add(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(Frame):
+
+    def __init__(self, parent = None, **kwargs):
         if parent == None:
             # create a new window
             parent = Tk()
-        self.myParent = parent
+        self.parent = parent
+        Frame.__init__(self, parent)
+        self.editbox = TextEditor(self, **kwargs)
+        self.editbox.pack(side=TOP)
 
-        self.top_frame = Frame(parent)
-
-        scrollbar = Scrollbar(self.top_frame)
-        # textarea with vertical scrollbar
-        self.editbox = Text(self.top_frame, yscrollcommand=scrollbar.set)
-        # add scrollbar to main frame, associate it with our editbox
-        scrollbar.pack(side=RIGHT, fill=Y)
-        scrollbar.config(command=self.editbox.yview)
-
-        # put textarea into top frame, using all available space
-        self.editbox.pack(anchor=CENTER, fill=BOTH)
-        self.top_frame.pack(side=TOP)
-
+        bottom = Frame(parent)
         # lower left subframe which will contain a textfield and a Search button
-        self.bottom_left_frame = Frame(parent)
-        self.textfield = Entry(self.bottom_left_frame)
+        bottom_left_frame = Frame(bottom)
+        self.textfield = Entry(bottom_left_frame)
         self.textfield.pack(side=LEFT, fill=X, expand=1)
 
-        buttonSearch = Button(self.bottom_left_frame, text='Find', command=self.find)
+        buttonSearch = Button(bottom_left_frame, text='Find next',
+                              command=self.find)
         buttonSearch.pack(side=RIGHT)
-        self.bottom_left_frame.pack(side=LEFT, expand=1)
-        
+        bottom_left_frame.pack(side=LEFT, expand=1)
+
         # lower right subframe which will contain OK and Cancel buttons
-        self.bottom_right_frame = Frame(parent)
+        bottom_right_frame = Frame(bottom)
 
-        buttonOK = Button(self.bottom_right_frame, text='OK', command=self.pressedOK)
-        buttonCancel = Button(self.bottom_right_frame, text='Cancel', command=parent.destroy)
+        buttonOK = Button(bottom_right_frame, text='OK',
+                          command=self.pressedOK)
+        buttonCancel = Button(bottom_right_frame, text='Cancel',
+                              command=parent.destroy)
         buttonOK.pack(side=LEFT, fill=X)
         buttonCancel.pack(side=RIGHT, fill=X)
-        self.bottom_right_frame.pack(side=RIGHT, expand=1)
+        bottom_right_frame.pack(side=RIGHT, expand=1)
 
+        bottom.pack(side=TOP)
+
         # create a toplevel menu
         # menubar = Menu(root)
         # menubar.add_command(label="Hello!", command=self.hello)
@@ -74,8 +255,8 @@
 
         # display the menu
         # root.config(menu=menubar)
+        self.pack()
 
-
     def edit(self, text, jumpIndex = None, highlight = None):
         """
         Parameters:
@@ -93,7 +274,7 @@
         # start search if required
         if highlight:
             self.textfield.insert(END, highlight)
-            self.find()
+            self.editbox.find_all(highlight)
         if jumpIndex:
             print jumpIndex
             line = text[:jumpIndex].count('\n') + 1 # lines are indexed starting at 1
@@ -102,55 +283,51 @@
             # 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.myParent.mainloop()
+        self.parent.mainloop()
         return self.text 
 
     def find(self):
-        '''
-        Action-function for the Button: highlight all occurences of a string.
-        Taken from O'Reilly's Python in a Nutshell.
-        '''
-        #remove previous uses of tag 'found', if any
-        self.editbox.tag_remove('found', '1.0', END)
-        # get string to look for (if empty, no searching)
+        # get text to search for
         s = self.textfield.get()
         if 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.editbox.search(s, idx, nocase=1, stopindex=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.editbox.tag_add('found', idx, lastidx)
-                # prepare to search for next occurence
-                idx = lastidx
-            # use a red foreground for all the tagged occurencs
-            self.editbox.tag_config('found', foreground='red')
-       
+            self.editbox.find_all(s)
 
+    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', 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.myParent.destroy()
+        self.parent.destroy()
 
     def __init__(self, parent = None):
         if parent == None:
             # create a new window
             parent = Tk()
-        self.myParent = parent
+        self.parent = parent
 
         #selectable: only one item
         self.listbox = Listbox(parent, selectmode=SINGLE)
         # put list into main frame, using all available space
         self.listbox.pack(anchor=CENTER, fill=BOTH)
-        
+
         # lower subframe which will contain one button
         self.bottom_frame = Frame(parent)
         self.bottom_frame.pack(side=BOTTOM)
@@ -178,9 +355,12 @@
 
 
 if __name__=="__main__":
-    root = Tk()
-    page = wikipedia.Page(wikipedia.getSite(), u'Wiki')
-    content = page.get()
-    myapp = EditBoxWindow(root)
-    myapp.edit(content, highlight = page.title())
-
+    try:
+        root = Tk()
+        page = wikipedia.Page(wikipedia.getSite(), u'Wiki')
+        content = page.get()
+        myapp = EditBoxWindow(root)
+        myapp.bind("<Control-d>", myapp.debug)
+        v = myapp.edit(content, highlight = page.title())
+    finally:
+        wikipedia.stopme()

Modified: trunk/pywikipedia/userinterfaces/tkinter_interface.py
===================================================================
--- trunk/pywikipedia/userinterfaces/tkinter_interface.py	2008-05-20 13:27:38 UTC (rev 5411)
+++ trunk/pywikipedia/userinterfaces/tkinter_interface.py	2008-05-20 18:14:52 UTC (rev 5412)
@@ -6,6 +6,7 @@
 import time
 import tkMessageBox, tkSimpleDialog
 from Tkinter import *
+from gui import EditBoxWindow
 
 color_pattern = re.compile(r"%s\{(?P<colorname>\w+)\}" % "\x03")
 
@@ -25,96 +26,6 @@
         self.window.mainloop()
 
 
-class EditBoxWindow:
-    def __init__(self, text):
-        # create a new window if necessary
-        #self.parent = parent or Tk()
-        self.top = Toplevel()
-        self.top_frame = Frame(self.top)
-
-        scrollbar = Scrollbar(self.top_frame)
-        # textarea with vertical scrollbar
-        self.editbox = Text(self.top_frame, yscrollcommand=scrollbar.set)
-        # add scrollbar to main frame, associate it with our editbox
-        scrollbar.pack(side=RIGHT, fill=Y)
-        scrollbar.config(command=self.editbox.yview)
-        # put given text into our textarea
-        self.editbox.insert(END, text)
-
-        # put textarea into top frame, using all available space
-        self.editbox.pack(anchor=CENTER, fill=BOTH)
-        self.top_frame.pack(side=TOP)
-        # enable word wrap
-        self.editbox.tag_add('all', '1.0', END)
-        self.editbox.tag_config('all', wrap=WORD)
-
-        # lower left subframe which will contain a textfield and a Search button
-        self.bottom_left_frame = Frame(self.top)
-        self.textfield = Entry(self.bottom_left_frame)
-        self.textfield.pack(side=LEFT, fill=X, expand=1)
-
-        buttonSearch = Button(self.bottom_left_frame, text='search', command=self.highlight)
-        buttonSearch.pack(side=RIGHT)
-        self.bottom_left_frame.pack(side=LEFT, expand=1)
-
-        # lower right subframe which will contain OK and Cancel buttons
-        self.bottom_right_frame = Frame(self.top)
-
-        buttonOK = Button(self.bottom_right_frame, text='OK', command=self.pressedOK)
-        buttonCancel = Button(self.bottom_right_frame, text='Cancel', command=self.top.destroy)
-        buttonOK.pack(side=LEFT, fill=X)
-        buttonCancel.pack(side=RIGHT, fill=X)
-        self.bottom_right_frame.pack(side=RIGHT, expand=1)
-
-        # create a toplevel menu
-        # menubar = Menu(root)
-        # menubar.add_command(label="Hello!", command=self.hello)
-        # menubar.add_command(label="Quit!", command=self.hello)
-
-        # display the menu
-        # root.config(menu=menubar)
-
-    def edit(self):
-        return self.text
-
-    def highlight(self, searchkey = None):
-        """
-        Action-function for the Button: highlight all occurences of string.
-        Taken from O'Reilly's Python in a Nutshell.
-        """
-        #remove previous uses of tag 'found', if any
-        self.editbox.tag_remove('found', '1.0', END)
-        # get string to look for (if empty, no searching)
-        s = searchkey or self.textfield.get()
-        if s:
-            # start from the beginning (and when we come to the end, stop)
-            idx = '1.0'
-            while True:
-                # highlight next occurence, exit loop if no more
-                idx =self.editbox.search(s, idx, nocase=1, stopindex=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.editbox.tag_add('found', idx, lastidx)
-                # prepare to search for next occurence
-                idx = lastidx
-            # use a red foreground for all the tagged occurencs
-            self.editbox.tag_config('found', foreground='red')
-
-    # called when user pushes the OK button.
-    # saves the buffer into a variable, and closes the window.
-    def pressedOK(self):
-        self.text = self.editbox.get('1.0', END)
-        # if the editbox contains ASCII characters only, editbox.get() will
-        # return string, otherwise unicode (very annoying). We only want
-        # it to return unicode, so we work around this.  
-        if type(self.text) == type(''):
-            self.text = unicode(self.text, 'ascii')
-        self.top.destroy()
-
-
 class CustomMessageBox(tkSimpleDialog.Dialog):
     def __init__(self, master, question, options, hotkeys, default=None):
         self.question = question





More information about the Pywikipedia-l mailing list