# ============================================================================
#
# Copyright (C) 2007-2013 Conceptive Engineering bvba. All rights reserved.
# www.conceptive.be / info@conceptive.be
#
# This file is part of the Camelot Library.
#
# This file may be used under the terms of the GNU General Public
# License version 2.0 as published by the Free Software Foundation
# and appearing in the file license.txt included in the packaging of
# this file. Please review this information to ensure GNU
# General Public Licensing requirements will be met.
#
# If you are unsure which license is appropriate for your use, please
# visit www.python-camelot.com or contact info@conceptive.be
#
# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
# For use of this library in commercial applications, please contact
# info@conceptive.be
#
# ============================================================================
import logging
logger = logging.getLogger('camelot.view.mainwindow')
from PyQt4.QtCore import Qt
from PyQt4 import QtGui, QtCore
from camelot.view.controls.busy_widget import BusyWidget
from camelot.view.controls.navpane2 import NavigationPane
from camelot.view.model_thread import post
from camelot.core.utils import ugettext as _
[docs]class MainWindow(QtGui.QMainWindow):
"""Main window of a Desktop Camelot application
:param gui_context: an :class:`camelot.admin.action.application_action.ApplicationActionGuiContext`
object
:param parent: a :class:`QtGui.QWidget` object or :class:`None`
.. attribute:: splash_screen
a :class:`QtGui.QWidget` that needs to be closed when
the main window is shown.
"""
def __init__(self, gui_context, parent=None):
from workspace import DesktopWorkspace
logger.debug('initializing main window')
QtGui.QMainWindow.__init__(self, parent)
self.splash_screen = None
self.toolbars = []
self.nav_pane = None
self.app_admin = gui_context.admin.get_application_admin()
logger.debug('setting up workspace')
self.workspace = DesktopWorkspace( self.app_admin, self )
self.gui_context = gui_context
self.gui_context.workspace = self.workspace
logger.debug('setting child windows dictionary')
logger.debug('setting central widget to our workspace')
self.setCentralWidget( self.workspace )
self.workspace.change_view_mode_signal.connect( self.change_view_mode )
self.workspace.last_view_closed_signal.connect( self.unmaximize_view )
self.workspace.view_activated_signal.connect( self.view_activated )
logger.debug('creating navigation pane')
post( self.app_admin.get_sections, self.set_sections )
logger.debug('creating the menus')
post( self.app_admin.get_main_menu, self.set_main_menu )
logger.debug('creating the toolbars')
post( self.app_admin.get_toolbar_actions,
self.set_left_toolbar_actions,
args = (Qt.LeftToolBarArea,) )
post( self.app_admin.get_toolbar_actions,
self.set_right_toolbar_actions,
args = (Qt.RightToolBarArea,) )
post( self.app_admin.get_toolbar_actions,
self.set_top_toolbar_actions,
args = (Qt.TopToolBarArea,) )
post( self.app_admin.get_toolbar_actions,
self.set_bottom_toolbar_actions,
args = (Qt.BottomToolBarArea,) )
post( self.app_admin.get_hidden_actions,
self.set_hidden_actions )
logger.debug('reading saved settings')
self.read_settings()
windowtitle = self.app_admin.get_name()
logger.debug( u'setting up window title: %s'%windowtitle )
self.setWindowTitle( windowtitle )
self.app_admin.title_changed_signal.connect( self.setWindowTitle )
logger.debug('initialization complete')
@QtCore.pyqtSlot()
[docs] def show( self ):
"""This method wait until the main window is completely set up, and
only then shows it. This is a workaround for a bug in Qt on OS X
https://bugreports.qt.nokia.com/browse/QTBUG-18567
"""
post( lambda:None, self._delayed_show )
@QtCore.pyqtSlot(object)
def _delayed_show( self, _o ):
"""Call to the underlying :meth:`QMainWindow.show`, to be used in
:meth:`MainWindow.show`
"""
super( MainWindow, self ).show()
if self.splash_screen:
self.splash_screen.close()
@QtCore.pyqtSlot()
[docs] def unmaximize_view( self ):
"""Show the navigation pane and the menu bar if they exist """
if self.navpane:
self.navpane.show()
if self.menuBar():
self.menuBar().show()
@QtCore.pyqtSlot()
[docs] def change_view_mode( self ):
"""Switch between hidden or shown menubar and navigation pane"""
if self.menuBar().isHidden():
if self.navpane:
self.navpane.show()
self.menuBar().show()
else:
if self.navpane:
self.navpane.hide()
self.menuBar().hide()
[docs] def read_settings( self ):
"""Restore the geometry of the main window to its last saved state"""
settings = QtCore.QSettings()
self.restoreGeometry(settings.value('geometry').toByteArray())
[docs] def write_settings(self):
"""Store the current geometry of the main window"""
logger.debug('writing application settings')
settings = QtCore.QSettings()
settings.setValue('geometry', QtCore.QVariant(self.saveGeometry()))
logger.debug('settings written')
@QtCore.pyqtSlot( object )
[docs] def set_main_menu( self, main_menu ):
"""Set the main menu
:param main_menu: a list of :class:`camelot.admin.menu.Menu` objects,
as returned by the :meth:`camelot.admin.application_admin.ApplicationAdmin.get_main_menu`
method.
"""
from camelot.view.controls.action_widget import ActionAction
if main_menu == None:
return
menu_bar = self.menuBar()
for menu in main_menu:
menu_bar.addMenu( menu.render( self.gui_context, menu_bar ) )
for qaction in menu_bar.findChildren( ActionAction ):
qaction.triggered.connect( self.action_triggered )
[docs] def get_gui_context( self ):
"""Get the :class:`GuiContext` of the active view in the mainwindow,
or the :class:`GuiContext` of the application.
:return: a :class:`camelot.admin.action.base.GuiContext`
"""
active_view = self.gui_context.workspace.active_view()
if active_view:
return active_view.gui_context
return self.gui_context
@QtCore.pyqtSlot( object, object )
[docs] def set_toolbar_actions( self, toolbar_area, toolbar_actions ):
"""Set the toolbar for a specific area
:param toolbar_area: the area on which to put the toolbar, from
:class:`Qt.LeftToolBarArea` through :class:`Qt.BottomToolBarArea`
:param toolbar_actions: a list of :class:`camelot.admin.action..base.Action` objects,
as returned by the :meth:`camelot.admin.application_admin.ApplicationAdmin.get_toolbar_actions`
method.
"""
from camelot.view.controls.action_widget import ActionAction
if toolbar_actions != None:
#
# gather menu bar actions to prevent duplication of QActions
#
qactions = dict()
menu_bar = self.menuBar()
if menu_bar:
for qaction in menu_bar.findChildren( ActionAction ):
qactions[qaction.action] = qaction
toolbar = QtGui.QToolBar( _('Toolbar') )
self.addToolBar( toolbar_area, toolbar )
toolbar.setObjectName( 'MainWindowToolBar_%i'%toolbar_area )
toolbar.setMovable( False )
toolbar.setFloatable( False )
for action in toolbar_actions:
qaction = qactions.get( action, None )
if qaction == None:
qaction = action.render( self.gui_context, toolbar )
qaction.triggered.connect( self.action_triggered )
toolbar.addAction( qaction )
self.toolbars.append( toolbar )
toolbar.addWidget( BusyWidget() )
@QtCore.pyqtSlot( object )
def set_left_toolbar_actions( self, toolbar_actions ):
self.set_toolbar_actions( Qt.LeftToolBarArea, toolbar_actions )
@QtCore.pyqtSlot( object )
def set_right_toolbar_actions( self, toolbar_actions ):
self.set_toolbar_actions( Qt.RightToolBarArea, toolbar_actions )
@QtCore.pyqtSlot( object )
def set_top_toolbar_actions( self, toolbar_actions ):
self.set_toolbar_actions( Qt.TopToolBarArea, toolbar_actions )
@QtCore.pyqtSlot( object )
def set_bottom_toolbar_actions( self, toolbar_actions ):
self.set_toolbar_actions( Qt.BottomToolBarArea, toolbar_actions )
@QtCore.pyqtSlot( object )
def set_hidden_actions( self, hidden_actions ):
from camelot.view.controls.action_widget import ActionAction
for action in hidden_actions:
action_action = ActionAction( action, self.gui_context, self )
action_action.triggered.connect( self.action_triggered )
self.addAction( action_action )
@QtCore.pyqtSlot()
[docs] def view_activated( self ):
"""Update the state of the actions when the active tab in the
desktop widget has changed"""
from camelot.view.controls.action_widget import ActionAction
gui_context = self.get_gui_context()
model_context = gui_context.create_model_context()
for toolbar in self.toolbars:
for qaction in toolbar.actions():
if isinstance( qaction, ActionAction ):
post( qaction.action.get_state,
qaction.set_state,
args = ( model_context, ) )
menu_bar = self.menuBar()
if menu_bar:
for qaction in menu_bar.findChildren( ActionAction ):
post( qaction.action.get_state,
qaction.set_state,
args = ( model_context, ) )
@QtCore.pyqtSlot( bool )
[docs] def action_triggered( self, _checked = False ):
"""Execute an action that was triggered somewhere in the main window,
such as the toolbar or the main menu"""
action_action = self.sender()
gui_context = self.get_gui_context()
action_action.action.gui_run( gui_context )
@QtCore.pyqtSlot( object )
[docs] def set_sections( self, sections ):
"""Set the sections of the navigation pane
:param main_menu: a list of :class:`camelot.admin.section.Section` objects,
as returned by the :meth:`camelot.admin.application_admin.ApplicationAdmin.get_sections`
method.
"""
if sections != None:
self.navpane = NavigationPane(
self.app_admin,
workspace=self.workspace,
parent=self
)
self.addDockWidget( Qt.LeftDockWidgetArea, self.navpane )
else:
self.navpane = None
def closeEvent( self, event ):
from camelot.view.model_thread import get_model_thread
model_thread = get_model_thread()
self.workspace.close_all_views()
self.write_settings()
logger.info( 'closing mainwindow' )
model_thread.stop()
super( MainWindow, self ).closeEvent( event )
QtCore.QCoreApplication.exit(0)