Source code for camelot.view.controls.editors.one2manyeditor

#  ============================================================================
#
#  Copyright (C) 2007-2016 Conceptive Engineering bvba.
#  www.conceptive.be / info@conceptive.be
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are met:
#      * Redistributions of source code must retain the above copyright
#        notice, this list of conditions and the following disclaimer.
#      * Redistributions in binary form must reproduce the above copyright
#        notice, this list of conditions and the following disclaimer in the
#        documentation and/or other materials provided with the distribution.
#      * Neither the name of Conceptive Engineering nor the
#        names of its contributors may be used to endorse or promote products
#        derived from this software without specific prior written permission.
#  
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
#  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
#  DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
#  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
#  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
#  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
#  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
#  ============================================================================

import logging

from camelot.admin.action.list_action import ListActionGuiContext
from camelot.view.model_thread import object_thread, post
from camelot.view import register
from ....core.qt import Qt, QtCore, QtWidgets, QtGui, variant_to_py
from ..action_widget import ActionAction
from .wideeditor import WideEditor
from .customeditor import CustomEditor

LOGGER = logging.getLogger('camelot.view.controls.editors.onetomanyeditor')


[docs]class One2ManyEditor(CustomEditor, WideEditor): """ :param admin: the Admin interface for the objects on the one side of the relation :param create_inline: if False, then a new entity will be created within a new window, if True, it will be created inline :param proxy: the proxy class to use to present the data in the list or the query to the table view :param column_width: the width of the editor in number of characters :param rows: minimum number of rows visible after creating the editor, set_value needs to be called to set the actual data to the editor """ def __init__(self, admin=None, parent=None, create_inline=False, direction='onetomany', field_name='onetomany', column_width=None, proxy=None, rows=5, **kw): CustomEditor.__init__(self, parent, column_width=column_width) self.setObjectName(field_name) layout = QtWidgets.QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) # # Setup table # from camelot.view.controls.tableview import AdminTableWidget from camelot.view.proxy.collection_proxy import CollectionProxy # parent set by layout manager table = AdminTableWidget(admin, self) table.setObjectName('table') layout.setSizeConstraint(QtGui.QLayout.SetNoConstraint) self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) self.setMinimumHeight((self._font_height + 5) * rows) table.verticalHeader().sectionClicked.connect( self.trigger_list_action ) model = (proxy or CollectionProxy)(admin) table.setModel(model) register.register(model, table) self.admin = admin self.direction = direction self.create_inline = create_inline layout.addWidget(table) self.setLayout(layout) self._new_message = None self.gui_context = ListActionGuiContext() self.gui_context.view = self self.gui_context.admin = self.admin self.gui_context.item_view = table post(self.admin.get_related_toolbar_actions, self.set_right_toolbar_actions, args=(Qt.RightToolBarArea, self.direction)) post(self.get_columns, self.set_columns) @QtCore.qt_slot(object) def set_right_toolbar_actions(self, toolbar_actions): if toolbar_actions is not None: toolbar = QtWidgets.QToolBar(self) toolbar.setOrientation(Qt.Vertical) for action in toolbar_actions: qaction = action.render(self.gui_context, toolbar) if isinstance(qaction, QtWidgets.QWidget): toolbar.addWidget(qaction) else: qaction.triggered.connect(self.action_triggered) toolbar.addAction(qaction) self.layout().addWidget(toolbar) # set field attributes might have been called before the # toolbar was created self.update_action_status() @QtCore.qt_slot(bool) def action_triggered(self, _checked=False): action_action = self.sender() action_action.action.gui_run(self.gui_context) def set_field_attributes(self, **kwargs): super(One2ManyEditor, self).set_field_attributes(**kwargs) self.gui_context.field_attributes = kwargs self.update_action_status() def get_columns(self): return self.admin.get_columns() def update_action_status(self): toolbar = self.findChild(QtWidgets.QToolBar) if toolbar: model_context = self.gui_context.create_model_context() for qaction in toolbar.actions(): if isinstance(qaction, ActionAction): post(qaction.action.get_state, qaction.set_state, args=(model_context, ))
[docs] def get_model(self): """ :return: a :class:`QtGui.QAbstractItemModel` or `None` """ table = self.findChild(QtWidgets.QWidget, 'table') if table is not None: return table.model()
@QtCore.qt_slot(object) def set_columns(self, columns): from ..delegates.delegatemanager import DelegateManager table = self.findChild(QtWidgets.QWidget, 'table') if table is not None: delegate = DelegateManager(columns, parent=self) table.setItemDelegate(delegate) model = table.model() if model is not None: model.set_columns(columns) for i in range(model.columnCount()): txtwidth = variant_to_py( model.headerData(i, Qt.Horizontal, Qt.SizeHintRole) ).width() table.setColumnWidth(i, txtwidth) def set_value(self, collection): collection = CustomEditor.set_value(self, collection) model = self.get_model() if model is not None: # even if the collection 'is' the same object as the current # one, still need to set it, since the content of the collection # might have changed. model.set_value(collection) model_context = self.gui_context.create_model_context() for toolbar in self.findChildren(QtWidgets.QToolBar): for qaction in toolbar.actions(): post(qaction.action.get_state, qaction.set_state, args=(model_context, )) #post( model._extend_cache, self.update_delegates ) def activate_editor(self, number_of_rows): assert object_thread(self) # return # Activating this code can cause segfaults # see ticket 765 in web issues # # The segfault seems no longer there after disabling the # editor before setting a new model, but the code below # seems to have no effect. table = self.findChild(QtWidgets.QWidget, 'table') if table is not None: index = table.model().index(max(0, number_of_rows - 1), 0) table.scrollToBottom() table.setCurrentIndex(index) table.edit(index) @QtCore.qt_slot(int) def trigger_list_action(self, index): table = self.findChild(QtWidgets.QWidget, 'table') # close the editor to prevent certain Qt crashes table.close_editor() if self.admin.list_action: self.admin.list_action.gui_run(self.gui_context)