1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 """Main window GUI"""
29
30 import settings
31 import os
32 import sys
33 import logging
34
35 logger = logging.getLogger('mainwindow')
36 logger.setLevel(logging.INFO)
37
38
39
40
41
42 from sqlalchemy.databases import sqlite
43 import sqlite3
44
45 from PyQt4 import QtGui
46 from PyQt4 import QtCore
47 from PyQt4.QtCore import Qt
48 from PyQt4.QtTest import QTest
49
50 from camelot.view import art
51 from camelot.view.helpers import createAction, addActions
52 from camelot.view.controls.navpane import PaneButton, NavigationPane
53 from camelot.view.controls.printer import Printer
54 from camelot.view.controls.animation import Throbber
55 from camelot.view.model_thread import get_model_thread, construct_model_thread
56 from camelot.view.response_handler import ResponseHandler
57 from camelot.view.remote_signals import construct_signal_handler
58
59 QT_MAJOR_VERSION = float('.'.join(str(QtCore.QT_VERSION_STR).split('.')[0:2]))
60
61 _ = lambda x: x
62
63
64 -class MainWindow(QtGui.QMainWindow):
65
66 - def __init__(self, app_admin, parent=None):
67 from workspace import construct_workspace
68 logger.debug('initializing main window')
69 super(MainWindow, self).__init__(parent)
70
71 self.app_admin = app_admin
72
73 logger.debug('setting up workspace')
74 self.workspace = construct_workspace(self)
75
76 logger.debug('setting child windows dictionary')
77
78 logger.debug('setting central widget to our workspace')
79 self.setCentralWidget(self.workspace)
80
81 self.connect(self.workspace, QtCore.SIGNAL('subWindowActivated(QMdiSubWindow *)'),
82 self.updateMenus)
83
84 logger.debug('creating navigation pane')
85 self.createNavigationPane()
86
87 logger.debug('creating all the required actions')
88 self.createActions()
89
90 logger.debug('creating the menus')
91 self.createMenus()
92
93 logger.debug('creating the toolbars')
94 self.createToolBars()
95
96 logger.debug('creating status bar')
97 self.createStatusBar()
98
99 logger.debug('updating menus')
100 self.updateMenus()
101
102 logger.debug('reading saved settings')
103 self.readSettings()
104
105 logger.debug('setting up printer object')
106 self.printer = Printer()
107
108 logger.debug('setting up window title')
109 self.setWindowTitle(self.app_admin.getName())
110
111
112 logger.debug('initialization complete')
113
114
115
117 logger.debug('showing about message box')
118 abtmsg = self.app_admin.getAbout()
119 QtGui.QMessageBox.about(self, _('About'), _(abtmsg))
120 logger.debug('about message closed')
121
122 - def readSettings(self):
123
124 settings = QtCore.QSettings()
125 self.restoreGeometry(settings.value('geometry').toByteArray())
126
127
128
129
130 - def writeSettings(self):
131
132 logger.debug('writing application settings')
133 settings = QtCore.QSettings()
134 settings.setValue('geometry', QtCore.QVariant(self.saveGeometry()))
135 settings.setValue('state', QtCore.QVariant(self.saveState()))
136 logger.debug('settings written')
137
138 - def runAction(self, name, callable):
139 progress = QtGui.QProgressDialog('Please wait', 'Run in background', 0, 0)
140 progress.setWindowTitle(name)
141 progress.show()
142 mt = get_model_thread()
143
144 def exception_in_action(e):
145 progress.close()
146 QtGui.QMessageBox.warning(self, 'Error while %s'%name, str(e))
147
148 mt.post(callable, lambda *args:progress.close(), exception_in_action)
149
150
151 - def createActions(self):
152
153 self.saveAct = createAction(self,
154 _('&Save'),
155 self.save,
156 QtGui.QKeySequence.Save,
157 art.icon16('actions/document-save'),
158 _('Save'))
159
160 temp = art.icon16('actions/document-properties')
161 self.pageSetupAct = createAction(self,
162 _('Page Setup...'),
163 self.pageSetup,
164 actionicon=temp,
165 tip=_('Page Setup...'))
166
167 self.printAct = createAction(self,
168 _('Print...'),
169 self.printDoc,
170 QtGui.QKeySequence.Print,
171 art.icon16('actions/document-print'),
172 _('Print...'))
173
174 temp = art.icon16('actions/document-print-preview')
175 self.previewAct = createAction(self,
176 _('Print Preview'),
177 self.previewDoc,
178 actionicon=temp,
179 tip=_('Print Preview'))
180
181 self.exitAct = createAction(self,
182 _('E&xit'),
183 self.close,
184 tip=_('Exit the application'))
185
186 tip = _("Cut the current selection's contents to the clipboard")
187 self.cutAct = createAction(self,
188 _('Cu&t'),
189 self.cut,
190 QtGui.QKeySequence.Cut,
191 art.icon16('actions/edit-cut'),
192 tip)
193
194 tip = _("Copy the current selection's contents to the clipboard")
195 self.copyAct = createAction(self,
196 _('&Copy'),
197 self.copy,
198 QtGui.QKeySequence.Copy,
199 art.icon16('actions/edit-copy'),
200 tip)
201
202 tip = _("Paste the clipboard's contents into the current selection")
203 self.pasteAct = createAction(self,
204 _('&Paste'),
205 self.paste,
206 QtGui.QKeySequence.Paste,
207 art.icon16('actions/edit-paste'),
208 tip)
209
210 self.closeAct = createAction(self,
211 _('Cl&ose'),
212 self.workspace.closeActiveSubWindow,
213 shortcut = QtGui.QKeySequence.Close,
214 tip=_('Close the active window'))
215
216 self.closeAllAct = createAction(self,
217 _('Close &All'),
218 self.workspace.closeAllSubWindows,
219 tip=_('Close all the windows'))
220
221 self.cascadeAct = createAction(self,
222 _('&Cascade windows'),
223 self.workspace.cascadeSubWindows,
224 tip=_('Arranges all the child windows '
225 'in a cascade pattern.'))
226
227 self.separatorAct = QtGui.QAction(self)
228 self.separatorAct.setSeparator(True)
229
230 self.aboutAct = createAction(self,
231 _('&About'),
232 self.about,
233 tip=_("Show the application's About box"))
234
235 self.newAct = createAction(self,
236 _('New'),
237 self.new,
238 QtGui.QKeySequence.New,
239 art.icon16('actions/document-new'),
240 _('New'))
241
242 self.deleteAct = createAction(self,
243 _('Delete'),
244 self.delete,
245 QtGui.QKeySequence.Delete,
246 art.icon16('places/user-trash'),
247 _('Delete'))
248
249 self.viewFirstAct = createAction(self,
250 _('First'),
251 self.viewFirst,
252 QtGui.QKeySequence.MoveToStartOfDocument,
253 art.icon16('actions/go-first'),
254 _('First'))
255
256 self.viewLastAct = createAction(self,
257 _('Last'),
258 self.viewLast,
259 QtGui.QKeySequence.MoveToEndOfDocument,
260 art.icon16('actions/go-last'),
261 _('Last'))
262
263 self.viewNextAct = createAction(self,
264 _('Next'),
265 self.viewNext,
266 QtGui.QKeySequence.MoveToNextPage,
267 art.icon16('actions/go-next'),
268 _('Next'))
269
270 self.viewPreviousAct = createAction(self,
271 _('Previous'),
272 self.viewPrevious,
273 QtGui.QKeySequence.MoveToPreviousPage,
274 art.icon16('actions/go-previous'),
275 _('Previous'))
276
277 if QT_MAJOR_VERSION > 4.3:
278 self.viewFirstAct.setIconVisibleInMenu(False)
279 self.viewLastAct.setIconVisibleInMenu(False)
280 self.viewNextAct.setIconVisibleInMenu(False)
281 self.viewPreviousAct.setIconVisibleInMenu(False)
282
283 temp = art.icon16('mimetypes/x-office-spreadsheet')
284 self.exportToExcelAct = createAction(self,
285 _('Export to MS Excel'),
286 self.exportToExcel,
287 actionicon=temp,
288 tip=_('Export to MS Excel'))
289
290 temp = art.icon16('mimetypes/x-office-document')
291 self.exportToWordAct = createAction(self,
292 _('Export to MS Word'),
293 self.exportToWord,
294 actionicon=temp,
295 tip=_('Export to MS Word'))
296 temp = art.icon16('actions/mail-message-new')
297 self.exportToMailAct = createAction(self,
298 _('Send by e-mail'),
299 self.exportToMail,
300 actionicon=temp,
301 tip=_('Send by e-mail'))
302
303 self.app_actions = []
304 for name, icon, callable in self.app_admin.getActions():
305
306 def bind_callable(name, callable):
307 return lambda:self.runAction(name, callable)
308
309 self.app_actions.append(createAction(self,
310 name,
311 slot=bind_callable(name, callable),
312 actionicon=icon,
313 tip=name))
314
315
316
319
322
325
328
331
332 - def printDoc(self):
333 active = self.activeMdiChild()
334 self.printer.printView(active.widget(), self)
335
336 - def previewDoc(self):
337 self.printer.preview(self.activeMdiChild().widget(), self)
338
340 self.activeMdiChild().widget().newRow()
341
344
345 - def pageSetup(self):
347
348 - def viewFirst(self):
349 """selects view's first row"""
350 active = self.activeMdiChild()
351 active.widget().viewFirst()
352
353 - def viewLast(self):
354 """selects view's last row"""
355 active = self.activeMdiChild()
356 active.widget().viewLast()
357
358 - def viewNext(self):
359 """selects view's next row"""
360 active = self.activeMdiChild()
361 active.widget().viewNext()
362
363 - def viewPrevious(self):
364 """selects view's previous row"""
365 active = self.activeMdiChild()
366 active.widget().viewPrevious()
367
368 - def exportToExcel(self):
369 """creates an excel file from the view"""
370
371 mt = get_model_thread()
372
373 def export():
374 import os
375 import tempfile
376 from export.excel import ExcelExport
377 title = self.activeMdiChild().widget().getTitle()
378 columns = self.activeMdiChild().widget().getColumns()
379 data = [d for d in self.activeMdiChild().widget().getData()]
380 objExcel = ExcelExport()
381 xls_fd, xls_fn = tempfile.mkstemp(suffix='.xls')
382 objExcel.exportToFile(xls_fn, title, columns, data)
383
384 try:
385 import pythoncom
386 import win32com.client
387 pythoncom.CoInitialize()
388 excel_app = win32com.client.Dispatch("Excel.Application")
389 except Exception, e:
390 """We're probably not running windows, so try gnumeric"""
391 logger.warning('Unable to launch excel', exc_info=e)
392 os.system('gnumeric "%s"'%xls_fn)
393 return
394
395 excel_app.Visible = True
396 excel_app.Workbooks.Open(xls_fn)
397
398 mt.post(export)
399
400 - def exportToWord(self):
401 """Use windows COM to export the active child window to MS word,
402 by using its toHtml function"""
403
404 mt = get_model_thread()
405
406 def export():
407 from export.word import open_html_in_word
408 html = self.activeMdiChild().widget().toHtml()
409 open_html_in_word(html)
410
411 mt.post(export)
412
413 - def exportToMail(self):
414
415 mt = get_model_thread()
416
417 def export():
418 from export.outlook import open_html_in_outlook
419 html = self.activeMdiChild().widget().toHtml()
420 open_html_in_outlook(html)
421
422 mt.post(export)
423
424
425 - def createMenus(self):
426 self.fileMenu = self.menuBar().addMenu(_('&File'))
427 addActions(self.fileMenu, (self.closeAct,
428 None, self.saveAct,
429 None, self.pageSetupAct,
430 self.previewAct,
431 self.printAct, None))
432
433 self.exportMenu = QtGui.QMenu(_('Export To'))
434 addActions(self.exportMenu, (self.exportToExcelAct,
435 self.exportToWordAct,
436 self.exportToMailAct,
437 ))
438 self.fileMenu.addMenu(self.exportMenu)
439
440 addActions(self.fileMenu, (None, self.exitAct))
441
442 self.editMenu = self.menuBar().addMenu(_('&Edit'))
443
444 addActions(self.editMenu, (self.cutAct,
445 self.copyAct,
446 self.pasteAct))
447
448 self.viewMenu = self.menuBar().addMenu(_('View'))
449 gotoMenu = self.viewMenu.addMenu(_('Go To'))
450 addActions(gotoMenu, (self.viewFirstAct,
451 self.viewPreviousAct,
452 self.viewNextAct,
453 self.viewLastAct))
454
455 self.windowMenu = self.menuBar().addMenu(_('&Window'))
456 self.connect(self.windowMenu, QtCore.SIGNAL('aboutToShow()'),
457 self.updateWindowMenu)
458
459 self.menuBar().addSeparator()
460
461 self.helpMenu = self.menuBar().addMenu(_('&Help'))
462 addActions(self.helpMenu, (self.aboutAct, ))
463
464
465
466 - def resizeEvent(self, event):
467 """Needed to correctly position the throbber"""
468 QtGui.QMainWindow.resizeEvent(self, event)
469
470
471
472
473
474 - def updateMenus(self):
475 hasMdiChild = (self.activeMdiChild() is not None)
476 self.saveAct.setEnabled(hasMdiChild)
477 self.pasteAct.setEnabled(hasMdiChild)
478 self.closeAct.setEnabled(hasMdiChild)
479
480 self.closeAllAct.setEnabled(hasMdiChild)
481 self.cascadeAct.setEnabled(hasMdiChild)
482
483 self.pageSetupAct.setEnabled(hasMdiChild)
484 self.previewAct.setEnabled(hasMdiChild)
485 self.printAct.setEnabled(hasMdiChild)
486
487 self.newAct.setEnabled(hasMdiChild)
488 self.deleteAct.setEnabled(hasMdiChild)
489 self.viewFirstAct.setEnabled(hasMdiChild)
490 self.viewPreviousAct.setEnabled(hasMdiChild)
491 self.viewNextAct.setEnabled(hasMdiChild)
492 self.viewLastAct.setEnabled(hasMdiChild)
493
494 self.exportToWordAct.setEnabled(hasMdiChild)
495 self.exportToExcelAct.setEnabled(hasMdiChild)
496 self.exportToMailAct.setEnabled(hasMdiChild)
497
498 self.separatorAct.setVisible(hasMdiChild)
499
500
501
502
503 hasSelection = False
504 self.cutAct.setEnabled(hasSelection)
505 self.copyAct.setEnabled(hasSelection)
506
508 self.windowMenu.clear()
509 self.windowMenu.addAction(self.closeAllAct)
510 self.windowMenu.addAction(self.cascadeAct)
511 self.windowMenu.addAction(self.separatorAct)
512
513 windows = self.workspace.subWindowList()
514
515 self.separatorAct.setVisible(len(windows) != 0)
516
517 i = 0
518
519 for child in windows:
520 if i < 9:
521 text = self.tr('&%1 %2').arg(i + 1).arg(child.windowTitle())
522 else:
523 text = self.tr('%1 %2').arg(i + 1).arg(child.windowTitle())
524
525 i += 1
526
527 action = self.windowMenu.addAction(text)
528 action.setCheckable(True)
529 action.setChecked(child == self.activeMdiChild())
530
531 def create_window_activator(window):
532
533 def activate_window():
534 self.workspace.setActiveSubWindow(window)
535
536 return activate_window
537
538 self.connect(action, QtCore.SIGNAL('triggered()'), create_window_activator(child))
539
540
541
543 self.printToolBar = self.addToolBar(_('Print'))
544 self.printToolBar.setObjectName('PrintToolBar')
545 self.printToolBar.setMovable(False)
546 self.printToolBar.setFloatable(False)
547 addActions(self.printToolBar, (self.printAct, self.previewAct))
548
549 self.editToolBar = self.addToolBar(_('Edit'))
550 self.editToolBar.setObjectName('EditToolBar')
551 self.editToolBar.setMovable(False)
552 self.editToolBar.setFloatable(False)
553 addActions(self.editToolBar, (self.cutAct,
554 self.copyAct,
555 self.pasteAct))
556
557 self.viewToolBar = self.addToolBar(_('View'))
558 self.viewToolBar.setObjectName('ViewToolBar')
559 self.viewToolBar.setMovable(False)
560 self.viewToolBar.setFloatable(False)
561 addActions(self.viewToolBar, (self.newAct,
562 self.deleteAct,
563 self.viewFirstAct,
564 self.viewPreviousAct,
565 self.viewNextAct,
566 self.viewLastAct))
567
568 self.exportToolBar = self.addToolBar(_('Export'))
569 self.exportToolBar.setObjectName('ExportToolBar')
570 self.exportToolBar.setMovable(False)
571 self.exportToolBar.setFloatable(False)
572 addActions(self.exportToolBar, (self.exportToExcelAct,
573 self.exportToWordAct,
574 self.exportToMailAct,))
575
576
577 if self.app_actions:
578 self.applicationToolBar = self.addToolBar(_('Application'))
579 self.applicationToolBar.setObjectName('ApplicationToolBar')
580 self.applicationToolBar.setMovable(False)
581 self.applicationToolBar.setFloatable(False)
582 addActions(self.exportToolBar, self.app_actions)
583
584
585
587 self.navpane = NavigationPane(self.app_admin, parent=self)
588 self.addDockWidget(Qt.LeftDockWidgetArea, self.navpane)
589
590 self.connect(self.navpane.treewidget,
591 QtCore.SIGNAL('itemClicked(QTreeWidgetItem *, int)'),
592 self.createMdiChild)
593
594
595 firstbutton = self.navpane.buttons[0]
596 QTest.mousePress(firstbutton, Qt.LeftButton)
597
598
599
600 - def createMdiChild(self, item):
601 index = self.navpane.treewidget.indexFromItem(item)
602 model = self.navpane.models[index.row()]
603 logger.debug('creating model %s' % str(model[0]))
604
605 child = model[0].createTableView(model[1], parent=self)
606 self.workspace.addSubWindow(child)
607
608 self.connect(child, QtCore.SIGNAL("copyAvailable(bool)"),
609 self.cutAct.setEnabled)
610 self.connect(child, QtCore.SIGNAL("copyAvailable(bool)"),
611 self.copyAct.setEnabled)
612 child.showMaximized()
613
614 - def activeMdiChild(self):
615 return self.workspace.activeSubWindow()
616
617
618
619 - def createStatusBar(self):
620 self.status = self.statusBar()
621 self.status.showMessage(_('Ready'), 5000)
622
623
624
625 - def closeEvent(self, event):
626 self.workspace.closeAllSubWindows()
627 if self.activeMdiChild():
628 event.ignore()
629 else:
630 self.writeSettings()
631 event.accept()
632