Package qubx :: Module table_panel
[hide private]
[frames] | no frames]

Source Code for Module qubx.table_panel

  1  """Components for the top level of the QUB Express or Fitness. 
  2   
  3  Copyright 2008-2012 Research Foundation State University of New York  
  4  This file is part of QUB Express.                                           
  5   
  6  QUB Express is free software; you can redistribute it and/or modify           
  7  it under the terms of the GNU General Public License as published by  
  8  the Free Software Foundation, either version 3 of the License, or     
  9  (at your option) any later version.                                   
 10   
 11  QUB Express is distributed in the hope that it will be useful,                
 12  but WITHOUT ANY WARRANTY; without even the implied warranty of        
 13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         
 14  GNU General Public License for more details.                          
 15   
 16  You should have received a copy of the GNU General Public License,    
 17  named LICENSE.txt, in the QUB Express program directory.  If not, see         
 18  <http://www.gnu.org/licenses/>.                                       
 19   
 20   
 21  """ 
 22   
 23   
 24  import os 
 25  import traceback 
 26   
 27  import gtk 
 28  import gobject 
 29  import numpy 
 30   
 31  import qubx.data_types 
 32  import qubx.faces 
 33  import qubx.GTK 
 34  import qubx.pyenv 
 35  import qubx.table 
 36  import qubx.tableGTK 
 37  import qubx.toolspace 
 38  import qubx.trialset 
 39   
 40  from itertools import izip, count 
 41  from math import * 
 42  from gtk import gdk 
 43  from qubx.GTK import pack_item, pack_space, pack_hsep, pack_vsep, pack_label, pack_button, pack_check, pack_radio, pack_scrolled, build_menuitem 
 44  from qubx.util_types import * 
 45   
 46   
 47   
48 -class TableTab(Anon):
49 """Info about one tab in L{TablesFace}. 50 51 @ivar table: L{qubx.table.Table} 52 @ivar view: L{qubx.tableGTK.TableView} 53 @ivar lamb: bound and curried ref to Tables._onSelect; brings table to front when a row/item is externally selected (e.g. click a state) 54 @ivar onedit: bound and curried ref to Tables._onEdit; seals a restore point if the table has associated undoStack 55 @ivar undoStack: L{qubx.undo.UndoStack} that is already connected via L{qubx.table.TableUndo}, or None 56 @ivar controls: gtk.Widget to display above the table, or None 57 """ 58 pass
59 60
61 -class TablesFace(qubx.faces.Face):
62 """Tabbed panel for L{qubx.tableGTK.TableView}s. 63 64 @ivar tables: list of L{TableTab} 65 @ivar OnAddTable: L{WeakEvent}(table) 66 @ivar OnRemoveTable: L{WeakEvent}(table) 67 """ 68 69 __explore_featured = ['tables', 'book', 'mnuPaste', 'OnAddTable', 'OnRemoveTable', 'path', 'mnuFile', 'mnuRecent', 'menubar', 70 'add_recent', 'index', 'find_table', 'find_view', 'grab_focus', 'new_table', 'open_table', 'add_table', 71 'add_user_table', 'remove_table', 'close_table', 'show_table', 'show_prev_table', 'show_next_table'] 72
73 - def __init__(self, global_name=""):
74 qubx.faces.Face.__init__(self, ' Tables', global_name=global_name) 75 self.__explore_featured = self.__explore_featured[:] # modify local copy with child face names 76 self.ref = Reffer() 77 self.tables = [] 78 self.book = pack_item(gtk.Notebook(), self, expand=True) 79 self.book.connect('switch_page', self._onSwitchPage) 80 self.book.set_show_border(True) 81 self.book.set_scrollable(True) 82 self.OnAddTable = WeakEvent() 83 self.OnRemoveTable = WeakEvent() 84 self.mnuPaste = gtk.Menu() 85 build_menuitem('Paste rows', self.ref(self.__onClickPasteRows), menu=self.mnuPaste) 86 build_menuitem('Paste rows (replacing)', self.ref(bind_with_args_before(self.__onClickPasteRows, True)), menu=self.mnuPaste) 87 build_menuitem('Paste columns', self.ref(self.__onClickPasteCols), menu=self.mnuPaste) 88 self.path = "" 89 self.__recent = qubx.settings.SettingsMgr['Tables.Recent'].active 90 QubX = qubx.global_namespace.QubX 91 menu = self.mnuFile = gtk.Menu() 92 self.mnuRecent = gtk.Menu() 93 menu.append(build_menuitem('New Table...', self.ref(bind(self.new_table)))) 94 menu.append(build_menuitem('Open Table...', self.ref(bind(self.open_table)))) 95 menu.append(build_menuitem('Open Recent', submenu=self.mnuRecent)) 96 if QubX.has_modeling: 97 menu.append(build_menuitem('Open Trials...', self.ref(lambda item: QubX.Trials.open()))) 98 self.tab_sublayers.append(qubx.toolspace.SubLayer_Popup_PNG(self.mnuFile, os.path.join(qubx.global_namespace.app_path, 'icons', 'folder.png'), 99 w=qubx.faces.TOOLTOGGLE_WIDTH_EM, h=qubx.faces.TOOLTOGGLE_WIDTH_EM, 100 border=qubx.faces.TOOLTOGGLE_TAB_BORDER, 101 tooltip="Tables: File menu", 102 on_popup=self.ref(self.__onPopupFile))) 103 self.menubar = gtk.MenuBar() 104 self.menubar.append(build_menuitem('File', self.ref(bind(self.__onPopupFile)), submenu=menu)) 105 self.itemNB = gtk.MenuItem('Notebook') 106 self.itemNB.connect('activate', self.__onPopupNB) 107 self.itemNB.show() 108 self.menubar.append(self.itemNB)
109 - def __onPopupFile(self):
110 self.mnuRecent.foreach(lambda item: self.mnuRecent.remove(item)) 111 for node in qubx.tree.children(self.__recent): 112 if os.path.exists(node.name): 113 build_menuitem(node.name, self.ref(bind(self.open_table, node.name)), menu=self.mnuRecent) 114 return True
115 - def __onPopupNB(self, item):
116 self.tables[self.book.get_current_page()].view.btnNotebook.sub.on_popup()
117 - def add_recent(self, path):
118 node = self.__recent.insert(path) 119 node = node.sibling 120 i = 1 121 while not node.isNull: 122 if (i >= qubx.faces.RECENT_FILES_COUNT) or (node.name == path): 123 to_remove, node = node, node.sibling 124 self.__recent.remove(to_remove) 125 else: 126 node = node.sibling 127 i += 1
128 - def onShow(self, showing):
129 for t in self.tables: 130 t.view.showing = showing
131 - def index(self, table):
132 for i, t in enumerate(self.tables): 133 if t.table == table: 134 return i 135 return -1
136 - def find_table(self, label):
137 for i, t in enumerate(self.tables): 138 if t.table.label == label: 139 return t.table
140 - def find_view(self, label):
141 for i, t in enumerate(self.tables): 142 if t.table.label == label: 143 return t.view
144 - def grab_focus(self):
145 if self.showing and self.pop_info.state: 146 self.parent_window.present() 147 self.tables[self.book.get_current_page()].view.grab_focus()
148 - def new_table(self, name=None, initial_row=True):
149 QubX = qubx.global_namespace.QubX 150 nm = name 151 if not nm: 152 dlg = qubx.GTK.NumEntryDialog('%s - New Table...'%QubX.appname, qubx.GTK.get_active_window(), 'Table name:', 'Untitled') 153 if dlg.run() == gtk.RESPONSE_ACCEPT: 154 nm = dlg.value 155 dlg.destroy() 156 if not nm: 157 return 158 qubx.pyenv.env.OnScriptable('QubX.Tables.new_table(name=%s, initial_row=%s)' % (repr(nm), repr(initial_row))) 159 table = self.find_table(nm) 160 if table: 161 self.show_table(table, show_tables=True) 162 else: 163 table = self.add_user_table(nm, initial_row=initial_row) 164 return table
165 - def open_table(self, fname=None):
166 QubX = qubx.global_namespace.QubX 167 if fname is None: 168 qubx.GTK.Open('%s - Open Table...'%QubX.appname, self.path, self.open_table, parent=qubx.GTK.get_active_window()) 169 return 170 self.path = os.path.split(fname)[0] 171 name = os.path.splitext(fname)[0] 172 txt = open(fname, 'r').read() 173 existing = self.find_table(name) 174 if existing: 175 self.show_table(existing, show_tables=True) 176 dlg = gtk.MessageDialog(qubx.GTK.get_active_window(), buttons=gtk.BUTTONS_OK, flags=gtk.DIALOG_MODAL, 177 message_format='There is already an open table named "%s."\nTo open the file, close this table, or rename the file.') 178 dlg.run() 179 dlg.destroy() 180 else: 181 qubx.pyenv.env.OnScriptable('QubX.open_table(%s)' % repr(name)) 182 table = self.add_user_table(os.path.split(name)[1], txt) 183 self.add_recent(fname)
184 - def add_table(self, table, undoStack=None, controls=None):
185 """Displays a L{qubx.table.Table} in a new tab. 186 187 @param table: L{qubx.table.Table} 188 @param undoStack: L{qubx.undo.UndoStack} that is already connected via L{qubx.table.TableUndo} 189 @param controls: gtk.Widget to display above the table, or None 190 """ 191 view = qubx.tableGTK.TableView(table, global_name='QubX.Tables.find_view("%s")'%table.label) 192 vbox = gtk.VBox() 193 if controls: 194 controls.OnClickClose += self.ref(self.__onClickCloseTable) 195 pack_item(controls, vbox) 196 pack_scrolled(view, vbox, expand=True) 197 index = 0 198 while (index < len(self.tables)) and (table.label > self.tables[index].table.label): 199 index += 1 200 vbox.show() 201 view_select_event = lambda i, f, s: self._onSelect(table, i, f, s) 202 view_edit_event = lambda lbl: self._onEdit(table, lbl) 203 table.OnSelect += view_select_event 204 view.OnEdit += view_edit_event 205 self.tables.insert(index, TableTab(table=table, view=view, lamb=view_select_event, onedit=view_edit_event, undoStack=undoStack, controls=controls)) 206 self.book.insert_page(vbox, gtk.Label(table.label), index) 207 self.__dict__[table.label.replace(" ", "")] = table 208 self.__explore_featured.append(table.label.replace(" ", "")) 209 self.OnAddTable(table)
210 - def add_user_table(self, label, txt=None, initial_row=True):
211 table = qubx.table.SimpleTable(label, auto_add_fields=True, 212 global_name='QubX.Tables.find_table(%s)' % repr(label), 213 sortable=True) 214 if txt: 215 table.from_text(txt) 216 elif initial_row: 217 table.append({}) 218 controls = qubx.tableGTK.TableViewCtrls(table, None, 219 qubx.tableGTK.TableViewCtrls.FILE_MENU, 220 qubx.tableGTK.TableViewCtrls.ADD, 221 qubx.tableGTK.TableViewCtrls.REMOVE, 222 qubx.tableGTK.TableViewCtrls.ADD_FIELD, 223 qubx.tableGTK.TableViewCtrls.COPY_ROWS, 224 qubx.tableGTK.TableViewCtrls.CALCULATE, 225 ('Paste...', self.__onClickPaste), 226 qubx.tableGTK.TableViewCtrls.PLOT, 227 qubx.tableGTK.TableViewCtrls.CLOSE) 228 self.add_table(table, controls=controls) 229 self.show_table(table, show_tables=True) 230 qubx.pyenv.env.globals['QubX'].Figures.Charts.prefer_table(table.label) 231 return table
232 - def remove_table(self, table):
233 """Takes away the tab that's displaying a L{qubx.table.Table}.""" 234 i = self.index(table) 235 t = self.tables[i] 236 del self.tables[i] 237 del self.__dict__[table.label.replace(" ", "")] 238 self.__explore_featured.remove(table.label.replace(" ", "")) 239 self.book.remove_page(i) 240 table.OnSelect -= t.lamb 241 t.view.OnEdit -= t.onedit 242 t.view.dispose() 243 self.OnRemoveTable(table)
244 - def close_table(self, table):
245 self.remove_table(table) 246 table.dispose()
247 - def show_table(self, table, show_tables=False):
248 self.book.set_current_page(self.index(table)) 249 if show_tables: 250 self.request_show()
251 - def show_prev_table(self):
252 i = self.book.get_current_page() 253 if i > 0: 254 self.book.set_current_page(i-1) 255 self.grab_focus()
256 - def show_next_table(self):
257 i = self.book.get_current_page() 258 if i < (len(self.tables)-1): 259 self.book.set_current_page(i+1) 260 self.grab_focus()
261 - def _onSwitchPage(self, book, page, page_num):
262 self.itemNB.set_submenu(self.tables[page_num].view.btnNotebook.sub.popup)
263 - def _onSelect(self, table, i, field, sender):
264 if i is None: return 265 self.show_table(table) # replaced by prompt_entry # , show_tables=bool(field)) 266 if self.showing: 267 self.request_show()
268 - def _onEdit(self, table, lbl):
269 i = self.index(table) 270 uu = self.tables[i].undoStack 271 if uu: 272 uu.seal_undo(lbl)
273 - def __onClickPaste(self):
274 self.mnuPaste.popup(None, None, None, 0, 0)
275 - def __onClickPasteRows(self, item, replace=False):
276 headers, types, columns = paste_table() 277 table = self.tables[self.book.get_current_page()].table 278 if len(columns) == 0: return 279 if replace: 280 table.clear() 281 for i in xrange(len(columns[0])): 282 table.append(dict([(hdr, col[i]) for hdr, col in izip(headers, columns)]))
283 - def __onClickPasteCols(self, item):
284 headers, types, columns = paste_table() 285 table = self.tables[self.book.get_current_page()].table 286 if len(columns) == 0: return 287 for i in xrange(len(columns[0])): 288 if i < len(table): 289 for hdr, col in izip(headers, columns): 290 table.set(i, hdr, col[i]) 291 else: 292 table.append(dict([(hdr, col[i]) for hdr, col in izip(headers, columns)]))
293 - def __onClickCloseTable(self, controls, table):
294 if not (isinstance(table, qubx.data_types.SelectionList) or isinstance(table, qubx.trialset.TrialSet)): 295 response = qubx.GTK.PromptChoices('Save %s before closing?' % table.label, ["Don't save", "Cancel", "Save"], parent=self.parent_window) 296 if response == 1: # cancel 297 return 298 if response == 2: 299 if not qubx.GTK.SaveAs('Save %s as...', self.path or qubx.pyenv.env.globals['documents_path'], table.label, 300 lambda fname: self.__do_save_table(table, fname), parent=self.parent_window): 301 return 302 qubx.pyenv.env.OnScriptable('QubX.Tables.close_table(QubX.Tables.find_table(%s))' % repr(table.label)) 303 gobject.idle_add(self.close_table, table)
304 - def __do_save_table(self, table, fname):
305 self.path = os.path.split(fname)[0] 306 open(fname, 'w').write(str(table.notebook['Default'])) 307 self.add_recent(fname)
308 309
310 -def paste_table():
311 """Returns clipboard as interpreted by qubx.table.read_table_text; empty headers replaced with A, B, ...""" 312 headers, types, columns = qubx.table.read_table_text(gtk.clipboard_get("CLIPBOARD").wait_for_text() or "") 313 headers = [hdr or qubx.table.default_column_name(i) for i,hdr in enumerate(headers)] 314 return headers, types, columns
315