Coverage for textview.py: 26%

85 statements  

« prev     ^ index     » next       coverage.py v7.2.5, created at 2023-05-11 13:22 -0700

1"""Simple text browser for IDLE 

2 

3""" 

4from tkinter import Toplevel, Text, TclError,\ 

5 HORIZONTAL, VERTICAL, NS, EW, NSEW, NONE, WORD, SUNKEN 

6from tkinter.ttk import Frame, Scrollbar, Button 

7from tkinter.messagebox import showerror 

8 

9from idlelib.colorizer import color_config 

10 

11 

12class AutoHideScrollbar(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 

31class ScrollableTextFrame(Frame): 

32 """Display text with scrollbar(s).""" 

33 

34 def __init__(self, master, wrap=NONE, **kwargs): 

35 """Create a frame for Textview. 

36 

37 master - master widget for this frame 

38 wrap - type of text wrapping to use ('word', 'char' or 'none') 

39 

40 All parameters except for 'wrap' are passed to Frame.__init__(). 

41 

42 The Text widget is accessible via the 'text' attribute. 

43 

44 Note: Changing the wrapping mode of the text widget after 

45 instantiation is not supported. 

46 """ 

47 super().__init__(master, **kwargs) 

48 

49 text = self.text = Text(self, wrap=wrap) 

50 text.grid(row=0, column=0, sticky=NSEW) 

51 self.grid_rowconfigure(0, weight=1) 

52 self.grid_columnconfigure(0, weight=1) 

53 

54 # vertical scrollbar 

55 self.yscroll = AutoHideScrollbar(self, orient=VERTICAL, 

56 takefocus=False, 

57 command=text.yview) 

58 self.yscroll.grid(row=0, column=1, sticky=NS) 

59 text['yscrollcommand'] = self.yscroll.set 

60 

61 # horizontal scrollbar - only when wrap is set to NONE 

62 if wrap == NONE: 

63 self.xscroll = AutoHideScrollbar(self, orient=HORIZONTAL, 

64 takefocus=False, 

65 command=text.xview) 

66 self.xscroll.grid(row=1, column=0, sticky=EW) 

67 text['xscrollcommand'] = self.xscroll.set 

68 else: 

69 self.xscroll = None 

70 

71 

72class ViewFrame(Frame): 

73 "Display TextFrame and Close button." 

74 def __init__(self, parent, contents, wrap='word'): 

75 """Create a frame for viewing text with a "Close" button. 

76 

77 parent - parent widget for this frame 

78 contents - text to display 

79 wrap - type of text wrapping to use ('word', 'char' or 'none') 

80 

81 The Text widget is accessible via the 'text' attribute. 

82 """ 

83 super().__init__(parent) 

84 self.parent = parent 

85 self.bind('<Return>', self.ok) 

86 self.bind('<Escape>', self.ok) 

87 self.textframe = ScrollableTextFrame(self, relief=SUNKEN, height=700) 

88 

89 text = self.text = self.textframe.text 

90 text.insert('1.0', contents) 

91 text.configure(wrap=wrap, highlightthickness=0, state='disabled') 

92 color_config(text) 

93 text.focus_set() 

94 

95 self.button_ok = button_ok = Button( 

96 self, text='Close', command=self.ok, takefocus=False) 

97 self.textframe.pack(side='top', expand=True, fill='both') 

98 button_ok.pack(side='bottom') 

99 

100 def ok(self, event=None): 

101 """Dismiss text viewer dialog.""" 

102 self.parent.destroy() 

103 

104 

105class ViewWindow(Toplevel): 

106 "A simple text viewer dialog for IDLE." 

107 

108 def __init__(self, parent, title, contents, modal=True, wrap=WORD, 

109 *, _htest=False, _utest=False): 

110 """Show the given text in a scrollable window with a 'close' button. 

111 

112 If modal is left True, users cannot interact with other windows 

113 until the textview window is closed. 

114 

115 parent - parent of this dialog 

116 title - string which is title of popup dialog 

117 contents - text to display in dialog 

118 wrap - type of text wrapping to use ('word', 'char' or 'none') 

119 _htest - bool; change box location when running htest. 

120 _utest - bool; don't wait_window when running unittest. 

121 """ 

122 super().__init__(parent) 

123 self['borderwidth'] = 5 

124 # Place dialog below parent if running htest. 

125 x = parent.winfo_rootx() + 10 

126 y = parent.winfo_rooty() + (10 if not _htest else 100) 

127 self.geometry(f'=750x500+{x}+{y}') 

128 

129 self.title(title) 

130 self.viewframe = ViewFrame(self, contents, wrap=wrap) 

131 self.protocol("WM_DELETE_WINDOW", self.ok) 

132 self.button_ok = button_ok = Button(self, text='Close', 

133 command=self.ok, takefocus=False) 

134 self.viewframe.pack(side='top', expand=True, fill='both') 

135 

136 self.is_modal = modal 

137 if self.is_modal: 

138 self.transient(parent) 

139 self.grab_set() 

140 if not _utest: 

141 self.wait_window() 

142 

143 def ok(self, event=None): 

144 """Dismiss text viewer dialog.""" 

145 if self.is_modal: 

146 self.grab_release() 

147 self.destroy() 

148 

149 

150def view_text(parent, title, contents, modal=True, wrap='word', _utest=False): 

151 """Create text viewer for given text. 

152 

153 parent - parent of this dialog 

154 title - string which is the title of popup dialog 

155 contents - text to display in this dialog 

156 wrap - type of text wrapping to use ('word', 'char' or 'none') 

157 modal - controls if users can interact with other windows while this 

158 dialog is displayed 

159 _utest - bool; controls wait_window on unittest 

160 """ 

161 return ViewWindow(parent, title, contents, modal, wrap=wrap, _utest=_utest) 

162 

163 

164def view_file(parent, title, filename, encoding, modal=True, wrap='word', 

165 _utest=False): 

166 """Create text viewer for text in filename. 

167 

168 Return error message if file cannot be read. Otherwise calls view_text 

169 with contents of the file. 

170 """ 

171 try: 1b

172 with open(filename, 'r', encoding=encoding) as file: 1b

173 contents = file.read() 1b

174 except OSError: 

175 showerror(title='File Load Error', 

176 message=f'Unable to load file {filename!r} .', 

177 parent=parent) 

178 except UnicodeDecodeError as err: 

179 showerror(title='Unicode Decode Error', 

180 message=str(err), 

181 parent=parent) 

182 else: 

183 return view_text(parent, title, contents, modal, wrap=wrap, 1b

184 _utest=_utest) 

185 return None 

186 

187 

188if __name__ == '__main__': 188 ↛ 189line 188 didn't jump to line 189, because the condition on line 188 was never true

189 from unittest import main 

190 main('idlelib.idle_test.test_textview', verbosity=2, exit=False) 

191 

192 from idlelib.idle_test.htest import run 

193 run(ViewWindow)