Example #1
0
class WidgetSelector(QWidget):
    def __init__(self, parent):
        QWidget.__init__(self, parent)
        self.key_text = QLineEdit(self)
        self.widget_select = QComboBox(self)
        self.value_widget = None
        self.layout = QHBoxLayout(self)
        self.layout.addWidget(self.key_text)
        self.layout.addWidget(self.widget_select)

        self.options = NodeWidget.get_simple_widgets()
        for i, option in enumerate(self.options):
            self.widget_select.addItem(option.get_description(), i)

        self.widget_select.currentIndexChanged.connect(self.change_widget)


    def get_item(self):
        return {
            "key":unicode(self.key_text.text()),
            "value":self.value_widget.data
        }

    def change_widget(self):

        w_class = self.options[self.widget_select.currentIndex()]
        if self.value_widget:
            self.layout.removeWidget(self.value_widget)
            self.value_widget.setParent(None)
            self.value_widget = None

        element_scheme = StructuredNode({"Type":w_class.data_type})
        new_data = NodeWidget.get_default_data(element_scheme, None)
        self.value_widget = NodeWidget.create_node_widget("__not_exist", new_data, element_scheme)
        self.layout.addWidget(self.value_widget)
Example #2
0
class RadioBox(QWidget):
    def __init__(self, parent=None, items=None, spread=True):
        # If spread is False, insert a spacer in the layout so that the items don't use all the
        # space they're given but rather align left.
        if items is None:
            items = []
        QWidget.__init__(self, parent)
        self._buttons = []
        self._labels = items
        self._selected_index = 0
        self._spacer = horizontalSpacer() if not spread else None
        self._layout = QHBoxLayout(self)
        self._update_buttons()

    #--- Private
    def _update_buttons(self):
        if self._spacer is not None:
            self._layout.removeItem(self._spacer)
        to_remove = self._buttons[len(self._labels):]
        for button in to_remove:
            self._layout.removeWidget(button)
            button.setParent(None)
        del self._buttons[len(self._labels):]
        to_add = self._labels[len(self._buttons):]
        for _ in to_add:
            button = QRadioButton(self)
            self._buttons.append(button)
            self._layout.addWidget(button)
            button.toggled.connect(self.buttonToggled)
        if self._spacer is not None:
            self._layout.addItem(self._spacer)
        if not self._buttons:
            return
        for button, label in zip(self._buttons, self._labels):
            button.setText(label)
        self._update_selection()

    def _update_selection(self):
        self._selected_index = max(
            0, min(self._selected_index,
                   len(self._buttons) - 1))
        selected = self._buttons[self._selected_index]
        selected.setChecked(True)

    #--- Event Handlers
    def buttonToggled(self):
        for i, button in enumerate(self._buttons):
            if button.isChecked():
                self._selected_index = i
                self.itemSelected.emit(i)
                break

    #--- Signals
    itemSelected = pyqtSignal(int)

    #--- Properties
    @property
    def buttons(self):
        return self._buttons[:]

    @property
    def items(self):
        return self._labels[:]

    @items.setter
    def items(self, value):
        self._labels = value
        self._update_buttons()

    @property
    def selected_index(self):
        return self._selected_index

    @selected_index.setter
    def selected_index(self, value):
        self._selected_index = value
        self._update_selection()
Example #3
0
class RadioBox(QWidget):
    def __init__(self, parent=None, items=None, spread=True):
        # If spread is False, insert a spacer in the layout so that the items don't use all the
        # space they're given but rather align left.
        if items is None:
            items = []
        QWidget.__init__(self, parent)
        self._buttons = []
        self._labels = items
        self._selected_index = 0
        self._spacer = horizontalSpacer() if not spread else None
        self._layout = QHBoxLayout(self)
        self._update_buttons()
    
    #--- Private
    def _update_buttons(self):
        if self._spacer is not None:
            self._layout.removeItem(self._spacer)
        to_remove = self._buttons[len(self._labels):]
        for button in to_remove:
            self._layout.removeWidget(button)
            button.setParent(None)
        del self._buttons[len(self._labels):]
        to_add = self._labels[len(self._buttons):]
        for _ in to_add:
            button = QRadioButton(self)
            self._buttons.append(button)
            self._layout.addWidget(button)
            button.toggled.connect(self.buttonToggled)
        if self._spacer is not None:
            self._layout.addItem(self._spacer)
        if not self._buttons:
            return
        for button, label in zip(self._buttons, self._labels):
            button.setText(label)
        self._update_selection()
    
    def _update_selection(self):
        self._selected_index = max(0, min(self._selected_index, len(self._buttons)-1))
        selected = self._buttons[self._selected_index]
        selected.setChecked(True)
    
    #--- Event Handlers
    def buttonToggled(self):
        for i, button in enumerate(self._buttons):
            if button.isChecked():
                self._selected_index = i
                self.itemSelected.emit(i)
                break
    
    #--- Signals
    itemSelected = pyqtSignal(int)
    
    #--- Properties
    @property
    def buttons(self):
        return self._buttons[:]
    
    @property
    def items(self):
        return self._labels[:]
    
    @items.setter
    def items(self, value):
        self._labels = value
        self._update_buttons()
    
    @property
    def selected_index(self):
        return self._selected_index
    
    @selected_index.setter
    def selected_index(self, value):
        self._selected_index = value
        self._update_selection()
Example #4
0
class LineEdit(QLineEdit):
    inactiveText  = QtDynamicProperty('inactiveText',  unicode)
    widgetSpacing = QtDynamicProperty('widgetSpacing', int)

    def __init__(self, parent=None, contents=u""):
        QLineEdit.__init__(self, contents, parent)
        box_direction = QBoxLayout.RightToLeft if self.isRightToLeft() else QBoxLayout.LeftToRight
        self.inactiveText = u""
        self.left_widget = SideWidget(self)
        self.left_widget.resize(0, 0)
        self.left_layout = QHBoxLayout(self.left_widget)
        self.left_layout.setContentsMargins(0, 0, 0, 0)
        self.left_layout.setDirection(box_direction)
        self.left_layout.setSizeConstraint(QLayout.SetFixedSize)
        self.right_widget = SideWidget(self)
        self.right_widget.resize(0, 0)
        self.right_layout = QHBoxLayout(self.right_widget)
        self.right_layout.setContentsMargins(0, 0, 0, 0)
        self.right_layout.setDirection(box_direction)
        self.right_layout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum))
        self.widgetSpacing = 2
        self.left_widget.sizeHintChanged.connect(self._update_text_margins)
        self.right_widget.sizeHintChanged.connect(self._update_text_margins)

    @property
    def left_margin(self):
        return self.left_widget.sizeHint().width() + 2*self.right_layout.spacing()

    @property
    def right_margin(self):
        return self.right_widget.sizeHint().width() + 2*self.right_layout.spacing()

    def _update_text_margins(self):
        self.setTextMargins(self.left_margin, 0, self.right_margin, 0)
        self._update_side_widget_locations()

    def _update_side_widget_locations(self):
        option = QStyleOptionFrameV2()
        self.initStyleOption(option)
        spacing = self.right_layout.spacing()
        text_rect = self.style().subElementRect(QStyle.SE_LineEditContents, option, self)
        text_rect.adjust(spacing, 0, -spacing, 0)
        mid_height = text_rect.center().y() + 1 - (text_rect.height() % 2) # need -1 correction for odd heights -Dan
        if self.left_layout.count() > 0:
            left_height = mid_height - self.left_widget.height()/2
            left_width = self.left_widget.width()
            if left_width == 0:
                left_height = mid_height - self.left_widget.sizeHint().height()/2
            self.left_widget.move(text_rect.x(), left_height)
        text_rect.setX(self.left_margin)
        text_rect.setY(mid_height - self.right_widget.sizeHint().height()/2.0)
        text_rect.setHeight(self.right_widget.sizeHint().height())
        self.right_widget.setGeometry(text_rect)

    def event(self, event):
        event_type = event.type()
        if event_type == QEvent.LayoutDirectionChange:
            box_direction = QBoxLayout.RightToLeft if self.isRightToLeft() else QBoxLayout.LeftToRight
            self.left_layout.setDirection(box_direction)
            self.right_layout.setDirection(box_direction)
        elif event_type == QEvent.DynamicPropertyChange:
            property_name = event.propertyName()
            if property_name == 'widgetSpacing':
                self.left_layout.setSpacing(self.widgetSpacing)
                self.right_layout.setSpacing(self.widgetSpacing)
                self._update_text_margins()
            elif property_name == 'inactiveText':
                self.update()
        return QLineEdit.event(self, event)

    def resizeEvent(self, event):
        self._update_side_widget_locations()
        QLineEdit.resizeEvent(self, event)

    def paintEvent(self, event):
        QLineEdit.paintEvent(self, event)
        if not self.hasFocus() and not self.text() and self.inactiveText:
            options = QStyleOptionFrameV2()
            self.initStyleOption(options)
            text_rect = self.style().subElementRect(QStyle.SE_LineEditContents, options, self)
            text_rect.adjust(self.left_margin+2, 0, -self.right_margin, 0)
            painter = QPainter(self)
            painter.setPen(self.palette().brush(QPalette.Disabled, QPalette.Text).color())
            painter.drawText(text_rect, Qt.AlignLeft | Qt.AlignVCenter, self.inactiveText)

    def addHeadWidget(self, widget):
        if self.isRightToLeft():
            self.right_layout.insertWidget(1, widget)
        else:
            self.left_layout.addWidget(widget)

    def addTailWidget(self, widget):
        if self.isRightToLeft():
            self.left_layout.addWidget(widget)
        else:
            self.right_layout.insertWidget(1, widget)

    def removeWidget(self, widget):
        self.left_layout.removeWidget(widget)
        self.right_layout.removeWidget(widget)
        widget.hide()
Example #5
0
class FiltersListWidget(QWidget):
    def __init__(self, client, parent=None):
        QWidget.__init__(self, parent)

        self.client = client
        self.filtersWidgetsList = []
        self.args = {}
        self.filters = []

        self.filtersWidget = QHBoxLayout(self)
        self.filtersWidget.setContentsMargins(5, 1, 1, 1)

        label = u'<h3>%s</h3>' % tr("Filters:")
        self.filtersWidget.addWidget(QLabel(label))
        self.add_link = createLink(':/icons-20/add.png', self.addFilter)
        self.filtersWidget.addWidget(self.add_link)
        self.filtersWidget.addStretch()

        self.compatibility = None

    def addFilter(self):
        dialog = EditFilterDialog(self.client, self.args, self.filters, parent=self)
        value = dialog.run()
        if value:
            self.filterChanged(value.filter_arg, value.getValue())

    def update(self, args, filters):
        for filterWidget in self.filtersWidgetsList:
            self.filtersWidget.removeWidget(filterWidget)
            filterWidget.setParent(None)
            filterWidget.hide()

        self.args = args
        self.filters = filters
        self.filtersWidgetsList = []

        for key, value in args.items():
            try:
                filterWidget = FilterWidget(self.client, key, value, self)
            except FilterWidgetError:
                continue

            self.connect(filterWidget, SIGNAL('changeFilter'), self.filterChanged)
            self.connect(filterWidget, SIGNAL('removeFilter'), self.removeFilter)
            self.filtersWidget.insertWidget(1, filterWidget)
            self.filtersWidgetsList += [filterWidget]

        available_filters = set(self.filters).difference(set(self.args.iterkeys()))
        if len(available_filters) > 0:
            self.add_link.show()
        else:
            self.add_link.hide()

        if not filters and not self.filtersWidgetsList:
            self.hide()
        else:
            self.show()

    def filterChanged(self, key, value):
        QTimer.singleShot(0, lambda: self.emit(SIGNAL('changeFilter'), key, value))

    def removeFilter(self, key):
        QTimer.singleShot(0, lambda: self.emit(SIGNAL('removeFilter'), key))

    def setCompatibility(self, compatibility):
        self.compatibility = compatibility
Example #6
0
class NavigationBar( QFrame ):
    " Navigation bar at the top of the editor (python only) "

    STATE_OK_UTD = 0        # Parsed OK, context up to date
    STATE_OK_CHN = 1        # Parsed OK, context changed
    STATE_BROKEN_UTD = 2    # Parsed with errors, context up to date
    STATE_BROKEN_CHN = 3    # Parsed with errors, context changed
    STATE_UNKNOWN = 4

    def __init__( self, editor, parent ):
        QFrame.__init__( self, parent )
        self.__editor = editor

        # It is always not visible at the beginning because there is no
        # editor content at the start
        self.setVisible( False )

        # There is no parser info used to display values
        self.__currentInfo = None
        self.__currentIconState = self.STATE_UNKNOWN
        self.__connected = False
        self.__path = []    # List of PathElement starting after the
                            # global scope

        self.__createLayout()

        # Create the update timer
        self.__updateTimer = QTimer( self )
        self.__updateTimer.setSingleShot( True )
        self.__updateTimer.timeout.connect( self.updateBar )

        # Connect to the change file type signal
        mainWindow = GlobalData().mainWindow
        editorsManager = mainWindow.editorsManagerWidget.editorsManager
        self.connect( editorsManager, SIGNAL( 'fileTypeChanged' ),
                      self.__onFileTypeChanged )
        return

    def getEditor( self ):
        " Provides the editor "
        return self.__editor

    def __connectEditorSignals( self ):
        " When it is a python file - connect to the editor signals "
        if self.__connected:
            return

        self.__editor.cursorPositionChanged.connect( self.__cursorPositionChanged )
        self.__editor.SCEN_CHANGE.connect( self.__onBufferChanged )
        self.__connected = True
        return

    def __disconnectEditorSignals( self ):
        " Disconnect the editor signals when the file is not a python one "
        if not self.__connected:
            return

        self.__editor.cursorPositionChanged.disconnect( self.__cursorPositionChanged )
        self.__editor.SCEN_CHANGE.disconnect( self.__onBufferChanged )
        self.__connected = False
        return

    def __createLayout( self ):
        " Creates the layout "
        self.setFixedHeight( 24 )
        self.__layout = QHBoxLayout( self )
        self.__layout.setMargin( 0 )
        self.__layout.setContentsMargins( 0, 0, 0, 0 )

        # Set the background color

        # Create info icon
        self.__infoIcon = QLabel()
        self.__layout.addWidget( self.__infoIcon )

        self.__globalScopeCombo = NavBarComboBox( self )
        self.__globalScopeCombo.jumpToLine.connect( self.__onJumpToLine )
        self.__layout.addWidget( self.__globalScopeCombo )

        self.__spacer = QWidget()
        self.__spacer.setSizePolicy( QSizePolicy.Expanding,
                                     QSizePolicy.Expanding )
        self.__layout.addWidget( self.__spacer )
        return

    def __updateInfoIcon( self, state ):
        " Updates the information icon "
        if state == self.__currentIconState:
            return

        if state == self.STATE_OK_UTD:
            self.__infoIcon.setPixmap( PixmapCache().getPixmap( 'nbokutd.png' ) )
            self.__infoIcon.setToolTip( "Context is up to date" )
            self.__currentIconState = self.STATE_OK_UTD
        elif state == self.STATE_OK_CHN:
            self.__infoIcon.setPixmap( PixmapCache().getPixmap( 'nbokchn.png' ) )
            self.__infoIcon.setToolTip( "Context is not up to date; will be updated on idle" )
            self.__currentIconState = self.STATE_OK_CHN
        elif state == self.STATE_BROKEN_UTD:
            self.__infoIcon.setPixmap( PixmapCache().getPixmap( 'nbbrokenutd.png' ) )
            self.__infoIcon.setToolTip( "Context might be invalid due to invalid python code" )
            self.__currentIconState = self.STATE_BROKEN_UTD
        else:
            # STATE_BROKEN_CHN
            self.__infoIcon.setPixmap( PixmapCache().getPixmap( 'nbbrokenchn.png' ) )
            self.__infoIcon.setToolTip( "Context might be invalid; will be updated on idle" )
            self.__currentIconState = self.STATE_BROKEN_CHN
        return

    def resizeEvent( self, event ):
        " Editor has resized "
        QFrame.resizeEvent( self, event )
        return

    def __onFileTypeChanged( self, fileName, uuid, newFileType ):
        " Triggered when a buffer content type has changed "

        if self.parent().getUUID() != uuid:
            return

        if newFileType not in [ Python3FileType, PythonFileType ] or \
           not Settings().showNavigationBar:
            self.__disconnectEditorSignals()
            self.__updateTimer.stop()
            self.__currentInfo = None
            self.setVisible( False )
            self.__currentIconState = self.STATE_UNKNOWN
            return

        # Update the bar and show it
        self.setVisible( True )
        self.updateBar()
        return

    def updateSettings( self ):
        " Called when navigation bar settings have been updated "
        if not Settings().showNavigationBar:
            self.__disconnectEditorSignals()
            self.__updateTimer.stop()
            self.__currentInfo = None
            self.setVisible( False )
        else:
            self.setVisible( True )
            self.updateBar()
        return

    def updateBar( self ):
        " Triggered when the timer is fired "
        self.__updateTimer.stop()  # just in case

        if self.parent().getFileType() not in [ Python3FileType,
                                                PythonFileType ]:
            return

        if not self.__connected:
            self.__connectEditorSignals()

        # Parse the buffer content
        self.__currentInfo = getBriefModuleInfoFromMemory( self.__editor.text() )

        # Decide what icon to use
        if self.__currentInfo.isOK:
            self.__updateInfoIcon( self.STATE_OK_UTD )
        else:
            self.__updateInfoIcon( self.STATE_BROKEN_UTD )

        # Calc the cursor context
        context = getContext( self.__editor, self.__currentInfo, True, False )

        # Display the context
        self.__populateGlobalScope()
        if context.length == 0:
            self.__globalScopeCombo.setCurrentIndex( -1 )
        else:
            index = self.__globalScopeCombo.findData( context.levels[ 0 ][ 0 ].line )
            self.__globalScopeCombo.setCurrentIndex( index )

        usedFromStore = 0
        index = 1
        while index < context.length:
            if len( self.__path ) < index:
                newPathItem = PathElement( self )
                self.__path.append( newPathItem )
                self.__layout.addWidget( newPathItem.icon )
                self.__layout.addWidget( newPathItem.combo )
                combo = newPathItem.combo
                combo.pathIndex = len( self.__path ) - 1
                combo.jumpToLine.connect( self.__onJumpToLine )
            else:
                self.__path[ index - 1 ].icon.setVisible( True )
                self.__path[ index - 1 ].combo.setVisible( True )
                combo = self.__path[ index - 1 ].combo
                combo.clear()

            # Populate the combo box
            self.__populateClassesAndFunctions( context.levels[ index - 1 ][ 0 ],
                                                combo )
            combo.setCurrentIndex( combo.findData( context.levels[ index ][ 0 ].line ) )
            index += 1
            usedFromStore += 1

        # it might need to have one more level with nothing selected
        if context.length > 0:
            if len( context.levels[ context.length - 1 ][ 0 ].functions ) > 0 or \
               len( context.levels[ context.length - 1 ][ 0 ].classes ) > 0:
                # Need to add a combo
                if len( self.__path ) <= usedFromStore:
                    newPathItem = PathElement( self )
                    self.__path.append( newPathItem )
                    self.__layout.addWidget( newPathItem.icon )
                    self.__layout.addWidget( newPathItem.combo )
                    combo = newPathItem.combo
                    combo.pathIndex = len( self.__path ) - 1
                    combo.jumpToLine.connect( self.__onJumpToLine )
                else:
                    self.__path[ index - 1 ].icon.setVisible( True )
                    self.__path[ index - 1 ].combo.setVisible( True )
                    combo = self.__path[ index - 1 ].combo
                    combo.clear()

                self.__populateClassesAndFunctions( context.levels[ context.length - 1 ][ 0 ],
                                                    combo )
                combo.setCurrentIndex( -1 )
                usedFromStore += 1

        # Hide extra components if so
        index = usedFromStore
        while index < len( self.__path ):
            self.__path[ index ].icon.setVisible( False )
            self.__path[ index ].combo.setVisible( False )
            index += 1

        # Make sure the spacer is the last item
        self.__layout.removeWidget( self.__spacer )
        self.__layout.addWidget( self.__spacer )
        return

    def __populateGlobalScope( self ):
        " Repopulates the global scope combo box "
        self.__globalScopeCombo.clear()

        self.__populateClassesAndFunctions( self.__currentInfo,
                                            self.__globalScopeCombo )

        if not Settings().navbarglobalsimports:
            return

        if len( self.__currentInfo.globals ) == 0 and \
           len( self.__currentInfo.imports ) == 0:
            return

        if self.__globalScopeCombo.count() != 0:
            self.__globalScopeCombo.insertSeparator(
                                self.__globalScopeCombo.count() )

        for glob in self.__currentInfo.globals:
            self.__globalScopeCombo.addItem( PixmapCache().getIcon( 'globalvar.png' ),
                                             glob.name, glob.line )
        for imp in self.__currentInfo.imports:
            self.__globalScopeCombo.addItem( PixmapCache().getIcon( 'imports.png' ),
                                             imp.name, imp.line )
        return

    @staticmethod
    def __populateClassesAndFunctions( infoObj, combo ):
        " Populates the given combo with classes and functions from the info object "
        for klass in infoObj.classes:
            combo.addItem( PixmapCache().getIcon( 'class.png' ),
                           klass.name, klass.line )
        for func in infoObj.functions:
            if func.isPrivate():
                icon = PixmapCache().getIcon( 'method_private.png' )
            elif func.isProtected():
                icon = PixmapCache().getIcon( 'method_protected.png' )
            else:
                icon = PixmapCache().getIcon( 'method.png' )

            combo.addItem( icon, func.name, func.line )
        return

    def __cursorPositionChanged( self, line, pos ):
        " Cursor position changed "
        self.__onNeedUpdate()
        return

    def __onBufferChanged( self ):
        " Buffer changed "
        self.__onNeedUpdate()
        return

    def __onNeedUpdate( self ):
        " Triggered to update status icon and to restart the timer "
        self.__updateTimer.stop()
        if self.__currentInfo.isOK:
            self.__updateInfoIcon( self.STATE_OK_CHN )
        else:
            self.__updateInfoIcon( self.STATE_BROKEN_CHN )
        self.__updateTimer.start( IDLE_TIMEOUT )
        return

    def __onJumpToLine( self, line ):
        " Triggered when it needs to jump to a line "
        self.__editor.gotoLine( line, 0 )
        self.__editor.setFocus()
        return

    def setFocusToLastCombo( self ):
        " Activates the last combo "
        if self.__currentInfo is None:
            return
        for index in xrange( len( self.__path ) - 1, -1, -1 ):
            if self.__path[ index ].combo.isVisible():
                self.__path[ index ].combo.setFocus()
                self.__path[ index ].combo.showPopup()
                return

        self.__globalScopeCombo.setFocus()
        self.__globalScopeCombo.showPopup()
        return

    def activateCombo( self, currentCombo, newIndex ):
        " Triggered when a neighbour combo should be activated "
        if newIndex == -1:
            if len( self.__path ) > 0:
                if self.__path[ 0 ].combo.isVisible():
                    currentCombo.hidePopup()
            self.__globalScopeCombo.setFocus()
            self.__globalScopeCombo.showPopup()
            return

        if newIndex >= len( self.__path ):
            # This is the most right one
            return

        if self.__path[ newIndex ].combo.isVisible():
            currentCombo.hidePopup()
            self.__path[ newIndex ].combo.setFocus()
            self.__path[ newIndex ].combo.showPopup()
        return
Example #7
0
class LineEdit(QLineEdit):
    inactiveText = QtDynamicProperty('inactiveText', unicode)
    widgetSpacing = QtDynamicProperty('widgetSpacing', int)

    def __init__(self, parent=None, contents=u""):
        QLineEdit.__init__(self, contents, parent)
        box_direction = QBoxLayout.RightToLeft if self.isRightToLeft(
        ) else QBoxLayout.LeftToRight
        self.inactiveText = u""
        self.left_widget = SideWidget(self)
        self.left_widget.resize(0, 0)
        self.left_layout = QHBoxLayout(self.left_widget)
        self.left_layout.setContentsMargins(0, 0, 0, 0)
        self.left_layout.setDirection(box_direction)
        self.left_layout.setSizeConstraint(QLayout.SetFixedSize)
        self.right_widget = SideWidget(self)
        self.right_widget.resize(0, 0)
        self.right_layout = QHBoxLayout(self.right_widget)
        self.right_layout.setContentsMargins(0, 0, 0, 0)
        self.right_layout.setDirection(box_direction)
        self.right_layout.addItem(
            QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum))
        self.widgetSpacing = 2
        self.left_widget.sizeHintChanged.connect(self._update_text_margins)
        self.right_widget.sizeHintChanged.connect(self._update_text_margins)

    @property
    def left_margin(self):
        return self.left_widget.sizeHint().width(
        ) + 2 * self.right_layout.spacing()

    @property
    def right_margin(self):
        return self.right_widget.sizeHint().width(
        ) + 2 * self.right_layout.spacing()

    def _update_text_margins(self):
        self.setTextMargins(self.left_margin, 0, self.right_margin, 0)
        self._update_side_widget_locations()

    def _update_side_widget_locations(self):
        option = QStyleOptionFrameV2()
        self.initStyleOption(option)
        spacing = self.right_layout.spacing()
        text_rect = self.style().subElementRect(QStyle.SE_LineEditContents,
                                                option, self)
        text_rect.adjust(spacing, 0, -spacing, 0)
        mid_height = text_rect.center().y() + 1 - (
            text_rect.height() % 2)  # need -1 correction for odd heights -Dan
        if self.left_layout.count() > 0:
            left_height = mid_height - self.left_widget.height() / 2
            left_width = self.left_widget.width()
            if left_width == 0:
                left_height = mid_height - self.left_widget.sizeHint().height(
                ) / 2
            self.left_widget.move(text_rect.x(), left_height)
        text_rect.setX(self.left_margin)
        text_rect.setY(mid_height -
                       self.right_widget.sizeHint().height() / 2.0)
        text_rect.setHeight(self.right_widget.sizeHint().height())
        self.right_widget.setGeometry(text_rect)

    def event(self, event):
        event_type = event.type()
        if event_type == QEvent.LayoutDirectionChange:
            box_direction = QBoxLayout.RightToLeft if self.isRightToLeft(
            ) else QBoxLayout.LeftToRight
            self.left_layout.setDirection(box_direction)
            self.right_layout.setDirection(box_direction)
        elif event_type == QEvent.DynamicPropertyChange:
            property_name = event.propertyName()
            if property_name == 'widgetSpacing':
                self.left_layout.setSpacing(self.widgetSpacing)
                self.right_layout.setSpacing(self.widgetSpacing)
                self._update_text_margins()
            elif property_name == 'inactiveText':
                self.update()
        return QLineEdit.event(self, event)

    def resizeEvent(self, event):
        self._update_side_widget_locations()
        QLineEdit.resizeEvent(self, event)

    def paintEvent(self, event):
        QLineEdit.paintEvent(self, event)
        if not self.hasFocus() and not self.text() and self.inactiveText:
            options = QStyleOptionFrameV2()
            self.initStyleOption(options)
            text_rect = self.style().subElementRect(QStyle.SE_LineEditContents,
                                                    options, self)
            text_rect.adjust(self.left_margin + 2, 0, -self.right_margin, 0)
            painter = QPainter(self)
            painter.setPen(self.palette().brush(QPalette.Disabled,
                                                QPalette.Text).color())
            painter.drawText(text_rect, Qt.AlignLeft | Qt.AlignVCenter,
                             self.inactiveText)

    def addHeadWidget(self, widget):
        if self.isRightToLeft():
            self.right_layout.insertWidget(1, widget)
        else:
            self.left_layout.addWidget(widget)

    def addTailWidget(self, widget):
        if self.isRightToLeft():
            self.left_layout.addWidget(widget)
        else:
            self.right_layout.insertWidget(1, widget)

    def removeWidget(self, widget):
        self.left_layout.removeWidget(widget)
        self.right_layout.removeWidget(widget)
        widget.hide()
Example #8
0
class FragmentFrame(QFrame):

    # Signals:
    SIG_REMOVE_FRAGMENT = 'removeFragment()'
    # Fragment edition switch
    FRAGMENT_EDITION = False

    view = None
    fetcher = None

    def __init__(self, fragment, args, client, animation_manager=None, parent=None, has_menu_pos=True):
        """
            @fragment [Fragment] user_settings.Fragment object to describe fragment
            @client [RpcdClient] client
            @parent [QWidget] parent widget
        """

        #assert isinstance(client, RpcdClient)
        assert frag_types.has_key(fragment.type)

        QFrame.__init__(self, parent)

        self.animation_manager = animation_manager
        self.has_menu_pos = has_menu_pos
        self.fragment = fragment
        self.fetcher = frag_types[fragment.type].fetcher(fragment, args, client)
        self.connect(self.fetcher, SIGNAL(self.fetcher.ERROR_SIGNAL), self.errorHandler)
        self.window = parent
        self.cumulative_mode = False
        self.interval = Interval('daily')

        self.setFragmentColor()

        self.setFrameShadow(QFrame.Sunken)
        self.setFrameShape(QFrame.StyledPanel)
        self.setContextMenuPolicy(Qt.ActionsContextMenu)

        self.toolspace = None
        self.vbox = QVBoxLayout()
#        self.vbox.setContentsMargins(9,0,9,9)
        self.vbox.setContentsMargins(9,0,9,0)
        self.setLayout(self.vbox)

        self.stacked = QStackedWidget(self)
        updating_label = QLabel("<img src=\":/icons/refresh.png\" /><br />%s" % self.tr("Updating..."))
        updating_label.setAlignment(Qt.AlignHCenter|Qt.AlignVCenter)
        self.stacked.addWidget(updating_label)

        # Create all actions for the rightclick context menu
        self.action_list = []
        self.switch_actions = {}

        self.viewlayout = QHBoxLayout()
        self.viewlayout.setContentsMargins(0,0,0,0)
        self.viewlayout.setSpacing(2)
        self.viewlayout.addStretch()
        widget = QWidget()
        widget.setLayout(self.viewlayout)
        self.vbox.addWidget(widget)

        self.line = QFrame(self)
        self.line.setFrameShape(QFrame.HLine)
        self.line.setFrameShadow(QFrame.Sunken)
        self.line.setObjectName("line")

        # Menu to choose position of fragment
        if self.has_menu_pos:
            self.pos_menu = QMenu(tr('Position'), self)
#        self.pos_action = QAction(tr('Position'), self.pos_menu)

        def make_lambda(l):
            """ usefull to create the lambda function with a copied parameter. or it'll bug """
            return lambda: QTimer.singleShot(0, lambda: self.setView(l))

        self.buttons = []

        button = QToolButton()
        button.visible = True
        button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Ignored)
        button.setMinimumSize(0,16)
        button.setIcon(QIcon(":/icons/refresh.png"))
        button.setFixedHeight(16)
        button.setToolTip(tr("Refresh"))
        self.connect(button, SIGNAL("clicked()"), self.updateData)
        self.viewlayout.addWidget(button)
        self.buttons.append(button)

        # All of the views available for this kind of fragment.
        if len(frag_types[fragment.type].views) > 1:
            for label in frag_types[fragment.type].views:

                try:
                    item_name = views_list_label[label]
                except KeyError:
                    continue

                # item_name returns a unicode string, but PyQT (Qt 4.2.1) won't convert it to a char*
                # unless we convert it to a non-unicode string ....
                action = QAction(QIcon(':/icons/%s' % label),
                                 tr("Switch to %s") % self.tr(unicode(item_name)), self)
                self.connect(action, SIGNAL("triggered()"), make_lambda(label))
                self.action_list += [action]
                self.switch_actions[label] = action

                button = QToolButton()
                button.visible = True
                button.setBackgroundRole(QPalette.Button)
                button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Minimum)
                button.setMinimumSize(0,16)
                button.setFixedHeight(16)
                button.setIcon(QIcon(":/icons/%s" % label))
                button.setToolTip(tr("Switch to %s") % self.tr(unicode(item_name)))
                self.connect(button, SIGNAL("clicked()"), make_lambda(label))
                self.viewlayout.addWidget(button)
                self.buttons.append(button)

            # Separator
            action = QAction(self)
            action.setSeparator(True)
            self.action_list += [action]

        # Refresh
        action = QAction(QIcon(':/icons/refresh.png'), tr('Refresh'), self)
        self.connect(action, SIGNAL('triggered()'), self.updateData)
        self.action_list += [action]
        if self.FRAGMENT_EDITION:
            # Edit
            action = QAction(QIcon(':/icons/edit.png'), tr('Edit this fragment...'), self)
            self.connect(action, SIGNAL('triggered()'), self.editFragment)
            self.action_list += [action]
            # Delete
            action = QAction(QIcon(':/icons/moins.png'), tr('Delete this fragment'), self)
            self.connect(action, SIGNAL('triggered()'), self.removeFragment)
            self.action_list += [action]

        self.setView(fragment.view, update=False)

        self.setAcceptDrops(True)
        self.pos = -1

    def mouseMoveEvent(self, event):
        if event.buttons() != Qt.LeftButton:
            return

        mimeData = QMimeData()
        if self.pos == -1:
            return
        mimeData.setData("splitter/fragment", QByteArray.number(self.pos))
        drag = QDrag(self)
        drag.setMimeData(mimeData)
        drag.setHotSpot(event.pos() - self.rect().topLeft())

        dropAction = drag.start(Qt.MoveAction)

        if dropAction == Qt.MoveAction:
            self.close()

    def dragEnterEvent(self, event):
        event.accept()

    def dropEvent(self, event):

        data = event.mimeData().data("splitter/fragment").toInt()
        if not data[1]:
            return

        frame_pos = data[0]

        if frame_pos == self.pos:
            return

        event.setDropAction(Qt.MoveAction)
        event.accept()

        self.window.changedPositionFragment(self.pos, frame_pos)

#    def __del__(self):
#        self.destructor()

    def destructor(self):
        if self.view:
            self.freeView()
        if self.fetcher:
            self.disconnect(self.fetcher, SIGNAL(self.fetcher.ERROR_SIGNAL), self.errorHandler)
            self.fetcher.destructor()
            self.fetcher = None

    def getView(self):
        return self.view

    def getFragment(self):
        return self.fragment

    def setCumulative(self, cumulative):
        self.cumulative_mode = cumulative
        if self.view:
            self.view.setCumulative(cumulative)

    def setInterval(self, interval):
        self.interval = interval
        if self.view:
            self.view.setInterval(interval)

    def setFragmentColor(self):

        # XXX: We have to put classes which we colorize, because some objects bug
        # when we try to put a background on them. For example, do NOT colorize
        # QScrollBar, it'll not work anyway:
        # http://doc.trolltech.com/4.4/stylesheet-examples.html#customizing-qscrollbar
        # "The QScrollBar can be styled using its subcontrols like handle,
        # add-line, sub-line, and so on. Note that if one property or
        # sub-control is customized, all the other properties or sub-controls
        # must be customized as well."
        self.setStyleSheet("""
         QFrame, QPushButton, QTableCornerButton, QAbstractSpinBox, QLineEdit {
             background-color: #%06X;
         }

         """ % self.fragment.background_color)

    def editFragment(self):
        if AddFragDialog(self.window, self.fragment, self).run():
            self.setFragmentColor()
            self.setView(self.fragment.view, update=True)

    def removeFragment(self):
        reply = QMessageBox.question(self, self.tr("Delete a fragment"),
                                           unicode(self.tr('Are you sure to delete the fragment "%s" from the view?')) % self.fragment.title,
                                           QMessageBox.Yes|QMessageBox.No)

        if reply == QMessageBox.Yes:
            QTimer.singleShot(0, self._removeFragment_emit)

    def _removeFragment_emit(self):
        self.emit(SIGNAL(self.SIG_REMOVE_FRAGMENT))

#    def resizeEvent(self, event):
#        QFrame.resizeEvent(self, event)
#        self.view.resize(event.size())

    def freeView(self):
        if self.view:
            self.view.destructor()
            self.disconnect(self.view, SIGNAL('open_page'), self._open_page)
            self.disconnect(self.view, SIGNAL('add_filter'), self._add_filter)
            self.disconnect(self.view, SIGNAL('updating'), self._show_animation)
            self.disconnect(self.view, SIGNAL('updated'), self._show_view)
            self.disconnect(self.view, SIGNAL('EAS_Message'), self.EAS_SendMessage)
            self.stacked.removeWidget(self.view)
            self.vbox.removeWidget(self.stacked)
            self.vbox.removeWidget(self.line)
            self.view.setParent(None)
            self.view.hide()
            self.view.deleteLater()
        if self.toolspace:
            self.viewlayout.removeWidget(self.toolspace)
            self.toolspace.setParent(None)
            self.toolspace.hide()
            self.toolspace.deleteLater()

        if self.view and hasattr(self.view, "uuid"):
            if self.animation_manager:
                self.animation_manager.remove(self.view.uuid)
            self.view = None
        self.toolspace = None

    def setView(self, label, update=True):

        # If there isn't any view for this fragment, use the first available view
        # of this kind of fragment.
        if not label:
            assert frag_types.has_key(self.fragment.type)
            assert len(frag_types[self.fragment.type].views) > 0

            label = frag_types[self.fragment.type].views[0]


        for button in self.buttons:
            if label in button.toolTip():
                button.visible = False
            else:
                if not button.isEnabled():
                    button.visible = True
#        assert views_list.has_key(label)

        self.freeView()

        # Create the view object.
        self.view = views_list[label](self.fetcher, self)
        if label == "histo" or label == 'pie':
            self.view.is_graphics_view = True
            if label == 'histo':
                self.view.chart_type = BARCHART
            else:
                self.view.chart_type = PIECHART
        else:
            self.view.is_graphics_view = False

        if label != "error":
            self.connect(self, SIGNAL("closed"), self.setClosed)
            if self.animation_manager:
                if self.view.is_graphics_view:
                    self.connect(self.view, SIGNAL("animation_done(QString)"), self, SIGNAL("animation_done(QString)"))
                self.animation_manager.addView(self.view)

        self.connect(self.view, SIGNAL("showButtons"), self.showButtonsSlot)
        self.connect(self.view, SIGNAL("autoRefresh"), self.updateData)
                
        self.view.setCumulative(self.cumulative_mode)
        self.view.setInterval(self.interval)
        self.stacked.insertWidget(0, self.view)
        self._show_view()
        self.connect(self.view, SIGNAL('open_page'), self._open_page)
        self.connect(self.view, SIGNAL('add_filter'), self._add_filter)
        self.connect(self.view, SIGNAL('updating'), self._show_animation)
        self.connect(self.view, SIGNAL('updated'), self._show_view)
        self.connect(self.view, SIGNAL('EAS_Message'), self.EAS_SendMessage)

        # Set some features if there are available on each or each type of widget.
        if hasattr(self.view, 'setFrameShape'):
            self.view.setFrameShape(QFrame.NoFrame)
        self.view.setContextMenuPolicy(Qt.ActionsContextMenu)

        # All views can ask me to display a toolspace (a widget with all kind of
        # things in).
        self.view.title.setText(self.view.getTitle())
        self.fragment.view = label

        self.toolspace = self.view.getToolspace()
        self.viewlayout.insertWidget(0, self.toolspace)
        self.vbox.addWidget(self.line)
        self.vbox.addWidget(self.stacked)

        # Set the new menu.
        for action in self.actions():
            self.removeAction(action)

        for view_label, action in self.switch_actions.items():
            action.setEnabled(view_label != self.fragment.view)

        for action in self.action_list:
            self.addAction(action)
            self.view.addAction(action)

        # Add view's eventual menu.
        view_actions = self.view.getActions()
        if view_actions:
            separator = QAction(self)
            separator.setSeparator(True)
            view_actions = [separator] + view_actions

            for action in view_actions:
                self.view.addAction(action)
                self.addAction(action)

        if self.has_menu_pos:
            self.view.addAction(self.pos_menu.menuAction())
            self.addAction(self.pos_menu.menuAction())

        if update:
            self.updateData()

    def setClosed(self):
        if self.view:
            self.view.setClosed()
        self.destructor()

    def errorHandler(self, e):
        """ This method is called when fetcher raises an error. """

        # Store error in fragment, and the ErrorFragmentView will able to
        # load it

        error = exceptionAsUnicode(e)
        self.fragment.error = error

        # We keep last view in the fragment, to prevent setView() method to
        # put 'error' in the fragment.view string attribute.
        last_view = self.fragment.view
        self.setView('error', update=False) # load the error fragment
        self.fragment.view = last_view

    def showButtonsSlot(self):
        for button in self.buttons:
            if hasattr(button, 'visible'):
                if button.visible:
                    button.setEnabled(True)
                else:
                    button.setEnabled(False)

    def updateData(self):

        if hasattr(self.fragment, 'error'):
            self.setView(self.fragment.view, update=False)
            del self.fragment.error

        for button in self.buttons:
            button.setEnabled(False)

        self.view.requestData()

    def _open_page(self, *args, **kwargs):
        self.emit(SIGNAL('open_page'), *args, **kwargs)

    def _add_filter(self, *args, **kwargs):
        self.emit(SIGNAL('add_filter'), *args, **kwargs)

    def EAS_SendMessage(self, *args, **kwargs):
        self.emit(SIGNAL('EAS_Message'), *args, **kwargs)

    def _show_animation(self):
        self.stacked.setCurrentIndex(1)

    def _show_view(self):
        self.stacked.setCurrentIndex(0)
Example #9
0
class MainWindow(QMainWindow):
    '''
    classdocs
    '''
    
    widgetList = list()
    slideIterator = 0

    def __init__(self, parent=None):
        '''
        Constructor
        '''
        super(MainWindow, self).__init__(parent)
        
        self.setStyleSheet("background: #000;")

        defaultWidget = QWidget(self)
        self.defaultWidgetLayout = QHBoxLayout()
        self.defaultWidgetLayout.setMargin(0)
        defaultWidget.setLayout(self.defaultWidgetLayout)
        self.setCentralWidget(defaultWidget)
        
        # load widgets for display
        self.widgetList.append(Slideshow(self))
        self.widgetList.append(Calendar(self))
        self.widgetList.append(VoivoiShow(self))
        
        # setup widgets
        self.widgetList[0].setup("/Users/fluetke/Projekte/FabLab/fablab-info/src/testImg", 5)
        self.widgetList[1].setup("https://www.google.com/calendar/ical/3qrstaor19f0airf92rvf5qn4g%40group.calendar.google.com/public/basic.ics")
        self.widgetList[2].setup("127.0.0.1","test","secret",2)

        #set slide timer
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.nextSlide)
    
    def run(self,interval):
        self.timer.start(interval)
        self.defaultWidgetLayout.addWidget(self.widgetList[self.slideIterator%len(self.widgetList)])
        self.nextSlide()
        #self.widgetList[self.slideIterator%len(self.widgetList)].run()
        #self.slideIterator+=1
    
    # TODO: fix problems with slide changes causing slides to fall out of scope
    def nextSlide(self):
        oldWid = None
        
        if len(self.widgetList) > 1:
        
            oldWid = self.widgetList[(self.slideIterator-1)%len(self.widgetList)]
            print(oldWid)
    
        if oldWid != None:
            oldWid.stop()
            oldWid.hide()
            if self.defaultWidgetLayout.removeWidget(oldWid):
                oldWid.setParent(self)
                   
            qDebug("Changing Slides")
            tmpWidget = self.widgetList[self.slideIterator%len(self.widgetList)]
            self.defaultWidgetLayout.addWidget(tmpWidget)
            tmpWidget.run()
            tmpWidget.show()

            self.slideIterator = 0 if self.slideIterator==len(self.widgetList)-1 else self.slideIterator+1