# Based on iwidgets2.2.0/scrolledtext.itk code. import Tkinter import Pmw class ScrolledText(Pmw.MegaWidget): def __init__(self, parent = None, **kw): # Define the megawidget options. INITOPT = Pmw.INITOPT optiondefs = ( ('labelmargin', 0, INITOPT), ('labelpos', None, INITOPT), ('scrollmargin', 2, INITOPT), ('vscrollmode', 'dynamic', self._vscrollMode), ('hscrollmode', 'dynamic', self._hscrollMode), ) self.defineoptions(kw, optiondefs) # Initialise the base class (after defining the options). Pmw.MegaWidget.__init__(self, parent) # Create the components. interior = self.interior() # Create the text widget. self._textbox = self.createcomponent('text', (), None, Tkinter.Text, (interior,), xscrollcommand=self._scrollTextX, yscrollcommand=self._scrollTextY) self._textbox.grid(row = 2, column = 2, sticky = 'news') interior.grid_rowconfigure(2, weight = 1, minsize = 0) interior.grid_columnconfigure(2, weight = 1, minsize = 0) # Create the vertical scrollbar self._vertScrollbar = self.createcomponent('vertscrollbar', (), 'Scrollbar', Tkinter.Scrollbar, (interior,), orient='vertical', command=self._textbox.yview) # Create the horizontal scrollbar self._horizScrollbar = self.createcomponent('horizscrollbar', (), 'Scrollbar', Tkinter.Scrollbar, (interior,), orient='horizontal', command=self._textbox.xview) self.createlabel(interior, childCols = 3, childRows = 3) # Initialise instance variables. self._vertMode = None self._horizMode = None self._horizRecurse = 0 # Check keywords and initialise options. self.initialiseoptions(ScrolledText) # ====================================================================== # Configuration methods. def _vscrollMode(self): mode = self['vscrollmode'] if mode == 'static': self._vertScrollbarDisplay(1) elif mode == 'dynamic' or mode == 'none': self._vertScrollbarDisplay(0) else: message = 'bad vscrollmode option "%s": should be static, dynamic, or none' % mode raise ValueError, message def _hscrollMode(self): mode = self['hscrollmode'] if mode == 'static': self._horizScrollbarDisplay(1) elif mode == 'dynamic' or mode == 'none': self._horizScrollbarDisplay(0) else: message = 'bad hscrollmode option "%s": should be static, dynamic, or none' % mode raise ValueError, message # ====================================================================== # Public methods. def clear(self): self._textbox.delete('1.0', 'end') def importfile(self, fileName, where = 'end'): file = open(fileName, 'r') self._textbox.insert(where, file.read()) file.close() def exportfile(self, fileName): file = open(fileName, 'w') file.write(self._textbox.get('1.0', 'end')) file.close() def settext(self, text): disabled = (self._textbox.cget('state') == 'disabled') if disabled: self._textbox.configure(state='normal') self._textbox.delete('0.0', 'end') self._textbox.insert('end', text) if disabled: self._textbox.configure(state='disabled') # Override Tkinter.Text get method, so that if it is called with # no arguments, return all text (consistent with other widgets). def get(self, first=None, last=None): if not first and not last: return self._textbox.get('1.0', 'end') else: return self._textbox.get(first, last) # ====================================================================== # Private methods. def _vertScrollbarDisplay(self, mode): if mode != self._vertMode: self._vertMode = mode margin = self['scrollmargin'] interior = self.interior() if self._vertMode: self._vertScrollbar.grid(row = 2, column = 4, sticky = 'news') interior.grid_columnconfigure(3, minsize = margin) else: self._vertScrollbar.grid_forget() interior.grid_columnconfigure(3, minsize = 0) def _horizScrollbarDisplay(self, mode): if mode != self._horizMode: self._horizMode = mode margin = self['scrollmargin'] interior = self.interior() if self._horizMode: self._horizScrollbar.grid(row = 4, column = 2, sticky = 'news') interior.grid_rowconfigure(3, minsize = margin) else: self._horizScrollbar.grid_forget() interior.grid_rowconfigure(3, minsize = 0) def _scrollTextX(self, first, last): self._horizScrollbar.set(first, last) # Infinite recursion must be avoided for dynamic horizontal scrollbars. # A max depth of 1 is allowed for hiding the scrollbar, 2 for showing # it, so that showing always 'wins'! if self['hscrollmode'] == 'dynamic': if first == '0' and last == '1': if self._horizMode: if self._horizRecurse < 2: self._horizRecurse = self._horizRecurse + 1 self._horizScrollbarDisplay(0) self.update_idletasks() self._horizRecurse = self._horizRecurse - 1 else: if not self._horizMode: if self._horizRecurse < 3: self._horizRecurse = self._horizRecurse + 1 self._horizScrollbarDisplay(1) self.update_idletasks() self._horizRecurse = self._horizRecurse - 1 def _scrollTextY(self, first, last): # The text widget calls this just after it has been created, # with arguments ('0', '0'). if first == '0' and last == '0': return self._vertScrollbar.set(first, last) if self['vscrollmode'] == 'dynamic': if first == '0' and last == '1': self._vertScrollbarDisplay(0) else: self._vertScrollbarDisplay(1) # Need to explicitly forward this to override the stupid # (grid_)bbox method inherited from Tkinter.Frame.Grid. def bbox(self, index): return self._textbox.bbox(index) Pmw.forwardmethods(ScrolledText, Tkinter.Text, '_textbox')