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)
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()
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()
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()
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
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
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()
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)
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