I'm building a text editor that has an Undo/Redo feature. To do this, I basically invoke a callback every time the user presses a key; the callback copies the entire contents of the Text widget to a history list. Later, if the user presses Ctrl-Z, the widget steps back through the history list, and if the user presses Ctrl-Y, it steps forward.

Now, I not only want the widget to recall its previous contents when the user does an Undo, but I also want it to remember the position of the insertion cursor and automatically set the cursor to that position. I did this by saving the index (i.e. "1.0") of the cursor each time a new history item is added; then, when that item is recalled, I can set the cursor back to that index. Of course, the Text widget doesn't actually HAVE a method to explicitly set the position of the cursor; so I simulated that behavior by using event_generate to pretend the user had actually clicked at the desired position.

Now, here's the problem. The "pretend clicking" method will only work if it can get the on-screen coordinates of the index, which means the index needs to actually be on screen. To ensure that it is, I used the Text widget's "see" method, which is supposed to scroll the widget so the desired index is visible, IF it isn't already visible.

But the "see" method doesn't work correctly. If the user enters text on the first "screen" (i.e. when the scroll bar is at the top) and then hits Ctrl-Z, no scrolling occurs -- the index is still on screen. But if the added text is on a later "screen", then the widget scrolls so the index is in the center of the screen, even if it's already visible! I tried explicitly using the "bbox" method, which is supposed to return None if the index is off-screen; but that didn't work either!

I think the problem is that when I perform an Undo, I completely erase the Text widget and refill it with the previous history item -- this seems to mess up the indices somehow. Any thoughts as to how I can fix this?

Here's some sample code. Be warned, it's pretty long. To test it, first scroll down a few pages; type some characters into the text widget; scroll down a little more, but make sure the new text is still visible; and then hit Ctrl-Z. The added text is still visible, so the widget SHOULDN'T scroll, but it does anyway.
----------------------------------------------------------------------------------------


#!/usr/bin/env python
import sys, os, Tkinter, types, tkFont, Pmw


def makeResizable(widget): for i in range(0, widget.grid_size()[0]): widget.columnconfigure(i, weight=1) for i in range(0, widget.grid_size()[1]): widget.rowconfigure(i, weight=1)

class seeTest:

   def __init__(self, root):

       self.root = root

self.tableText = Pmw.ScrolledText(self.root,
text_bg='white',
text_wrap='none')
self.tableText.grid(row=0, rowspan=20, column=0, columnspan=20, sticky='nsew')


       fileName = "/home/textfile.txt" #this can be any text file
       file = open(fileName, "r")
       lines = file.readlines()
       file.close()
       for l in lines:
           self.tableText.component('text').insert("end", l)
       self.tableText.component('text').see("1.0")

       self.root.update_idletasks()
       self.history = [(self.tableText.getvalue(), "1.0"), ]
       self.curHist = 0

self.tableText.component('text').bind("<Any-KeyPress>", self.AddHistory)
self.tableText.component('text').bind("<Control-Any-KeyPress>", self.AddHistory)
self.tableText.component('text').bind("<Any-KeyRelease>", self.AddHistory)
self.tableText.component('text').bind("<Control-Any-KeyRelease>", self.AddHistory)
self.tableText.component('text').bind("<KeyPress-Control_L>", self.Dummy)
self.tableText.component('text').bind("<KeyPress-Control_R>", self.Dummy)
self.tableText.component('text').bind("<KeyRelease-Control_L>", self.Dummy)
self.tableText.component('text').bind("<KeyRelease-Control_R>", self.Dummy)
self.tableText.component('text').bind("<Control-KeyPress-z>", self.Undo)
self.tableText.component('text').bind("<Control-KeyPress-Z>", self.Undo)
self.tableText.component('text').bind("<Control-KeyRelease-z>", self.Dummy)
self.tableText.component('text').bind("<Control-KeyRelease-Z>", self.Dummy)


       self.root.update()
       makeResizable(self.root)

def AddHistory(self, *args):
self.tableText.update_idletasks()
text = self.tableText.getvalue()
if text != (self.history[self.curHist])[0]:
while self.curHist < len(self.history)-1:
self.history.pop()
self.root.update_idletasks()
index = str(self.tableText.component('text').index(Tkinter.INSERT))
self.history.append( (text, index) )
self.curHist += 1


   def Undo(self, *args):
       if self.curHist == 0:
           self.root.bell()
       else:
           self.curHist -= 1
           text = (self.history[self.curHist])[0]
           if text[-1] == '\n':
               text = text[:-1]
           if self.curHist == 0:
               index = (self.history[1])[1]
           else:
               index = (self.history[self.curHist])[1]
           self.tableText.component('text').delete("1.0", "end")
           self.tableText.component('text').insert("end", text)

           self.tableText.component('text').update_idletasks()
           self.tableText.component('text').see(index)
       return "break"

   def Dummy(self, *args):
       return "break"

def main():

   root = Tkinter.Tk()
   root.geometry("1000x600+0+0")
   Pmw.initialise(root)
   test = seeTest(root)

   root.mainloop()

if __name__=='__main__': main()
_______________________________________________
Tkinter-discuss mailing list
[email protected]
http://mail.python.org/mailman/listinfo/tkinter-discuss

Reply via email to