Coverage for larch/wxlib/reportframe.py: 19%

178 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2024-10-16 21:04 +0000

1import sys 

2import wx 

3import wx.grid as wxgrid 

4import wx.lib.scrolledpanel as scrolled 

5from wx.richtext import RichTextCtrl 

6 

7from . import (FONTSIZE, Font, FRAMESTYLE, MenuItem, LEFT, CEN, SimpleText, 

8 FileOpen, FileSave, GridPanel, Button, HLine, pack) 

9 

10from larch.utils.strutils import break_longstring 

11 

12LEFT = wx.ALIGN_LEFT 

13CEN |= wx.ALL 

14 

15NROWS = 25 

16 

17# from larch.wxlib import (BitmapButton, SetTip, GridPanel, FloatCtrl, 

18# FloatSpin, FloatSpinWithPin, get_icon, SimpleText, 

19# pack, Button, HLine, Choice, Check, MenuItem, 

20# GUIColors, CEN, LEFT, FRAMESTYLE, Font, FileSave, 

21# FileOpen, FONTSIZE) 

22 

23class ReportFrame(wx.Frame): 

24 """basic frame for displaying a text report -- should be improved! 

25 """ 

26 def __init__(self, parent=None, text=None, size=(725, 600), 

27 title='Report', default_filename='out.txt', wildcard='*.txt', **kws): 

28 self.default_filename = default_filename 

29 self.wildcard = wildcard 

30 wx.Frame.__init__(self, parent, size=size, style=FRAMESTYLE, **kws) 

31 self.SetTitle(title) 

32 self.menubar = wx.MenuBar() 

33 fmenu = wx.Menu() 

34 

35 MenuItem(self, fmenu, "Save", "Save Text to File", self.onSave) 

36 MenuItem(self, fmenu, "Quit", "Exit", self.onClose) 

37 self.menubar.Append(fmenu, "&File") 

38 self.SetMenuBar(self.menubar) 

39 self.Bind(wx.EVT_CLOSE, self.onClose) 

40 

41 self.report = RichTextCtrl(self,size=size, style=wx.VSCROLL) 

42 self.report.SetEditable(False) 

43 self.report.SetFont(wx.Font(FONTSIZE+1, wx.MODERN, wx.NORMAL, wx.BOLD)) 

44 

45 self.report.SetMinSize((500, 500)) 

46 

47 sizer = wx.BoxSizer(wx.VERTICAL) 

48 sizer.Add(self.report, 1, wx.ALL|wx.GROW, 2) 

49 pack(self, sizer) 

50 if text is not None: 

51 self.set_text(text) 

52 self.Show() 

53 self.Raise() 

54 

55 def set_text(self, text): 

56 self.report.SetEditable(True) 

57 self.report.SetValue(text) 

58 self.report.SetEditable(False) 

59 

60 

61 def onClose(self, event=None): 

62 self.Destroy() 

63 

64 def onSave(self, eventt=None): 

65 wildcard = f'{self.wildcard}|All files (*.*)|*.*' 

66 path = FileSave(self, message='Save text to file', 

67 wildcard=wildcard, 

68 default_file=self.default_filename) 

69 if path is not None: 

70 with open(path, 'w', encoding=sys.getdefaultencoding()) as fh: 

71 fh.write(self.report.GetValue()) 

72 fh.write('') 

73 

74 

75class DictFrame(wx.Frame): 

76 """ simple display of dict""" 

77 def __init__(self, parent, data=None, title='Dictionary', **kws): 

78 self.parent = parent 

79 self.title = title 

80 if data is None: data = {} 

81 self.data = data 

82 

83 wx.Frame.__init__(self, None, -1, title, style=FRAMESTYLE, **kws) 

84 

85 

86 export_btn = Button(self, 'Save to Tab-Separated File', size=(225, -1), 

87 action=self.export) 

88 

89 collabels = [' Label ', ' Value '] 

90 colsizes = [200, 550] 

91 coltypes = ['string', 'string'] 

92 coldefs = [' ', ' '] 

93 

94 self.datagrid = DataTableGrid(self, nrows=NROWS, 

95 collabels=collabels, 

96 datatypes=coltypes, 

97 defaults=coldefs, 

98 colsizes=colsizes, 

99 rowlabelsize=40) 

100 

101 self.datagrid.SetMinSize((850, 500)) 

102 self.datagrid.EnableEditing(False) 

103 

104 sizer = wx.BoxSizer(wx.VERTICAL) 

105 sizer.Add(export_btn, 0, LEFT, 2) 

106 sizer.Add((5, 5), 0, LEFT, 2) 

107 sizer.Add(self.datagrid, 1, LEFT, 2) 

108 

109 pack(self, sizer) 

110 

111 self.Show() 

112 self.Raise() 

113 self.SetSize(self.GetBestSize()) 

114 wx.CallAfter(self.set_data) 

115 

116 def export(self, event=None): 

117 wildcard = 'CSV file (*.csv)|*.csv|All files (*.*)|*.*' 

118 fname = self.title + '.csv' 

119 fname = FileSave(self, message='Save Tab-Separated-Value Data File', 

120 wildcard=wildcard, default_file=fname) 

121 if fname is None: 

122 return 

123 

124 buff = ['Label\tValue'] 

125 for k, v in self.data.items(): 

126 k = k.replace('\t', '_') 

127 if not isinstance(v, str): v = repr(v) 

128 v = v.replace('\t', ' ') 

129 buff.append(f"{k}\t{v}") 

130 

131 buff.append('') 

132 with open(fname, 'w', encoding=sys.getdefaultencoding()) as fh: 

133 fh.write('\n'.join(buff)) 

134 

135 msg = f"Exported data '{fname}'" 

136 writer = getattr(self.parent, 'write_message', sys.stdout) 

137 writer(msg) 

138 

139 

140 def set_data(self, data=None): 

141 if data is None: 

142 data = self.data 

143 if data is None: 

144 return 

145 

146 grid_data = [] 

147 rowsize = [] 

148 n_entries = len(data) 

149 

150 for key, val in data.items(): 

151 if not isinstance(val, str): 

152 val = repr(val) 

153 xval = break_longstring(val) 

154 val = '\n'.join(xval) 

155 rowsize.append(len(xval)) 

156 grid_data.append([key, val]) 

157 

158 nrows = self.datagrid.table.GetRowsCount() 

159 if len(grid_data) > nrows: 

160 self.datagrid.AppendRows(len(grid_data)+8 - nrows) 

161 

162 self.datagrid.table.Clear() 

163 self.datagrid.table.data = grid_data 

164 for i, rsize in enumerate(rowsize): 

165 self.datagrid.SetRowSize(i, rsize*20) 

166 

167 self.datagrid.table.View.Refresh() 

168 

169 

170class DataTable(wxgrid.GridTableBase): 

171 def __init__(self, nrows=NROWS, collabels=['a', 'b'], 

172 datatypes=['str', 'float:12,4'], 

173 defaults=[None, None]): 

174 

175 wxgrid.GridTableBase.__init__(self) 

176 

177 self.ncols = len(collabels) 

178 self.nrows = nrows 

179 self.colLabels = collabels 

180 self.dataTypes = [] 

181 for i, d in enumerate(datatypes): 

182 if d.lower().startswith('str'): 

183 self.dataTypes.append(wxgrid.GRID_VALUE_STRING) 

184 defval = '' 

185 elif d.lower().startswith('float:'): 

186 xt, opt = d.split(':') 

187 self.dataTypes.append(wxgrid.GRID_VALUE_FLOAT+':%s' % opt) 

188 defval = 0.0 

189 if defaults[i] is None: 

190 defaults[i] = defval 

191 self.defaults = defaults 

192 self.data = [] 

193 for i in range(self.nrows): 

194 self.data.append(defaults) 

195 

196 def GetNumberRows(self): 

197 return self.nrows 

198 

199 def GetNumberCols(self): 

200 return self.ncols 

201 

202 def GetValue(self, row, col): 

203 try: 

204 return self.data[row][col] 

205 except IndexError: 

206 return '' 

207 

208 def SetValue(self, row, col, value): 

209 self.data[row][col] = value 

210 

211 def GetColLabelValue(self, col): 

212 return self.colLabels[col] 

213 

214 def GetRowLabelValue(self, row): 

215 return " %d" % (row+1) 

216 

217 def GetTypeName(self, row, col): 

218 return self.dataTypes[col] 

219 

220 def CanGetValueAs(self, row, col, typeName): 

221 colType = self.dataTypes[col].split(':')[0] 

222 if typeName == colType: 

223 return True 

224 else: 

225 return False 

226 

227 def CanSetValueAs(self, row, col, typeName): 

228 return self.CanGetValueAs(row, col, typeName) 

229 

230 def AppendRows(self, numRows=1, **kws): 

231 self.nrows = self.nrows + numRows 

232 msg = wxgrid.GridTableMessage(self, 

233 wxgrid.GRIDTABLE_NOTIFY_ROWS_APPENDED, numRows) 

234 self.GetView().ProcessTableMessage(msg) 

235 return True 

236 

237 

238class DataTableGrid(wxgrid.Grid): 

239 def __init__(self, parent, nrows=NROWS, rowlabelsize=35, 

240 collabels=['a', 'b'], 

241 datatypes=['str', 'float:12,4'], 

242 defaults=['', ''], 

243 colsizes=[200, 100]): 

244 

245 wxgrid.Grid.__init__(self, parent, -1) 

246 

247 self.table = DataTable(nrows=nrows, collabels=collabels, 

248 datatypes=datatypes, defaults=defaults) 

249 

250 self.SetTable(self.table, True) 

251 self.SetRowLabelSize(rowlabelsize) 

252 self.SetMargins(10, 10) 

253 self.EnableDragRowSize() 

254 self.EnableDragColSize() 

255 self.AutoSizeColumns(False) 

256 for i, csize in enumerate(colsizes): 

257 self.SetColSize(i, csize) 

258 

259 self.Bind(wxgrid.EVT_GRID_CELL_LEFT_DCLICK, self.OnLeftDClick) 

260 

261 def OnLeftDClick(self, evt): 

262 if self.CanEnableCellControl(): 

263 self.EnableCellEditControl()