Package camelot :: Package camelot :: Package view :: Module mainwindow
[hide private]
[frames] | no frames]

Source Code for Module camelot.camelot.view.mainwindow

  1  #  ============================================================================ 
  2  # 
  3  #  Copyright (C) 2007-2008 Conceptive Engineering bvba. All rights reserved. 
  4  #  www.conceptive.be / project-camelot@conceptive.be 
  5  # 
  6  #  This file is part of the Camelot Library. 
  7  # 
  8  #  This file may be used under the terms of the GNU General Public 
  9  #  License version 2.0 as published by the Free Software Foundation 
 10  #  and appearing in the file LICENSE.GPL included in the packaging of 
 11  #  this file.  Please review the following information to ensure GNU 
 12  #  General Public Licensing requirements will be met: 
 13  #  http://www.trolltech.com/products/qt/opensource.html 
 14  # 
 15  #  If you are unsure which license is appropriate for your use, please 
 16  #  review the following information: 
 17  #  http://www.trolltech.com/products/qt/licensing.html or contact 
 18  #  project-camelot@conceptive.be. 
 19  # 
 20  #  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 
 21  #  WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 
 22  # 
 23  #  For use of this library in commercial applications, please contact 
 24  #  project-camelot@conceptive.be 
 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  # Dummy imports to fool the windows installer and force 
 40  # it to include the right packages 
 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 #QtCore.QTimer.singleShot(0, self.doInitialization) 112 logger.debug('initialization complete')
113 114 # Application settings 115
116 - def about(self):
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 # TODO: improve settings reading 124 settings = QtCore.QSettings() 125 self.restoreGeometry(settings.value('geometry').toByteArray())
126 # Don't restore state, since it messes up the toolbar if stuff was 127 # added 128 # self.restoreState(settings.value('state').toByteArray()) 129
130 - def writeSettings(self):
131 # TODO: improve settings saving 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 # QAction objects creation methods
151 - def createActions(self):
152 # TODO: change status tip 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 # QAction slots and methods implementations 316
317 - def save(self):
318 pass
319
320 - def saveAs(self):
321 pass
322
323 - def cut(self):
324 pass
325
326 - def copy(self):
327 pass
328
329 - def paste(self):
330 pass
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
339 - def new(self):
340 self.activeMdiChild().widget().newRow()
341
342 - def delete(self):
343 self.activeMdiChild().widget().deleteSelectedRows()
344
345 - def pageSetup(self):
346 pass
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 # Menus 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 # TODO: add refresh action 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 #self.throbber = Throbber(self.menuBar()) 465
466 - def resizeEvent(self, event):
467 """Needed to correctly position the throbber""" 468 QtGui.QMainWindow.resizeEvent(self, event)
469 # always put throbber in top right corner 470 #x = self.width() - self.throbber.sizeHint().width() - 5 471 #y = 2 472 #self.throbber.move(x, y) 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 # TODO: selecting text in views for copy/cut/paste 501 #hasSelection = (self.activeMdiChild() is not None and 502 # self.activeMdiChild().textCursor().hasSelection()) 503 hasSelection = False 504 self.cutAct.setEnabled(hasSelection) 505 self.copyAct.setEnabled(hasSelection)
506
507 - def updateWindowMenu(self):
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 # Toolbars 541
542 - def createToolBars(self):
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 # Navigation Pane 585
586 - def createNavigationPane(self):
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 # use QTest to auto select first button :) 595 firstbutton = self.navpane.buttons[0] 596 QTest.mousePress(firstbutton, Qt.LeftButton)
597 598 # Interface for child windows 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 # Statusbar 618
619 - def createStatusBar(self):
620 self.status = self.statusBar() 621 self.status.showMessage(_('Ready'), 5000)
622 623 # Events (re)implementations 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