blob: 4867a80db1abe6da978e62cad9de801627fa8396 [file] [log] [blame]
Kurt B. Kaiser09cb74b2003-06-12 04:20:56 +00001"""Simple text browser for IDLE
2
Steven M. Gava44d3d1a2001-07-31 06:59:02 +00003"""
Tal Einat604e7b92018-09-25 15:10:14 +03004from tkinter import Toplevel, Text, TclError,\
5 HORIZONTAL, VERTICAL, N, S, E, W
csabella42bc8be2017-06-29 18:42:17 -04006from tkinter.ttk import Frame, Scrollbar, Button
Terry Jan Reedy82c46152016-06-22 04:54:18 -04007from tkinter.messagebox import showerror
Steven M. Gava44d3d1a2001-07-31 06:59:02 +00008
Tal Einatc87d9f42018-09-23 15:23:15 +03009from idlelib.colorizer import color_config
10
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040011
Tal Einat604e7b92018-09-25 15:10:14 +030012class AutoHiddenScrollbar(Scrollbar):
13 """A scrollbar that is automatically hidden when not needed.
14
15 Only the grid geometry manager is supported.
16 """
17 def set(self, lo, hi):
18 if float(lo) > 0.0 or float(hi) < 1.0:
19 self.grid()
20 else:
21 self.grid_remove()
22 super().set(lo, hi)
23
24 def pack(self, **kwargs):
25 raise TclError(f'{self.__class__.__name__} does not support "pack"')
26
27 def place(self, **kwargs):
28 raise TclError(f'{self.__class__.__name__} does not support "place"')
29
30
csabella42bc8be2017-06-29 18:42:17 -040031class TextFrame(Frame):
32 "Display text with scrollbar."
33
Tal Einat604e7b92018-09-25 15:10:14 +030034 def __init__(self, parent, rawtext, wrap='word'):
csabella42bc8be2017-06-29 18:42:17 -040035 """Create a frame for Textview.
36
37 parent - parent widget for this frame
38 rawtext - text to display
39 """
40 super().__init__(parent)
41 self['relief'] = 'sunken'
42 self['height'] = 700
csabella42bc8be2017-06-29 18:42:17 -040043
Tal Einat604e7b92018-09-25 15:10:14 +030044 self.text = text = Text(self, wrap=wrap, highlightthickness=0)
Tal Einatc87d9f42018-09-23 15:23:15 +030045 color_config(text)
Tal Einat604e7b92018-09-25 15:10:14 +030046 text.grid(row=0, column=0, sticky=N+S+E+W)
47 self.grid_rowconfigure(0, weight=1)
48 self.grid_columnconfigure(0, weight=1)
csabella42bc8be2017-06-29 18:42:17 -040049 text.insert(0.0, rawtext)
50 text['state'] = 'disabled'
51 text.focus_set()
52
Tal Einat604e7b92018-09-25 15:10:14 +030053 # vertical scrollbar
54 self.yscroll = yscroll = AutoHiddenScrollbar(self, orient=VERTICAL,
55 takefocus=False,
56 command=text.yview)
57 text['yscrollcommand'] = yscroll.set
58 yscroll.grid(row=0, column=1, sticky=N+S)
59
60 if wrap == 'none':
61 # horizontal scrollbar
62 self.xscroll = xscroll = AutoHiddenScrollbar(self, orient=HORIZONTAL,
63 takefocus=False,
64 command=text.xview)
65 text['xscrollcommand'] = xscroll.set
66 xscroll.grid(row=1, column=0, sticky=E+W)
csabella42bc8be2017-06-29 18:42:17 -040067
68
69class ViewFrame(Frame):
70 "Display TextFrame and Close button."
Tal Einat604e7b92018-09-25 15:10:14 +030071 def __init__(self, parent, text, wrap='word'):
csabella42bc8be2017-06-29 18:42:17 -040072 super().__init__(parent)
73 self.parent = parent
74 self.bind('<Return>', self.ok)
75 self.bind('<Escape>', self.ok)
Tal Einat604e7b92018-09-25 15:10:14 +030076 self.textframe = TextFrame(self, text, wrap=wrap)
csabella42bc8be2017-06-29 18:42:17 -040077 self.button_ok = button_ok = Button(
78 self, text='Close', command=self.ok, takefocus=False)
79 self.textframe.pack(side='top', expand=True, fill='both')
80 button_ok.pack(side='bottom')
81
82 def ok(self, event=None):
83 """Dismiss text viewer dialog."""
84 self.parent.destroy()
85
86
87class ViewWindow(Toplevel):
Terry Jan Reedya3623c82016-08-31 19:45:39 -040088 "A simple text viewer dialog for IDLE."
Kurt B. Kaiser09cb74b2003-06-12 04:20:56 +000089
Tal Einat604e7b92018-09-25 15:10:14 +030090 def __init__(self, parent, title, text, modal=True, wrap='word',
Terry Jan Reedybfebfd82017-09-30 17:37:53 -040091 *, _htest=False, _utest=False):
Louie Luba365da2017-05-18 05:51:31 +080092 """Show the given text in a scrollable window with a 'close' button.
Guido van Rossum8ce8a782007-11-01 19:42:39 +000093
Louie Luba365da2017-05-18 05:51:31 +080094 If modal is left True, users cannot interact with other windows
95 until the textview window is closed.
Terry Jan Reedy537e2c82014-06-05 03:38:34 -040096
csabella0aa0a062017-05-28 06:50:55 -040097 parent - parent of this dialog
98 title - string which is title of popup dialog
99 text - text to display in dialog
Tal Einat604e7b92018-09-25 15:10:14 +0300100 wrap - type of text wrapping to use ('word', 'char' or 'none')
Terry Jan Reedy537e2c82014-06-05 03:38:34 -0400101 _htest - bool; change box location when running htest.
Louie Luba365da2017-05-18 05:51:31 +0800102 _utest - bool; don't wait_window when running unittest.
Steven M. Gavad721c482001-07-31 10:46:53 +0000103 """
csabella42bc8be2017-06-29 18:42:17 -0400104 super().__init__(parent)
105 self['borderwidth'] = 5
Terry Jan Reedya3623c82016-08-31 19:45:39 -0400106 # Place dialog below parent if running htest.
csabella42bc8be2017-06-29 18:42:17 -0400107 x = parent.winfo_rootx() + 10
108 y = parent.winfo_rooty() + (10 if not _htest else 100)
109 self.geometry(f'=750x500+{x}+{y}')
Steven M. Gava44d3d1a2001-07-31 06:59:02 +0000110
Steven M. Gavad721c482001-07-31 10:46:53 +0000111 self.title(title)
Tal Einat604e7b92018-09-25 15:10:14 +0300112 self.viewframe = ViewFrame(self, text, wrap=wrap)
csabella0aa0a062017-05-28 06:50:55 -0400113 self.protocol("WM_DELETE_WINDOW", self.ok)
csabella42bc8be2017-06-29 18:42:17 -0400114 self.button_ok = button_ok = Button(self, text='Close',
115 command=self.ok, takefocus=False)
116 self.viewframe.pack(side='top', expand=True, fill='both')
Terry Jan Reedye91e7632012-02-05 15:14:20 -0500117
Tal Einatdd743692018-08-02 10:30:06 +0300118 self.is_modal = modal
119 if self.is_modal:
Terry Jan Reedye91e7632012-02-05 15:14:20 -0500120 self.transient(parent)
121 self.grab_set()
Louie Luba365da2017-05-18 05:51:31 +0800122 if not _utest:
123 self.wait_window()
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000124
csabella0aa0a062017-05-28 06:50:55 -0400125 def ok(self, event=None):
126 """Dismiss text viewer dialog."""
Tal Einatdd743692018-08-02 10:30:06 +0300127 if self.is_modal:
128 self.grab_release()
Steven M. Gavad721c482001-07-31 10:46:53 +0000129 self.destroy()
Steven M. Gava44d3d1a2001-07-31 06:59:02 +0000130
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000131
Tal Einat604e7b92018-09-25 15:10:14 +0300132def view_text(parent, title, text, modal=True, wrap='word', _utest=False):
csabella42bc8be2017-06-29 18:42:17 -0400133 """Create text viewer for given text.
csabella0aa0a062017-05-28 06:50:55 -0400134
135 parent - parent of this dialog
136 title - string which is the title of popup dialog
137 text - text to display in this dialog
Tal Einat604e7b92018-09-25 15:10:14 +0300138 wrap - type of text wrapping to use ('word', 'char' or 'none')
csabella0aa0a062017-05-28 06:50:55 -0400139 modal - controls if users can interact with other windows while this
140 dialog is displayed
141 _utest - bool; controls wait_window on unittest
142 """
Tal Einat604e7b92018-09-25 15:10:14 +0300143 return ViewWindow(parent, title, text, modal, wrap=wrap, _utest=_utest)
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000144
csabella0aa0a062017-05-28 06:50:55 -0400145
Tal Einat604e7b92018-09-25 15:10:14 +0300146def view_file(parent, title, filename, encoding, modal=True, wrap='word',
147 _utest=False):
csabella42bc8be2017-06-29 18:42:17 -0400148 """Create text viewer for text in filename.
csabella0aa0a062017-05-28 06:50:55 -0400149
150 Return error message if file cannot be read. Otherwise calls view_text
151 with contents of the file.
152 """
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000153 try:
Éric Araujoccf03a12011-08-01 17:29:36 +0200154 with open(filename, 'r', encoding=encoding) as file:
155 contents = file.read()
Terry Jan Reedy82c46152016-06-22 04:54:18 -0400156 except OSError:
157 showerror(title='File Load Error',
csabella42bc8be2017-06-29 18:42:17 -0400158 message=f'Unable to load file {filename!r} .',
Terry Jan Reedy82c46152016-06-22 04:54:18 -0400159 parent=parent)
160 except UnicodeDecodeError as err:
161 showerror(title='Unicode Decode Error',
162 message=str(err),
163 parent=parent)
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000164 else:
Tal Einat604e7b92018-09-25 15:10:14 +0300165 return view_text(parent, title, contents, modal, wrap=wrap,
166 _utest=_utest)
terryjreedy295304d2017-05-17 22:59:46 -0400167 return None
Louie Luba365da2017-05-18 05:51:31 +0800168
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000169
Steven M. Gava44d3d1a2001-07-31 06:59:02 +0000170if __name__ == '__main__':
Terry Jan Reedy4d921582018-06-19 19:12:52 -0400171 from unittest import main
172 main('idlelib.idle_test.test_textview', verbosity=2, exit=False)
173
Terry Jan Reedy1b392ff2014-05-24 18:48:18 -0400174 from idlelib.idle_test.htest import run
csabella42bc8be2017-06-29 18:42:17 -0400175 run(ViewWindow)