root/trunk/camelot/view/mainwindow.py @ 1109

Revision 1109, 21.2 KB (checked in by erikj, 8 months ago)

add duplicate rows action to table view

Line 
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
28import logging
29
30logger = logging.getLogger('mainwindow')
31logger.setLevel(logging.INFO)
32
33#
34# Dummy imports to fool the windows installer and force
35# it to include the right packages
36#
37from sqlalchemy.databases import sqlite
38import sqlite3
39
40from PyQt4.QtCore import Qt
41from PyQt4 import QtGui, QtCore
42from PyQt4 import QtWebKit
43
44from camelot.view.art import Icon
45from camelot.action import createAction, addActions
46from camelot.view.controls.navpane import NavigationPane
47from camelot.view.controls.printer import Printer
48from camelot.view.model_thread import post
49
50QT_MAJOR_VERSION = float('.'.join(str(QtCore.QT_VERSION_STR).split('.')[0:2]))
51
52from camelot.core.utils import ugettext as _
53
54
55class MainWindow(QtGui.QMainWindow):
56    """Main window GUI"""
57
58    def __init__(self, app_admin, parent=None):
59        from workspace import construct_workspace
60        logger.debug('initializing main window')
61        QtGui.QMainWindow.__init__(self, parent)
62
63        self.app_admin = app_admin
64
65        logger.debug('setting up workspace')
66        self.workspace = construct_workspace(self)
67
68        logger.debug('setting child windows dictionary')
69
70        logger.debug('setting central widget to our workspace')
71        self.setCentralWidget(self.workspace)
72
73        self.connect(
74            self.workspace,
75            QtCore.SIGNAL('subWindowActivated(QMdiSubWindow *)'),
76            self.updateMenus
77        )
78
79        logger.debug('creating navigation pane')
80        self.createNavigationPane()
81
82        logger.debug('creating all the required actions')
83        self.createActions()
84
85        logger.debug('creating the menus')
86        self.createMenus()
87
88        logger.debug('creating the toolbars')
89        self.createToolBars()
90
91        logger.debug('creating status bar')
92        self.createStatusBar()
93
94        logger.debug('updating menus')
95        self.updateMenus()
96
97        logger.debug('reading saved settings' )
98        self.readSettings()
99
100        logger.debug('setting up printer object')
101        self.printer = Printer()
102
103        logger.debug('setting up window title')
104        self.setWindowTitle(self.app_admin.get_name())
105       
106        #QtCore.QTimer.singleShot(0, self.doInitialization)
107        logger.debug('initialization complete')
108       
109    # Application settings
110
111    def about(self):
112        logger.debug('showing about message box')
113        abtmsg = self.app_admin.get_about()
114        QtGui.QMessageBox.about(self, _('About'), _(abtmsg))
115        logger.debug('about message closed')
116
117    def readSettings(self):
118        # TODO: improve settings reading
119        settings = QtCore.QSettings()
120        self.restoreGeometry(settings.value('geometry').toByteArray())
121        # Don't restore state, since it messes up the toolbar if stuff was
122        # added
123        # self.restoreState(settings.value('state').toByteArray())
124
125    def writeSettings(self):
126        # TODO: improve settings saving
127        logger.debug('writing application settings')
128        settings = QtCore.QSettings()
129        settings.setValue('geometry', QtCore.QVariant(self.saveGeometry()))
130        settings.setValue('state', QtCore.QVariant(self.saveState()))
131        logger.debug('settings written')
132
133    def display_exception_message_box(self, exc_info):
134        from controls.exception import model_thread_exception_message_box
135        model_thread_exception_message_box(exc_info)
136       
137    def runAction(self, name, callable):
138        progress = QtGui.QProgressDialog('Please wait', QtCore.QString(), 0, 0)
139        progress.setWindowTitle(name)
140        progress.show()
141        post(
142            callable,
143            progress.close,
144            exception=self.display_exception_message_box
145        )
146
147    def createActions(self):
148        icon_save = Icon('tango/16x16/actions/document-save.png').fullpath()
149        icon_pgsetup = Icon('tango/16x16/actions/document-properties.png').fullpath()
150        icon_print = Icon('tango/16x16/actions/document-print.png').fullpath()
151        icon_preview = Icon('tango/16x16/actions/document-print-preview.png').fullpath()
152        icon_copy = Icon('tango/16x16/actions/edit-copy.png').fullpath()
153
154        icon_new = Icon('tango/16x16/actions/document-new.png').fullpath()
155        icon_delete = Icon('tango/16x16/places/user-trash.png').fullpath()
156
157        icon_gofirst = Icon('tango/16x16/actions/go-first.png').fullpath()
158        icon_golast = Icon('tango/16x16/actions/go-last.png').fullpath()
159        icon_gonext = Icon('tango/16x16/actions/go-next.png').fullpath()
160        icon_goprevious = Icon('tango/16x16/actions/go-previous.png').fullpath()
161
162        icon_excel = Icon('tango/16x16/mimetypes/x-office-spreadsheet.png').fullpath()
163        icon_word = Icon('tango/16x16/mimetypes/x-office-document.png').fullpath()
164        icon_mail = Icon('tango/16x16/actions/mail-message-new.png').fullpath()
165
166        icon_import = Icon('tango/16x16/mimetypes/text-x-generic.png').fullpath()
167
168        icon_help = Icon('tango/16x16/apps/help-browser.png').fullpath()
169
170        # TODO: change some of the status tips
171        self.saveAct = createAction(
172            parent=self,
173            text=_('&Save'),
174            slot=self.save,
175            shortcut=QtGui.QKeySequence.Save,
176            actionicon=icon_save,
177            tip=_('Save')
178        )
179
180        self.pageSetupAct = createAction(
181            parent=self,
182            text=_('Page Setup...'),
183            slot=self.pageSetup,
184            actionicon=icon_pgsetup,
185            tip=_('Page Setup...')
186        )
187
188        self.printAct = createAction(
189            parent=self,
190            text=_('Print...'),
191            slot=self.printDoc,
192            shortcut=QtGui.QKeySequence.Print,
193            actionicon=icon_print,
194            tip=_('Print...')
195        )
196
197        self.previewAct = createAction(
198            parent=self,
199            text=_('Print Preview'),
200            slot=self.previewDoc,
201            actionicon=icon_preview,
202            tip=_('Print Preview')
203        )
204
205        self.exitAct= createAction(
206            parent=self,
207            text=_('E&xit'),
208            slot=self.close,
209            tip=_('Exit the application')
210        )
211
212        self.copyAct = createAction(
213            parent=self,
214            text=_('&Copy'),
215            slot=self.copy,
216            shortcut=QtGui.QKeySequence.Copy,
217            actionicon=icon_copy,
218            tip=_("Duplicate the selected rows")
219        )
220
221        # BUG: there is a problem with setting a key sequence for closing
222        #      a subwindow.  PyQt adopts defaults from specific platforms
223        #      but we want the sequence Ctrl+W on every platform.  there-
224        #      fore we set the string 'Ctrl+W', but PyQt defaults will
225        #      still work.
226        self.closeAct = createAction(
227            parent=self,
228            text=_('Cl&ose'),
229            slot=self.workspace.closeActiveSubWindow,
230            shortcut='Ctrl+W',
231            tip=_('Close the active window')
232        )
233
234        self.closeAllAct = createAction(
235            parent=self,
236            text=_('Close &All'),
237            slot=self.workspace.closeAllSubWindows,
238            tip=_('Close all the windows')
239        )
240
241        self.cascadeAct = createAction(
242            parent=self,
243            text=_('&Cascade windows'),
244            slot=self.workspace.cascadeSubWindows,
245            tip=_('Arranges all the child windows in a cascade pattern.')
246        )
247
248        self.separatorAct = QtGui.QAction(self)
249        self.separatorAct.setSeparator(True)
250
251        self.aboutAct = createAction(
252            parent=self,
253            text=_('&About'),
254            slot=self.about,
255            tip=_("Show the application's About box")
256        )
257
258        self.helpAct = createAction(
259            parent=self,
260            text=_('Help'),
261            slot=self.help,
262            shortcut=QtGui.QKeySequence.HelpContents,
263            actionicon=icon_help,
264            tip=_('Help content')
265        )
266
267        self.newAct = createAction(
268            parent=self,
269            text=_('New'),
270            slot=self.new,
271            shortcut=QtGui.QKeySequence.New,
272            actionicon=icon_new,
273            tip=_('New')
274        )
275
276        self.deleteAct = createAction(
277            parent=self,
278            text=_('Delete'),
279            slot=self.delete,
280            shortcut=QtGui.QKeySequence.Delete,
281            actionicon=icon_delete,
282            tip=_('Delete')
283        )
284
285        self.viewFirstAct = createAction(
286            parent=self,
287            text=_('First'),
288            slot=self.viewFirst,
289            shortcut=QtGui.QKeySequence.MoveToStartOfDocument,
290            actionicon=icon_gofirst,
291            tip=_('First')
292        )
293
294        self.viewLastAct = createAction(
295            parent=self,
296            text=_('Last'),
297            slot=self.viewLast,
298            shortcut=QtGui.QKeySequence.MoveToEndOfDocument,
299            actionicon=icon_golast,
300            tip=_('Last')
301        )
302
303        self.viewNextAct = createAction(
304            parent=self,
305            text=_('Next'),
306            slot=self.viewNext,
307            shortcut=QtGui.QKeySequence.MoveToNextPage,
308            actionicon=icon_gonext,
309            tip=_('Next')
310        )
311
312        self.viewPreviousAct = createAction(
313            parent=self,
314            text=_('Previous'),
315            slot=self.viewPrevious,
316            shortcut=QtGui.QKeySequence.MoveToPreviousPage,
317            actionicon=icon_goprevious,
318            tip=_('Previous')
319        )
320
321        if QT_MAJOR_VERSION > 4.3:
322            self.viewFirstAct.setIconVisibleInMenu(False)
323            self.viewLastAct.setIconVisibleInMenu(False)
324            self.viewNextAct.setIconVisibleInMenu(False)
325            self.viewPreviousAct.setIconVisibleInMenu(False)
326
327        self.exportToExcelAct = createAction(
328            parent=self,
329            text=_('Export to MS Excel'),
330            slot=self.exportToExcel,
331            actionicon=icon_excel,
332            tip=_('Export to MS Excel')
333        )
334
335        self.exportToWordAct = createAction(
336            parent=self,
337            text=_('Export to MS Word'),
338            slot=self.exportToWord,
339            actionicon=icon_word,
340            tip=_('Export to MS Word')
341        )
342
343        self.exportToMailAct = createAction(
344            parent=self,
345            text=_('Send by e-mail'),
346            slot=self.exportToMail,
347            actionicon=icon_mail,
348            tip=_('Send by e-mail')
349        )
350
351        self.importFromFileAct = createAction(
352            parent=self,
353            text=_('Import from file'),
354            slot=self.importFromFile,
355            actionicon=icon_import,
356            tip=_('Import from file')
357        )
358
359        from camelot.action.refresh import SessionRefresh
360
361        self.sessionRefreshAct = SessionRefresh(self)
362
363        self.app_actions = []
364        for action in self.app_admin.get_actions():
365
366            def bind_action(parent, action):
367               
368                def slot(*args):
369                    action.run(parent)
370                   
371                return slot
372                   
373            self.app_actions.append(
374                createAction(
375                    parent=self,
376                    text=unicode(action.get_verbose_name()),
377                    slot=bind_action(self, action),
378                    actionicon=action.get_icon().getQIcon(),
379                    tip=unicode(action.get_verbose_name())
380                )
381            )
382
383    # QAction slots and methods implementations
384
385    def help(self):
386        TOP_LEVEL = None
387        self.view = QtWebKit.QWebView(TOP_LEVEL)
388        #print self.app_admin.get_help_url()
389        #print self.app_admin.get_help_base()
390        #index_file = open(self.app_admin.get_help_url(),'r')
391        #self.view.setHtml (index_file.read(), self.app_admin.get_help_base())
392        self.view.load(self.app_admin.get_help_url())
393        self.view.setWindowTitle(_('Help Browser'))
394        self.view.setWindowIcon(self.helpAct.icon())
395        self.view.show()
396
397    def save(self):
398        pass
399
400    def saveAs(self):
401        pass
402
403    def copy(self):
404        self.activeMdiChild().widget().copy_selected_rows()
405
406    def printDoc(self):
407        self.previewDoc()
408
409    def previewDoc(self):
410        active = self.activeMdiChild()
411        from camelot.admin.form_action import PrintHtmlFormAction
412
413        class PrintPreview(PrintHtmlFormAction):
414
415            def html(self, entity_getter):
416                return active.widget().toHtml()
417
418        action = PrintPreview('Print Preview')
419        action.run(lambda:None)
420
421    def new(self):
422        self.activeMdiChild().widget().newRow()
423
424    def delete(self):
425        self.activeMdiChild().widget().deleteSelectedRows()
426
427    def pageSetup(self):
428        pass
429
430    def viewFirst(self):
431        """selects view's first row"""
432        active = self.activeMdiChild()
433        active.widget().viewFirst()
434
435    def viewLast(self):
436        """selects view's last row"""
437        active = self.activeMdiChild()
438        active.widget().viewLast()
439
440    def viewNext(self):
441        """selects view's next row"""
442        active = self.activeMdiChild()
443        active.widget().viewNext()
444
445    def viewPrevious(self):
446        """selects view's previous row"""
447        active = self.activeMdiChild()
448        active.widget().viewPrevious()
449
450    def exportToExcel(self):
451        """creates an excel file from the view"""
452
453        def export():
454            from export.excel import open_data_with_excel
455            title = self.activeMdiChild().widget().getTitle()
456            columns = self.activeMdiChild().widget().getColumns()
457            data = [d for d in self.activeMdiChild().widget().getData()]
458            open_data_with_excel(title, columns, data)
459
460        post(export)
461
462    def exportToWord(self):
463        """Use windows COM to export the active child window to MS word,
464        by using its toHtml function"""
465
466        def export():
467            from export.word import open_html_in_word
468            html = self.activeMdiChild().widget().toHtml()
469            open_html_in_word(html)
470
471        post(export)
472
473    def exportToMail(self):
474
475        def export():
476            from export.outlook import open_html_in_outlook
477            html = self.activeMdiChild().widget().toHtml()
478            open_html_in_outlook(html)
479
480        post(export)
481
482    def importFromFile(self):
483        self.activeMdiChild().widget().importFromFile()
484
485    def createMenus(self):
486       
487        self.fileMenu = self.menuBar().addMenu(_('&File'))
488        addActions(self.fileMenu, (
489            self.closeAct,
490            None,
491            self.saveAct,
492            None,
493            self.pageSetupAct,
494            self.previewAct,
495            self.printAct,
496            None
497        ))
498
499        self.exportMenu = QtGui.QMenu(_('Export To'))
500        addActions(self.exportMenu, (
501            self.exportToExcelAct,
502            self.exportToWordAct,
503            self.exportToMailAct,
504        ))
505        self.fileMenu.addMenu(self.exportMenu)
506
507        self.importMenu = QtGui.QMenu(_('Import From'))
508        addActions(self.importMenu, (self.importFromFileAct,))
509        self.fileMenu.addMenu(self.importMenu)
510
511        addActions(self.fileMenu, (None, self.exitAct))
512
513        self.editMenu = self.menuBar().addMenu(_('&Edit'))
514
515        addActions(self.editMenu, (self.copyAct,))
516       
517        self.viewMenu = self.menuBar().addMenu(_('View'))
518        addActions(self.viewMenu, (self.sessionRefreshAct,))
519        gotoMenu = self.viewMenu.addMenu(_('Go To'))
520        addActions(gotoMenu, (
521            self.viewFirstAct,
522            self.viewPreviousAct,
523            self.viewNextAct,
524            self.viewLastAct
525        ))
526        self.windowMenu = self.menuBar().addMenu(_('&Window'))
527        self.connect(
528            self.windowMenu,
529            QtCore.SIGNAL('aboutToShow()'),
530            #self.updateWindowMenu
531            self.app_admin.update_window_menu(self)
532        )
533
534        self.menuBar().addSeparator()
535
536        self.helpMenu = self.menuBar().addMenu(_('&Help'))
537        addActions(self.helpMenu, (self.helpAct, self.aboutAct))
538
539    def updateMenus(self):
540        hasMdiChild = (self.activeMdiChild() is not None)
541        self.saveAct.setEnabled(hasMdiChild)
542        self.closeAct.setEnabled(hasMdiChild)
543
544        self.closeAllAct.setEnabled(hasMdiChild)
545        self.cascadeAct.setEnabled(hasMdiChild)
546
547        self.pageSetupAct.setEnabled(hasMdiChild)
548        self.previewAct.setEnabled(hasMdiChild)
549        self.printAct.setEnabled(hasMdiChild)
550
551        self.newAct.setEnabled(hasMdiChild)
552        self.deleteAct.setEnabled(hasMdiChild)
553        self.copyAct.setEnabled(hasMdiChild)
554        self.viewFirstAct.setEnabled(hasMdiChild)
555        self.viewPreviousAct.setEnabled(hasMdiChild)
556        self.viewNextAct.setEnabled(hasMdiChild)
557        self.viewLastAct.setEnabled(hasMdiChild)
558
559        self.exportToWordAct.setEnabled(hasMdiChild)
560        self.exportToExcelAct.setEnabled(hasMdiChild)
561        self.exportToMailAct.setEnabled(hasMdiChild)
562
563        self.importFromFileAct.setEnabled(hasMdiChild)
564
565        self.separatorAct.setVisible(hasMdiChild)
566
567    #def updateWindowMenu(self):
568    #    self.windowMenu.clear()
569    #    self.windowMenu.addAction(self.closeAllAct)
570    #    self.windowMenu.addAction(self.cascadeAct)
571    #    self.windowMenu.addAction(self.separatorAct)
572
573    #    windows = self.workspace.subWindowList()
574
575    #    self.separatorAct.setVisible(len(windows) != 0)
576
577    #    for i, child in enumerate(windows):
578    #        title = child.windowTitle()
579    #        if i < 9:
580    #            text = _('&%s %s' % (i+1, title))
581    #        else:
582    #            text = _('%s %s' % (i+1, title))
583
584    #        action = self.windowMenu.addAction(text)
585    #        action.setCheckable(True)
586    #        action.setChecked(child == self.activeMdiChild())
587
588    #        def create_window_activator(window):
589
590    #            def activate_window():
591    #                self.workspace.setActiveSubWindow(window)
592
593    #            return activate_window
594
595    #        self.connect(
596    #            action,
597    #            QtCore.SIGNAL('triggered()'),
598    #            create_window_activator(child)
599    #        )
600
601    # Toolbars
602
603    def get_tool_bar(self):
604        return self.tool_bar
605   
606    def createToolBars(self):
607        #
608        # All actions are put in one toolbar, to ease unit testing and
609        # generation of screenshots
610        #
611        self.tool_bar = self.addToolBar(_('Toolbar'))
612        self.tool_bar.setObjectName('ToolBar')
613        self.tool_bar.setMovable(False)
614        self.tool_bar.setFloatable(False)
615        addActions(self.tool_bar, (
616            self.newAct,
617            self.copyAct,
618            self.deleteAct,
619            self.viewFirstAct,
620            self.viewPreviousAct,
621            self.viewNextAct,
622            self.viewLastAct
623        ))
624
625        addActions(self.tool_bar, (
626            self.exportToExcelAct,
627            self.exportToWordAct,
628            self.exportToMailAct,
629        ))
630
631        addActions(self.tool_bar, (self.printAct, self.previewAct))
632
633        addActions(self.tool_bar, (self.helpAct,))
634
635        if self.app_actions:
636            addActions(self.tool_bar, self.app_actions)
637
638    # Navigation Pane
639
640    def createNavigationPane(self):
641        self.navpane = NavigationPane(self.app_admin, parent=self)
642        self.addDockWidget(Qt.LeftDockWidgetArea, self.navpane)
643
644        self.connect(
645            self.navpane.treewidget,
646            QtCore.SIGNAL('itemClicked(QTreeWidgetItem *, int)'),
647            self.createMdiChild
648        )
649
650    # Interface for child windows
651
652    def createMdiChild(self, item):
653        index = self.navpane.treewidget.indexFromItem(item)
654        section_item = self.navpane.items[index.row()]
655        child = section_item.get_action().run(self.workspace)
656        assert child != None
657        subwindow = self.workspace.addSubWindow(child)
658        subwindow.showMaximized()
659
660    def activeMdiChild(self):
661        return self.workspace.activeSubWindow()
662
663    # Statusbar
664
665    def createStatusBar(self):
666        from controls.statusbar import StatusBar
667        statusbar = StatusBar(self)
668        self.setStatusBar(statusbar)
669        statusbar.showMessage(_('Ready'), 5000)
670
671    # Events
672
673    def closeEvent(self, event):
674        self.workspace.closeAllSubWindows()
675        if self.activeMdiChild():
676            event.ignore()
677        else:
678            self.writeSettings()
679            event.accept()
Note: See TracBrowser for help on using the browser.