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
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
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
74 qubx.faces.Face.__init__(self, ' Tables', global_name=global_name)
75 self.__explore_featured = self.__explore_featured[:]
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)
116 self.tables[self.book.get_current_page()].view.btnNotebook.sub.on_popup()
132 for i, t in enumerate(self.tables):
133 if t.table == table:
134 return i
135 return -1
148 - def new_table(self, name=None, initial_row=True):
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)
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
252 i = self.book.get_current_page()
253 if i > 0:
254 self.book.set_current_page(i-1)
255 self.grab_focus()
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):
274 self.mnuPaste.popup(None, None, None, 0, 0)
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:
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)
308
309
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