Example #1
0
    def __init__(self,
                 admin=None,
                 parent=None,
                 editable=True,
                 field_name='manytoone',
                 actions=[
                     field_action.ClearObject(),
                     field_action.SelectObject(),
                     field_action.NewObject(),
                     field_action.OpenObject()
                 ],
                 **kwargs):
        """
        :param entity_admin : The Admin interface for the object on the one
        side of the relation
        """
        CustomEditor.__init__(self, parent)
        self.setSizePolicy(QtGui.QSizePolicy.Preferred,
                           QtGui.QSizePolicy.Fixed)
        self.setObjectName(field_name)
        self.admin = admin
        self.new_value = None
        self._entity_representation = ''
        self.obj = None
        self._last_highlighted_entity_getter = None

        self.layout = QtWidgets.QHBoxLayout()
        self.layout.setSpacing(0)
        self.layout.setContentsMargins(0, 0, 0, 0)

        # Search input
        self.search_input = DecoratedLineEdit(self)
        self.search_input.setPlaceholderText(_('Search...'))
        self.search_input.textEdited.connect(self.textEdited)
        self.search_input.set_minimum_width(20)
        self.search_input.arrow_down_key_pressed.connect(
            self.on_arrow_down_key_pressed)
        # suppose garbage was entered, we need to refresh the content
        self.search_input.editingFinished.connect(
            self.search_input_editing_finished)
        self.setFocusProxy(self.search_input)

        # Search Completer
        self.completer = QtGui.QCompleter()
        self.completions_model = self.CompletionsModel(self.completer)
        self.completer.setModel(self.completions_model)
        self.completer.setCaseSensitivity(Qt.CaseInsensitive)
        self.completer.setCompletionMode(
            QtGui.QCompleter.UnfilteredPopupCompletion)
        self.completer.activated[QtCore.QModelIndex].connect(
            self.completionActivated)
        self.completer.highlighted[QtCore.QModelIndex].connect(
            self.completion_highlighted)
        self.search_input.setCompleter(self.completer)

        # Setup layout
        self.layout.addWidget(self.search_input)
        self.setLayout(self.layout)
        self.add_actions(actions, self.layout)
        get_signal_handler().connect_signals(self)
Example #2
0
    def __init__(self,
                 parent=None,
                 editable=True,
                 nullable=True,
                 **kwargs):
        CustomEditor.__init__(self, parent)

        self.date_format = local_date_format()
        self.line_edit = DecoratedLineEdit()
        self.line_edit.set_background_text( QtCore.QDate(2000,1,1).toString(self.date_format) )

        # The order of creation of this widgets and their parenting
        # seems very sensitive under windows and creates system crashes
        # so don't change this without extensive testing on windows
        special_date_menu = QtGui.QMenu(self)
        calendar_widget_action = QtGui.QWidgetAction(special_date_menu)
        self.calendar_widget = QtGui.QCalendarWidget(special_date_menu)
        self.calendar_widget.activated.connect(self.calendar_widget_activated)
        self.calendar_widget.clicked.connect(self.calendar_widget_activated)
        calendar_widget_action.setDefaultWidget(self.calendar_widget)

        self.calendar_action_trigger.connect( special_date_menu.hide )
        special_date_menu.addAction(calendar_widget_action)
        special_date_menu.addAction(_('Today'))
        special_date_menu.addAction(_('Far future'))
        self.special_date = QtGui.QToolButton(self)
        self.special_date.setIcon( self.special_date_icon.getQIcon() )
        self.special_date.setAutoRaise(True)
        self.special_date.setToolTip(_('Calendar and special dates'))
        self.special_date.setMenu(special_date_menu)
        self.special_date.setPopupMode(QtGui.QToolButton.InstantPopup)
        self.special_date.setFixedHeight(self.get_height())
        self.special_date.setFocusPolicy(Qt.ClickFocus)
        # end of sensitive part

        if nullable:
            special_date_menu.addAction(_('Clear'))

        self.hlayout = QtGui.QHBoxLayout()
        self.hlayout.addWidget(self.line_edit)
        self.hlayout.addWidget(self.special_date)

        self.hlayout.setContentsMargins(0, 0, 0, 0)
        self.hlayout.setMargin(0)
        self.hlayout.setSpacing(0)
        self.hlayout.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
        
        self.setContentsMargins(0, 0, 0, 0)
        self.setLayout(self.hlayout)

        self.minimum = datetime.date.min
        self.maximum = datetime.date.max
        self.setFocusProxy(self.line_edit)

        self.line_edit.editingFinished.connect( self.line_edit_finished )
        self.line_edit.textEdited.connect(self.text_edited)
        special_date_menu.triggered.connect(self.set_special_date)
Example #3
0
    def setup_widget(self):
        layout = QtWidgets.QHBoxLayout()
        #
        # Setup label
        #
        self.label = QtWidgets.QLabel(self)
        self.label.installEventFilter(self)
        self.label.setAlignment( Qt.AlignHCenter|Qt.AlignVCenter )
        layout.addWidget(self.label)
        
        self.filename = DecoratedLineEdit( self )
        self.filename.setVisible( False )
        #
        # Setup buttons
        #
        button_layout = QtWidgets.QVBoxLayout()
        button_layout.setSpacing( 0 )
        button_layout.setContentsMargins( 0, 0, 0, 0)
        
        copy_button = QtWidgets.QToolButton()
        copy_button.setDefaultAction( ActionFactory.copy(self, self.copy_to_clipboard ) )
        copy_button.setAutoRaise(True)
        copy_button.setFocusPolicy(Qt.ClickFocus)

        paste_button = QtWidgets.QToolButton()
        paste_button.setDefaultAction( ActionFactory.paste(self, self.paste_from_clipboard ) )
        paste_button.setAutoRaise(True)
        paste_button.setObjectName('paste')
        paste_button.setFocusPolicy(Qt.ClickFocus)
        
        self.add_actions(self.actions, button_layout)
        button_layout.addWidget(copy_button)
        button_layout.addWidget(paste_button)
        button_layout.addStretch()

        layout.addLayout(button_layout)
        #label_button_layout.addStretch()
        self.setLayout( layout )
        self.clear_image()
        QtWidgets.QApplication.clipboard().dataChanged.connect( self.clipboard_data_changed )
        self.clipboard_data_changed()

        # horizontal policy is always expanding, to fill the width of a column
        # in a form
        vertical_size_policy = QtGui.QSizePolicy.Expanding

        if self.preview_width != 0:
            self.label.setMinimumWidth(self.preview_width)
        if self.preview_height != 0:
            self.label.setFixedHeight(self.preview_height)
            vertical_size_policy = QtGui.QSizePolicy.Fixed
        self.setSizePolicy(QtGui.QSizePolicy.Expanding, vertical_size_policy)
        self.label.setSizePolicy(QtGui.QSizePolicy.Expanding, vertical_size_policy)
Example #4
0
    def __init__(self, parent):
        QtGui.QWidget.__init__(self, parent)
        layout = QtGui.QHBoxLayout()
        layout.setSpacing(0)
        layout.setContentsMargins(3, 3, 3, 3)

        #
        # The search timer reduced the number of search signals that are
        # emitted, by waiting for the next keystroke before emitting the
        # search signal
        #
        timer = QtCore.QTimer(self)
        timer.setInterval(300)
        timer.setSingleShot(True)
        timer.setObjectName('timer')
        timer.timeout.connect(self.emit_search)

        # Search button
        self.search_button = QtGui.QToolButton()
        icon = Icon('tango/16x16/actions/system-search.png').getQIcon()
        self.search_button.setIcon(icon)
        self.search_button.setIconSize(QtCore.QSize(14, 14))
        self.search_button.setAutoRaise(True)
        self.search_button.setToolTip(_('Expand or collapse search options'))
        self.search_button.clicked.connect(self.emit_expand_search_options)

        # Search input
        from camelot.view.controls.decorated_line_edit import DecoratedLineEdit
        self.search_input = DecoratedLineEdit(self)
        self.search_input.set_background_text(_('Search...'))
        #self.search_input.setStyleSheet('QLineEdit{ border-radius: 0.25em;}')
        self.search_input.returnPressed.connect(self.emit_search)
        self.search_input.textEdited.connect(self._start_search_timer)
        self.search_input.arrow_down_key_pressed.connect(
            self.on_arrow_down_key_pressed)

        self.setFocusProxy(self.search_input)

        # Cancel button
        self.cancel_button = QtGui.QToolButton()
        icon = Icon('tango/16x16/actions/edit-clear.png').getQIcon()
        self.cancel_button.setIcon(icon)
        self.cancel_button.setIconSize(QtCore.QSize(14, 14))
        self.cancel_button.setAutoRaise(True)
        self.cancel_button.clicked.connect(self.emit_cancel)

        # Setup layout
        layout.addWidget(self.search_button)
        layout.addWidget(self.search_input)
        layout.addWidget(self.cancel_button)
        self.setLayout(layout)
    def __init__(self,
                 parent=None,
                 editable=True,
                 address_type=None,
                 address_validator=default_address_validator,
                 field_name='virtual_address',
                 **kwargs):
        """
        :param address_type: limit the allowed address to be entered to be
            of a certain time, can be 'phone', 'fax', 'email', 'mobile', 'pager'.
            If set to None, all types are allowed.
            
        Upto now, the corrected address returned by the address validator is
        not yet taken into account.
        """
        CustomEditor.__init__(self, parent)
        self.setObjectName(field_name)
        self._address_type = address_type
        self._address_validator = address_validator
        self.layout = QtGui.QHBoxLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.combo = QtGui.QComboBox()
        self.combo.addItems(camelot.types.VirtualAddress.virtual_address_types)
        self.combo.setEnabled(editable)
        if address_type:
            self.combo.setVisible(False)
        self.layout.addWidget(self.combo)
        self.editor = DecoratedLineEdit(self)
        self.editor.setEnabled(editable)
        self.editor.set_minimum_width(30)
        self.layout.addWidget(self.editor)
        self.setFocusProxy(self.editor)
        self.editable = editable
        nullIcon = Icon('tango/16x16/apps/internet-mail.png').getQIcon()
        self.label = QtGui.QToolButton()
        self.label.setIcon(nullIcon)
        self.label.setAutoRaise(True)
        self.label.setEnabled(False)
        self.label.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.label.setFocusPolicy(Qt.ClickFocus)
        self.label.clicked.connect(self.mail_click)
        self.label.hide()

        self.layout.addWidget(self.label)
        self.editor.editingFinished.connect(self.emit_editing_finished)
        self.editor.textEdited.connect(self.editorValueChanged)
        self.combo.currentIndexChanged.connect(self.comboIndexChanged)

        self.setLayout(self.layout)
        self.checkValue(self.editor.text())
Example #6
0
    def __init__(self, parent = None,
                       editable = True,
                       nullable = True, 
                       field_name = 'date',
                       **kwargs):
        CustomEditor.__init__(self, parent)

        self.setObjectName( field_name )
        self.date_format = local_date_format()
        self.line_edit = DecoratedLineEdit()
        self.line_edit.set_minimum_width( len( self.date_format ) )
        self.line_edit.set_background_text( QtCore.QDate(2000,1,1).toString(self.date_format) )

        # The order of creation of this widgets and their parenting
        # seems very sensitive under windows and creates system crashes
        # so don't change this without extensive testing on windows
        special_date_menu = QtGui.QMenu(self)
        calendar_widget_action = QtGui.QWidgetAction(special_date_menu)
        self.calendar_widget = QtGui.QCalendarWidget(special_date_menu)
        self.calendar_widget.activated.connect(self.calendar_widget_activated)
        self.calendar_widget.clicked.connect(self.calendar_widget_activated)
        calendar_widget_action.setDefaultWidget(self.calendar_widget)

        self.calendar_action_trigger.connect( special_date_menu.hide )
        special_date_menu.addAction(calendar_widget_action)
        special_date_menu.addAction(_('Today'))
        special_date_menu.addAction(_('Far future'))
        self.special_date = QtGui.QToolButton(self)
        self.special_date.setIcon( self.special_date_icon.getQIcon() )
        self.special_date.setAutoRaise(True)
        self.special_date.setToolTip(_('Calendar and special dates'))
        self.special_date.setMenu(special_date_menu)
        self.special_date.setPopupMode(QtGui.QToolButton.InstantPopup)
        self.special_date.setFixedHeight(self.get_height())
        self.special_date.setFocusPolicy(Qt.ClickFocus)
        # end of sensitive part

        if nullable:
            special_date_menu.addAction(_('Clear'))

        self.hlayout = QtGui.QHBoxLayout()
        self.hlayout.addWidget(self.line_edit)
        self.hlayout.addWidget(self.special_date)

        self.hlayout.setContentsMargins(0, 0, 0, 0)
        self.hlayout.setSpacing(0)
        self.hlayout.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
        
        self.setContentsMargins(0, 0, 0, 0)
        self.setLayout(self.hlayout)

        self.minimum = datetime.date.min
        self.maximum = datetime.date.max
        self.setFocusProxy(self.line_edit)

        self.line_edit.editingFinished.connect( self.line_edit_finished )
        self.line_edit.textEdited.connect(self.text_edited)
        special_date_menu.triggered.connect(self.set_special_date)
Example #7
0
    def setup_widget(self):
        """Called inside init, overwrite this method for custom
        file edit widgets"""
        self.layout = QtWidgets.QHBoxLayout()
        self.layout.setSpacing(0)
        self.layout.setContentsMargins(0, 0, 0, 0)

        # Filename
        self.filename = DecoratedLineEdit(self)
        self.filename.set_minimum_width(20)
        self.filename.setFocusPolicy(Qt.ClickFocus)

        # Setup layout
        self.document_label = QtWidgets.QLabel(self)
        self.document_label.setPixmap(self.document_pixmap.getQPixmap())
        self.layout.addWidget(self.document_label)
        self.layout.addWidget(self.filename)
        self.add_actions(self.actions, self.layout)
        self.setLayout(self.layout)
Example #8
0
    def setup_widget(self):
        """Called inside init, overwrite this method for custom
        file edit widgets"""
        layout = QtGui.QHBoxLayout()
        layout.setSpacing(0)
        layout.setContentsMargins(0, 0, 0, 0)

        browse_button = QtGui.QToolButton(self)
        browse_button.setFocusPolicy(Qt.ClickFocus)
        browse_button.setIcon(self.browse_icon.getQIcon())
        browse_button.setToolTip(_('Browse'))
        browse_button.setAutoRaise(True)
        browse_button.clicked.connect(self.browse_button_clicked)

        self.filename = DecoratedLineEdit(self)
        self.filename.editingFinished.connect(self.filename_editing_finished)
        self.setFocusProxy(self.filename)

        layout.addWidget(self.filename)
        layout.addWidget(browse_button)
        self.setLayout(layout)
Example #9
0
    def __init__(self, parent):
        QtGui.QWidget.__init__(self, parent)
        layout = QtGui.QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(3)

        # Search button
        self.search_button = QtGui.QToolButton()
        icon = Icon('tango/16x16/actions/system-search.png').getQIcon()
        self.search_button.setIcon(icon)
        self.search_button.setIconSize(QtCore.QSize(14, 14))
        self.search_button.setAutoRaise(True)
        self.search_button.setToolTip(_('Expand or collapse search options'))
        self.search_button.clicked.connect(self.emit_expand_search_options)

        # Search input
        from camelot.view.controls.decorated_line_edit import DecoratedLineEdit
        self.search_input = DecoratedLineEdit(self)
        self.search_input.set_background_text(_('Search...'))
        self.search_input.setToolTip(_('type words to search for'))
        #self.search_input.setStyleSheet('QLineEdit{ border-radius: 0.25em;}')
        self.search_input.returnPressed.connect(self.emit_search)
        self.search_input.textEdited.connect(self.emit_search)

        self.setFocusProxy(self.search_input)

        # Cancel button
        self.cancel_button = QtGui.QToolButton()
        icon = Icon('tango/16x16/actions/edit-clear.png').getQIcon()
        self.cancel_button.setIcon(icon)
        self.cancel_button.setIconSize(QtCore.QSize(14, 14))
        self.cancel_button.setAutoRaise(True)
        self.cancel_button.clicked.connect(self.emit_cancel)

        # Setup layout
        layout.addWidget(self.search_button)
        layout.addWidget(self.search_input)
        layout.addWidget(self.cancel_button)
        self.setLayout(layout)
Example #10
0
    def __init__(self, parent):
        QtGui.QWidget.__init__(self, parent)
        layout = QtGui.QHBoxLayout()
        layout.setSpacing(0)
        layout.setContentsMargins(3, 3, 3, 3)
        
        #
        # The search timer reduced the number of search signals that are
        # emitted, by waiting for the next keystroke before emitting the
        # search signal
        #
        timer = QtCore.QTimer( self )
        timer.setInterval( 300 )
        timer.setSingleShot( True )
        timer.setObjectName( 'timer' )
        timer.timeout.connect( self.emit_search )

        # Search button
        self.search_button = QtGui.QToolButton()
        icon = Icon('tango/16x16/actions/system-search.png').getQIcon()
        self.search_button.setIcon(icon)
        self.search_button.setIconSize(QtCore.QSize(14, 14))
        self.search_button.setAutoRaise(True)
        self.search_button.setToolTip(_('Expand or collapse search options'))
        self.search_button.clicked.connect( self.emit_expand_search_options )

        # Search input
        from camelot.view.controls.decorated_line_edit import DecoratedLineEdit
        self.search_input = DecoratedLineEdit(self)
        self.search_input.set_background_text(_('Search...'))
        #self.search_input.setStyleSheet('QLineEdit{ border-radius: 0.25em;}')
        self.search_input.returnPressed.connect( self.emit_search )
        self.search_input.textEdited.connect( self._start_search_timer )
        self.search_input.arrow_down_key_pressed.connect(self.on_arrow_down_key_pressed)

        self.setFocusProxy( self.search_input )

        # Cancel button
        self.cancel_button = QtGui.QToolButton()
        icon = Icon('tango/16x16/actions/edit-clear.png').getQIcon()
        self.cancel_button.setIcon(icon)
        self.cancel_button.setIconSize(QtCore.QSize(14, 14))
        self.cancel_button.setAutoRaise(True)
        self.cancel_button.clicked.connect( self.emit_cancel )

        # Setup layout
        layout.addWidget(self.search_button)
        layout.addWidget(self.search_input)
        layout.addWidget(self.cancel_button)
        self.setLayout(layout)
Example #11
0
    def __init__(self, 
                 parent = None, 
                 editable = True, 
                 address_type = None, 
                 address_validator = default_address_validator,
                 field_name = 'virtual_address',
                 **kwargs):
        """
        :param address_type: limit the allowed address to be entered to be
            of a certain time, can be 'phone', 'fax', 'email', 'mobile', 'pager'.
            If set to None, all types are allowed.
            
        Upto now, the corrected address returned by the address validator is
        not yet taken into account.
        """
        CustomEditor.__init__(self, parent)
        self.setObjectName( field_name )
        self._address_type = address_type
        self._address_validator = address_validator
        self.layout = QtGui.QHBoxLayout()
        self.layout.setContentsMargins( 0, 0, 0, 0)
        self.combo = QtGui.QComboBox()
        self.combo.addItems(camelot.types.VirtualAddress.virtual_address_types)
        self.combo.setEnabled(editable)
        if address_type:
            self.combo.setVisible(False)
        self.layout.addWidget(self.combo)
        self.editor = DecoratedLineEdit( self )
        self.editor.setEnabled(editable)
        self.editor.set_minimum_width( 30 )
        self.layout.addWidget(self.editor)
        self.setFocusProxy(self.editor)
        self.editable = editable
        nullIcon = Icon('tango/16x16/apps/internet-mail.png').getQIcon()
        self.label = QtGui.QToolButton()
        self.label.setIcon(nullIcon)
        self.label.setAutoRaise(True)
        self.label.setEnabled(False)
        self.label.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.label.setFocusPolicy(Qt.ClickFocus)
        self.label.clicked.connect( self.mail_click )
        self.label.hide()

        self.layout.addWidget(self.label)
        self.editor.editingFinished.connect(self.emit_editing_finished)
        self.editor.textEdited.connect(self.editorValueChanged)
        self.combo.currentIndexChanged.connect(self.comboIndexChanged)

        self.setLayout(self.layout)
        self.checkValue(self.editor.text())
Example #12
0
    def setup_widget(self):
        """Called inside init, overwrite this method for custom
        file edit widgets"""
        layout = QtGui.QHBoxLayout()
        layout.setSpacing(0)
        layout.setContentsMargins(0, 0, 0, 0)

        browse_button = QtGui.QToolButton( self )
        browse_button.setFocusPolicy( Qt.ClickFocus )
        browse_button.setIcon( self.browse_icon.getQIcon() )
        browse_button.setToolTip( _('Browse') )
        browse_button.setAutoRaise( True )
        browse_button.clicked.connect( self.browse_button_clicked )

        self.filename = DecoratedLineEdit(self)
        self.filename.editingFinished.connect( self.filename_editing_finished )
        self.setFocusProxy( self.filename )
        
        layout.addWidget( self.filename )
        layout.addWidget( browse_button )
        self.setLayout( layout )
Example #13
0
    def __init__(self, parent):
        QtGui.QWidget.__init__(self, parent)
        layout = QtGui.QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(3)

        # Search button
        self.search_button = QtGui.QToolButton()
        icon = Icon('tango/16x16/actions/system-search.png').getQIcon()
        self.search_button.setIcon(icon)
        self.search_button.setIconSize(QtCore.QSize(14, 14))
        self.search_button.setAutoRaise(True)
        self.search_button.setToolTip(_('Expand or collapse search options'))
        self.search_button.clicked.connect( self.emit_expand_search_options )

        # Search input
        from camelot.view.controls.decorated_line_edit import DecoratedLineEdit
        self.search_input = DecoratedLineEdit(self)
        self.search_input.set_background_text(_('Search...'))
        self.search_input.setToolTip(_('type words to search for'))
        #self.search_input.setStyleSheet('QLineEdit{ border-radius: 0.25em;}')
        self.search_input.returnPressed.connect( self.emit_search )
        self.search_input.textEdited.connect( self.emit_search )

        self.setFocusProxy( self.search_input )

        # Cancel button
        self.cancel_button = QtGui.QToolButton()
        icon = Icon('tango/16x16/actions/edit-clear.png').getQIcon()
        self.cancel_button.setIcon(icon)
        self.cancel_button.setIconSize(QtCore.QSize(14, 14))
        self.cancel_button.setAutoRaise(True)
        self.cancel_button.clicked.connect( self.emit_cancel )

        # Setup layout
        layout.addWidget(self.search_button)
        layout.addWidget(self.search_input)
        layout.addWidget(self.cancel_button)
        self.setLayout(layout)
class VirtualAddressEditor(CustomEditor):
    def __init__(self,
                 parent=None,
                 editable=True,
                 address_type=None,
                 address_validator=default_address_validator,
                 field_name='virtual_address',
                 **kwargs):
        """
        :param address_type: limit the allowed address to be entered to be
            of a certain time, can be 'phone', 'fax', 'email', 'mobile', 'pager'.
            If set to None, all types are allowed.
            
        Upto now, the corrected address returned by the address validator is
        not yet taken into account.
        """
        CustomEditor.__init__(self, parent)
        self.setObjectName(field_name)
        self._address_type = address_type
        self._address_validator = address_validator
        self.layout = QtGui.QHBoxLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.combo = QtGui.QComboBox()
        self.combo.addItems(camelot.types.VirtualAddress.virtual_address_types)
        self.combo.setEnabled(editable)
        if address_type:
            self.combo.setVisible(False)
        self.layout.addWidget(self.combo)
        self.editor = DecoratedLineEdit(self)
        self.editor.setEnabled(editable)
        self.editor.set_minimum_width(30)
        self.layout.addWidget(self.editor)
        self.setFocusProxy(self.editor)
        self.editable = editable
        nullIcon = Icon('tango/16x16/apps/internet-mail.png').getQIcon()
        self.label = QtGui.QToolButton()
        self.label.setIcon(nullIcon)
        self.label.setAutoRaise(True)
        self.label.setEnabled(False)
        self.label.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.label.setFocusPolicy(Qt.ClickFocus)
        self.label.clicked.connect(self.mail_click)
        self.label.hide()

        self.layout.addWidget(self.label)
        self.editor.editingFinished.connect(self.emit_editing_finished)
        self.editor.textEdited.connect(self.editorValueChanged)
        self.combo.currentIndexChanged.connect(self.comboIndexChanged)

        self.setLayout(self.layout)
        self.checkValue(self.editor.text())

    @QtCore.pyqtSlot()
    def comboIndexChanged(self):
        self.checkValue(self.editor.text())
        self.emit_editing_finished()

    def set_value(self, value):
        value = CustomEditor.set_value(self, value)
        if value:
            self.editor.setText(value[1])
            idx = camelot.types.VirtualAddress.virtual_address_types.index(
                self._address_type or value[0])
            self.combo.setCurrentIndex(idx)
            icon = Icon('tango/16x16/devices/printer.png').getQIcon()
            # These icons don't exist any more in the new tango icon set
            #            if str(self.combo.currentText()) == 'phone':
            #                icon = Icon('tango/16x16/devices/phone.png').getQIcon()
            if str(self.combo.currentText()) == 'fax':
                icon = Icon('tango/16x16/devices/printer.png').getQIcon()
#            if str(self.combo.currentText()) == 'mobile':
#                icon = Icon('tango/16x16/devices/mobile.png').getQIcon()
#            if str(self.combo.currentText()) == 'im':
#                icon = Icon('tango/16x16/places/instant-messaging.png').getQIcon()
#            if str(self.combo.currentText()) == 'pager':
#                icon = Icon('tango/16x16/devices/pager.png').getQIcon()
            if str(self.combo.currentText()) == 'email':
                icon = Icon('tango/16x16/apps/internet-mail.png').getQIcon()
                #self.label.setFocusPolicy(Qt.StrongFocus)
                #self.label.setAutoFillBackground(True)
                self.label.setIcon(icon)
                self.label.setEnabled(self.editable)
                self.label.show()
            else:
                self.label.hide()
                self.label.setIcon(icon)
                self.label.setEnabled(self.editable)
                self.label.setToolButtonStyle(Qt.ToolButtonIconOnly)

#      self.update()
#      self.label.update()
#      self.layout.update()

            self.checkValue(value[1])

    def get_value(self):
        value = (unicode(self.combo.currentText()),
                 unicode(self.editor.text()))
        return CustomEditor.get_value(self) or value

    def set_enabled(self, editable=True):
        self.combo.setEnabled(editable)
        self.editor.setEnabled(editable)
        if not editable:
            self.label.setEnabled(False)
        else:
            if self.combo.currentText() == 'email':
                self.label.setEnabled(True)

    def checkValue(self, text):
        address_type = unicode(self.combo.currentText())
        valid, _corrected = self._address_validator(address_type,
                                                    unicode(text))
        self.editor.set_valid(valid)

    def editorValueChanged(self, text):
        self.checkValue(text)

    @QtCore.pyqtSlot()
    def mail_click(self):
        address = self.editor.text()
        url = QtCore.QUrl()
        url.setUrl(u'mailto:%s?subject=Subject' % unicode(address))
        QtGui.QDesktopServices.openUrl(url)

    def emit_editing_finished(self):
        self.value = []
        self.value.append(str(self.combo.currentText()))
        self.value.append(str(self.editor.text()))
        self.set_value(self.value)
        # emiting editingFinished without a value for the mechanism itself will lead to
        # integrity errors
        if self.value[1]:
            self.editingFinished.emit()

    def set_background_color(self, background_color):
        set_background_color_palette(self.editor, background_color)

    def set_field_attributes(self,
                             editable=True,
                             background_color=None,
                             tooltip=None,
                             **kwargs):
        self.set_enabled(editable)
        self.set_background_color(background_color)
        self.setToolTip(unicode(tooltip or ''))
Example #15
0
    def setup_widget(self):
        layout = QtGui.QVBoxLayout()
        layout.setSpacing( 0 )
        label_button_layout = QtGui.QHBoxLayout()
        #
        # Setup label
        #
        self.label = QtGui.QLabel(self)
        self.label.installEventFilter(self)
        self.label.setAlignment( Qt.AlignHCenter|Qt.AlignVCenter )
        label_button_layout.addWidget(self.label)
        
        self.filename = DecoratedLineEdit( self )
        self.filename.setVisible( False )
        #
        # Setup buttons
        #
        button_layout = QtGui.QVBoxLayout()
        button_layout.setSpacing( 0 )
        button_layout.setContentsMargins( 0, 0, 0, 0)

        self.save_as_button = QtGui.QToolButton()
        self.save_as_button.setFocusPolicy( Qt.ClickFocus )
        self.save_as_button.setIcon(self.save_as_icon.getQIcon())
        self.save_as_button.setToolTip(_('Save file as'))
        self.save_as_button.setAutoRaise(True)
        self.save_as_button.clicked.connect(self.save_as_button_clicked)
        
        self.open_button = QtGui.QToolButton()
        self.open_button.setIcon(self.open_icon.getQIcon())
        self.open_button.setAutoRaise(True)
        self.open_button.setToolTip(unicode(_('open image')))
        self.open_button.clicked.connect(self.open_button_clicked)

        self.add_button = QtGui.QToolButton()
        self.add_button.setFocusPolicy( Qt.StrongFocus )
        self.add_button.setIcon(self.add_icon.getQIcon())
        self.add_button.setToolTip(_('Attach file'))
        self.add_button.clicked.connect(self.add_button_clicked)
        self.add_button.setAutoRaise(True)
        
        self.clear_button = QtGui.QToolButton()
        self.clear_button.setIcon(self.clear_icon.getQIcon())
        self.clear_button.setToolTip(unicode(_('delete image')))
        self.clear_button.setAutoRaise(True)
        self.clear_button.clicked.connect(self.clear_button_clicked)
        self.clear_button.setFocusPolicy(Qt.ClickFocus)
        
        copy_button = QtGui.QToolButton()
        copy_button.setDefaultAction( ActionFactory.copy(self, self.copy_to_clipboard ) )
        copy_button.setAutoRaise(True)
        copy_button.setFocusPolicy(Qt.ClickFocus)

        paste_button = QtGui.QToolButton()
        paste_button.setDefaultAction( ActionFactory.paste(self, self.paste_from_clipboard ) )
        paste_button.setAutoRaise(True)
        paste_button.setObjectName('paste')
        paste_button.setFocusPolicy(Qt.ClickFocus)
        
        #button_layout.addStretch()
        button_layout.addWidget(self.open_button)
        button_layout.addWidget(self.save_as_button)
        button_layout.addWidget(self.add_button)
        button_layout.addWidget(self.clear_button)
        button_layout.addWidget(copy_button)
        button_layout.addWidget(paste_button)

        label_button_layout.addLayout(button_layout)
        label_button_layout.addStretch()
        layout.addLayout( label_button_layout )
        #layout.addStretch()
        self.setLayout( layout )
        self.clear_image()
        QtGui.QApplication.clipboard().dataChanged.connect( self.clipboard_data_changed )
        self.clipboard_data_changed()
Example #16
0
class VirtualAddressEditor(CustomEditor):

    def __init__(self, 
                 parent = None, 
                 editable = True, 
                 address_type = None, 
                 address_validator = default_address_validator,
                 field_name = 'virtual_address',
                 **kwargs):
        """
        :param address_type: limit the allowed address to be entered to be
            of a certain time, can be 'phone', 'fax', 'email', 'mobile', 'pager'.
            If set to None, all types are allowed.
            
        Upto now, the corrected address returned by the address validator is
        not yet taken into account.
        """
        CustomEditor.__init__(self, parent)
        self.setObjectName( field_name )
        self._address_type = address_type
        self._address_validator = address_validator
        self.layout = QtGui.QHBoxLayout()
        self.layout.setContentsMargins( 0, 0, 0, 0)
        self.combo = QtGui.QComboBox()
        self.combo.addItems(camelot.types.VirtualAddress.virtual_address_types)
        self.combo.setEnabled(editable)
        if address_type:
            self.combo.setVisible(False)
        self.layout.addWidget(self.combo)
        self.editor = DecoratedLineEdit( self )
        self.editor.setEnabled(editable)
        self.editor.set_minimum_width( 30 )
        self.layout.addWidget(self.editor)
        self.setFocusProxy(self.editor)
        self.editable = editable
        nullIcon = Icon('tango/16x16/apps/internet-mail.png').getQIcon()
        self.label = QtGui.QToolButton()
        self.label.setIcon(nullIcon)
        self.label.setAutoRaise(True)
        self.label.setEnabled(False)
        self.label.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.label.setFocusPolicy(Qt.ClickFocus)
        self.label.clicked.connect( self.mail_click )
        self.label.hide()

        self.layout.addWidget(self.label)
        self.editor.editingFinished.connect(self.emit_editing_finished)
        self.editor.textEdited.connect(self.editorValueChanged)
        self.combo.currentIndexChanged.connect(self.comboIndexChanged)

        self.setLayout(self.layout)
        self.checkValue(self.editor.text())

    @QtCore.pyqtSlot()
    def comboIndexChanged(self):
        self.checkValue(self.editor.text())
        self.emit_editing_finished()

    def set_value(self, value):
        value = CustomEditor.set_value(self, value)
        if value:
            self.editor.setText(value[1])
            idx = camelot.types.VirtualAddress.virtual_address_types.index(self._address_type or value[0])
            self.combo.setCurrentIndex(idx)
            icon = Icon('tango/16x16/devices/printer.png').getQIcon()
# These icons don't exist any more in the new tango icon set
#            if str(self.combo.currentText()) == 'phone':
#                icon = Icon('tango/16x16/devices/phone.png').getQIcon()
            if str(self.combo.currentText()) == 'fax':
                icon = Icon('tango/16x16/devices/printer.png').getQIcon()
#            if str(self.combo.currentText()) == 'mobile':
#                icon = Icon('tango/16x16/devices/mobile.png').getQIcon()
#            if str(self.combo.currentText()) == 'im':
#                icon = Icon('tango/16x16/places/instant-messaging.png').getQIcon()
#            if str(self.combo.currentText()) == 'pager':
#                icon = Icon('tango/16x16/devices/pager.png').getQIcon()
            if str(self.combo.currentText()) == 'email':
                icon = Icon('tango/16x16/apps/internet-mail.png').getQIcon()
                #self.label.setFocusPolicy(Qt.StrongFocus)
                #self.label.setAutoFillBackground(True)
                self.label.setIcon(icon)
                self.label.setEnabled( self.editable )
                self.label.show()
            else:
                self.label.hide()
                self.label.setIcon(icon)
                self.label.setEnabled(self.editable)
                self.label.setToolButtonStyle(Qt.ToolButtonIconOnly)

#      self.update()
#      self.label.update()
#      self.layout.update()


            self.checkValue(value[1])

    def get_value(self):
        value = (unicode(self.combo.currentText()), unicode(self.editor.text()))
        return CustomEditor.get_value(self) or value

    def set_enabled(self, editable=True):
        self.combo.setEnabled(editable)
        self.editor.setEnabled(editable)
        if not editable:
            self.label.setEnabled(False)
        else:
            if self.combo.currentText() == 'email':
                self.label.setEnabled(True)

    def checkValue(self, text):
        address_type = unicode( self.combo.currentText() )
        valid, _corrected = self._address_validator( address_type, unicode( text ) )
        self.editor.set_valid( valid )

    def editorValueChanged(self, text):
        self.checkValue(text)

    @QtCore.pyqtSlot()
    def mail_click(self):
        address = self.editor.text()
        url = QtCore.QUrl()
        url.setUrl( u'mailto:%s?subject=Subject'%unicode(address) )
        QtGui.QDesktopServices.openUrl(url)

    def emit_editing_finished(self):
        self.value = []
        self.value.append(str(self.combo.currentText()))
        self.value.append(str(self.editor.text()))
        self.set_value(self.value)
        # emiting editingFinished without a value for the mechanism itself will lead to
        # integrity errors
        if self.value[1]:
            self.editingFinished.emit()

    def set_background_color(self, background_color):
        set_background_color_palette( self.editor, background_color )
            
    def set_field_attributes(self, editable = True,
                                   background_color = None,
                                   tooltip = None, **kwargs):
        self.set_enabled(editable)
        self.set_background_color(background_color)
        self.setToolTip(unicode(tooltip or ''))
Example #17
0
class Many2OneEditor(CustomEditor, AbstractManyToOneEditor):
    """Widget for editing many 2 one relations"""

    new_icon = Icon('tango/16x16/actions/document-new.png')
    search_icon = Icon('tango/16x16/actions/system-search.png')

    class CompletionsModel(QtCore.QAbstractListModel):

        def __init__(self, parent=None):
            QtCore.QAbstractListModel.__init__(self, parent)
            self._completions = []

        def setCompletions(self, completions):
            self._completions = completions
            self.layoutChanged.emit()

        def data(self, index, role):
            if role == Qt.DisplayRole:
                return QtCore.QVariant(self._completions[index.row()][0])
            elif role == Qt.EditRole:
                return QtCore.QVariant(self._completions[index.row()][1])
            return QtCore.QVariant()

        def rowCount(self, index=None):
            return len(self._completions)

        def columnCount(self, index=None):
            return 1

    def __init__(self, admin=None, parent=None, editable=True, **kwargs):
        """:param entity_admin : The Admin interface for the object on the one
        side of the relation
        """

        CustomEditor.__init__(self, parent)
        self.admin = admin
        self.entity_set = False
        self._editable = editable
        self._entity_representation = ''
        self.entity_instance_getter = None
        self._last_highlighted_entity_getter = None

        self.layout = QtGui.QHBoxLayout()
        self.layout.setSpacing(0)
        self.layout.setMargin(0)

        # Search button
        self.search_button = QtGui.QToolButton()
        self.search_button.setAutoRaise(True)
        self.search_button.setFocusPolicy(Qt.ClickFocus)
        self.search_button.setFixedHeight(self.get_height())
        self.search_button.clicked.connect(self.searchButtonClicked)
        self.search_button.setIcon(
            Icon('tango/16x16/actions/edit-clear.png').getQIcon()
        )
        self.search_button.setToolTip(unicode(_('clear')))

        # Open button
        self.open_button = QtGui.QToolButton()
        self.open_button.setAutoRaise(True)
        self.open_button.setFocusPolicy(Qt.ClickFocus)
        self.open_button.setFixedHeight(self.get_height())
        self.open_button.clicked.connect(self.openButtonClicked)
        self.open_button.setIcon( self.new_icon.getQIcon() )
        self.open_button.setToolTip(unicode(_('new')))

        # Search input
        self.search_input = DecoratedLineEdit(self)
        self.search_input.set_background_text(_('Search...'))
        self.search_input.textEdited.connect(self.textEdited)
        # suppose garbage was entered, we need to refresh the content
        self.search_input.editingFinished.connect( self.search_input_editing_finished )
        self.setFocusProxy(self.search_input)

        # Search Completer
        self.completer = QtGui.QCompleter()
        self.completions_model = self.CompletionsModel(self.completer)
        self.completer.setModel(self.completions_model)
        self.completer.setCaseSensitivity(Qt.CaseInsensitive)
        self.completer.setCompletionMode(
            QtGui.QCompleter.UnfilteredPopupCompletion
        )
        #self.completer.activated.connect(self.completionActivated)
        #self.completer.highlighted.connect(self.completion_highlighted)
        self.completer.activated[QtCore.QModelIndex].connect(self.completionActivated)
        self.completer.highlighted[QtCore.QModelIndex].connect(self.completion_highlighted)
        self.search_input.setCompleter(self.completer)

        # Setup layout
        self.layout.addWidget(self.search_input)
        self.layout.addWidget(self.search_button)
        self.layout.addWidget(self.open_button)
        self.setLayout(self.layout)

    def set_field_attributes(self, editable=True, background_color=None, **kw):
        self.set_editable(editable)
        self.set_background_color(background_color)

    def set_editable(self, editable):
        self._editable = editable
        self.search_input.setEnabled(editable)
        self.search_button.setEnabled(editable)

    def textEdited(self, text):
        self._last_highlighted_entity_getter = None
        text = self.search_input.user_input()

        def create_search_completion(text):
            return lambda: self.search_completions(text)

        post(
            create_search_completion(unicode(text)),
            self.display_search_completions
        )
        self.completer.complete()

    @model_function
    def search_completions(self, text):
        """Search for object that match text, to fill the list of completions

        :return: a list of tuples of (object_representation, object_getter)
        """
        search_decorator = create_entity_search_query_decorator(
            self.admin, text
        )
        if search_decorator:
            sresult = [
                (unicode(e), create_constant_function(e))
                for e in search_decorator(self.admin.entity.query).limit(20)
            ]
            return text, sresult
        return text, []

    @gui_function
    def display_search_completions(self, prefix_and_completions):
        prefix, completions = prefix_and_completions
        self.completions_model.setCompletions(completions)
        self.completer.setCompletionPrefix(prefix)
        self.completer.complete()

    def completionActivated(self, index):
        object_getter = index.data(Qt.EditRole)
        self.setEntity(variant_to_pyobject(object_getter))

    def completion_highlighted(self, index ):
        object_getter = index.data(Qt.EditRole)
        pyob = variant_to_pyobject(object_getter)
        self._last_highlighted_entity_getter = pyob

    def openButtonClicked(self):
        if self.entity_set:
            return self.createFormView()
        else:
            return self.createNew()

    def returnPressed(self):
        if not self.entity_set:
            self.createSelectView()

    def searchButtonClicked(self):
        if self.entity_set:
            self.setEntity(lambda:None)
        else:
            self.createSelectView()

    def trashButtonClicked(self):
        self.setEntity(lambda:None)

    @gui_function
    def createNew(self):

        @model_function
        def get_has_subclasses():
            return len(self.admin.get_subclass_tree())

        post(get_has_subclasses, self.show_new_view)

    @gui_function
    def show_new_view(self, has_subclasses):
        from camelot.view.workspace import show_top_level
        selected = QtGui.QDialog.Accepted
        admin = self.admin
        if has_subclasses:
            from camelot.view.controls.inheritance import SubclassDialog
            select_subclass = SubclassDialog(self, self.admin)
            select_subclass.setWindowTitle(_('select'))
            selected = select_subclass.exec_()
            admin = select_subclass.selected_subclass
        if selected:
            form = admin.create_new_view()
            form.entity_created_signal.connect(self.selectEntity)
            # @todo: dirty trick to keep reference
            #self.__new_form = form
            show_top_level( form, self )

    def createFormView(self):
        if self.entity_instance_getter:

            def get_admin_and_title():
                obj = self.entity_instance_getter()
                admin = self.admin.get_related_entity_admin(obj.__class__)
                return admin, ''

            post(get_admin_and_title, self.show_form_view)

    def show_form_view(self, admin_and_title):
        from camelot.view.workspace import show_top_level
        admin, title = admin_and_title

        def create_collection_getter(instance_getter):
            return lambda:[instance_getter()]

        from camelot.view.proxy.collection_proxy import CollectionProxy

        model = CollectionProxy(
            admin,
            create_collection_getter(self.entity_instance_getter),
            admin.get_fields
        )
        model.dataChanged.connect(self.dataChanged)
        form = admin.create_form_view(title, model, 0)
        # @todo : dirty trick to keep reference
        #self.__form = form
        show_top_level( form, self )

    def dataChanged(self, index1, index2):
        self.setEntity(self.entity_instance_getter, False)

    def search_input_editing_finished(self):
        if not self.entity_set:
            # Only try to 'guess' what the user meant when no entity is set
            # to avoid inappropriate removal of data, (eg when the user presses
            # Esc, editingfinished will be called as well, and we should not
            # overwrite the current entity set)
            if self._last_highlighted_entity_getter:
                self.setEntity(self._last_highlighted_entity_getter)
            elif not self.entity_set and self.completions_model.rowCount()==1:
                # There is only one possible option
                index = self.completions_model.index(0,0)
                entity_getter = variant_to_pyobject(index.data(Qt.EditRole))
                self.setEntity(entity_getter)
        self.search_input.set_user_input(self._entity_representation)

    def set_value(self, value):
        """:param value: either ValueLoading, or a function that returns None
        or the entity to be shown in the editor"""
        self._last_highlighted_entity_getter = None
        value = CustomEditor.set_value(self, value)
        if value:
            self.setEntity(value, propagate = False)

    def get_value(self):
        """:return: a function that returns the selected entity or ValueLoading
        or None"""
        value = CustomEditor.get_value(self)
        if not value:
            value = self.entity_instance_getter
        return value

    @QtCore.pyqtSlot(tuple)
    def set_instance_represenation(self, representation_and_propagate):
        """Update the gui"""
        ((desc, pk), propagate) = representation_and_propagate
        self._entity_representation = desc
        self.search_input.set_user_input(desc)

        if pk != False:
            self.open_button.setIcon(
                Icon('tango/16x16/places/folder.png').getQIcon()
            )
            self.open_button.setToolTip(unicode(_('open')))
            self.open_button.setEnabled(True)

            self.search_button.setIcon(
                Icon('tango/16x16/actions/edit-clear.png').getQIcon()
            )
            self.search_button.setToolTip(unicode(_('clear')))
            self.entity_set = True
        else:
            self.open_button.setIcon( self.new_icon.getQIcon() )
            self.open_button.setToolTip(unicode(_('new')))
            self.open_button.setEnabled(self._editable)

            self.search_button.setIcon( self.search_icon.getQIcon() )
            self.search_button.setToolTip(_('Search'))
            self.entity_set = False

        if propagate:
            self.editingFinished.emit()

    def setEntity(self, entity_instance_getter, propagate=True):

        def create_instance_getter(entity_instance):
            return lambda:entity_instance

        def get_instance_represenation():
            """Get a representation of the instance

            :return: (unicode, pk) its unicode representation and its primary
            key or ('', False) if the instance was None"""
            entity = entity_instance_getter()
            self.entity_instance_getter = create_instance_getter(entity)
            if entity and hasattr(entity, 'id'):
                return ((unicode(entity), entity.id), propagate)
            elif entity:
                return ((unicode(entity), False), propagate)
            return ((None, False), propagate)

        post(get_instance_represenation, self.set_instance_represenation)

    def selectEntity(self, entity_instance_getter):
        self.setEntity(entity_instance_getter)
Example #18
0
class ImageEditor( FileEditor ):
    """Editor to view and edit image files, this is a customized
    implementation of a FileEditor"""

    filter = """Image files (*.bmp *.jpg *.jpeg *.mng *.png *.pbm *.pgm *.ppm
*.tiff *.xbm *.xpm) All files (*)"""

    def __init__( self,
                  parent=None,
                  storage=None,
                  preview_width=100,
                  preview_height=100,
                  field_name = 'image',
                  **kwargs ):
        self.preview_width = preview_width
        self.preview_height = preview_height
        FileEditor.__init__(
            self, parent=parent, storage=storage,
            **kwargs
        )
        self.setObjectName( field_name )

    def setup_widget(self):
        layout = QtGui.QVBoxLayout()
        layout.setSpacing( 0 )
        label_button_layout = QtGui.QHBoxLayout()
        #
        # Setup label
        #
        self.label = QtGui.QLabel(self)
        self.label.installEventFilter(self)
        self.label.setAlignment( Qt.AlignHCenter|Qt.AlignVCenter )
        label_button_layout.addWidget(self.label)
        
        self.filename = DecoratedLineEdit( self )
        self.filename.setVisible( False )
        #
        # Setup buttons
        #
        button_layout = QtGui.QVBoxLayout()
        button_layout.setSpacing( 0 )
        button_layout.setContentsMargins( 0, 0, 0, 0)

        self.save_as_button = QtGui.QToolButton()
        self.save_as_button.setFocusPolicy( Qt.ClickFocus )
        self.save_as_button.setIcon(self.save_as_icon.getQIcon())
        self.save_as_button.setToolTip(_('Save file as'))
        self.save_as_button.setAutoRaise(True)
        self.save_as_button.clicked.connect(self.save_as_button_clicked)
        
        self.open_button = QtGui.QToolButton()
        self.open_button.setIcon(self.open_icon.getQIcon())
        self.open_button.setAutoRaise(True)
        self.open_button.setToolTip(unicode(_('open image')))
        self.open_button.clicked.connect(self.open_button_clicked)

        self.add_button = QtGui.QToolButton()
        self.add_button.setFocusPolicy( Qt.StrongFocus )
        self.add_button.setIcon(self.add_icon.getQIcon())
        self.add_button.setToolTip(_('Attach file'))
        self.add_button.clicked.connect(self.add_button_clicked)
        self.add_button.setAutoRaise(True)
        
        self.clear_button = QtGui.QToolButton()
        self.clear_button.setIcon(self.clear_icon.getQIcon())
        self.clear_button.setToolTip(unicode(_('delete image')))
        self.clear_button.setAutoRaise(True)
        self.clear_button.clicked.connect(self.clear_button_clicked)
        self.clear_button.setFocusPolicy(Qt.ClickFocus)
        
        copy_button = QtGui.QToolButton()
        copy_button.setDefaultAction( ActionFactory.copy(self, self.copy_to_clipboard ) )
        copy_button.setAutoRaise(True)
        copy_button.setFocusPolicy(Qt.ClickFocus)

        paste_button = QtGui.QToolButton()
        paste_button.setDefaultAction( ActionFactory.paste(self, self.paste_from_clipboard ) )
        paste_button.setAutoRaise(True)
        paste_button.setObjectName('paste')
        paste_button.setFocusPolicy(Qt.ClickFocus)
        
        #button_layout.addStretch()
        button_layout.addWidget(self.open_button)
        button_layout.addWidget(self.save_as_button)
        button_layout.addWidget(self.add_button)
        button_layout.addWidget(self.clear_button)
        button_layout.addWidget(copy_button)
        button_layout.addWidget(paste_button)

        label_button_layout.addLayout(button_layout)
        label_button_layout.addStretch()
        layout.addLayout( label_button_layout )
        #layout.addStretch()
        self.setLayout( layout )
        self.clear_image()
        QtGui.QApplication.clipboard().dataChanged.connect( self.clipboard_data_changed )
        self.clipboard_data_changed()
        
    @QtCore.pyqtSlot()
    def clipboard_data_changed(self):
        paste_button = self.findChild(QtGui.QWidget, 'paste')
        if paste_button:
            mime_data = QtGui.QApplication.clipboard().mimeData()
            paste_button.setVisible( mime_data.hasImage() )
            
    @QtCore.pyqtSlot()
    def paste_from_clipboard(self):
        """Paste an image from the clipboard into the editor"""
        mime_data = QtGui.QApplication.clipboard().mimeData()
        if mime_data.hasImage():
            byte_array = QtCore.QByteArray()
            buffer = QtCore.QBuffer( byte_array )
            image = QtGui.QImage( mime_data.imageData() )
            image.save( buffer, 'PNG' )
            
            def create_checkin( byte_array ):
                return lambda:self.checkin_byte_array(byte_array, '.png')
            
            post( create_checkin( byte_array ), self.stored_file_ready )
        
    def checkin_byte_array(self, byte_array, suffix):
        """Check a byte_array into the storage"""
        import cStringIO
        stream = cStringIO.StringIO( byte_array.data() )
        return self.storage.checkin_stream( 'clipboard', suffix, stream)
        
    def set_enabled(self, editable=True):
        self.clear_button.setEnabled(editable)
        self.open_button.setEnabled(editable)
        self.add_button.setEnabled(editable)
        self.label.setEnabled(editable)

    def set_pixmap(self, pixmap):
        self.label.setPixmap(pixmap)
        self.draw_border()

    def set_image(self, image):
        self.set_pixmap(QtGui.QPixmap.fromImage(image))
        
    @QtCore.pyqtSlot()
    def copy_to_clipboard(self):
        """Copy the image to the clipboard"""
        if self.value:
            post( self.value.checkout_image, self.set_image_to_clipboard )
        
    def set_image_to_clipboard(self, image):
        clipboard = QtGui.QApplication.clipboard()
        clipboard.setImage( image )

    def clear_image(self):
        dummy_image = Icon('tango/32x32/mimetypes/image-x-generic.png')
        self.set_pixmap(dummy_image.getQPixmap())

    def set_value(self, value):
        old_value = self.value
        value = super( ImageEditor, self ).set_value( value )
        if value:
            if value != old_value:
                post(
                    lambda:value.checkout_thumbnail(
                        self.preview_width,
                        self.preview_height
                    ),
                    self.set_image
                )
        else:
            self.clear_image()
        return value

    def draw_border(self):
        self.label.setFrameShape(QtGui.QFrame.Box)
        self.label.setFrameShadow(QtGui.QFrame.Plain)
        self.label.setLineWidth(1)
        self.label.setFixedSize(self.preview_width, self.preview_height)

    def show_fullscreen(self, image):
        lite_box = LiteBoxView(self)
        lite_box.show_fullscreen_image(image)

    def eventFilter(self, object, event):
        if not object.isWidgetType():
            return False
        if event.type() != QtCore.QEvent.MouseButtonPress:
            return False
        if event.modifiers() != QtCore.Qt.NoModifier:
            return False
        if event.buttons() == QtCore.Qt.LeftButton:
            if self.value:
                post(
                    lambda:self.value.checkout_thumbnail(640,480),
                    self.show_fullscreen
                )
            return True
        return False
Example #19
0
class FileEditor(CustomEditor):
    """Widget for editing File fields"""

    filter = "All files (*)"
    add_icon = Icon("tango/16x16/actions/list-add.png")
    open_icon = Icon("tango/16x16/actions/document-open.png")
    clear_icon = Icon("tango/16x16/actions/edit-delete.png")
    save_as_icon = Icon("tango/16x16/actions/document-save-as.png")
    document_pixmap = Icon("tango/16x16/mimetypes/x-office-document.png")

    def __init__(self, parent=None, storage=None, field_name="file", remove_original=False, **kwargs):
        CustomEditor.__init__(self, parent)
        self.setObjectName(field_name)
        self.storage = storage
        self.filename = None  # the widget containing the filename
        self.value = None
        self.remove_original = remove_original
        self.setup_widget()

    def setup_widget(self):
        """Called inside init, overwrite this method for custom
        file edit widgets"""
        self.layout = QtGui.QHBoxLayout()
        self.layout.setSpacing(0)
        self.layout.setContentsMargins(0, 0, 0, 0)

        # Save As button
        self.save_as_button = QtGui.QToolButton()
        self.save_as_button.setFocusPolicy(Qt.ClickFocus)
        self.save_as_button.setIcon(self.save_as_icon.getQIcon())
        self.save_as_button.setToolTip(_("Save file as"))
        self.save_as_button.setAutoRaise(True)
        self.save_as_button.clicked.connect(self.save_as_button_clicked)

        # Clear button
        self.clear_button = QtGui.QToolButton()
        self.clear_button.setFocusPolicy(Qt.ClickFocus)
        self.clear_button.setIcon(self.clear_icon.getQIcon())
        self.clear_button.setToolTip(_("Delete file"))
        self.clear_button.setAutoRaise(True)
        self.clear_button.clicked.connect(self.clear_button_clicked)

        # Open button
        self.open_button = QtGui.QToolButton()
        self.open_button.setFocusPolicy(Qt.ClickFocus)
        self.open_button.setIcon(self.open_icon.getQIcon())
        self.open_button.setToolTip(_("Open file"))
        self.open_button.clicked.connect(self.open_button_clicked)
        self.open_button.setAutoRaise(True)

        # Add button
        self.add_button = QtGui.QToolButton()
        self.add_button.setFocusPolicy(Qt.StrongFocus)
        self.add_button.setIcon(self.add_icon.getQIcon())
        self.add_button.setToolTip(_("Attach file"))
        self.add_button.clicked.connect(self.add_button_clicked)
        self.add_button.setAutoRaise(True)

        # Filename
        self.filename = DecoratedLineEdit(self)
        self.filename.set_minimum_width(20)
        self.filename.setFocusPolicy(Qt.ClickFocus)

        # Search Completer
        #
        # Turn completion off, since it creates a thread per field on a form
        #
        # self.completer = QtGui.QCompleter()
        # self.completions_model = QtGui.QFileSystemModel()
        # self.completer.setCompletionMode(
        #    QtGui.QCompleter.UnfilteredPopupCompletion
        # )
        # self.completer.setModel( self.completions_model )
        # self.completer.activated[QtCore.QModelIndex].connect(self.file_completion_activated)
        # self.filename.setCompleter( self.completer )
        # settings = QtCore.QSettings()
        # last_path = settings.value('lastpath').toString()

        # # This setting of a rootPath causes a major delay on Windows, since
        # # the QFileSystemModel starts to fetch file information in a non-
        # # blocking way (although the documentation state the opposite).
        # # On Linux, there is no such delay, so it's safe to set such a root
        # # path and let the underlaying system start indexing.
        # import sys
        # if sys.platform != "win32":
        #    self.completions_model.setRootPath( last_path )

        # Setup layout
        self.document_label = QtGui.QLabel(self)
        self.document_label.setPixmap(self.document_pixmap.getQPixmap())
        self.layout.addWidget(self.document_label)
        self.layout.addWidget(self.filename)
        self.layout.addWidget(self.clear_button)
        self.layout.addWidget(self.open_button)
        self.layout.addWidget(self.add_button)
        self.layout.addWidget(self.save_as_button)
        self.setLayout(self.layout)

    def file_completion_activated(self, index):
        from camelot.view.storage import create_stored_file

        source_index = index.model().mapToSource(index)
        if not self.completions_model.isDir(source_index):
            path = self.completions_model.filePath(source_index)
            create_stored_file(
                self,
                self.storage,
                self.stored_file_ready,
                filter=self.filter,
                remove_original=self.remove_original,
                filename=path,
            )

    def set_value(self, value):
        value = CustomEditor.set_value(self, value)
        self.value = value
        if value:
            self.clear_button.setVisible(True)
            self.save_as_button.setVisible(True)
            self.open_button.setVisible(True)
            self.add_button.setVisible(False)
            self.filename.setText(value.verbose_name)
        else:
            self.clear_button.setVisible(False)
            self.save_as_button.setVisible(False)
            self.open_button.setVisible(False)
            self.add_button.setVisible(True)
            self.filename.setText("")
        return value

    def get_value(self):
        return CustomEditor.get_value(self) or self.value

    def set_field_attributes(self, editable=True, background_color=None, tooltip=None, remove_original=False, **kwargs):
        self.set_enabled(editable)
        if self.filename:
            set_background_color_palette(self.filename, background_color)
            self.filename.setToolTip(unicode(tooltip or ""))
        self.remove_original = remove_original

    def set_enabled(self, editable=True):
        self.clear_button.setEnabled(editable)
        self.add_button.setEnabled(editable)
        self.filename.setEnabled(editable)
        self.filename.setReadOnly(not editable)
        self.document_label.setEnabled(editable)
        self.setAcceptDrops(editable)

    def stored_file_ready(self, stored_file):
        """Slot to be called when a new stored_file has been created by
        the storage"""
        self.set_value(stored_file)
        self.editingFinished.emit()

    def save_as_button_clicked(self):
        from camelot.view.storage import save_stored_file

        value = self.get_value()
        if value:
            save_stored_file(self, value)

    def add_button_clicked(self):
        from camelot.view.storage import create_stored_file

        create_stored_file(
            self, self.storage, self.stored_file_ready, filter=self.filter, remove_original=self.remove_original
        )

    def open_button_clicked(self):
        from camelot.view.storage import open_stored_file

        open_stored_file(self, self.value)

    def clear_button_clicked(self):
        answer = QtGui.QMessageBox.question(
            self,
            _("Remove this file ?"),
            _("If you continue, you will no longer be able to open this file."),
            QtGui.QMessageBox.Yes,
            QtGui.QMessageBox.No,
        )
        if answer == QtGui.QMessageBox.Yes:
            self.value = None
            self.editingFinished.emit()

    #
    # Drag & Drop
    #
    def dragEnterEvent(self, event):
        event.acceptProposedAction()

    def dragMoveEvent(self, event):
        event.acceptProposedAction()

    def dropEvent(self, event):
        from camelot.view.storage import create_stored_file

        if event.mimeData().hasUrls():
            url = event.mimeData().urls()[0]
            filename = url.toLocalFile()
            if filename:
                create_stored_file(
                    self,
                    self.storage,
                    self.stored_file_ready,
                    filter=self.filter,
                    remove_original=self.remove_original,
                    filename=filename,
                )
Example #20
0
class LocalFileEditor(CustomEditor):
    """Widget for browsing local files and directories"""

    browse_icon = Icon('tango/16x16/places/folder-saved-search.png')

    def __init__(self,
                 parent=None,
                 field_name='local_file',
                 directory=False,
                 save_as=False,
                 file_filter='All files (*)',
                 **kwargs):
        CustomEditor.__init__(self, parent)
        self.setObjectName(field_name)
        self._directory = directory
        self._save_as = save_as
        self._file_filter = file_filter
        self.setup_widget()

    def setup_widget(self):
        """Called inside init, overwrite this method for custom
        file edit widgets"""
        layout = QtGui.QHBoxLayout()
        layout.setSpacing(0)
        layout.setContentsMargins(0, 0, 0, 0)

        browse_button = QtGui.QToolButton(self)
        browse_button.setFocusPolicy(Qt.ClickFocus)
        browse_button.setIcon(self.browse_icon.getQIcon())
        browse_button.setToolTip(_('Browse'))
        browse_button.setAutoRaise(True)
        browse_button.clicked.connect(self.browse_button_clicked)

        self.filename = DecoratedLineEdit(self)
        self.filename.editingFinished.connect(self.filename_editing_finished)
        self.setFocusProxy(self.filename)

        layout.addWidget(self.filename)
        layout.addWidget(browse_button)
        self.setLayout(layout)

    @QtCore.pyqtSlot()
    def filename_editing_finished(self):
        self.valueChanged.emit()
        self.editingFinished.emit()

    @QtCore.pyqtSlot()
    def browse_button_clicked(self):
        current_directory = os.path.dirname(self.get_value())
        if self._directory:
            value = QtGui.QFileDialog.getExistingDirectory(
                self, directory=current_directory)
        elif self._save_as:
            value = QtGui.QFileDialog.getSaveFileName(
                self, filter=self._file_filter, directory=current_directory)
        else:
            value = QtGui.QFileDialog.getOpenFileName(
                self, filter=self._file_filter, directory=current_directory)
        value = os.path.abspath(unicode(value))
        self.filename.setText(value)
        self.valueChanged.emit()
        self.editingFinished.emit()

    def set_value(self, value):
        value = CustomEditor.set_value(self, value)
        if value:
            self.filename.setText(value)
        else:
            self.filename.setText('')
        self.valueChanged.emit()
        return value

    def get_value(self):
        return CustomEditor.get_value(self) or unicode(self.filename.text())

    value = QtCore.pyqtProperty(str, get_value, set_value)

    def set_field_attributes(self,
                             editable=True,
                             background_color=None,
                             tooltip=None,
                             **kwargs):
        self.setEnabled(editable)
        if self.filename:
            set_background_color_palette(self.filename, background_color)
            self.filename.setToolTip(unicode(tooltip or ''))
Example #21
0
class SimpleSearchControl(QtGui.QWidget):
    """A control that displays a single text field in which search keywords can
  be typed

  emits a search and a cancel signal if the user starts or cancels the search
  """

    expand_search_options_signal = QtCore.pyqtSignal()
    cancel_signal = QtCore.pyqtSignal()
    search_signal = QtCore.pyqtSignal(str)
    

    def __init__(self, parent):
        QtGui.QWidget.__init__(self, parent)
        layout = QtGui.QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(3)

        # Search button
        self.search_button = QtGui.QToolButton()
        icon = Icon('tango/16x16/actions/system-search.png').getQIcon()
        self.search_button.setIcon(icon)
        self.search_button.setIconSize(QtCore.QSize(14, 14))
        self.search_button.setAutoRaise(True)
        self.search_button.setToolTip(_('Expand or collapse search options'))
        self.search_button.clicked.connect( self.emit_expand_search_options )

        # Search input
        from camelot.view.controls.decorated_line_edit import DecoratedLineEdit
        self.search_input = DecoratedLineEdit(self)
        self.search_input.set_background_text(_('Search...'))
        self.search_input.setToolTip(_('type words to search for'))
        #self.search_input.setStyleSheet('QLineEdit{ border-radius: 0.25em;}')
        self.search_input.returnPressed.connect( self.emit_search )
        self.search_input.textEdited.connect( self.emit_search )

        self.setFocusProxy( self.search_input )

        # Cancel button
        self.cancel_button = QtGui.QToolButton()
        icon = Icon('tango/16x16/actions/edit-clear.png').getQIcon()
        self.cancel_button.setIcon(icon)
        self.cancel_button.setIconSize(QtCore.QSize(14, 14))
        self.cancel_button.setAutoRaise(True)
        self.cancel_button.clicked.connect( self.emit_cancel )

        # Setup layout
        layout.addWidget(self.search_button)
        layout.addWidget(self.search_input)
        layout.addWidget(self.cancel_button)
        self.setLayout(layout)

    def search(self, search_text):
        """Start searching for search_text"""
        self.search_input.setText(search_text)
        self.emit_search()

    @QtCore.pyqtSlot()
    def emit_expand_search_options(self):
        self.expand_search_options_signal.emit()

    @QtCore.pyqtSlot()
    @QtCore.pyqtSlot(str)
    def emit_search(self, str=''):
        text = unicode(self.search_input.user_input())
        self.search_signal.emit( text )

    @QtCore.pyqtSlot()
    def emit_cancel(self):
        self.search_input.setText('')
        self.cancel_signal.emit()
Example #22
0
class SimpleSearchControl(QtGui.QWidget):
    """A control that displays a single text field in which search keywords can
  be typed

  emits a search and a cancel signal if the user starts or cancels the search
  """

    expand_search_options_signal = QtCore.pyqtSignal()
    cancel_signal = QtCore.pyqtSignal()
    search_signal = QtCore.pyqtSignal(str)

    def __init__(self, parent):
        QtGui.QWidget.__init__(self, parent)
        layout = QtGui.QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(3)

        # Search button
        self.search_button = QtGui.QToolButton()
        icon = Icon('tango/16x16/actions/system-search.png').getQIcon()
        self.search_button.setIcon(icon)
        self.search_button.setIconSize(QtCore.QSize(14, 14))
        self.search_button.setAutoRaise(True)
        self.search_button.setToolTip(_('Expand or collapse search options'))
        self.search_button.clicked.connect(self.emit_expand_search_options)

        # Search input
        from camelot.view.controls.decorated_line_edit import DecoratedLineEdit
        self.search_input = DecoratedLineEdit(self)
        self.search_input.set_background_text(_('Search...'))
        self.search_input.setToolTip(_('type words to search for'))
        #self.search_input.setStyleSheet('QLineEdit{ border-radius: 0.25em;}')
        self.search_input.returnPressed.connect(self.emit_search)
        self.search_input.textEdited.connect(self.emit_search)

        self.setFocusProxy(self.search_input)

        # Cancel button
        self.cancel_button = QtGui.QToolButton()
        icon = Icon('tango/16x16/actions/edit-clear.png').getQIcon()
        self.cancel_button.setIcon(icon)
        self.cancel_button.setIconSize(QtCore.QSize(14, 14))
        self.cancel_button.setAutoRaise(True)
        self.cancel_button.clicked.connect(self.emit_cancel)

        # Setup layout
        layout.addWidget(self.search_button)
        layout.addWidget(self.search_input)
        layout.addWidget(self.cancel_button)
        self.setLayout(layout)

    def search(self, search_text):
        """Start searching for search_text"""
        self.search_input.setText(search_text)
        self.emit_search()

    @QtCore.pyqtSlot()
    def emit_expand_search_options(self):
        self.expand_search_options_signal.emit()

    @QtCore.pyqtSlot()
    @QtCore.pyqtSlot(str)
    def emit_search(self, str=''):
        text = unicode(self.search_input.user_input())
        self.search_signal.emit(text)

    @QtCore.pyqtSlot()
    def emit_cancel(self):
        self.search_input.setText('')
        self.cancel_signal.emit()
Example #23
0
class DateEditor(CustomEditor):
    """Widget for editing date values"""

    calendar_action_trigger = QtCore.pyqtSignal()
    special_date_icon = Icon('tango/16x16/apps/office-calendar.png')
    
    def __init__(self, parent = None,
                       editable = True,
                       nullable = True, 
                       field_name = 'date',
                       **kwargs):
        CustomEditor.__init__(self, parent)

        self.setObjectName( field_name )
        self.date_format = local_date_format()
        self.line_edit = DecoratedLineEdit()
        self.line_edit.set_minimum_width( len( self.date_format ) )
        self.line_edit.set_background_text( QtCore.QDate(2000,1,1).toString(self.date_format) )

        # The order of creation of this widgets and their parenting
        # seems very sensitive under windows and creates system crashes
        # so don't change this without extensive testing on windows
        special_date_menu = QtGui.QMenu(self)
        calendar_widget_action = QtGui.QWidgetAction(special_date_menu)
        self.calendar_widget = QtGui.QCalendarWidget(special_date_menu)
        self.calendar_widget.activated.connect(self.calendar_widget_activated)
        self.calendar_widget.clicked.connect(self.calendar_widget_activated)
        calendar_widget_action.setDefaultWidget(self.calendar_widget)

        self.calendar_action_trigger.connect( special_date_menu.hide )
        special_date_menu.addAction(calendar_widget_action)
        special_date_menu.addAction(_('Today'))
        special_date_menu.addAction(_('Far future'))
        self.special_date = QtGui.QToolButton(self)
        self.special_date.setIcon( self.special_date_icon.getQIcon() )
        self.special_date.setAutoRaise(True)
        self.special_date.setToolTip(_('Calendar and special dates'))
        self.special_date.setMenu(special_date_menu)
        self.special_date.setPopupMode(QtGui.QToolButton.InstantPopup)
        self.special_date.setFixedHeight(self.get_height())
        self.special_date.setFocusPolicy(Qt.ClickFocus)
        # end of sensitive part

        if nullable:
            special_date_menu.addAction(_('Clear'))

        self.hlayout = QtGui.QHBoxLayout()
        self.hlayout.addWidget(self.line_edit)
        self.hlayout.addWidget(self.special_date)

        self.hlayout.setContentsMargins(0, 0, 0, 0)
        self.hlayout.setSpacing(0)
        self.hlayout.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
        
        self.setContentsMargins(0, 0, 0, 0)
        self.setLayout(self.hlayout)

        self.minimum = datetime.date.min
        self.maximum = datetime.date.max
        self.setFocusProxy(self.line_edit)

        self.line_edit.editingFinished.connect( self.line_edit_finished )
        self.line_edit.textEdited.connect(self.text_edited)
        special_date_menu.triggered.connect(self.set_special_date)

    def calendar_widget_activated(self, date):
        self.calendar_action_trigger.emit()
        self.set_value(date)
        self.editingFinished.emit()
        self.line_edit.setFocus()

    def line_edit_finished(self):
        self.setProperty( 'value', QtCore.QVariant( self.get_value() ) )
        self.valueChanged.emit()
        self.editingFinished.emit()

    def focusOutEvent(self, event):
        # explicitely set value on focus out to format the date in case
        # it was entered unformatted
        value = self.get_value()
        self.set_value( value )
        self.editingFinished.emit()

    def set_value(self, value):
        value = CustomEditor.set_value(self, value)
        self.setProperty( 'value', QtCore.QVariant( value ) )
        if value:
            qdate = QtCore.QDate(value)
            formatted_date = qdate.toString(self.date_format)
            self.line_edit.set_user_input(formatted_date)
            self.calendar_widget.setSelectedDate(qdate)
        else:
            self.line_edit.set_user_input('')
        self.valueChanged.emit()

    def text_edited(self, text ):
        try:
            date_from_string( self.line_edit.user_input() )
            self.line_edit.set_valid(True)
            self.valueChanged.emit()
        except ParsingError:
            self.line_edit.set_valid(False)

    def get_value(self):
        try:
            value = date_from_string( self.line_edit.user_input() )
        except ParsingError:
            value = None
        return CustomEditor.get_value(self) or value

    def set_field_attributes(self, editable = True,
                                   background_color = None,
                                   tooltip = None, **kwargs):
        self.set_enabled(editable)
        self.set_background_color(background_color)
        self.line_edit.setToolTip(unicode(tooltip or ''))

    def set_background_color(self, background_color):
        set_background_color_palette( self.line_edit, background_color )

    def set_enabled(self, editable=True):
        self.line_edit.setEnabled(editable)
        if editable:
            self.special_date.show()
        else:
            self.special_date.hide()

    def set_special_date(self, action):
        if action.text().compare(_('Today')) == 0:
            self.set_value(datetime.date.today())
        elif action.text().compare(_('Far future')) == 0:
            self.set_value(datetime.date( year = 2400, month = 12, day = 31 ))
        elif action.text().compare(_('Clear')) == 0:
            self.set_value(None)
        self.line_edit.setFocus()
        self.editingFinished.emit()
Example #24
0
class FileEditor(CustomEditor):
    """Widget for editing File fields"""

    document_pixmap = Icon('tango/16x16/mimetypes/x-office-document.png')

    def __init__(self,
                 parent=None,
                 storage=None,
                 field_name='file',
                 remove_original=False,
                 actions=[
                     field_action.DetachFile(),
                     field_action.OpenFile(),
                     field_action.UploadFile(),
                     field_action.SaveFile()
                 ],
                 **kwargs):
        CustomEditor.__init__(self, parent)
        self.setSizePolicy(QtGui.QSizePolicy.Preferred,
                           QtGui.QSizePolicy.Fixed)
        self.setObjectName(field_name)
        self.storage = storage
        self.filename = None  # the widget containing the filename
        self.value = None
        self.file_name = None
        self.remove_original = remove_original
        self.actions = actions
        self.setup_widget()

    def setup_widget(self):
        """Called inside init, overwrite this method for custom
        file edit widgets"""
        self.layout = QtWidgets.QHBoxLayout()
        self.layout.setSpacing(0)
        self.layout.setContentsMargins(0, 0, 0, 0)

        # Filename
        self.filename = DecoratedLineEdit(self)
        self.filename.set_minimum_width(20)
        self.filename.setFocusPolicy(Qt.ClickFocus)

        # Setup layout
        self.document_label = QtWidgets.QLabel(self)
        self.document_label.setPixmap(self.document_pixmap.getQPixmap())
        self.layout.addWidget(self.document_label)
        self.layout.addWidget(self.filename)
        self.add_actions(self.actions, self.layout)
        self.setLayout(self.layout)

    def file_completion_activated(self, index):
        from camelot.view.storage import create_stored_file
        source_index = index.model().mapToSource(index)
        if not self.completions_model.isDir(source_index):
            path = self.completions_model.filePath(source_index)
            create_stored_file(
                self,
                self.storage,
                self.stored_file_ready,
                filter=self.filter,
                remove_original=self.remove_original,
                filename=path,
            )

    def set_value(self, value):
        value = CustomEditor.set_value(self, value)
        self.value = value
        if value is not None:
            self.filename.setText(value.verbose_name)
        else:
            self.filename.setText('')
        self.update_actions()
        return value

    def get_value(self):
        return CustomEditor.get_value(self) or self.value

    def set_field_attributes(self, **kwargs):
        super(FileEditor, self).set_field_attributes(**kwargs)
        self.set_enabled(kwargs.get('editable', False))
        if self.filename:
            set_background_color_palette(self.filename,
                                         kwargs.get('background_color', None))
            self.filename.setToolTip(six.text_type(
                kwargs.get('tooltip') or ''))
        self.remove_original = kwargs.get('remove_original', False)

    def set_enabled(self, editable=True):
        self.filename.setEnabled(editable)
        self.filename.setReadOnly(not editable)
        self.document_label.setEnabled(editable)
        self.setAcceptDrops(editable)

    #
    # Drag & Drop
    #
    def dragEnterEvent(self, event):
        event.acceptProposedAction()

    def dragMoveEvent(self, event):
        event.acceptProposedAction()

    def dropEvent(self, event):
        from camelot.view.storage import create_stored_file
        if event.mimeData().hasUrls():
            url = event.mimeData().urls()[0]
            filename = url.toLocalFile()
            if filename:
                create_stored_file(
                    self,
                    self.storage,
                    self.stored_file_ready,
                    filter=self.filter,
                    remove_original=self.remove_original,
                    filename=filename,
                )
Example #25
0
class SimpleSearchControl(AbstractSearchWidget):
    """A control that displays a single text field in which search keywords can
    be typed

    emits a search and a cancel signal if the user starts or cancels the search
    """

    def __init__(self, parent):
        QtGui.QWidget.__init__(self, parent)
        layout = QtGui.QHBoxLayout()
        layout.setSpacing(0)
        layout.setContentsMargins(3, 3, 3, 3)
        
        #
        # The search timer reduced the number of search signals that are
        # emitted, by waiting for the next keystroke before emitting the
        # search signal
        #
        timer = QtCore.QTimer( self )
        timer.setInterval( 300 )
        timer.setSingleShot( True )
        timer.setObjectName( 'timer' )
        timer.timeout.connect( self.emit_search )

        # Search button
        self.search_button = QtGui.QToolButton()
        icon = Icon('tango/16x16/actions/system-search.png').getQIcon()
        self.search_button.setIcon(icon)
        self.search_button.setIconSize(QtCore.QSize(14, 14))
        self.search_button.setAutoRaise(True)
        self.search_button.setToolTip(_('Expand or collapse search options'))
        self.search_button.clicked.connect( self.emit_expand_search_options )

        # Search input
        from camelot.view.controls.decorated_line_edit import DecoratedLineEdit
        self.search_input = DecoratedLineEdit(self)
        self.search_input.set_background_text(_('Search...'))
        #self.search_input.setStyleSheet('QLineEdit{ border-radius: 0.25em;}')
        self.search_input.returnPressed.connect( self.emit_search )
        self.search_input.textEdited.connect( self._start_search_timer )
        self.search_input.arrow_down_key_pressed.connect(self.on_arrow_down_key_pressed)

        self.setFocusProxy( self.search_input )

        # Cancel button
        self.cancel_button = QtGui.QToolButton()
        icon = Icon('tango/16x16/actions/edit-clear.png').getQIcon()
        self.cancel_button.setIcon(icon)
        self.cancel_button.setIconSize(QtCore.QSize(14, 14))
        self.cancel_button.setAutoRaise(True)
        self.cancel_button.clicked.connect( self.emit_cancel )

        # Setup layout
        layout.addWidget(self.search_button)
        layout.addWidget(self.search_input)
        layout.addWidget(self.cancel_button)
        self.setLayout(layout)

    def search(self, search_text):
        """Start searching for search_text"""
        self.search_input.setText(search_text)
        self.emit_search()

    @QtCore.pyqtSlot()
    @QtCore.pyqtSlot(str)
    def _start_search_timer(self, str=''):
        timer = self.findChild( QtCore.QTimer, 'timer' )
        if timer:
            timer.stop()
            timer.start()
        
    @QtCore.pyqtSlot()
    def emit_expand_search_options(self):
        self.expand_search_options_signal.emit()

    @QtCore.pyqtSlot()
    @QtCore.pyqtSlot(str)
    def emit_search(self, str=''):
        timer = self.findChild( QtCore.QTimer, 'timer' )
        if timer:
            timer.stop()
        text = unicode(self.search_input.user_input())
        self.search_signal.emit( text )

    @QtCore.pyqtSlot()
    def emit_cancel(self):
        timer = self.findChild( QtCore.QTimer, 'timer' )
        if timer:
            timer.stop()
        self.search_input.setText('')
        self.cancel_signal.emit()

    @QtCore.pyqtSlot()
    def on_arrow_down_key_pressed(self):
        self.on_arrow_down_signal.emit()
Example #26
0
    def __init__(self, admin=None, parent=None, editable=True, **kwargs):
        """:param entity_admin : The Admin interface for the object on the one
        side of the relation
        """

        CustomEditor.__init__(self, parent)
        self.admin = admin
        self.entity_set = False
        self._editable = editable
        self._entity_representation = ''
        self.entity_instance_getter = None
        self._last_highlighted_entity_getter = None

        self.layout = QtGui.QHBoxLayout()
        self.layout.setSpacing(0)
        self.layout.setMargin(0)

        # Search button
        self.search_button = QtGui.QToolButton()
        self.search_button.setAutoRaise(True)
        self.search_button.setFocusPolicy(Qt.ClickFocus)
        self.search_button.setFixedHeight(self.get_height())
        self.search_button.clicked.connect(self.searchButtonClicked)
        self.search_button.setIcon(
            Icon('tango/16x16/actions/edit-clear.png').getQIcon())
        self.search_button.setToolTip(unicode(_('clear')))

        # Open button
        self.open_button = QtGui.QToolButton()
        self.open_button.setAutoRaise(True)
        self.open_button.setFocusPolicy(Qt.ClickFocus)
        self.open_button.setFixedHeight(self.get_height())
        self.open_button.clicked.connect(self.openButtonClicked)
        self.open_button.setIcon(self.new_icon.getQIcon())
        self.open_button.setToolTip(unicode(_('new')))

        # Search input
        self.search_input = DecoratedLineEdit(self)
        self.search_input.set_background_text(_('Search...'))
        self.search_input.textEdited.connect(self.textEdited)
        # suppose garbage was entered, we need to refresh the content
        self.search_input.editingFinished.connect(
            self.search_input_editing_finished)
        self.setFocusProxy(self.search_input)

        # Search Completer
        self.completer = QtGui.QCompleter()
        self.completions_model = self.CompletionsModel(self.completer)
        self.completer.setModel(self.completions_model)
        self.completer.setCaseSensitivity(Qt.CaseInsensitive)
        self.completer.setCompletionMode(
            QtGui.QCompleter.UnfilteredPopupCompletion)
        #self.completer.activated.connect(self.completionActivated)
        #self.completer.highlighted.connect(self.completion_highlighted)
        self.completer.activated[QtCore.QModelIndex].connect(
            self.completionActivated)
        self.completer.highlighted[QtCore.QModelIndex].connect(
            self.completion_highlighted)
        self.search_input.setCompleter(self.completer)

        # Setup layout
        self.layout.addWidget(self.search_input)
        self.layout.addWidget(self.search_button)
        self.layout.addWidget(self.open_button)
        self.setLayout(self.layout)
Example #27
0
class SimpleSearchControl(AbstractSearchWidget):
    """A control that displays a single text field in which search keywords can
    be typed

    emits a search and a cancel signal if the user starts or cancels the search
    """
    def __init__(self, parent):
        QtGui.QWidget.__init__(self, parent)
        layout = QtGui.QHBoxLayout()
        layout.setSpacing(0)
        layout.setContentsMargins(3, 3, 3, 3)

        #
        # The search timer reduced the number of search signals that are
        # emitted, by waiting for the next keystroke before emitting the
        # search signal
        #
        timer = QtCore.QTimer(self)
        timer.setInterval(300)
        timer.setSingleShot(True)
        timer.setObjectName('timer')
        timer.timeout.connect(self.emit_search)

        # Search button
        self.search_button = QtGui.QToolButton()
        icon = Icon('tango/16x16/actions/system-search.png').getQIcon()
        self.search_button.setIcon(icon)
        self.search_button.setIconSize(QtCore.QSize(14, 14))
        self.search_button.setAutoRaise(True)
        self.search_button.setToolTip(_('Expand or collapse search options'))
        self.search_button.clicked.connect(self.emit_expand_search_options)

        # Search input
        from camelot.view.controls.decorated_line_edit import DecoratedLineEdit
        self.search_input = DecoratedLineEdit(self)
        self.search_input.set_background_text(_('Search...'))
        #self.search_input.setStyleSheet('QLineEdit{ border-radius: 0.25em;}')
        self.search_input.returnPressed.connect(self.emit_search)
        self.search_input.textEdited.connect(self._start_search_timer)
        self.search_input.arrow_down_key_pressed.connect(
            self.on_arrow_down_key_pressed)

        self.setFocusProxy(self.search_input)

        # Cancel button
        self.cancel_button = QtGui.QToolButton()
        icon = Icon('tango/16x16/actions/edit-clear.png').getQIcon()
        self.cancel_button.setIcon(icon)
        self.cancel_button.setIconSize(QtCore.QSize(14, 14))
        self.cancel_button.setAutoRaise(True)
        self.cancel_button.clicked.connect(self.emit_cancel)

        # Setup layout
        layout.addWidget(self.search_button)
        layout.addWidget(self.search_input)
        layout.addWidget(self.cancel_button)
        self.setLayout(layout)

    def search(self, search_text):
        """Start searching for search_text"""
        self.search_input.setText(search_text)
        self.emit_search()

    @QtCore.pyqtSlot()
    @QtCore.pyqtSlot(str)
    def _start_search_timer(self, str=''):
        timer = self.findChild(QtCore.QTimer, 'timer')
        if timer:
            timer.stop()
            timer.start()

    @QtCore.pyqtSlot()
    def emit_expand_search_options(self):
        self.expand_search_options_signal.emit()

    @QtCore.pyqtSlot()
    @QtCore.pyqtSlot(str)
    def emit_search(self, str=''):
        timer = self.findChild(QtCore.QTimer, 'timer')
        if timer:
            timer.stop()
        text = unicode(self.search_input.user_input())
        self.search_signal.emit(text)

    @QtCore.pyqtSlot()
    def emit_cancel(self):
        timer = self.findChild(QtCore.QTimer, 'timer')
        if timer:
            timer.stop()
        self.search_input.setText('')
        self.cancel_signal.emit()

    @QtCore.pyqtSlot()
    def on_arrow_down_key_pressed(self):
        self.on_arrow_down_signal.emit()
Example #28
0
    def setup_widget(self):
        """Called inside init, overwrite this method for custom
        file edit widgets"""
        self.layout = QtGui.QHBoxLayout()
        self.layout.setSpacing(0)
        self.layout.setContentsMargins(0, 0, 0, 0)

        # Save As button
        self.save_as_button = QtGui.QToolButton()
        self.save_as_button.setFocusPolicy(Qt.ClickFocus)
        self.save_as_button.setIcon(self.save_as_icon.getQIcon())
        self.save_as_button.setToolTip(_("Save file as"))
        self.save_as_button.setAutoRaise(True)
        self.save_as_button.clicked.connect(self.save_as_button_clicked)

        # Clear button
        self.clear_button = QtGui.QToolButton()
        self.clear_button.setFocusPolicy(Qt.ClickFocus)
        self.clear_button.setIcon(self.clear_icon.getQIcon())
        self.clear_button.setToolTip(_("Delete file"))
        self.clear_button.setAutoRaise(True)
        self.clear_button.clicked.connect(self.clear_button_clicked)

        # Open button
        self.open_button = QtGui.QToolButton()
        self.open_button.setFocusPolicy(Qt.ClickFocus)
        self.open_button.setIcon(self.open_icon.getQIcon())
        self.open_button.setToolTip(_("Open file"))
        self.open_button.clicked.connect(self.open_button_clicked)
        self.open_button.setAutoRaise(True)

        # Add button
        self.add_button = QtGui.QToolButton()
        self.add_button.setFocusPolicy(Qt.StrongFocus)
        self.add_button.setIcon(self.add_icon.getQIcon())
        self.add_button.setToolTip(_("Attach file"))
        self.add_button.clicked.connect(self.add_button_clicked)
        self.add_button.setAutoRaise(True)

        # Filename
        self.filename = DecoratedLineEdit(self)
        self.filename.set_minimum_width(20)
        self.filename.setFocusPolicy(Qt.ClickFocus)

        # Search Completer
        #
        # Turn completion off, since it creates a thread per field on a form
        #
        # self.completer = QtGui.QCompleter()
        # self.completions_model = QtGui.QFileSystemModel()
        # self.completer.setCompletionMode(
        #    QtGui.QCompleter.UnfilteredPopupCompletion
        # )
        # self.completer.setModel( self.completions_model )
        # self.completer.activated[QtCore.QModelIndex].connect(self.file_completion_activated)
        # self.filename.setCompleter( self.completer )
        # settings = QtCore.QSettings()
        # last_path = settings.value('lastpath').toString()

        # # This setting of a rootPath causes a major delay on Windows, since
        # # the QFileSystemModel starts to fetch file information in a non-
        # # blocking way (although the documentation state the opposite).
        # # On Linux, there is no such delay, so it's safe to set such a root
        # # path and let the underlaying system start indexing.
        # import sys
        # if sys.platform != "win32":
        #    self.completions_model.setRootPath( last_path )

        # Setup layout
        self.document_label = QtGui.QLabel(self)
        self.document_label.setPixmap(self.document_pixmap.getQPixmap())
        self.layout.addWidget(self.document_label)
        self.layout.addWidget(self.filename)
        self.layout.addWidget(self.clear_button)
        self.layout.addWidget(self.open_button)
        self.layout.addWidget(self.add_button)
        self.layout.addWidget(self.save_as_button)
        self.setLayout(self.layout)
Example #29
0
class LocalFileEditor( CustomEditor ):
    """Widget for browsing local files and directories"""

    browse_icon =  Icon( 'tango/16x16/places/folder-saved-search.png' )

    def __init__(self, 
                 parent = None, 
                 field_name = 'local_file', 
                 directory = False, 
                 save_as = False,
                 file_filter = 'All files (*)',
                 **kwargs):
        CustomEditor.__init__(self, parent)
        self.setObjectName( field_name )
        self._directory = directory
        self._save_as = save_as
        self._file_filter = file_filter
        self.setup_widget()

    def setup_widget(self):
        """Called inside init, overwrite this method for custom
        file edit widgets"""
        layout = QtGui.QHBoxLayout()
        layout.setSpacing(0)
        layout.setContentsMargins(0, 0, 0, 0)

        browse_button = QtGui.QToolButton( self )
        browse_button.setFocusPolicy( Qt.ClickFocus )
        browse_button.setIcon( self.browse_icon.getQIcon() )
        browse_button.setToolTip( _('Browse') )
        browse_button.setAutoRaise( True )
        browse_button.clicked.connect( self.browse_button_clicked )

        self.filename = DecoratedLineEdit(self)
        self.filename.editingFinished.connect( self.filename_editing_finished )
        self.setFocusProxy( self.filename )
        
        layout.addWidget( self.filename )
        layout.addWidget( browse_button )
        self.setLayout( layout )

    @QtCore.pyqtSlot()
    def filename_editing_finished(self):
        self.valueChanged.emit()
        self.editingFinished.emit()

    @QtCore.pyqtSlot()
    def browse_button_clicked(self):
        current_directory = os.path.dirname( self.get_value() )
        if self._directory:
            value = QtGui.QFileDialog.getExistingDirectory( self,
                                                            directory = current_directory )
        elif self._save_as:
            value = QtGui.QFileDialog.getSaveFileName( self,
                                                       filter = self._file_filter,
                                                       directory = current_directory )
        else:
            value = QtGui.QFileDialog.getOpenFileName( self,
                                                       filter = self._file_filter,
                                                       directory = current_directory )
        value = os.path.abspath( unicode( value ) )
        self.filename.setText( value )
        self.valueChanged.emit()
        self.editingFinished.emit()

    def set_value(self, value):
        value = CustomEditor.set_value(self, value)
        if value:
            self.filename.setText( value )
        else:
            self.filename.setText( '' )
        self.valueChanged.emit()
        return value

    def get_value(self):
        return CustomEditor.get_value(self) or unicode( self.filename.text() )
    
    value = QtCore.pyqtProperty( str, get_value, set_value )

    def set_field_attributes( self, 
                              editable = True,
                              background_color = None,
                              tooltip = None,
                              **kwargs):
        self.setEnabled( editable )
        if self.filename:
            set_background_color_palette( self.filename, background_color )
            self.filename.setToolTip(unicode(tooltip or ''))
Example #30
0
class FileEditor(CustomEditor):
    """Widget for editing File fields"""

    filter = 'All files (*)'
    add_icon = Icon('tango/16x16/actions/list-add.png')
    open_icon = Icon('tango/16x16/actions/document-open.png')
    clear_icon = Icon('tango/16x16/actions/edit-delete.png')
    save_as_icon = Icon('tango/16x16/actions/document-save-as.png')
    document_pixmap = Icon('tango/16x16/mimetypes/x-office-document.png')

    def __init__(self,
                 parent=None,
                 storage=None,
                 field_name='file',
                 remove_original=False,
                 **kwargs):
        CustomEditor.__init__(self, parent)
        self.setObjectName(field_name)
        self.storage = storage
        self.filename = None  # the widget containing the filename
        self.value = None
        self.remove_original = remove_original
        self.setup_widget()

    def setup_widget(self):
        """Called inside init, overwrite this method for custom
        file edit widgets"""
        self.layout = QtGui.QHBoxLayout()
        self.layout.setSpacing(0)
        self.layout.setContentsMargins(0, 0, 0, 0)

        # Save As button
        self.save_as_button = QtGui.QToolButton()
        self.save_as_button.setFocusPolicy(Qt.ClickFocus)
        self.save_as_button.setIcon(self.save_as_icon.getQIcon())
        self.save_as_button.setToolTip(_('Save file as'))
        self.save_as_button.setAutoRaise(True)
        self.save_as_button.clicked.connect(self.save_as_button_clicked)

        # Clear button
        self.clear_button = QtGui.QToolButton()
        self.clear_button.setFocusPolicy(Qt.ClickFocus)
        self.clear_button.setIcon(self.clear_icon.getQIcon())
        self.clear_button.setToolTip(_('Delete file'))
        self.clear_button.setAutoRaise(True)
        self.clear_button.clicked.connect(self.clear_button_clicked)

        # Open button
        self.open_button = QtGui.QToolButton()
        self.open_button.setFocusPolicy(Qt.ClickFocus)
        self.open_button.setIcon(self.open_icon.getQIcon())
        self.open_button.setToolTip(_('Open file'))
        self.open_button.clicked.connect(self.open_button_clicked)
        self.open_button.setAutoRaise(True)

        # Add button
        self.add_button = QtGui.QToolButton()
        self.add_button.setFocusPolicy(Qt.StrongFocus)
        self.add_button.setIcon(self.add_icon.getQIcon())
        self.add_button.setToolTip(_('Attach file'))
        self.add_button.clicked.connect(self.add_button_clicked)
        self.add_button.setAutoRaise(True)

        # Filename
        self.filename = DecoratedLineEdit(self)
        self.filename.set_minimum_width(20)
        self.filename.setFocusPolicy(Qt.ClickFocus)

        # Search Completer
        #
        # Turn completion off, since it creates a thread per field on a form
        #
        # self.completer = QtGui.QCompleter()
        # self.completions_model = QtGui.QFileSystemModel()
        # self.completer.setCompletionMode(
        #    QtGui.QCompleter.UnfilteredPopupCompletion
        # )
        # self.completer.setModel( self.completions_model )
        # self.completer.activated[QtCore.QModelIndex].connect(self.file_completion_activated)
        # self.filename.setCompleter( self.completer )
        # settings = QtCore.QSettings()
        # last_path = settings.value('lastpath').toString()

        # # This setting of a rootPath causes a major delay on Windows, since
        # # the QFileSystemModel starts to fetch file information in a non-
        # # blocking way (although the documentation state the opposite).
        # # On Linux, there is no such delay, so it's safe to set such a root
        # # path and let the underlaying system start indexing.
        # import sys
        # if sys.platform != "win32":
        #    self.completions_model.setRootPath( last_path )

        # Setup layout
        self.document_label = QtGui.QLabel(self)
        self.document_label.setPixmap(self.document_pixmap.getQPixmap())
        self.layout.addWidget(self.document_label)
        self.layout.addWidget(self.filename)
        self.layout.addWidget(self.clear_button)
        self.layout.addWidget(self.open_button)
        self.layout.addWidget(self.add_button)
        self.layout.addWidget(self.save_as_button)
        self.setLayout(self.layout)

    def file_completion_activated(self, index):
        from camelot.view.storage import create_stored_file
        source_index = index.model().mapToSource(index)
        if not self.completions_model.isDir(source_index):
            path = self.completions_model.filePath(source_index)
            create_stored_file(
                self,
                self.storage,
                self.stored_file_ready,
                filter=self.filter,
                remove_original=self.remove_original,
                filename=path,
            )

    def set_value(self, value):
        value = CustomEditor.set_value(self, value)
        self.value = value
        if value:
            self.clear_button.setVisible(True)
            self.save_as_button.setVisible(True)
            self.open_button.setVisible(True)
            self.add_button.setVisible(False)
            self.filename.setText(value.verbose_name)
        else:
            self.clear_button.setVisible(False)
            self.save_as_button.setVisible(False)
            self.open_button.setVisible(False)
            self.add_button.setVisible(True)
            self.filename.setText('')
        return value

    def get_value(self):
        return CustomEditor.get_value(self) or self.value

    def set_field_attributes(self,
                             editable=True,
                             background_color=None,
                             tooltip=None,
                             remove_original=False,
                             **kwargs):
        self.set_enabled(editable)
        if self.filename:
            set_background_color_palette(self.filename, background_color)
            self.filename.setToolTip(unicode(tooltip or ''))
        self.remove_original = remove_original

    def set_enabled(self, editable=True):
        self.clear_button.setEnabled(editable)
        self.add_button.setEnabled(editable)
        self.filename.setEnabled(editable)
        self.filename.setReadOnly(not editable)
        self.document_label.setEnabled(editable)
        self.setAcceptDrops(editable)

    def stored_file_ready(self, stored_file):
        """Slot to be called when a new stored_file has been created by
        the storage"""
        self.set_value(stored_file)
        self.editingFinished.emit()

    def save_as_button_clicked(self):
        from camelot.view.storage import save_stored_file
        value = self.get_value()
        if value:
            save_stored_file(self, value)

    def add_button_clicked(self):
        from camelot.view.storage import create_stored_file
        create_stored_file(
            self,
            self.storage,
            self.stored_file_ready,
            filter=self.filter,
            remove_original=self.remove_original,
        )

    def open_button_clicked(self):
        from camelot.view.storage import open_stored_file
        open_stored_file(self, self.value)

    def clear_button_clicked(self):
        answer = QtGui.QMessageBox.question(
            self, _('Remove this file ?'),
            _('If you continue, you will no longer be able to open this file.'
              ), QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
        if answer == QtGui.QMessageBox.Yes:
            self.value = None
            self.editingFinished.emit()

    #
    # Drag & Drop
    #
    def dragEnterEvent(self, event):
        event.acceptProposedAction()

    def dragMoveEvent(self, event):
        event.acceptProposedAction()

    def dropEvent(self, event):
        from camelot.view.storage import create_stored_file
        if event.mimeData().hasUrls():
            url = event.mimeData().urls()[0]
            filename = url.toLocalFile()
            if filename:
                create_stored_file(
                    self,
                    self.storage,
                    self.stored_file_ready,
                    filter=self.filter,
                    remove_original=self.remove_original,
                    filename=filename,
                )
Example #31
0
class DateEditor(CustomEditor):
    """Widget for editing date values"""

    calendar_action_trigger = QtCore.pyqtSignal()
    special_date_icon = Icon('tango/16x16/apps/office-calendar.png')
    
    def __init__(self,
                 parent=None,
                 editable=True,
                 nullable=True,
                 **kwargs):
        CustomEditor.__init__(self, parent)

        self.date_format = local_date_format()
        self.line_edit = DecoratedLineEdit()
        self.line_edit.set_background_text( QtCore.QDate(2000,1,1).toString(self.date_format) )

        # The order of creation of this widgets and their parenting
        # seems very sensitive under windows and creates system crashes
        # so don't change this without extensive testing on windows
        special_date_menu = QtGui.QMenu(self)
        calendar_widget_action = QtGui.QWidgetAction(special_date_menu)
        self.calendar_widget = QtGui.QCalendarWidget(special_date_menu)
        self.calendar_widget.activated.connect(self.calendar_widget_activated)
        self.calendar_widget.clicked.connect(self.calendar_widget_activated)
        calendar_widget_action.setDefaultWidget(self.calendar_widget)

        self.calendar_action_trigger.connect( special_date_menu.hide )
        special_date_menu.addAction(calendar_widget_action)
        special_date_menu.addAction(_('Today'))
        special_date_menu.addAction(_('Far future'))
        self.special_date = QtGui.QToolButton(self)
        self.special_date.setIcon( self.special_date_icon.getQIcon() )
        self.special_date.setAutoRaise(True)
        self.special_date.setToolTip(_('Calendar and special dates'))
        self.special_date.setMenu(special_date_menu)
        self.special_date.setPopupMode(QtGui.QToolButton.InstantPopup)
        self.special_date.setFixedHeight(self.get_height())
        self.special_date.setFocusPolicy(Qt.ClickFocus)
        # end of sensitive part

        if nullable:
            special_date_menu.addAction(_('Clear'))

        self.hlayout = QtGui.QHBoxLayout()
        self.hlayout.addWidget(self.line_edit)
        self.hlayout.addWidget(self.special_date)

        self.hlayout.setContentsMargins(0, 0, 0, 0)
        self.hlayout.setMargin(0)
        self.hlayout.setSpacing(0)
        self.hlayout.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
        
        self.setContentsMargins(0, 0, 0, 0)
        self.setLayout(self.hlayout)

        self.minimum = datetime.date.min
        self.maximum = datetime.date.max
        self.setFocusProxy(self.line_edit)

        self.line_edit.editingFinished.connect( self.line_edit_finished )
        self.line_edit.textEdited.connect(self.text_edited)
        special_date_menu.triggered.connect(self.set_special_date)

    def calendar_widget_activated(self, date):
        self.calendar_action_trigger.emit()
        self.set_value(date)
        self.editingFinished.emit()

    def line_edit_finished(self):
        self.setProperty( 'value', QtCore.QVariant( self.get_value() ) )
        self.valueChanged.emit()
        self.editingFinished.emit()

    def focusOutEvent(self, event):
        self.setProperty( 'value', QtCore.QVariant( self.get_value() ) )
        self.valueChanged.emit()
        self.editingFinished.emit()

    def set_value(self, value):
        value = CustomEditor.set_value(self, value)
        self.setProperty( 'value', QtCore.QVariant( value ) )
        if value:
            qdate = QtCore.QDate(value)
            formatted_date = qdate.toString(self.date_format)
            self.line_edit.set_user_input(formatted_date)
            self.calendar_widget.setSelectedDate(qdate)
        else:
            self.line_edit.set_user_input('')
        self.valueChanged.emit()

    def text_edited(self, text ):
        try:
            date_from_string( self.line_edit.user_input() )
            self.line_edit.set_valid(True)
            self.valueChanged.emit()
        except ParsingError:
            self.line_edit.set_valid(False)

    def get_value(self):
        try:
            value = date_from_string( self.line_edit.user_input() )
        except ParsingError:
            value = None
        return CustomEditor.get_value(self) or value

    def set_enabled(self, editable=True):
        self.line_edit.setEnabled(editable)
        if editable:
            self.special_date.show()
        else:
            self.special_date.hide()

    def set_special_date(self, action):
        if action.text().compare(_('Today')) == 0:
            self.set_value(datetime.date.today())
        elif action.text().compare(_('Far future')) == 0:
            self.set_value(datetime.date( year = 2400, month = 12, day = 31 ))
        elif action.text().compare(_('Clear')) == 0:
            self.set_value(None)
        self.editingFinished.emit()
Example #32
0
class ImageEditor( FileEditor ):
    """Editor to view and edit image files, this is a customized
    implementation of a FileEditor"""

    filter = """Image files (*.bmp *.jpg *.jpeg *.mng *.png *.pbm *.pgm *.ppm *.tiff *.xbm *.xpm)
All files (*)"""

    def __init__( self,
                  parent=None,
                  storage=None,
                  preview_width=100,
                  preview_height=100,
                  field_name = 'image',
                  **kwargs ):
        self.preview_width = preview_width
        self.preview_height = preview_height
        FileEditor.__init__(
            self, parent=parent, storage=storage,
            **kwargs
        )
        self.setObjectName( field_name )

    def setup_widget(self):
        layout = QtWidgets.QHBoxLayout()
        #
        # Setup label
        #
        self.label = QtWidgets.QLabel(self)
        self.label.installEventFilter(self)
        self.label.setAlignment( Qt.AlignHCenter|Qt.AlignVCenter )
        layout.addWidget(self.label)
        
        self.filename = DecoratedLineEdit( self )
        self.filename.setVisible( False )
        #
        # Setup buttons
        #
        button_layout = QtWidgets.QVBoxLayout()
        button_layout.setSpacing( 0 )
        button_layout.setContentsMargins( 0, 0, 0, 0)
        
        copy_button = QtWidgets.QToolButton()
        copy_button.setDefaultAction( ActionFactory.copy(self, self.copy_to_clipboard ) )
        copy_button.setAutoRaise(True)
        copy_button.setFocusPolicy(Qt.ClickFocus)

        paste_button = QtWidgets.QToolButton()
        paste_button.setDefaultAction( ActionFactory.paste(self, self.paste_from_clipboard ) )
        paste_button.setAutoRaise(True)
        paste_button.setObjectName('paste')
        paste_button.setFocusPolicy(Qt.ClickFocus)
        
        self.add_actions(self.actions, button_layout)
        button_layout.addWidget(copy_button)
        button_layout.addWidget(paste_button)
        button_layout.addStretch()

        layout.addLayout(button_layout)
        #label_button_layout.addStretch()
        self.setLayout( layout )
        self.clear_image()
        QtWidgets.QApplication.clipboard().dataChanged.connect( self.clipboard_data_changed )
        self.clipboard_data_changed()

        # horizontal policy is always expanding, to fill the width of a column
        # in a form
        vertical_size_policy = QtGui.QSizePolicy.Expanding

        if self.preview_width != 0:
            self.label.setMinimumWidth(self.preview_width)
        if self.preview_height != 0:
            self.label.setFixedHeight(self.preview_height)
            vertical_size_policy = QtGui.QSizePolicy.Fixed
        self.setSizePolicy(QtGui.QSizePolicy.Expanding, vertical_size_policy)
        self.label.setSizePolicy(QtGui.QSizePolicy.Expanding, vertical_size_policy)
        
    @QtCore.qt_slot()
    def clipboard_data_changed(self):
        paste_button = self.findChild(QtWidgets.QWidget, 'paste')
        if paste_button:
            mime_data = QtWidgets.QApplication.clipboard().mimeData()
            paste_button.setVisible( mime_data.hasImage() )
            
    @QtCore.qt_slot()
    def paste_from_clipboard(self):
        """Paste an image from the clipboard into the editor"""
        mime_data = QtWidgets.QApplication.clipboard().mimeData()
        if mime_data.hasImage():
            byte_array = QtCore.QByteArray()
            buffer = QtCore.QBuffer( byte_array )
            image = QtGui.QImage( mime_data.imageData() )
            image.save( buffer, 'PNG' )
            
            def create_checkin( byte_array ):
                return lambda:self.checkin_byte_array(byte_array, '.png')
            
            post( create_checkin( byte_array ), self.stored_file_ready )
        
    def checkin_byte_array(self, byte_array, suffix):
        """Check a byte_array into the storage"""
        stream = six.StringIO( byte_array.data() )
        return self.storage.checkin_stream( 'clipboard', suffix, stream)
        
    def set_enabled(self, editable=True):
        self.setAcceptDrops(editable)

    def set_pixmap(self, pixmap):
        self.label.setPixmap(pixmap)
        self.draw_border()

    def set_image(self, image):
        self.set_pixmap(QtGui.QPixmap.fromImage(image))
        
    @QtCore.qt_slot()
    def copy_to_clipboard(self):
        """Copy the image to the clipboard"""
        if self.value:
            post( self.value.checkout_image, self.set_image_to_clipboard )
        
    def set_image_to_clipboard(self, image):
        clipboard = QtWidgets.QApplication.clipboard()
        clipboard.setImage( image )

    def clear_image(self):
        dummy_image = Icon('tango/32x32/mimetypes/image-x-generic.png')
        self.set_pixmap(dummy_image.getQPixmap())

    def set_value(self, value):
        value = super( ImageEditor, self ).set_value( value )
        if value is not None:
            if value.name != self.file_name:
                if self.preview_height and self.preview_width:
                    post(
                        lambda:value.checkout_thumbnail(
                            self.preview_width,
                            self.preview_height
                        ),
                        self.set_image
                    )
                else:
                    post(
                        lambda:value.checkout_image(),
                        self.set_image
                    )
                # store the file name of which a previous is shown in the editor,
                # to ensure the preview is updated when this changes
                self.file_name = value.name
        else:
            self.clear_image()
        return value

    def draw_border(self):
        self.label.setFrameShape(QtWidgets.QFrame.Box)
        self.label.setFrameShadow(QtWidgets.QFrame.Plain)
        self.label.setLineWidth(1)

    def show_fullscreen(self, image):
        lite_box = LiteBoxView(self)
        lite_box.show_fullscreen_image(image)

    def eventFilter(self, object, event):
        if not object.isWidgetType():
            return False
        if event.type() != QtCore.QEvent.MouseButtonPress:
            return False
        if event.modifiers() != QtCore.Qt.NoModifier:
            return False
        if event.buttons() == QtCore.Qt.LeftButton:
            if self.value:
                post(
                    lambda:self.value.checkout_thumbnail(640,480),
                    self.show_fullscreen
                )
            return True
        return False
Example #33
0
    def setup_widget(self):
        layout = QtGui.QVBoxLayout()
        layout.setSpacing(0)
        label_button_layout = QtGui.QHBoxLayout()
        #
        # Setup label
        #
        self.label = QtGui.QLabel(self)
        self.label.installEventFilter(self)
        self.label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        label_button_layout.addWidget(self.label)

        self.filename = DecoratedLineEdit(self)
        self.filename.setVisible(False)
        #
        # Setup buttons
        #
        button_layout = QtGui.QVBoxLayout()
        button_layout.setSpacing(0)
        button_layout.setContentsMargins(0, 0, 0, 0)

        self.save_as_button = QtGui.QToolButton()
        self.save_as_button.setFocusPolicy(Qt.ClickFocus)
        self.save_as_button.setIcon(self.save_as_icon.getQIcon())
        self.save_as_button.setToolTip(_('Save file as'))
        self.save_as_button.setAutoRaise(True)
        self.save_as_button.clicked.connect(self.save_as_button_clicked)

        self.open_button = QtGui.QToolButton()
        self.open_button.setIcon(self.open_icon.getQIcon())
        self.open_button.setAutoRaise(True)
        self.open_button.setToolTip(unicode(_('open image')))
        self.open_button.clicked.connect(self.open_button_clicked)

        self.add_button = QtGui.QToolButton()
        self.add_button.setFocusPolicy(Qt.StrongFocus)
        self.add_button.setIcon(self.add_icon.getQIcon())
        self.add_button.setToolTip(_('Attach file'))
        self.add_button.clicked.connect(self.add_button_clicked)
        self.add_button.setAutoRaise(True)

        self.clear_button = QtGui.QToolButton()
        self.clear_button.setIcon(self.clear_icon.getQIcon())
        self.clear_button.setToolTip(unicode(_('delete image')))
        self.clear_button.setAutoRaise(True)
        self.clear_button.clicked.connect(self.clear_button_clicked)
        self.clear_button.setFocusPolicy(Qt.ClickFocus)

        copy_button = QtGui.QToolButton()
        copy_button.setDefaultAction(
            ActionFactory.copy(self, self.copy_to_clipboard))
        copy_button.setAutoRaise(True)
        copy_button.setFocusPolicy(Qt.ClickFocus)

        paste_button = QtGui.QToolButton()
        paste_button.setDefaultAction(
            ActionFactory.paste(self, self.paste_from_clipboard))
        paste_button.setAutoRaise(True)
        paste_button.setObjectName('paste')
        paste_button.setFocusPolicy(Qt.ClickFocus)

        #button_layout.addStretch()
        button_layout.addWidget(self.open_button)
        button_layout.addWidget(self.save_as_button)
        button_layout.addWidget(self.add_button)
        button_layout.addWidget(self.clear_button)
        button_layout.addWidget(copy_button)
        button_layout.addWidget(paste_button)

        label_button_layout.addLayout(button_layout)
        label_button_layout.addStretch()
        layout.addLayout(label_button_layout)
        #layout.addStretch()
        self.setLayout(layout)
        self.clear_image()
        QtGui.QApplication.clipboard().dataChanged.connect(
            self.clipboard_data_changed)
        self.clipboard_data_changed()
Example #34
0
class Many2OneEditor(CustomEditor, AbstractManyToOneEditor):
    """Widget for editing many 2 one relations"""

    new_icon = Icon('tango/16x16/actions/document-new.png')
    search_icon = Icon('tango/16x16/actions/system-search.png')

    class CompletionsModel(QtCore.QAbstractListModel):
        def __init__(self, parent=None):
            QtCore.QAbstractListModel.__init__(self, parent)
            self._completions = []

        def setCompletions(self, completions):
            self._completions = completions
            self.layoutChanged.emit()

        def data(self, index, role):
            if role == Qt.DisplayRole:
                return QtCore.QVariant(self._completions[index.row()][0])
            elif role == Qt.EditRole:
                return QtCore.QVariant(self._completions[index.row()][1])
            return QtCore.QVariant()

        def rowCount(self, index=None):
            return len(self._completions)

        def columnCount(self, index=None):
            return 1

    def __init__(self, admin=None, parent=None, editable=True, **kwargs):
        """:param entity_admin : The Admin interface for the object on the one
        side of the relation
        """

        CustomEditor.__init__(self, parent)
        self.admin = admin
        self.entity_set = False
        self._editable = editable
        self._entity_representation = ''
        self.entity_instance_getter = None
        self._last_highlighted_entity_getter = None

        self.layout = QtGui.QHBoxLayout()
        self.layout.setSpacing(0)
        self.layout.setMargin(0)

        # Search button
        self.search_button = QtGui.QToolButton()
        self.search_button.setAutoRaise(True)
        self.search_button.setFocusPolicy(Qt.ClickFocus)
        self.search_button.setFixedHeight(self.get_height())
        self.search_button.clicked.connect(self.searchButtonClicked)
        self.search_button.setIcon(
            Icon('tango/16x16/actions/edit-clear.png').getQIcon())
        self.search_button.setToolTip(unicode(_('clear')))

        # Open button
        self.open_button = QtGui.QToolButton()
        self.open_button.setAutoRaise(True)
        self.open_button.setFocusPolicy(Qt.ClickFocus)
        self.open_button.setFixedHeight(self.get_height())
        self.open_button.clicked.connect(self.openButtonClicked)
        self.open_button.setIcon(self.new_icon.getQIcon())
        self.open_button.setToolTip(unicode(_('new')))

        # Search input
        self.search_input = DecoratedLineEdit(self)
        self.search_input.set_background_text(_('Search...'))
        self.search_input.textEdited.connect(self.textEdited)
        # suppose garbage was entered, we need to refresh the content
        self.search_input.editingFinished.connect(
            self.search_input_editing_finished)
        self.setFocusProxy(self.search_input)

        # Search Completer
        self.completer = QtGui.QCompleter()
        self.completions_model = self.CompletionsModel(self.completer)
        self.completer.setModel(self.completions_model)
        self.completer.setCaseSensitivity(Qt.CaseInsensitive)
        self.completer.setCompletionMode(
            QtGui.QCompleter.UnfilteredPopupCompletion)
        #self.completer.activated.connect(self.completionActivated)
        #self.completer.highlighted.connect(self.completion_highlighted)
        self.completer.activated[QtCore.QModelIndex].connect(
            self.completionActivated)
        self.completer.highlighted[QtCore.QModelIndex].connect(
            self.completion_highlighted)
        self.search_input.setCompleter(self.completer)

        # Setup layout
        self.layout.addWidget(self.search_input)
        self.layout.addWidget(self.search_button)
        self.layout.addWidget(self.open_button)
        self.setLayout(self.layout)

    def set_field_attributes(self, editable=True, background_color=None, **kw):
        self.set_editable(editable)
        self.set_background_color(background_color)

    def set_editable(self, editable):
        self._editable = editable
        self.search_input.setEnabled(editable)
        self.search_button.setEnabled(editable)

    def textEdited(self, text):
        self._last_highlighted_entity_getter = None
        text = self.search_input.user_input()

        def create_search_completion(text):
            return lambda: self.search_completions(text)

        post(create_search_completion(unicode(text)),
             self.display_search_completions)
        self.completer.complete()

    @model_function
    def search_completions(self, text):
        """Search for object that match text, to fill the list of completions

        :return: a list of tuples of (object_representation, object_getter)
        """
        search_decorator = create_entity_search_query_decorator(
            self.admin, text)
        if search_decorator:
            sresult = [
                (unicode(e), create_constant_function(e))
                for e in search_decorator(self.admin.entity.query).limit(20)
            ]
            return text, sresult
        return text, []

    @gui_function
    def display_search_completions(self, prefix_and_completions):
        prefix, completions = prefix_and_completions
        self.completions_model.setCompletions(completions)
        self.completer.setCompletionPrefix(prefix)
        self.completer.complete()

    def completionActivated(self, index):
        object_getter = index.data(Qt.EditRole)
        self.setEntity(variant_to_pyobject(object_getter))

    def completion_highlighted(self, index):
        object_getter = index.data(Qt.EditRole)
        pyob = variant_to_pyobject(object_getter)
        self._last_highlighted_entity_getter = pyob

    def openButtonClicked(self):
        if self.entity_set:
            return self.createFormView()
        else:
            return self.createNew()

    def returnPressed(self):
        if not self.entity_set:
            self.createSelectView()

    def searchButtonClicked(self):
        if self.entity_set:
            self.setEntity(lambda: None)
        else:
            self.createSelectView()

    def trashButtonClicked(self):
        self.setEntity(lambda: None)

    @gui_function
    def createNew(self):
        @model_function
        def get_has_subclasses():
            return len(self.admin.get_subclass_tree())

        post(get_has_subclasses, self.show_new_view)

    @gui_function
    def show_new_view(self, has_subclasses):
        from camelot.view.workspace import show_top_level
        selected = QtGui.QDialog.Accepted
        admin = self.admin
        if has_subclasses:
            from camelot.view.controls.inheritance import SubclassDialog
            select_subclass = SubclassDialog(self, self.admin)
            select_subclass.setWindowTitle(_('select'))
            selected = select_subclass.exec_()
            admin = select_subclass.selected_subclass
        if selected:
            form = admin.create_new_view()
            form.entity_created_signal.connect(self.selectEntity)
            # @todo: dirty trick to keep reference
            #self.__new_form = form
            show_top_level(form, self)

    def createFormView(self):
        if self.entity_instance_getter:

            def get_admin_and_title():
                obj = self.entity_instance_getter()
                admin = self.admin.get_related_entity_admin(obj.__class__)
                return admin, ''

            post(get_admin_and_title, self.show_form_view)

    def show_form_view(self, admin_and_title):
        from camelot.view.workspace import show_top_level
        admin, title = admin_and_title

        def create_collection_getter(instance_getter):
            return lambda: [instance_getter()]

        from camelot.view.proxy.collection_proxy import CollectionProxy

        model = CollectionProxy(
            admin, create_collection_getter(self.entity_instance_getter),
            admin.get_fields)
        model.dataChanged.connect(self.dataChanged)
        form = admin.create_form_view(title, model, 0)
        # @todo : dirty trick to keep reference
        #self.__form = form
        show_top_level(form, self)

    def dataChanged(self, index1, index2):
        self.setEntity(self.entity_instance_getter, False)

    def search_input_editing_finished(self):
        if not self.entity_set:
            # Only try to 'guess' what the user meant when no entity is set
            # to avoid inappropriate removal of data, (eg when the user presses
            # Esc, editingfinished will be called as well, and we should not
            # overwrite the current entity set)
            if self._last_highlighted_entity_getter:
                self.setEntity(self._last_highlighted_entity_getter)
            elif not self.entity_set and self.completions_model.rowCount(
            ) == 1:
                # There is only one possible option
                index = self.completions_model.index(0, 0)
                entity_getter = variant_to_pyobject(index.data(Qt.EditRole))
                self.setEntity(entity_getter)
        self.search_input.set_user_input(self._entity_representation)

    def set_value(self, value):
        """:param value: either ValueLoading, or a function that returns None
        or the entity to be shown in the editor"""
        self._last_highlighted_entity_getter = None
        value = CustomEditor.set_value(self, value)
        if value:
            self.setEntity(value, propagate=False)

    def get_value(self):
        """:return: a function that returns the selected entity or ValueLoading
        or None"""
        value = CustomEditor.get_value(self)
        if not value:
            value = self.entity_instance_getter
        return value

    @QtCore.pyqtSlot(tuple)
    def set_instance_represenation(self, representation_and_propagate):
        """Update the gui"""
        ((desc, pk), propagate) = representation_and_propagate
        self._entity_representation = desc
        self.search_input.set_user_input(desc)

        if pk != False:
            self.open_button.setIcon(
                Icon('tango/16x16/places/folder.png').getQIcon())
            self.open_button.setToolTip(unicode(_('open')))
            self.open_button.setEnabled(True)

            self.search_button.setIcon(
                Icon('tango/16x16/actions/edit-clear.png').getQIcon())
            self.search_button.setToolTip(unicode(_('clear')))
            self.entity_set = True
        else:
            self.open_button.setIcon(self.new_icon.getQIcon())
            self.open_button.setToolTip(unicode(_('new')))
            self.open_button.setEnabled(self._editable)

            self.search_button.setIcon(self.search_icon.getQIcon())
            self.search_button.setToolTip(_('Search'))
            self.entity_set = False

        if propagate:
            self.editingFinished.emit()

    def setEntity(self, entity_instance_getter, propagate=True):
        def create_instance_getter(entity_instance):
            return lambda: entity_instance

        def get_instance_represenation():
            """Get a representation of the instance

            :return: (unicode, pk) its unicode representation and its primary
            key or ('', False) if the instance was None"""
            entity = entity_instance_getter()
            self.entity_instance_getter = create_instance_getter(entity)
            if entity and hasattr(entity, 'id'):
                return ((unicode(entity), entity.id), propagate)
            elif entity:
                return ((unicode(entity), False), propagate)
            return ((None, False), propagate)

        post(get_instance_represenation, self.set_instance_represenation)

    def selectEntity(self, entity_instance_getter):
        self.setEntity(entity_instance_getter)
Example #35
0
    def __init__(self, admin=None, parent=None, editable=True, **kwargs):
        """:param entity_admin : The Admin interface for the object on the one
        side of the relation
        """

        CustomEditor.__init__(self, parent)
        self.admin = admin
        self.entity_set = False
        self._editable = editable
        self._entity_representation = ''
        self.entity_instance_getter = None
        self._last_highlighted_entity_getter = None

        self.layout = QtGui.QHBoxLayout()
        self.layout.setSpacing(0)
        self.layout.setMargin(0)

        # Search button
        self.search_button = QtGui.QToolButton()
        self.search_button.setAutoRaise(True)
        self.search_button.setFocusPolicy(Qt.ClickFocus)
        self.search_button.setFixedHeight(self.get_height())
        self.search_button.clicked.connect(self.searchButtonClicked)
        self.search_button.setIcon(
            Icon('tango/16x16/actions/edit-clear.png').getQIcon()
        )
        self.search_button.setToolTip(unicode(_('clear')))

        # Open button
        self.open_button = QtGui.QToolButton()
        self.open_button.setAutoRaise(True)
        self.open_button.setFocusPolicy(Qt.ClickFocus)
        self.open_button.setFixedHeight(self.get_height())
        self.open_button.clicked.connect(self.openButtonClicked)
        self.open_button.setIcon( self.new_icon.getQIcon() )
        self.open_button.setToolTip(unicode(_('new')))

        # Search input
        self.search_input = DecoratedLineEdit(self)
        self.search_input.set_background_text(_('Search...'))
        self.search_input.textEdited.connect(self.textEdited)
        # suppose garbage was entered, we need to refresh the content
        self.search_input.editingFinished.connect( self.search_input_editing_finished )
        self.setFocusProxy(self.search_input)

        # Search Completer
        self.completer = QtGui.QCompleter()
        self.completions_model = self.CompletionsModel(self.completer)
        self.completer.setModel(self.completions_model)
        self.completer.setCaseSensitivity(Qt.CaseInsensitive)
        self.completer.setCompletionMode(
            QtGui.QCompleter.UnfilteredPopupCompletion
        )
        #self.completer.activated.connect(self.completionActivated)
        #self.completer.highlighted.connect(self.completion_highlighted)
        self.completer.activated[QtCore.QModelIndex].connect(self.completionActivated)
        self.completer.highlighted[QtCore.QModelIndex].connect(self.completion_highlighted)
        self.search_input.setCompleter(self.completer)

        # Setup layout
        self.layout.addWidget(self.search_input)
        self.layout.addWidget(self.search_button)
        self.layout.addWidget(self.open_button)
        self.setLayout(self.layout)
Example #36
0
class ImageEditor(FileEditor):
    """Editor to view and edit image files, this is a customized
    implementation of a FileEditor"""

    filter = """Image files (*.bmp *.jpg *.jpeg *.mng *.png *.pbm *.pgm *.ppm
*.tiff *.xbm *.xpm) All files (*)"""

    def __init__(self,
                 parent=None,
                 storage=None,
                 preview_width=100,
                 preview_height=100,
                 field_name='image',
                 **kwargs):
        self.preview_width = preview_width
        self.preview_height = preview_height
        FileEditor.__init__(self, parent=parent, storage=storage, **kwargs)
        self.setObjectName(field_name)

    def setup_widget(self):
        layout = QtGui.QVBoxLayout()
        layout.setSpacing(0)
        label_button_layout = QtGui.QHBoxLayout()
        #
        # Setup label
        #
        self.label = QtGui.QLabel(self)
        self.label.installEventFilter(self)
        self.label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        label_button_layout.addWidget(self.label)

        self.filename = DecoratedLineEdit(self)
        self.filename.setVisible(False)
        #
        # Setup buttons
        #
        button_layout = QtGui.QVBoxLayout()
        button_layout.setSpacing(0)
        button_layout.setContentsMargins(0, 0, 0, 0)

        self.save_as_button = QtGui.QToolButton()
        self.save_as_button.setFocusPolicy(Qt.ClickFocus)
        self.save_as_button.setIcon(self.save_as_icon.getQIcon())
        self.save_as_button.setToolTip(_('Save file as'))
        self.save_as_button.setAutoRaise(True)
        self.save_as_button.clicked.connect(self.save_as_button_clicked)

        self.open_button = QtGui.QToolButton()
        self.open_button.setIcon(self.open_icon.getQIcon())
        self.open_button.setAutoRaise(True)
        self.open_button.setToolTip(unicode(_('open image')))
        self.open_button.clicked.connect(self.open_button_clicked)

        self.add_button = QtGui.QToolButton()
        self.add_button.setFocusPolicy(Qt.StrongFocus)
        self.add_button.setIcon(self.add_icon.getQIcon())
        self.add_button.setToolTip(_('Attach file'))
        self.add_button.clicked.connect(self.add_button_clicked)
        self.add_button.setAutoRaise(True)

        self.clear_button = QtGui.QToolButton()
        self.clear_button.setIcon(self.clear_icon.getQIcon())
        self.clear_button.setToolTip(unicode(_('delete image')))
        self.clear_button.setAutoRaise(True)
        self.clear_button.clicked.connect(self.clear_button_clicked)
        self.clear_button.setFocusPolicy(Qt.ClickFocus)

        copy_button = QtGui.QToolButton()
        copy_button.setDefaultAction(
            ActionFactory.copy(self, self.copy_to_clipboard))
        copy_button.setAutoRaise(True)
        copy_button.setFocusPolicy(Qt.ClickFocus)

        paste_button = QtGui.QToolButton()
        paste_button.setDefaultAction(
            ActionFactory.paste(self, self.paste_from_clipboard))
        paste_button.setAutoRaise(True)
        paste_button.setObjectName('paste')
        paste_button.setFocusPolicy(Qt.ClickFocus)

        #button_layout.addStretch()
        button_layout.addWidget(self.open_button)
        button_layout.addWidget(self.save_as_button)
        button_layout.addWidget(self.add_button)
        button_layout.addWidget(self.clear_button)
        button_layout.addWidget(copy_button)
        button_layout.addWidget(paste_button)

        label_button_layout.addLayout(button_layout)
        label_button_layout.addStretch()
        layout.addLayout(label_button_layout)
        #layout.addStretch()
        self.setLayout(layout)
        self.clear_image()
        QtGui.QApplication.clipboard().dataChanged.connect(
            self.clipboard_data_changed)
        self.clipboard_data_changed()

    @QtCore.pyqtSlot()
    def clipboard_data_changed(self):
        paste_button = self.findChild(QtGui.QWidget, 'paste')
        if paste_button:
            mime_data = QtGui.QApplication.clipboard().mimeData()
            paste_button.setVisible(mime_data.hasImage())

    @QtCore.pyqtSlot()
    def paste_from_clipboard(self):
        """Paste an image from the clipboard into the editor"""
        mime_data = QtGui.QApplication.clipboard().mimeData()
        if mime_data.hasImage():
            byte_array = QtCore.QByteArray()
            buffer = QtCore.QBuffer(byte_array)
            image = QtGui.QImage(mime_data.imageData())
            image.save(buffer, 'PNG')

            def create_checkin(byte_array):
                return lambda: self.checkin_byte_array(byte_array, '.png')

            post(create_checkin(byte_array), self.stored_file_ready)

    def checkin_byte_array(self, byte_array, suffix):
        """Check a byte_array into the storage"""
        import cStringIO
        stream = cStringIO.StringIO(byte_array.data())
        return self.storage.checkin_stream('clipboard', suffix, stream)

    def set_enabled(self, editable=True):
        self.clear_button.setEnabled(editable)
        self.open_button.setEnabled(editable)
        self.add_button.setEnabled(editable)
        self.label.setEnabled(editable)

    def set_pixmap(self, pixmap):
        self.label.setPixmap(pixmap)
        self.draw_border()

    def set_image(self, image):
        self.set_pixmap(QtGui.QPixmap.fromImage(image))

    @QtCore.pyqtSlot()
    def copy_to_clipboard(self):
        """Copy the image to the clipboard"""
        if self.value:
            post(self.value.checkout_image, self.set_image_to_clipboard)

    def set_image_to_clipboard(self, image):
        clipboard = QtGui.QApplication.clipboard()
        clipboard.setImage(image)

    def clear_image(self):
        dummy_image = Icon('tango/32x32/mimetypes/image-x-generic.png')
        self.set_pixmap(dummy_image.getQPixmap())

    def set_value(self, value):
        old_value = self.value
        value = super(ImageEditor, self).set_value(value)
        if value:
            if value != old_value:
                post(
                    lambda: value.checkout_thumbnail(self.preview_width, self.
                                                     preview_height),
                    self.set_image)
        else:
            self.clear_image()
        return value

    def draw_border(self):
        self.label.setFrameShape(QtGui.QFrame.Box)
        self.label.setFrameShadow(QtGui.QFrame.Plain)
        self.label.setLineWidth(1)
        self.label.setFixedSize(self.preview_width, self.preview_height)

    def show_fullscreen(self, image):
        lite_box = LiteBoxView(self)
        lite_box.show_fullscreen_image(image)

    def eventFilter(self, object, event):
        if not object.isWidgetType():
            return False
        if event.type() != QtCore.QEvent.MouseButtonPress:
            return False
        if event.modifiers() != QtCore.Qt.NoModifier:
            return False
        if event.buttons() == QtCore.Qt.LeftButton:
            if self.value:
                post(lambda: self.value.checkout_thumbnail(640, 480),
                     self.show_fullscreen)
            return True
        return False
Example #37
0
    def setup_widget(self):
        """Called inside init, overwrite this method for custom
        file edit widgets"""
        self.layout = QtGui.QHBoxLayout()
        self.layout.setSpacing(0)
        self.layout.setContentsMargins(0, 0, 0, 0)

        # Save As button
        self.save_as_button = QtGui.QToolButton()
        self.save_as_button.setFocusPolicy(Qt.ClickFocus)
        self.save_as_button.setIcon(self.save_as_icon.getQIcon())
        self.save_as_button.setToolTip(_('Save file as'))
        self.save_as_button.setAutoRaise(True)
        self.save_as_button.clicked.connect(self.save_as_button_clicked)

        # Clear button
        self.clear_button = QtGui.QToolButton()
        self.clear_button.setFocusPolicy(Qt.ClickFocus)
        self.clear_button.setIcon(self.clear_icon.getQIcon())
        self.clear_button.setToolTip(_('Delete file'))
        self.clear_button.setAutoRaise(True)
        self.clear_button.clicked.connect(self.clear_button_clicked)

        # Open button
        self.open_button = QtGui.QToolButton()
        self.open_button.setFocusPolicy(Qt.ClickFocus)
        self.open_button.setIcon(self.open_icon.getQIcon())
        self.open_button.setToolTip(_('Open file'))
        self.open_button.clicked.connect(self.open_button_clicked)
        self.open_button.setAutoRaise(True)

        # Add button
        self.add_button = QtGui.QToolButton()
        self.add_button.setFocusPolicy(Qt.StrongFocus)
        self.add_button.setIcon(self.add_icon.getQIcon())
        self.add_button.setToolTip(_('Attach file'))
        self.add_button.clicked.connect(self.add_button_clicked)
        self.add_button.setAutoRaise(True)

        # Filename
        self.filename = DecoratedLineEdit(self)
        self.filename.set_minimum_width(20)
        self.filename.setFocusPolicy(Qt.ClickFocus)

        # Search Completer
        #
        # Turn completion off, since it creates a thread per field on a form
        #
        # self.completer = QtGui.QCompleter()
        # self.completions_model = QtGui.QFileSystemModel()
        # self.completer.setCompletionMode(
        #    QtGui.QCompleter.UnfilteredPopupCompletion
        # )
        # self.completer.setModel( self.completions_model )
        # self.completer.activated[QtCore.QModelIndex].connect(self.file_completion_activated)
        # self.filename.setCompleter( self.completer )
        # settings = QtCore.QSettings()
        # last_path = settings.value('lastpath').toString()

        # # This setting of a rootPath causes a major delay on Windows, since
        # # the QFileSystemModel starts to fetch file information in a non-
        # # blocking way (although the documentation state the opposite).
        # # On Linux, there is no such delay, so it's safe to set such a root
        # # path and let the underlaying system start indexing.
        # import sys
        # if sys.platform != "win32":
        #    self.completions_model.setRootPath( last_path )

        # Setup layout
        self.document_label = QtGui.QLabel(self)
        self.document_label.setPixmap(self.document_pixmap.getQPixmap())
        self.layout.addWidget(self.document_label)
        self.layout.addWidget(self.filename)
        self.layout.addWidget(self.clear_button)
        self.layout.addWidget(self.open_button)
        self.layout.addWidget(self.add_button)
        self.layout.addWidget(self.save_as_button)
        self.setLayout(self.layout)
Example #38
0
class Many2OneEditor(CustomEditor):
    """Widget for editing many 2 one relations"""

    arrow_down_key_pressed = QtCore.qt_signal()

    class CompletionsModel(QtCore.QAbstractListModel):
        def __init__(self, parent=None):
            QtCore.QAbstractListModel.__init__(self, parent)
            self._completions = []

        def setCompletions(self, completions):
            self._completions = completions
            self.layoutChanged.emit()

        def data(self, index, role):
            return py_to_variant(self._completions[index.row()].get(role))

        def rowCount(self, index=None):
            return len(self._completions)

        def columnCount(self, index=None):
            return 1

    def __init__(self,
                 admin=None,
                 parent=None,
                 editable=True,
                 field_name='manytoone',
                 actions=[
                     field_action.ClearObject(),
                     field_action.SelectObject(),
                     field_action.NewObject(),
                     field_action.OpenObject()
                 ],
                 **kwargs):
        """
        :param entity_admin : The Admin interface for the object on the one
        side of the relation
        """
        CustomEditor.__init__(self, parent)
        self.setSizePolicy(QtGui.QSizePolicy.Preferred,
                           QtGui.QSizePolicy.Fixed)
        self.setObjectName(field_name)
        self.admin = admin
        self.new_value = None
        self._entity_representation = ''
        self.obj = None
        self._last_highlighted_entity_getter = None

        self.layout = QtWidgets.QHBoxLayout()
        self.layout.setSpacing(0)
        self.layout.setContentsMargins(0, 0, 0, 0)

        # Search input
        self.search_input = DecoratedLineEdit(self)
        self.search_input.setPlaceholderText(_('Search...'))
        self.search_input.textEdited.connect(self.textEdited)
        self.search_input.set_minimum_width(20)
        self.search_input.arrow_down_key_pressed.connect(
            self.on_arrow_down_key_pressed)
        # suppose garbage was entered, we need to refresh the content
        self.search_input.editingFinished.connect(
            self.search_input_editing_finished)
        self.setFocusProxy(self.search_input)

        # Search Completer
        self.completer = QtGui.QCompleter()
        self.completions_model = self.CompletionsModel(self.completer)
        self.completer.setModel(self.completions_model)
        self.completer.setCaseSensitivity(Qt.CaseInsensitive)
        self.completer.setCompletionMode(
            QtGui.QCompleter.UnfilteredPopupCompletion)
        self.completer.activated[QtCore.QModelIndex].connect(
            self.completionActivated)
        self.completer.highlighted[QtCore.QModelIndex].connect(
            self.completion_highlighted)
        self.search_input.setCompleter(self.completer)

        # Setup layout
        self.layout.addWidget(self.search_input)
        self.setLayout(self.layout)
        self.add_actions(actions, self.layout)
        get_signal_handler().connect_signals(self)

    def set_field_attributes(self, **kwargs):
        super(Many2OneEditor, self).set_field_attributes(**kwargs)
        set_background_color_palette(self.search_input,
                                     kwargs.get('background_color'))
        self.search_input.setToolTip(kwargs.get('tooltip') or '')
        self.search_input.setEnabled(kwargs.get('editable', False))
        self.update_actions()

    def on_arrow_down_key_pressed(self):
        self.arrow_down_key_pressed.emit()

    def textEdited(self, text):
        self._last_highlighted_entity_getter = None
        text = six.text_type(self.search_input.text())

        def create_search_completion(text):
            return lambda: self.search_completions(text)

        post(create_search_completion(six.text_type(text)),
             self.display_search_completions)
        self.completer.complete()

    def search_completions(self, text):
        """Search for object that match text, to fill the list of completions

        :return: a list of tuples of (dict_of_object_representation, object)
        """
        search_decorator = create_entity_search_query_decorator(
            self.admin, text)
        if search_decorator:
            sresult = [
                self.admin.get_search_identifiers(e)
                for e in search_decorator(self.admin.get_query()).limit(20)
            ]
            return text, sresult
        return text, []

    def display_search_completions(self, prefix_and_completions):
        assert object_thread(self)
        prefix, completions = prefix_and_completions
        self.completions_model.setCompletions(completions)
        self.completer.setCompletionPrefix(prefix)
        self.completer.complete()

    def completionActivated(self, index):
        obj = index.data(Qt.EditRole)
        self.set_object(variant_to_py(obj))

    def completion_highlighted(self, index):
        obj = index.data(Qt.EditRole)
        self._last_highlighted_entity_getter = variant_to_py(obj)

    @QtCore.qt_slot(object, object)
    def handle_entity_update(self, sender, entity):
        if entity is self.get_value():
            self.set_object(entity, False)

    @QtCore.qt_slot(object, object)
    def handle_entity_delete(self, sender, entity):
        if entity is self.get_value():
            self.set_object(None, False)

    @QtCore.qt_slot(object, object)
    def handle_entity_create(self, sender, entity):
        if entity is self.new_value:
            self.new_value = None
            self.set_object(entity)

    def search_input_editing_finished(self):
        if self.obj is None:
            # Only try to 'guess' what the user meant when no entity is set
            # to avoid inappropriate removal of data, (eg when the user presses
            # Esc, editingfinished will be called as well, and we should not
            # overwrite the current entity set)
            if self._last_highlighted_entity_getter:
                self.set_object(self._last_highlighted_entity_getter)
            elif self.completions_model.rowCount() == 1:
                # There is only one possible option
                index = self.completions_model.index(0, 0)
                entity_getter = variant_to_py(index.data(Qt.EditRole))
                self.set_object(entity_getter)
        self.search_input.setText(self._entity_representation or u'')

    def set_value(self, value):
        """:param value: either ValueLoading, or a function that returns None
        or the entity to be shown in the editor"""
        self._last_highlighted_entity_getter = None
        self.new_value = None
        value = CustomEditor.set_value(self, value)
        self.set_object(value, propagate=False)
        self.update_actions()

    def get_value(self):
        """:return: a function that returns the selected entity or ValueLoading
        or None"""
        value = CustomEditor.get_value(self)
        if value is not None:
            return value
        return self.obj

    @QtCore.qt_slot(tuple)
    def set_instance_representation(self, representation_and_propagate):
        """Update the gui"""
        (desc, propagate) = representation_and_propagate
        self._entity_representation = desc
        self.search_input.setText(desc or u'')

        if propagate:
            self.editingFinished.emit()

    def set_object(self, obj, propagate=True):
        self.obj = obj

        def get_instance_representation(obj, propagate):
            """Get a representation of the instance"""
            if obj is not None:
                return (self.admin.get_verbose_object_name(obj), propagate)
            return (None, propagate)

        post(
            update_wrapper(
                partial(get_instance_representation, obj, propagate),
                get_instance_representation), self.set_instance_representation)

    selected_object = property(fset=set_object)
Example #39
0
class VirtualAddressEditor(CustomEditor):
    def __init__(self,
                 parent=None,
                 address_type=None,
                 field_name='virtual_address',
                 **kwargs):
        """
        :param address_type: limit the allowed address to be entered to be
            of a certain time, can be 'phone', 'fax', 'email', 'mobile', 'pager'.
            If set to None, all types are allowed.
            
        Upto now, the corrected address returned by the address validator is
        not yet taken into account.
        """
        CustomEditor.__init__(self, parent)
        self.setSizePolicy(QtGui.QSizePolicy.Preferred,
                           QtGui.QSizePolicy.Fixed)
        self.setObjectName(field_name)
        self._address_type = address_type
        self.layout = QtWidgets.QHBoxLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.combo = QtWidgets.QComboBox()
        self.combo.addItems(camelot.types.VirtualAddress.virtual_address_types)
        self.layout.addWidget(self.combo)
        self.editor = DecoratedLineEdit(self)
        self.editor.set_minimum_width(30)
        if address_type:
            self.combo.setVisible(False)
            idx = camelot.types.VirtualAddress.virtual_address_types.index(
                address_type)
            self.combo.setCurrentIndex(idx)
        self.layout.addWidget(self.editor)
        self.setFocusProxy(self.editor)
        nullIcon = Icon('tango/16x16/apps/internet-mail.png').getQIcon()
        self.label = QtWidgets.QToolButton()
        self.label.setIcon(nullIcon)
        self.label.setAutoRaise(True)
        self.label.setEnabled(False)
        self.label.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.label.setFocusPolicy(Qt.ClickFocus)
        self.label.clicked.connect(self.mail_click)
        self.label.hide()
        self.layout.addWidget(self.label)
        self.editor.editingFinished.connect(self.emit_editing_finished)
        self.combo.currentIndexChanged.connect(self.comboIndexChanged)
        self.setLayout(self.layout)
        self.update_validator()

    @QtCore.qt_slot()
    def comboIndexChanged(self):
        self.update_validator()
        self.emit_editing_finished()

    def set_value(self, value):
        value = CustomEditor.set_value(self, value)
        if value is None:
            self.editor.setText('')
        else:
            self.editor.setText(value[1])
            idx = camelot.types.VirtualAddress.virtual_address_types.index(
                self._address_type or value[0])
            self.combo.setCurrentIndex(idx)
            icon = Icon('tango/16x16/devices/printer.png').getQIcon()
            if six.text_type(self.combo.currentText()) == 'fax':
                icon = Icon('tango/16x16/devices/printer.png').getQIcon()
            if six.text_type(self.combo.currentText()) == 'email':
                icon = Icon('tango/16x16/apps/internet-mail.png').getQIcon()
                self.label.setIcon(icon)
                self.label.show()
            else:
                self.label.hide()
                self.label.setIcon(icon)
                self.label.setToolButtonStyle(Qt.ToolButtonIconOnly)
            self.update_validator()

    def get_value(self):
        address_value = six.text_type(self.editor.text())
        if not len(address_value):
            value = None
        else:
            value = (six.text_type(self.combo.currentText()), address_value)
        return CustomEditor.get_value(self) or value

    def set_enabled(self, editable=True):
        self.combo.setEnabled(editable)
        self.editor.setEnabled(editable)
        if not editable:
            self.label.setEnabled(False)
        else:
            if self.combo.currentText() == 'email':
                self.label.setEnabled(True)

    def update_validator(self):
        address_type = six.text_type(self.combo.currentText())
        validator = validators.get(address_type, any_character_validator)
        # change the validator instead of the regexp of the validator to inform
        # the editor it needs to update its background color
        self.editor.setValidator(validator)

    @QtCore.qt_slot()
    def mail_click(self):
        address = self.editor.text()
        url = QtCore.QUrl()
        url.setUrl(u'mailto:%s?subject=Subject' % six.text_type(address))
        QtGui.QDesktopServices.openUrl(url)

    def emit_editing_finished(self):
        self.value = []
        self.value.append(six.text_type(self.combo.currentText()))
        self.value.append(six.text_type(self.editor.text()))
        self.set_value(self.value)
        # emiting editingFinished without a value for the mechanism itself will lead to
        # integrity errors
        if self.value[1]:
            self.editingFinished.emit()

    def set_background_color(self, background_color):
        set_background_color_palette(self.editor, background_color)

    def set_field_attributes(self, **kwargs):
        super(VirtualAddressEditor, self).set_field_attributes(**kwargs)
        self.set_enabled(kwargs.get('editable', False))
        self.setToolTip(six.text_type(kwargs.get('tooltip') or ''))