def _loadRecentFileActions(self): for i in range(self.MaxRecentFiles): action = QAction(self) action.setVisible(False) self.connect(action, SIGNAL("triggered()"), self.openRecent) self.recentFileActions.append(action) self._updateRecentFileActions()
def __init__(self): QMainWindow.__init__(self) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.loadTranslations() self.settings = QSettings("LVK Inc", "DataCenters") self.resourcesGraphEditor = ResourcesGraphEditor() self.demandGraphEditor = DemandGraphEditor() self.randomDialog = RandomDemandDialog() self.Vis = Vis() self.graphvis = GraphVis(self) self.demands = {} self.project = Project() # TODO: Captain, we have a problem! # For some reason, in Python 2.7 QSettings converts dicts to QVariant # So the ini file is undecypherable # This works fine in Python 3.2 by the way #if self.settings.value("vis"): #self.Vis.canvas.settings = self.settings.value("vis") #self.graphvis.settings = self.settings.value("graphVis") self.settingsDialog = SettingsDialog(self.Vis.canvas.settings, self.graphvis.settings) self.settingsDialog.ui.backup.setChecked( self.settings.value("backup").toBool()) self.settingsDialog.ui.autosave.setChecked( self.settings.value("autosave").toBool()) self.settingsDialog.ui.interval.setValue( self.settings.value("interval").toInt()[0]) i = 0 for s in self.languages: self.settingsDialog.ui.languages.addItem(s) if s == str(self.settings.value("language").toString()): self.settingsDialog.ui.languages.setCurrentIndex(i) i += 1 self.resourcesGraphEditor.setData(self.project.resources) for i in range(self.MaxRecentFiles): a = QAction(self) a.setVisible(False) a.setEnabled(False) if i <= 9: a.setShortcut(QKeySequence(self.tr("Alt+") + str(i + 1))) QObject.connect(a, SIGNAL("triggered()"), self.OpenRecentFile) self.ui.menuFile.insertAction(self.ui.actionExit, a) self.recentFileActions.append(a) self.UpdateRecentFileActions() self.basename = self.windowTitle() self.demandGraphEditor.demand_changed.connect(self.demandChanged) self.backupTimer = QTimer() self.backupTimer.setInterval(60000) self.backupTimer.setSingleShot(False) QObject.connect(self.backupTimer, SIGNAL("timeout()"), self.Backup) self.autosaveTimer = QTimer() self.autosaveTimer.setInterval(60000) self.autosaveTimer.setSingleShot(False) QObject.connect(self.autosaveTimer, SIGNAL("timeout()"), self.Autosave) self.Translate( str(self.settings.value("language", "English").toString())) self.projFilter = self.tr("Data centers projects (*.dcxml)") self.setWindowTitle(self.tr("Untitled") + " - " + self.basename) self.loadPlugins()
def loadpages(self, pages): def safe_connect(method, to): try: method.connect(to) except AttributeError: pass for PageClass in pages: action = QAction(self.menutoolbar) text = PageClass.title.ljust(13) action.setIconText(text) action.setIcon(QIcon(PageClass.icon)) action.setCheckable(True) if PageClass.projectpage: action.setVisible(False) self.projectbuttons.append(action) self.menutoolbar.insertAction(self.spaceraction, action) else: self.menutoolbar.insertAction(self.actionProject, action) iface = RoamInterface(RoamEvents, GPS, self) pagewidget = PageClass(iface, self) safe_connect(RoamEvents.selectionchanged, pagewidget.selection_changed) safe_connect(RoamEvents.projectloaded, pagewidget.project_loaded) pageindex = self.stackedWidget.insertWidget(-1, pagewidget) action.setProperty('page', pageindex) self.pluginactions.append(action) self.menuGroup.addAction(action)
class Tool(object): def __init__(self, name, help_link="", icon=None, enabled=True, checkable=False, popup_menu=False): super(Tool, self).__init__() self.__icon = icon self.__name = name self.__parent = None self.__enabled = enabled self.__checkable = checkable self.__help_link = help_link self.__is_popup_menu = popup_menu self.__action = QAction(self.getIcon(), self.getName(), None) self.__action.setIconText(self.getName()) self.__action.setEnabled(self.isEnabled()) self.__action.setCheckable(checkable) self.__action.triggered.connect(self.trigger) HelpCenter.addHelpToAction(self.__action, self.getHelpLink()) def getIcon(self): return self.__icon def getName(self): return self.__name def trigger(self): raise NotImplementedError() def setParent(self, parent): self.__parent = parent self.__action.setParent(parent) def parent(self): return self.__parent def isEnabled(self): return self.__enabled def getHelpLink(self): return self.__help_link def getAction(self): return self.__action def setVisible(self, visible): self.__action.setVisible(visible) def setEnabled(self, enabled): self.__action.setEnabled(enabled) def isPopupMenu(self): return self.__is_popup_menu
def __init__(self): QMainWindow.__init__(self) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.loadTranslations() self.settings = QSettings("LVK Inc", "DataCenters") self.resourcesGraphEditor = ResourcesGraphEditor() self.demandGraphEditor = DemandGraphEditor() self.randomDialog = RandomDemandDialog() self.Vis = Vis() self.graphvis = GraphVis(self) self.demands = {} self.project = Project() # TODO: Captain, we have a problem! # For some reason, in Python 2.7 QSettings converts dicts to QVariant # So the ini file is undecypherable # This works fine in Python 3.2 by the way #if self.settings.value("vis"): #self.Vis.canvas.settings = self.settings.value("vis") #self.graphvis.settings = self.settings.value("graphVis") self.settingsDialog = SettingsDialog(self.Vis.canvas.settings, self.graphvis.settings) self.settingsDialog.ui.backup.setChecked(self.settings.value("backup").toBool()) self.settingsDialog.ui.autosave.setChecked(self.settings.value("autosave").toBool()) self.settingsDialog.ui.interval.setValue(self.settings.value("interval").toInt()[0]) i = 0 for s in self.languages: self.settingsDialog.ui.languages.addItem(s) if s == str(self.settings.value("language").toString()): self.settingsDialog.ui.languages.setCurrentIndex(i) i += 1 self.resourcesGraphEditor.setData(self.project.resources) for i in range(self.MaxRecentFiles): a = QAction(self) a.setVisible(False) a.setEnabled(False) if i <= 9: a.setShortcut(QKeySequence(self.tr("Alt+") + str(i + 1))) QObject.connect(a, SIGNAL("triggered()"), self.OpenRecentFile); self.ui.menuFile.insertAction(self.ui.actionExit, a) self.recentFileActions.append(a) self.UpdateRecentFileActions() self.basename = self.windowTitle() self.demandGraphEditor.demand_changed.connect(self.demandChanged) self.backupTimer = QTimer() self.backupTimer.setInterval(60000) self.backupTimer.setSingleShot(False) QObject.connect(self.backupTimer, SIGNAL("timeout()"), self.Backup) self.autosaveTimer = QTimer() self.autosaveTimer.setInterval(60000) self.autosaveTimer.setSingleShot(False) QObject.connect(self.autosaveTimer, SIGNAL("timeout()"), self.Autosave) self.Translate(str(self.settings.value("language", "English").toString())) self.projFilter = self.tr("Data centers projects (*.dcxml)") self.setWindowTitle(self.tr("Untitled") + " - " + self.basename) self.loadPlugins()
def create_recentFileActs(self): """ Create Actions to open recent files """ self.recentFileActs = deque() for i in range(MAX_RECENT_FILES): a = QAction(self) a.setVisible(False) QtCore.QObject.connect(a, QtCore.SIGNAL(_fromUtf8("triggered()")), self.on_open_menu_file) self.menuRecent.addAction(a) self.recentFileActs.append(a)
def __createLayout( self ): " Creates the toolbar and layout " # Buttons printButton = QAction( PixmapCache().getIcon( 'printer.png' ), 'Print', self ) printButton.triggered.connect( self.__onPrint ) printButton.setEnabled( False ) printButton.setVisible( False ) printPreviewButton = QAction( PixmapCache().getIcon( 'printpreview.png' ), 'Print preview', self ) printPreviewButton.triggered.connect( self.__onPrintPreview ) printPreviewButton.setEnabled( False ) printPreviewButton.setVisible( False ) spacer = QWidget() spacer.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Expanding ) self.lineCounterButton = QAction( PixmapCache().getIcon( 'linecounter.png' ), 'Line counter', self ) self.lineCounterButton.triggered.connect( self.onLineCounter ) # The toolbar toolbar = QToolBar( self ) toolbar.setOrientation( Qt.Vertical ) toolbar.setMovable( False ) toolbar.setAllowedAreas( Qt.RightToolBarArea ) toolbar.setIconSize( QSize( 16, 16 ) ) toolbar.setFixedWidth( 28 ) toolbar.setContentsMargins( 0, 0, 0, 0 ) toolbar.addAction( printPreviewButton ) toolbar.addAction( printButton ) toolbar.addWidget( spacer ) toolbar.addAction( self.lineCounterButton ) self.__importsBar = ImportListWidget( self.__viewer ) self.__importsBar.hide() hLayout = QHBoxLayout() hLayout.setContentsMargins( 0, 0, 0, 0 ) hLayout.setSpacing( 0 ) hLayout.addWidget( self.__viewer ) hLayout.addWidget( toolbar ) self.setLayout( hLayout ) return
def _NH_SIPAccountManagerDidAddAccount(self, notification): account = notification.data.account action = QAction(account.id if account is not BonjourAccount() else u'Bonjour', None) action.setEnabled(True if account is not BonjourAccount() else BonjourAccount.mdns_available) action.setCheckable(True) action.setChecked(account.enabled) action.setData(account) action.triggered.connect(partial(self._AH_AccountActionTriggered, action)) self.accounts_menu.addAction(action) action = QAction(self.mwi_icons[0], account.id, None) action.setVisible(False if account is BonjourAccount() else account.enabled and account.message_summary.enabled) action.setEnabled(False if account is BonjourAccount() else account.voicemail_uri is not None) action.setData(account) action.triggered.connect(partial(self._AH_VoicemailActionTriggered, action)) self.voicemail_menu.addAction(action)
def __createLayout(self): " Creates the toolbar and layout " # Buttons printButton = QAction(PixmapCache().getIcon('printer.png'), 'Print', self) printButton.triggered.connect(self.__onPrint) printButton.setEnabled(False) printButton.setVisible(False) printPreviewButton = QAction(PixmapCache().getIcon('printpreview.png'), 'Print preview', self) printPreviewButton.triggered.connect(self.__onPrintPreview) printPreviewButton.setEnabled(False) printPreviewButton.setVisible(False) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.lineCounterButton = QAction( PixmapCache().getIcon('linecounter.png'), 'Line counter', self) self.lineCounterButton.triggered.connect(self.onLineCounter) # The toolbar toolbar = QToolBar(self) toolbar.setOrientation(Qt.Vertical) toolbar.setMovable(False) toolbar.setAllowedAreas(Qt.RightToolBarArea) toolbar.setIconSize(QSize(16, 16)) toolbar.setFixedWidth(28) toolbar.setContentsMargins(0, 0, 0, 0) toolbar.addAction(printPreviewButton) toolbar.addAction(printButton) toolbar.addWidget(spacer) toolbar.addAction(self.lineCounterButton) self.__importsBar = ImportListWidget(self.__viewer) self.__importsBar.hide() hLayout = QHBoxLayout() hLayout.setContentsMargins(0, 0, 0, 0) hLayout.setSpacing(0) hLayout.addWidget(self.__viewer) hLayout.addWidget(toolbar) self.setLayout(hLayout) return
class MapWidget(Ui_CanvasWidget, QMainWindow): def __init__(self, parent=None): super(MapWidget, self).__init__(parent) self.setupUi(self) self.firstshow = True self.layerbuttons = [] self.editfeaturestack = [] self.lastgpsposition = None self.project = None self.gps = None self.gpslogging = None self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas)) self.canvas.setCanvasColor(Qt.white) self.canvas.enableAntiAliasing(True) self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor) if hasattr(self.canvas, 'setParallelRenderingEnabled'): self.canvas.setParallelRenderingEnabled(True) pal = QgsPalLabeling() self.canvas.mapRenderer().setLabelingEngine(pal) self.canvas.setFrameStyle(QFrame.NoFrame) self.editgroup = QActionGroup(self) self.editgroup.setExclusive(True) self.editgroup.addAction(self.actionPan) self.editgroup.addAction(self.actionZoom_In) self.editgroup.addAction(self.actionZoom_Out) self.editgroup.addAction(self.actionInfo) self.actionGPS = GPSAction(":/icons/gps", self.canvas, self) self.projecttoolbar.addAction(self.actionGPS) gpsspacewidget= QWidget() gpsspacewidget.setMinimumWidth(30) gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.topspaceraction = self.projecttoolbar.insertWidget(self.actionGPS, gpsspacewidget) self.dataentryselection = QAction(self.projecttoolbar) self.dataentryaction = self.projecttoolbar.insertAction(self.topspaceraction, self.dataentryselection) self.dataentryselection.triggered.connect(self.select_data_entry) self.marker = GPSMarker(self.canvas) self.marker.hide() self.currentfeatureband = QgsRubberBand(self.canvas) self.currentfeatureband.setIconSize(20) self.currentfeatureband.setWidth(10) self.currentfeatureband.setColor(QColor(186, 93, 212, 76)) self.gpsband = QgsRubberBand(self.canvas) self.gpsband.setColor(QColor(0, 0, 212, 76)) self.gpsband.setWidth(5) RoamEvents.editgeometry.connect(self.queue_feature_for_edit) RoamEvents.selectioncleared.connect(self.clear_selection) RoamEvents.selectionchanged.connect(self.highlight_selection) RoamEvents.featureformloaded.connect(self.feature_form_loaded) self.connectButtons() def init_qgisproject(self, doc): parser = ProjectParser(doc) canvasnode = parser.canvasnode self.canvas.freeze() self.canvas.mapRenderer().readXML(canvasnode) self.canvaslayers = parser.canvaslayers() self.canvas.setLayerSet(self.canvaslayers) #red = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorRedPart", 255 )[0]; #green = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorGreenPart", 255 )[0]; #blue = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorBluePart", 255 )[0]; #color = QColor(red, green, blue); #self.canvas.setCanvasColor(color) self.canvas.updateScale() return self.canvas.mapRenderer().destinationCrs() def showEvent(self, *args, **kwargs): if self.firstshow: self.canvas.refresh() self.canvas.repaint() self.firstshow = False def feature_form_loaded(self, form, feature, project, editmode): self.currentfeatureband.setToGeometry(feature.geometry(), form.QGISLayer) def highlight_selection(self, results): self.clear_selection() for layer, features in results.iteritems(): band = self.selectionbands[layer] band.setColor(QColor(255, 0, 0, 200)) band.setIconSize(20) band.setWidth(2) band.setBrushStyle(Qt.NoBrush) band.reset(layer.geometryType()) for feature in features: band.addGeometry(feature.geometry(), layer) def highlight_active_selection(self, layer, feature, features): self.clear_selection() self.highlight_selection({layer: features}) self.currentfeatureband.setToGeometry(feature.geometry(), layer) def clear_selection(self): # Clear the main selection rubber band self.currentfeatureband.reset() # Clear the rest for band in self.selectionbands.itervalues(): band.reset() self.editfeaturestack = [] def queue_feature_for_edit(self, form, feature): def trigger_default_action(): for action in self.projecttoolbar.actions(): if action.property('dataentry') and action.isdefault: action.trigger() break self.editfeaturestack.append((form, feature)) self.load_form(form) trigger_default_action() def clear_temp_objects(self): def clear_tool_band(): """ Clear the rubber band of the active tool if it has one """ tool = self.canvas.mapTool() try: tool.clearBand() except AttributeError: # No clearBand method found, but that's cool. pass self.currentfeatureband.reset() clear_tool_band() def settings_updated(self, settings): self.actionGPS.updateGPSPort() gpslogging = settings.get('gpslogging', True) if self.gpslogging: self.gpslogging.logging = gpslogging def set_gps(self, gps, logging): self.gps = gps self.gpslogging = logging self.gps.gpsposition.connect(self.gps_update_canvas) self.gps.firstfix.connect(self.gps_first_fix) self.gps.gpsdisconnected.connect(self.gps_disconnected) def gps_update_canvas(self, position, gpsinfo): # Recenter map if we go outside of the 95% of the area if self.gpslogging.logging: self.gpsband.addPoint(position) self.gpsband.show() if roam.config.settings.get('gpscenter', True): if not self.lastgpsposition == position: self.lastposition = position rect = QgsRectangle(position, position) extentlimt = QgsRectangle(self.canvas.extent()) extentlimt.scale(0.95) if not extentlimt.contains(position): self.zoom_to_location(position) self.marker.show() self.marker.setCenter(position) def gps_first_fix(self, postion, gpsinfo): zoomtolocation = roam.config.settings.get('gpszoomonfix', True) if zoomtolocation: self.canvas.zoomScale(1000) self.zoom_to_location(postion) def zoom_to_location(self, position): rect = QgsRectangle(position, position) self.canvas.setExtent(rect) self.canvas.refresh() def gps_disconnected(self): self.marker.hide() def select_data_entry(self): def showformerror(form): pass def actions(): for form in self.project.forms: action = form.createuiaction() valid, failreasons = form.valid if not valid: roam.utils.warning("Form {} failed to load".format(form.label)) roam.utils.warning("Reasons {}".format(failreasons)) action.triggered.connect(partial(showformerror, form)) else: action.triggered.connect(partial(self.load_form, form)) yield action formpicker = PickActionDialog(msg="Select data entry form") formpicker.addactions(actions()) formpicker.exec_() def project_loaded(self, project): self.project = project self.actionPan.trigger() try: firstform = project.forms[0] self.load_form(firstform) self.dataentryselection.setVisible(True) except IndexError: self.dataentryselection.setVisible(False) # Enable the raster layers button only if the project contains a raster layer. layers = QgsMapLayerRegistry.instance().mapLayers().values() hasrasters = any(layer.type() == QgsMapLayer.RasterLayer for layer in layers) self.actionRaster.setEnabled(hasrasters) self.defaultextent = self.canvas.extent() roam.utils.info("Extent: {}".format(self.defaultextent.toString())) self.infoTool.selectionlayers = project.selectlayersmapping() self.canvas.freeze(False) self.canvas.refresh() def setMapTool(self, tool, *args): self.canvas.setMapTool(tool) def connectButtons(self): def connectAction(action, tool): action.toggled.connect(partial(self.setMapTool, tool)) def cursor(name): pix = QPixmap(name) pix = pix.scaled(QSize(24,24)) return QCursor(pix) self.zoomInTool = QgsMapToolZoom(self.canvas, False) self.zoomOutTool = QgsMapToolZoom(self.canvas, True) self.panTool = PanTool(self.canvas) self.infoTool = InfoTool(self.canvas) connectAction(self.actionZoom_In, self.zoomInTool) connectAction(self.actionZoom_Out, self.zoomOutTool) connectAction(self.actionPan, self.panTool) connectAction(self.actionInfo, self.infoTool) self.zoomInTool.setCursor(cursor(':/icons/in')) self.zoomOutTool.setCursor(cursor(':/icons/out')) self.infoTool.setCursor(cursor(':/icons/info')) self.actionRaster.triggered.connect(self.toggleRasterLayers) self.infoTool.infoResults.connect(RoamEvents.selectionchanged.emit) self.actionHome.triggered.connect(self.homeview) def homeview(self): """ Zoom the mapview canvas to the extents the project was opened at i.e. the default extent. """ self.canvas.setExtent(self.defaultextent) self.canvas.refresh() def load_form(self, form): self.clearCapatureTools() self.dataentryselection.setIcon(QIcon(form.icon)) self.dataentryselection.setText(form.icontext) self.create_capture_buttons(form) def create_capture_buttons(self, form): tool = form.getMaptool()(self.canvas) for action in tool.actions: # Create the action here. if action.ismaptool: action.toggled.connect(partial(self.setMapTool, tool)) # Set the action as a data entry button so we can remove it later. action.setProperty("dataentry", True) self.editgroup.addAction(action) self.layerbuttons.append(action) self.projecttoolbar.insertAction(self.topspaceraction, action) action.setChecked(action.isdefault) if hasattr(tool, 'geometryComplete'): add = partial(self.add_new_feature, form) tool.geometryComplete.connect(add) else: tool.finished.connect(self.openForm) tool.error.connect(partial(self.showUIMessage, form.label)) def add_new_feature(self, form, geometry): """ Add a new new feature to the given layer """ # TODO Extract into function. # NOTE This function is doing too much, acts as add and also edit. layer = form.QGISLayer if layer.geometryType() in [QGis.WKBMultiLineString, QGis.WKBMultiPoint, QGis.WKBMultiPolygon]: geometry.convertToMultiType() try: form, feature = self.editfeaturestack.pop() self.editfeaturegeometry(form, feature, newgeometry=geometry) return except IndexError: pass layer = form.QGISLayer fields = layer.pendingFields() feature = QgsFeature(fields) feature.setGeometry(geometry) for index in xrange(fields.count()): pkindexes = layer.dataProvider().pkAttributeIndexes() if index in pkindexes and layer.dataProvider().name() == 'spatialite': continue value = layer.dataProvider().defaultValue(index) feature[index] = value RoamEvents.open_feature_form(form, feature, editmode=False) def editfeaturegeometry(self, form, feature, newgeometry): # TODO Extract into function. layer = form.QGISLayer layer.startEditing() feature.setGeometry(newgeometry) layer.updateFeature(feature) saved = layer.commitChanges() if not saved: map(roam.utils.error, layer.commitErrors()) self.canvas.refresh() self.currentfeatureband.setToGeometry(feature.geometry(), layer) RoamEvents.editgeometry_complete.emit(form, feature) def clearCapatureTools(self): captureselected = False for action in self.projecttoolbar.actions(): if action.objectName() == "capture" and action.isChecked(): captureselected = True if action.property('dataentry'): self.projecttoolbar.removeAction(action) return captureselected def toggleRasterLayers(self): """ Toggle all raster layers on or off. """ if not self.canvaslayers: return #Freeze the canvas to save on UI refresh self.canvas.freeze() for layer in self.canvaslayers: if layer.layer().type() == QgsMapLayer.RasterLayer: layer.setVisible(not layer.isVisible()) # Really!? We have to reload the whole layer set every time? # WAT? self.canvas.setLayerSet(self.canvaslayers) self.canvas.freeze(False) self.canvas.refresh() def cleanup(self): self.gpsband.reset() self.gpsband.hide() self.clear_selection() self.clear_temp_objects() self.clearCapatureTools() self.canvas.freeze() self.canvas.clear() self.canvas.freeze(False) for action in self.layerbuttons: self.editgroup.removeAction(action)
class RecentFiles(): """Keeps track of last opened files.""" def __init__(self, parent = None): """Constructor""" self.parent = parent self.max_recent = 5 self.recent_files = [] self.recent_actions = [] self.action_clear_history = QAction(self.parent) self.create() self.update() def create(self): """Creates menu actions.""" for i in range(self.max_recent): action = QAction(self.parent) action.setIcon(QIcon(QPixmap(":/icons/action_rom.png"))) self.recent_actions.append(action) self.recent_actions[i].setVisible(False) self.parent.connect( self.recent_actions[i], SIGNAL("triggered()"), self.parent.file_open) self.parent.menuRecent.addAction(self.recent_actions[i]) self.parent.menuRecent.addSeparator() self.action_clear_history.setText("&Clear history") self.action_clear_history.setEnabled(False) self.action_clear_history.setVisible(True) self.action_clear_history.setIcon( QIcon(QPixmap(":/icons/action_clear.png"))) self.parent.connect( self.action_clear_history, SIGNAL("triggered()"), self.clear) self.parent.menuRecent.addAction(self.action_clear_history) def update(self): """Updates list of recent files.""" self.recent_files = self.parent.settings.qset.value("recent_files", []) num_files = min(len(self.recent_files), self.max_recent) for i in range(num_files): text = QFileInfo(self.recent_files[i]).fileName() self.recent_actions[i].setText(text) self.recent_actions[i].setData(self.recent_files[i]) self.recent_actions[i].setVisible(True) self.recent_actions[i].setToolTip(QFileInfo( self.recent_files[i]).filePath()) for j in range(num_files, self.max_recent): self.recent_actions[j].setVisible(False) self.action_clear_history.setEnabled((num_files > 0)) def add(self, filepath): """Adds file to recent files list.""" if filepath in self.recent_files: self.recent_files.remove(filepath) self.recent_files.insert(0, filepath) while len(self.recent_files) > 5: self.recent_files.pop(len(self.recent_files) - 1) self.parent.settings.qset.setValue( "recent_files", self.recent_files) self.update() def clear(self): """Clears list of recent files.""" self.parent.settings.qset.remove("recent_files") self.update()
class MainWindow(QMainWindow): def __init__(self, doc): QMainWindow.__init__(self, None) self.doc = doc self.app = doc.app self._setupUi() # Create base elements self.model = MainWindowModel(document=doc.model) self.model2view = {} self.apanel = AccountPanel(mainwindow=self) self.tpanel = TransactionPanel(mainwindow=self) self.mepanel = MassEditionPanel(mainwindow=self) self.scpanel = SchedulePanel(mainwindow=self) self.bpanel = BudgetPanel(mainwindow=self) self.cdrpanel = CustomDateRangePanel(mainwindow=self) self.arpanel = AccountReassignPanel(mainwindow=self) self.expanel = ExportPanel(mainwindow=self) self.alookup = Lookup(self, model=self.model.account_lookup) self.clookup = Lookup(self, model=self.model.completion_lookup) self.drsel = DateRangeSelector(mainwindow=self, view=self.dateRangeSelectorView) self.sfield = SearchField(model=self.model.search_field, view=self.searchLineEdit) self.recentDocuments = Recent(self.app, 'recentDocuments') self.recentDocuments.addMenu(self.menuOpenRecent) self.model.view = self self.model.connect() self._updateUndoActions() self._bindSignals() def _setupUi(self): # has to take place *before* base elements creation self.setWindowTitle("moneyGuru") self.resize(700, 580) self.centralwidget = QtGui.QWidget(self) self.verticalLayout = QtGui.QVBoxLayout(self.centralwidget) self.verticalLayout.setSpacing(0) self.verticalLayout.setMargin(0) self.topBar = QtGui.QWidget(self.centralwidget) self.horizontalLayout_2 = QHBoxLayout(self.topBar) self.horizontalLayout_2.setContentsMargins(2, 0, 2, 0) spacerItem = QtGui.QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout_2.addItem(spacerItem) self.dateRangeSelectorView = DateRangeSelectorView(self.topBar) self.dateRangeSelectorView.setMinimumSize(QSize(220, 0)) self.horizontalLayout_2.addWidget(self.dateRangeSelectorView) spacerItem1 = QtGui.QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout_2.addItem(spacerItem1) self.searchLineEdit = SearchEdit(self.topBar) self.searchLineEdit.setMaximumSize(QSize(240, 16777215)) self.horizontalLayout_2.addWidget(self.searchLineEdit) self.verticalLayout.addWidget(self.topBar) self.tabBar = QTabBar(self.centralwidget) self.tabBar.setMinimumSize(QSize(0, 20)) self.verticalLayout.addWidget(self.tabBar) self.mainView = QtGui.QStackedWidget(self.centralwidget) self.verticalLayout.addWidget(self.mainView) # Bottom buttons & status label self.bottomBar = QtGui.QWidget(self.centralwidget) self.horizontalLayout = QHBoxLayout(self.bottomBar) self.horizontalLayout.setMargin(2) self.horizontalLayout.setMargin(0) self.newItemButton = QPushButton(self.bottomBar) buttonSizePolicy = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed) buttonSizePolicy.setHorizontalStretch(0) buttonSizePolicy.setVerticalStretch(0) buttonSizePolicy.setHeightForWidth(self.newItemButton.sizePolicy().hasHeightForWidth()) self.newItemButton.setSizePolicy(buttonSizePolicy) self.newItemButton.setIcon(QIcon(QPixmap(':/plus_8'))) self.horizontalLayout.addWidget(self.newItemButton) self.deleteItemButton = QPushButton(self.bottomBar) self.deleteItemButton.setSizePolicy(buttonSizePolicy) self.deleteItemButton.setIcon(QIcon(QPixmap(':/minus_8'))) self.horizontalLayout.addWidget(self.deleteItemButton) self.editItemButton = QPushButton(self.bottomBar) self.editItemButton.setSizePolicy(buttonSizePolicy) self.editItemButton.setIcon(QIcon(QPixmap(':/info_gray_12'))) self.horizontalLayout.addWidget(self.editItemButton) self.horizontalLayout.addItem(horizontalSpacer(size=20)) self.graphVisibilityButton = QPushButton() self.graphVisibilityButton.setSizePolicy(buttonSizePolicy) self.graphVisibilityButton.setIcon(QIcon(QPixmap(':/graph_visibility_on_16'))) self.horizontalLayout.addWidget(self.graphVisibilityButton) self.piechartVisibilityButton = QPushButton() self.piechartVisibilityButton.setSizePolicy(buttonSizePolicy) self.piechartVisibilityButton.setIcon(QIcon(QPixmap(':/piechart_visibility_on_16'))) self.horizontalLayout.addWidget(self.piechartVisibilityButton) self.columnsVisibilityButton = QPushButton() self.columnsVisibilityButton.setSizePolicy(buttonSizePolicy) self.columnsVisibilityButton.setIcon(QIcon(QPixmap(':/columns_16'))) self.horizontalLayout.addWidget(self.columnsVisibilityButton) self.statusLabel = QtGui.QLabel(tr("Status")) self.statusLabel.setAlignment(Qt.AlignCenter) self.horizontalLayout.addWidget(self.statusLabel) self.verticalLayout.addWidget(self.bottomBar) self.setCentralWidget(self.centralwidget) self.menubar = QMenuBar(self) self.menubar.setGeometry(QRect(0, 0, 700, 20)) self.menuFile = QMenu(tr("File")) self.menuOpenRecent = QMenu(tr("Open Recent")) self.menuView = QMenu(tr("View")) self.menuDateRange = QMenu(tr("Date Range")) self.menuEdit = QMenu(tr("Edit")) self.menuHelp = QMenu(tr("Help")) self.setMenuBar(self.menubar) self.actionOpenDocument = QAction(tr("Open..."), self) self.actionOpenDocument.setShortcut("Ctrl+O") self.actionShowNetWorth = QAction(tr("Net Worth"), self) self.actionShowNetWorth.setShortcut("Ctrl+1") self.actionShowNetWorth.setIcon(QIcon(QPixmap(':/balance_sheet_48'))) self.actionShowProfitLoss = QAction(escapeamp(tr("Profit & Loss")), self) self.actionShowProfitLoss.setShortcut("Ctrl+2") self.actionShowProfitLoss.setIcon(QIcon(QPixmap(':/income_statement_48'))) self.actionShowTransactions = QAction(tr("Transactions"), self) self.actionShowTransactions.setShortcut("Ctrl+3") self.actionShowTransactions.setIcon(QIcon(QPixmap(':/transaction_table_48'))) self.actionShowSelectedAccount = QAction(tr("Show Account"), self) self.actionShowSelectedAccount.setShortcut("Ctrl+]") self.actionNewItem = QAction(tr("New Item"), self) self.actionNewItem.setShortcut("Ctrl+N") self.actionDeleteItem = QAction(tr("Remove Selected"), self) self.actionEditItem = QAction(tr("Show Info"), self) self.actionEditItem.setShortcut("Ctrl+I") self.actionToggleGraph = QAction(tr("Toggle Graph"), self) self.actionToggleGraph.setShortcut("Ctrl+Alt+G") self.actionTogglePieChart = QAction(tr("Toggle Pie Chart"), self) self.actionTogglePieChart.setShortcut("Ctrl+Alt+P") self.actionMoveUp = QAction(tr("Move Up"), self) self.actionMoveUp.setShortcut("Ctrl++") self.actionMoveDown = QAction(tr("Move Down"), self) self.actionMoveDown.setShortcut("Ctrl+-") self.actionNavigateBack = QAction(tr("Go Back"), self) self.actionNavigateBack.setShortcut("Ctrl+[") self.actionNewAccountGroup = QAction(tr("New Account Group"), self) self.actionNewAccountGroup.setShortcut("Ctrl+Shift+N") self.actionShowNextView = QAction(tr("Next View"), self) self.actionShowNextView.setShortcut("Ctrl+Shift+]") self.actionShowPreviousView = QAction(tr("Previous View"), self) self.actionShowPreviousView.setShortcut("Ctrl+Shift+[") self.actionNewDocument = QAction(tr("New Document"), self) self.actionOpenExampleDocument = QAction(tr("Open Example Document"), self) self.actionOpenPluginFolder = QAction(tr("Open Plugin Folder"), self) self.actionImport = QAction(tr("Import..."), self) self.actionImport.setShortcut("Ctrl+Alt+I") self.actionExport = QAction(tr("Export..."), self) self.actionExport.setShortcut("Ctrl+Alt+E") self.actionSave = QAction(tr("Save"), self) self.actionSave.setShortcut("Ctrl+S") self.actionSaveAs = QAction(tr("Save As..."), self) self.actionSaveAs.setShortcut("Ctrl+Shift+S") self.actionAbout = QAction(tr("About moneyGuru"), self) self.actionToggleReconciliationMode = QAction(tr("Toggle Reconciliation Mode"), self) self.actionToggleReconciliationMode.setShortcut("Ctrl+Shift+R") self.actionToggleAccountExclusion = QAction(tr("Toggle Exclusion Status of Account"), self) self.actionToggleAccountExclusion.setShortcut("Ctrl+Shift+X") self.actionShowSchedules = QAction(tr("Schedules"), self) self.actionShowSchedules.setShortcut("Ctrl+4") self.actionShowSchedules.setIcon(QIcon(QPixmap(':/schedules_48'))) self.actionShowBudgets = QAction(tr("Budgets"), self) self.actionShowBudgets.setShortcut("Ctrl+5") self.actionShowBudgets.setIcon(QIcon(QPixmap(':/budget_48'))) self.actionReconcileSelected = QAction(tr("Reconcile Selection"), self) self.actionReconcileSelected.setShortcut("Ctrl+R") self.actionMakeScheduleFromSelected = QAction(tr("Make Schedule from Selected"), self) self.actionMakeScheduleFromSelected.setShortcut("Ctrl+M") self.actionShowPreferences = QAction(tr("Preferences..."), self) self.actionPrint = QAction(tr("Print..."), self) self.actionPrint.setShortcut("Ctrl+P") self.actionQuit = QAction(tr("Quit moneyGuru"), self) self.actionQuit.setShortcut("Ctrl+Q") self.actionUndo = QAction(tr("Undo"), self) self.actionUndo.setShortcut("Ctrl+Z") self.actionRedo = QAction(tr("Redo"), self) self.actionRedo.setShortcut("Ctrl+Y") self.actionShowHelp = QAction(tr("moneyGuru Help"), self) self.actionShowHelp.setShortcut("F1") self.actionCheckForUpdate = QAction(tr("Check for update"), self) self.actionOpenDebugLog = QAction(tr("Open Debug Log"), self) self.actionDuplicateTransaction = QAction(tr("Duplicate Transaction"), self) self.actionDuplicateTransaction.setShortcut("Ctrl+D") self.actionJumpToAccount = QAction(tr("Jump to Account..."), self) self.actionJumpToAccount.setShortcut("Ctrl+Shift+A") self.actionNewTab = QAction(tr("New Tab"), self) self.actionNewTab.setShortcut("Ctrl+T") self.actionCloseTab = QAction(tr("Close Tab"), self) self.actionCloseTab.setShortcut("Ctrl+W") self.menuFile.addAction(self.actionNewDocument) self.menuFile.addAction(self.actionNewTab) self.menuFile.addAction(self.actionOpenDocument) self.menuFile.addAction(self.menuOpenRecent.menuAction()) self.menuFile.addAction(self.actionOpenExampleDocument) self.menuFile.addAction(self.actionOpenPluginFolder) self.menuFile.addAction(self.actionImport) self.menuFile.addSeparator() self.menuFile.addAction(self.actionCloseTab) self.menuFile.addAction(self.actionSave) self.menuFile.addAction(self.actionSaveAs) self.menuFile.addAction(self.actionExport) self.menuFile.addAction(self.actionPrint) self.menuFile.addAction(self.actionQuit) self.menuView.addAction(self.actionShowNetWorth) self.menuView.addAction(self.actionShowProfitLoss) self.menuView.addAction(self.actionShowTransactions) self.menuView.addAction(self.actionShowSchedules) self.menuView.addAction(self.actionShowBudgets) self.menuView.addAction(self.actionShowPreviousView) self.menuView.addAction(self.actionShowNextView) self.menuView.addAction(self.menuDateRange.menuAction()) self.menuView.addAction(self.actionShowPreferences) self.menuView.addAction(self.actionToggleGraph) self.menuView.addAction(self.actionTogglePieChart) self.menuEdit.addAction(self.actionNewItem) self.menuEdit.addAction(self.actionNewAccountGroup) self.menuEdit.addAction(self.actionDeleteItem) self.menuEdit.addAction(self.actionEditItem) self.menuEdit.addSeparator() self.menuEdit.addAction(self.actionMoveUp) self.menuEdit.addAction(self.actionMoveDown) self.menuEdit.addAction(self.actionDuplicateTransaction) self.menuEdit.addAction(self.actionMakeScheduleFromSelected) self.menuEdit.addAction(self.actionReconcileSelected) self.menuEdit.addAction(self.actionToggleReconciliationMode) self.menuEdit.addAction(self.actionToggleAccountExclusion) self.menuEdit.addSeparator() self.menuEdit.addAction(self.actionShowSelectedAccount) self.menuEdit.addAction(self.actionNavigateBack) self.menuEdit.addAction(self.actionJumpToAccount) self.menuEdit.addSeparator() self.menuEdit.addAction(self.actionUndo) self.menuEdit.addAction(self.actionRedo) self.menuHelp.addAction(self.actionShowHelp) self.menuHelp.addAction(self.actionCheckForUpdate) self.menuHelp.addAction(self.actionOpenDebugLog) self.menuHelp.addAction(self.actionAbout) mainmenus = [self.menuFile, self.menuEdit, self.menuView, self.menuHelp] for menu in mainmenus: self.menubar.addAction(menu.menuAction()) setAccelKeys(menu) setAccelKeys(self.menubar) self.tabBar.setMovable(True) self.tabBar.setTabsClosable(True) seq = QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_Right) self._shortcutNextTab = QShortcut(seq, self) seq = QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_Left) self._shortcutPrevTab = QShortcut(seq, self) # Linux setup if ISLINUX: self.actionCheckForUpdate.setVisible(False) # This only works on Windows def _bindSignals(self): self.newItemButton.clicked.connect(self.actionNewItem.trigger) self.deleteItemButton.clicked.connect(self.actionDeleteItem.trigger) self.editItemButton.clicked.connect(self.actionEditItem.trigger) self.graphVisibilityButton.clicked.connect(self.actionToggleGraph.trigger) self.piechartVisibilityButton.clicked.connect(self.actionTogglePieChart.trigger) self.columnsVisibilityButton.clicked.connect(self.columnsVisibilityButtonClicked) self.recentDocuments.mustOpenItem.connect(self.doc.open) self.doc.documentOpened.connect(self.recentDocuments.insertItem) self.doc.documentSavedAs.connect(self.recentDocuments.insertItem) self.doc.documentPathChanged.connect(self.documentPathChanged) self.tabBar.currentChanged.connect(self.currentTabChanged) self.tabBar.tabCloseRequested.connect(self.tabCloseRequested) self.tabBar.tabMoved.connect(self.tabMoved) # Views self.actionShowNetWorth.triggered.connect(self.showNetWorthTriggered) self.actionShowProfitLoss.triggered.connect(self.showProfitLossTriggered) self.actionShowTransactions.triggered.connect(self.showTransactionsTriggered) self.actionShowSchedules.triggered.connect(self.showSchedulesTriggered) self.actionShowBudgets.triggered.connect(self.showBudgetsTriggered) self.actionShowPreviousView.triggered.connect(self.showPreviousViewTriggered) self.actionShowNextView.triggered.connect(self.showNextViewTriggered) self.actionShowPreferences.triggered.connect(self.app.showPreferences) self.actionToggleGraph.triggered.connect(self.toggleGraphTriggered) self.actionTogglePieChart.triggered.connect(self.togglePieChartTriggered) # Document Edition self.actionNewItem.triggered.connect(self.newItemTriggered) self.actionNewAccountGroup.triggered.connect(self.newAccountGroupTriggered) self.actionDeleteItem.triggered.connect(self.deleteItemTriggered) self.actionEditItem.triggered.connect(self.editItemTriggered) self.actionMoveUp.triggered.connect(self.moveUpTriggered) self.actionMoveDown.triggered.connect(self.moveDownTriggered) self.actionDuplicateTransaction.triggered.connect(self.model.duplicate_item) self.actionUndo.triggered.connect(self.doc.model.undo) self.actionRedo.triggered.connect(self.doc.model.redo) # Open / Save / Import / Export / New self.actionNewDocument.triggered.connect(self.doc.new) self.actionOpenDocument.triggered.connect(self.doc.openDocument) self.actionOpenExampleDocument.triggered.connect(self.doc.openExampleDocument) self.actionOpenPluginFolder.triggered.connect(self.model.app.open_plugin_folder) self.actionImport.triggered.connect(self.doc.importDocument) self.actionSave.triggered.connect(self.doc.save) self.actionSaveAs.triggered.connect(self.doc.saveAs) self.actionExport.triggered.connect(self.model.export) # Misc self.actionNewTab.triggered.connect(self.model.new_tab) self.actionCloseTab.triggered.connect(self.closeTabTriggered) self.actionShowSelectedAccount.triggered.connect(self.model.show_account) self.actionNavigateBack.triggered.connect(self.navigateBackTriggered) self.actionJumpToAccount.triggered.connect(self.jumpToAccountTriggered) self.actionMakeScheduleFromSelected.triggered.connect(self.makeScheduleFromSelectedTriggered) self.actionReconcileSelected.triggered.connect(self.reconcileSelectedTriggered) self.actionToggleReconciliationMode.triggered.connect(self.toggleReconciliationModeTriggered) self.actionToggleAccountExclusion.triggered.connect(self.toggleAccountExclusionTriggered) self.actionPrint.triggered.connect(self._print) self.actionShowHelp.triggered.connect(self.app.showHelp) self.actionCheckForUpdate.triggered.connect(self.checkForUpdateTriggered) self.actionAbout.triggered.connect(self.aboutTriggered) self.actionOpenDebugLog.triggered.connect(self.openDebugLogTriggered) self.actionQuit.triggered.connect(self.close) # Extra Shortcuts self._shortcutNextTab.activated.connect(self.showNextViewTriggered) self._shortcutPrevTab.activated.connect(self.showPreviousViewTriggered) #--- QWidget overrides def closeEvent(self, event): if self.doc.confirmDestructiveAction(): event.accept() else: event.ignore() #--- Private def _print(self): dialog = QPrintDialog(self) if dialog.exec_() != QPrintDialog.Accepted: return printer = dialog.printer() currentView = self.mainView.currentWidget() viewPrinter = ViewPrinter(printer, currentView) currentView.fitViewsForPrint(viewPrinter) viewPrinter.render() def _getViewforPane(self, pane_type, pane_view): if pane_view in self.model2view: view = self.model2view[pane_view] else: view = PANETYPE2VIEWCLASS[pane_type](model=pane_view) self.model2view[pane_view] = view self.mainView.addWidget(view) view.restoreSubviewsSize() return view def _setTabIndex(self, index): if not self.tabBar.count(): return self.tabBar.setCurrentIndex(index) self._updateActionsState() pane_type = self.model.pane_type(index) pane_view = self.model.pane_view(index) view = self._getViewforPane(pane_type, pane_view) self.mainView.setCurrentWidget(view) view.setFocus() def _activeView(self): paneIndex = self.model.current_pane_index return self.model.pane_view(paneIndex) def _updateActionsState(self): # Updates enable/disable checked/unchecked state of all actions. These state can change # under various conditions: main view change, date range type change and when reconciliation # mode is toggled # Determine what actions are enabled view = self._activeView() viewType = view.VIEW_TYPE isSheet = viewType in {PaneType.NetWorth, PaneType.Profit} isTransactionOrEntryTable = viewType in {PaneType.Transaction, PaneType.Account} canToggleReconciliation = viewType == PaneType.Account and view.can_toggle_reconciliation_mode newItemLabel = { PaneType.NetWorth: tr("New Account"), PaneType.Profit: tr("New Account"), PaneType.Transaction: tr("New Transaction"), PaneType.Account: tr("New Transaction"), PaneType.Schedule: tr("New Schedule"), PaneType.Budget: tr("New Budget"), PaneType.GeneralLedger: tr("New Transaction"), }.get(viewType, tr("New Item")) #XXX make "New Item" disabled self.actionNewItem.setText(newItemLabel) self.actionNewAccountGroup.setEnabled(isSheet) self.actionMoveDown.setEnabled(isTransactionOrEntryTable) self.actionMoveUp.setEnabled(isTransactionOrEntryTable) self.actionDuplicateTransaction.setEnabled(isTransactionOrEntryTable) self.actionMakeScheduleFromSelected.setEnabled(isTransactionOrEntryTable) self.actionReconcileSelected.setEnabled(viewType == PaneType.Account and view.reconciliation_mode) self.actionShowNextView.setEnabled(self.model.current_pane_index < self.model.pane_count-1) self.actionShowPreviousView.setEnabled(self.model.current_pane_index > 0) self.actionShowSelectedAccount.setEnabled(isSheet or isTransactionOrEntryTable) self.actionNavigateBack.setEnabled(viewType == PaneType.Account) self.actionToggleReconciliationMode.setEnabled(canToggleReconciliation) self.actionToggleAccountExclusion.setEnabled(isSheet) def _updateUndoActions(self): if self.doc.model.can_undo(): self.actionUndo.setEnabled(True) self.actionUndo.setText(tr("Undo {0}").format(self.doc.model.undo_description())) else: self.actionUndo.setEnabled(False) self.actionUndo.setText(tr("Undo")) if self.doc.model.can_redo(): self.actionRedo.setEnabled(True) self.actionRedo.setText(tr("Redo {0}").format(self.doc.model.redo_description())) else: self.actionRedo.setEnabled(False) self.actionRedo.setText(tr("Redo")) #--- Actions # Views def showNetWorthTriggered(self): self.model.select_pane_of_type(PaneType.NetWorth) def showProfitLossTriggered(self): self.model.select_pane_of_type(PaneType.Profit) def showTransactionsTriggered(self): self.model.select_pane_of_type(PaneType.Transaction) def showSchedulesTriggered(self): self.model.select_pane_of_type(PaneType.Schedule) def showBudgetsTriggered(self): self.model.select_pane_of_type(PaneType.Budget) def showPreviousViewTriggered(self): self.model.select_previous_view() def showNextViewTriggered(self): self.model.select_next_view() # Document Edition def newItemTriggered(self): self.model.new_item() def newAccountGroupTriggered(self): self.model.new_group() def deleteItemTriggered(self): self.model.delete_item() def editItemTriggered(self): self.model.edit_item() def moveUpTriggered(self): self.model.move_up() def moveDownTriggered(self): self.model.move_down() # Misc def closeTabTriggered(self): self.model.close_pane(self.model.current_pane_index) def navigateBackTriggered(self): self.model.navigate_back() def jumpToAccountTriggered(self): self.model.jump_to_account() def makeScheduleFromSelectedTriggered(self): self.model.make_schedule_from_selected() def reconcileSelectedTriggered(self): self._activeView().etable.toggle_reconciled() def toggleReconciliationModeTriggered(self): self._activeView().toggle_reconciliation_mode() self._updateActionsState() def toggleAccountExclusionTriggered(self): viewType = self.model.pane_type(self.model.current_pane_index) if viewType in {PaneType.NetWorth, PaneType.Profit}: self._activeView().sheet.toggle_excluded() def toggleGraphTriggered(self): self.model.toggle_area_visibility(PaneArea.BottomGraph) def togglePieChartTriggered(self): self.model.toggle_area_visibility(PaneArea.RightChart) def columnsVisibilityButtonClicked(self): items = self.model.column_menu_items() if not items: return menu = QMenu() for i, (display, marked) in enumerate(items): action = menu.addAction(display) action.setCheckable(True) action.setChecked(marked) action.setData(i) action.triggered.connect(self.columnsMenuItemWasClicked) self._columnMenuHolder = menu # we need to hold a reference to it while it popups button = self.columnsVisibilityButton menu.popup(button.parentWidget().mapToGlobal(button.geometry().topLeft())) def columnsMenuItemWasClicked(self): action = self.sender() if action is not None: index = action.data() self.model.toggle_column_menu_item(index) def checkForUpdateTriggered(self): QProcess.execute('updater.exe', ['/checknow']) def aboutTriggered(self): self.app.showAboutBox() def openDebugLogTriggered(self): debugLogPath = op.join(getAppData(), 'debug.log') url = QUrl.fromLocalFile(debugLogPath) QDesktopServices.openUrl(url) #--- Other Signals def currentTabChanged(self, index): self.model.current_pane_index = index self._setTabIndex(index) def documentPathChanged(self): if self.doc.documentPath: title = "moneyGuru ({})".format(self.doc.documentPath) else: title = "moneyGuru" self.setWindowTitle(title) def tabCloseRequested(self, index): self.model.close_pane(index) def tabMoved(self, fromIndex, toIndex): self.model.move_pane(fromIndex, toIndex) #--- model --> view def change_current_pane(self): self._setTabIndex(self.model.current_pane_index) def refresh_panes(self): while self.tabBar.count() < self.model.pane_count: self.tabBar.addTab('') for i in range(self.model.pane_count): pane_label = self.model.pane_label(i) pane_label = escapeamp(pane_label) self.tabBar.setTabText(i, pane_label) pane_type = self.model.pane_type(i) pane_view = self.model.pane_view(i) # Ensure that the view's "view" has been created and bound self._getViewforPane(pane_type, pane_view) iconname = PANETYPE2ICON.get(pane_type) icon = QIcon(QPixmap(':/{0}'.format(iconname))) if iconname else QIcon() self.tabBar.setTabIcon(i, icon) # It's important that we proceed with tab removal *after* we've completed tab initialization. # We're walking on eggshells here. refresh_panes() can be called in multiple situations, one # of them is during the opening of a document. When that happens when another document was # previously opened, all views' model are uninitalized and don't have their "view" attribute # set yet. If we proceed with the setCurrentIndex() call below before _getViewforPane() # could be called above, we get a crash. if self.tabBar.currentIndex() < self.model.pane_count: # Normally, we don't touch the tabBar index here and wait for change_current_pane, # but when we remove tabs, it's possible that currentTabChanged end up being called and # then the tab selection is bugged. I tried disconnecting/reconnecting the signal, but # this is buggy. So when a selected tab is about to be removed, we change the selection # to the model's one immediately. self.tabBar.setCurrentIndex(self.model.current_pane_index) while self.tabBar.count() > self.model.pane_count: self.tabBar.removeTab(self.tabBar.count()-1) self.tabBar.setTabsClosable(self.model.pane_count > 1) def refresh_status_line(self): self.statusLabel.setText(self.model.status_line) def refresh_undo_actions(self): self._updateUndoActions() def restore_window_frame(self, frame): self.setGeometry(*frame) def save_window_frame(self): r = self.geometry() return (r.x(), r.y(), r.width(), r.height()) def show_message(self, msg): title = tr("Warning") QMessageBox.warning(self, title, msg) def update_area_visibility(self): hidden = self.model.hidden_areas graphimg = ':/graph_visibility_{}_16'.format('off' if PaneArea.BottomGraph in hidden else 'on') pieimg = ':/piechart_visibility_{}_16'.format('off' if PaneArea.RightChart in hidden else 'on') self.graphVisibilityButton.setIcon(QIcon(QPixmap(graphimg))) self.piechartVisibilityButton.setIcon(QIcon(QPixmap(pieimg))) def view_closed(self, index): self.tabBar.removeTab(index) self.tabBar.setTabsClosable(self.model.pane_count > 1)
def __createLayout( self ): " Creates the toolbar and layout " # Buttons printButton = QAction( PixmapCache().getIcon( 'printer.png' ), 'Print', self ) #printButton.setShortcut( 'Ctrl+' ) printButton.triggered.connect( self.__onPrint ) printButton.setVisible( False ) printPreviewButton = QAction( PixmapCache().getIcon( 'printpreview.png' ), 'Print preview', self ) #printPreviewButton.setShortcut( 'Ctrl+' ) printPreviewButton.triggered.connect( self.__onPrintPreview ) printPreviewButton.setVisible( False ) fixedSpacer = QWidget() fixedSpacer.setFixedHeight( 16 ) zoomInButton = QAction( PixmapCache().getIcon( 'zoomin.png' ), 'Zoom in (Ctrl+=)', self ) zoomInButton.setShortcut( 'Ctrl+=' ) zoomInButton.triggered.connect( self.onZoomIn ) self.__zoomInSynonim = QShortcut( "Ctrl++", self ) self.__zoomInSynonim.activated.connect( self.onZoomIn ) zoomOutButton = QAction( PixmapCache().getIcon( 'zoomout.png' ), 'Zoom out (Ctrl+-)', self ) zoomOutButton.setShortcut( 'Ctrl+-' ) zoomOutButton.triggered.connect( self.onZoomOut ) zoomResetButton = QAction( PixmapCache().getIcon( 'zoomreset.png' ), 'Zoom reset (Ctrl+0)', self ) zoomResetButton.setShortcut( 'Ctrl+0' ) zoomResetButton.triggered.connect( self.onZoomReset ) # Toolbar toolbar = QToolBar( self ) toolbar.setOrientation( Qt.Vertical ) toolbar.setMovable( False ) toolbar.setAllowedAreas( Qt.RightToolBarArea ) toolbar.setIconSize( QSize( 16, 16 ) ) toolbar.setFixedWidth( 28 ) toolbar.setContentsMargins( 0, 0, 0, 0 ) toolbar.addAction( printPreviewButton ) toolbar.addAction( printButton ) toolbar.addWidget( fixedSpacer ) toolbar.addAction( zoomInButton ) toolbar.addAction( zoomOutButton ) toolbar.addAction( zoomResetButton ) hLayout = QHBoxLayout() hLayout.setContentsMargins( 0, 0, 0, 0 ) hLayout.setSpacing( 0 ) hLayout.addWidget( self.__viewer ) hLayout.addWidget( toolbar ) self.__outsideChangesBar = OutsideChangeWidget( self.__viewer ) self.__outsideChangesBar.reloadRequest.connect( self.__onReload ) self.__outsideChangesBar.reloadAllNonModifiedRequest.connect( self.reloadAllNonModified ) self.__outsideChangesBar.hide() self.setLayout( hLayout ) return
class DocumentViewManager(QMainWindow): """ MDI area for displaying supporting documents within a given context e.g. supporting documents for a specific household based on the lifetime of the 'SourceDocumentManager' instance. """ def __init__(self, parent = None): QMainWindow.__init__(self, parent) self.setWindowFlags(Qt.Window) self._mdi_area = QMdiArea() self.setCentralWidget(self._mdi_area) self._mdi_area.subWindowActivated.connect(self.update_actions) self._viewer_mapper = QSignalMapper(self) self._viewer_mapper.mapped[QWidget].connect(self.set_active_sub_window) win_title = QApplication.translate("DocumentViewManager","Document Viewer") self.setWindowTitle(win_title) self.setUnifiedTitleAndToolBarOnMac(True) self.statusBar().showMessage(QApplication.translate("DocumentViewManager","Ready")) self._doc_viewers = {} self._create_menu_actions() self.update_actions() def _create_menu_actions(self): self._window_menu = self.menuBar().addMenu(QApplication.translate("DocumentViewManager","&Windows")) self._close_act = QAction(QApplication.translate("DocumentViewManager", "Cl&ose"), self) self._close_act.setStatusTip(QApplication.translate("DocumentViewManager", "Close the active document viewer")) self._close_act.triggered.connect(self._mdi_area.closeActiveSubWindow) self._close_all_act = QAction(QApplication.translate("DocumentViewManager", "Close &All"), self) self._close_all_act.setStatusTip(QApplication.translate("DocumentViewManager", "Close all the document viewers")) self._close_all_act.triggered.connect(self._mdi_area.closeAllSubWindows) self._tile_act = QAction(QApplication.translate("DocumentViewManager", "&Tile"), self) self._tile_act.setStatusTip(QApplication.translate("DocumentViewManager", "Tile the document viewers")) self._tile_act.triggered.connect(self._mdi_area.tileSubWindows) self._cascade_act = QAction(QApplication.translate("DocumentViewManager", "&Cascade"), self) self._cascade_act.setStatusTip(QApplication.translate("DocumentViewManager", "Cascade the document viewers")) self._cascade_act.triggered.connect(self._mdi_area.cascadeSubWindows) self._next_act = QAction(QApplication.translate("DocumentViewManager", "Ne&xt"), self) self._next_act.setStatusTip(QApplication.translate("DocumentViewManager", "Move the focus to the next document viewer")) self._next_act.triggered.connect(self._mdi_area.activateNextSubWindow) self._previous_act = QAction(QApplication.translate("DocumentViewManager", "Pre&vious"), self) self._previous_act.setStatusTip(QApplication.translate("DocumentViewManager", "Move the focus to the previous document viewer")) self._previous_act.triggered.connect(self._mdi_area.activatePreviousSubWindow) self._separator_act = QAction(self) self._separator_act.setSeparator(True) self.update_window_menu() self._window_menu.aboutToShow.connect(self.update_window_menu) def update_actions(self): if self._mdi_area.activeSubWindow(): has_mdi_child = True else: has_mdi_child = False self._close_act.setEnabled(has_mdi_child) self._close_all_act.setEnabled(has_mdi_child) self._tile_act.setEnabled(has_mdi_child) self._cascade_act.setEnabled(has_mdi_child) self._previous_act.setEnabled(has_mdi_child) self._next_act.setEnabled(has_mdi_child) self._separator_act.setVisible(has_mdi_child) def update_window_menu(self): self._window_menu.clear() self._window_menu.addAction(self._close_act) self._window_menu.addAction(self._close_all_act) self._window_menu.addSeparator() self._window_menu.addAction(self._tile_act) self._window_menu.addAction(self._cascade_act) self._window_menu.addSeparator() self._window_menu.addAction(self._next_act) self._window_menu.addAction(self._previous_act) self._window_menu.addAction(self._separator_act) windows = self._mdi_area.subWindowList() self._separator_act.setVisible(len(windows) != 0) for i, window in enumerate(windows): text = "%d. %s" % (i + 1, window.windowTitle()) win_action = self._window_menu.addAction(text) win_action.setCheckable(True) win_action.setChecked(window is self._mdi_area.activeSubWindow()) win_action.triggered.connect(self._viewer_mapper.map) self._viewer_mapper.setMapping(win_action, window) def load_viewer(self, document_widget): """ Open a new instance of the viewer or activate an existing one if the document had been previously loaded. :param document_widget: Contains all the necessary information required to load the specific document. """ doc_identifier = document_widget.file_identifier() if doc_identifier in self._doc_viewers: doc_sw = self._doc_viewers[doc_identifier] self._mdi_area.setActiveSubWindow(doc_sw) doc_sw.showNormal() else: doc_viewer = self._create_viewer(document_widget) abs_doc_path = self.absolute_document_path(document_widget) if not QFile.exists(abs_doc_path): msg = QApplication.translate("DocumentViewManager", "The selected document does not exist." "\nPlease check the supporting documents' " "repository setting.") QMessageBox.critical(self, QApplication.translate("DocumentViewManager","Invalid Document"), msg) return doc_viewer.load_document(abs_doc_path) self._doc_viewers[doc_identifier] = doc_viewer self._mdi_area.addSubWindow(doc_viewer) doc_viewer.show() if not self.isVisible(): self.setVisible(True) if self.isMinimized(): self.showNormal() def set_active_sub_window(self, viewer): if viewer: self._mdi_area.setActiveSubWindow(viewer) def absolute_document_path(self,document_widget): """ Build the absolute document path using info from the document widget. :param document_widget: Instance of document widget. :return: Absolute path of the supporting document. :rtype: str """ abs_path = "" file_manager = document_widget.fileManager if not file_manager is None: network_repository = file_manager.networkPath file_id = document_widget.file_identifier() doc_type = "%d"%(document_widget.documentType()) file_name, file_extension = guess_extension(document_widget.displayName()) abs_path = network_repository + "/" + doc_type + "/" +\ file_id + file_extension return abs_path def reset(self): """ Removes all document viewers in the view area. The QCloseEvent sent to each sub-window will decrement the register. """ self._mdi_area.closeAllSubWindows() def _create_viewer(self, document_widget): """ Creates a new instance of a document viewer. :param document_widget: Contains all the necessary information required to load the specific document. :return: Document viewer object :rtype: DocumentViewer """ doc_viewer = DocumentViewer(self._mdi_area, document_widget.file_identifier()) doc_viewer.setAttribute(Qt.WA_DeleteOnClose) doc_viewer.setWindowTitle(document_widget.displayName()) #TODO: Incorporate logic for determining viewer based on document type ph_viewer = PhotoViewer() doc_viewer.set_view_widget(ph_viewer) doc_viewer.closed.connect(self._on_viewer_closed) return doc_viewer def remove_viewer(self, viewer_id): """ Close and remove the viewer with the specified viewer ID. """ if viewer_id in self._doc_viewers: viewer = self._doc_viewers[viewer_id] self._mdi_area.setActiveSubWindow(viewer) self._mdi_area.closeActiveSubWindow() self._on_viewer_closed(viewer_id) def _on_viewer_closed(self,file_id): """ Slot raised when a document viewer is closed. """ if file_id in self._doc_viewers: del self._doc_viewers[file_id]
class Plugin(QObject): """Plugin interface implementation. """ def __init__(self): """Create and install the plugin """ QObject.__init__(self) self._dock = None self._saveAction = None self._dockInstalled = False core.workspace().currentDocumentChanged.connect( self._onDocumentChanged) # Disconnected. core.workspace().languageChanged.connect( self._onDocumentChanged) # Disconnected. # Install our CodeChat page into the settings dialog. core.uiSettingsManager().aboutToExecute.connect( self._onSettingsDialogAboutToExecute) # Disconnected. # Update preview dock when the settings dialog (which contains the # CodeChat enable checkbox) is changed. core.uiSettingsManager().dialogAccepted.connect( self._onDocumentChanged) # Disconnected. # Provide a "Set Sphinx Path" menu item. core.uiSettingsManager().dialogAccepted.connect( self._setSphinxActionVisibility) self._sphinxAction = QAction('Set Sphinx path', self._dock) self._sphinxAction.setShortcut(QKeySequence('Alt+Shift+S')) core.actionManager().addAction('mTools/aSetSphinxPath', self._sphinxAction) self._sphinxAction.triggered.connect(self.onSphinxPath) # If user's config .json file lacks it, populate CodeChat's default # config key and Sphinx's default config key. if not 'CodeChat' in core.config(): core.config()['CodeChat'] = {} core.config()['CodeChat']['Enabled'] = False core.config().flush() if not 'Sphinx' in core.config(): core.config()['Sphinx'] = {} core.config()['Sphinx']['Enabled'] = False core.config()['Sphinx']['Executable'] = u'sphinx-build' core.config()['Sphinx']['ProjectPath'] = u'' core.config()['Sphinx']['BuildOnSave'] = False core.config()['Sphinx']['OutputPath'] = os.path.join( '_build', 'html') core.config()['Sphinx']['AdvancedMode'] = False core.config()['Sphinx']['Cmdline'] = ( u'sphinx-build -d ' + os.path.join('_build', 'doctrees') + ' . ' + os.path.join('_build', 'html')) core.config().flush() self._setSphinxActionVisibility() def del_(self): """Uninstall the plugin """ core.actionManager().removeAction('mTools/aSetSphinxPath') if self._dockInstalled: self._removeDock() if self._dock is not None: self._dock.del_() core.workspace().currentDocumentChanged.disconnect( self._onDocumentChanged) core.workspace().languageChanged.disconnect(self._onDocumentChanged) core.uiSettingsManager().aboutToExecute.disconnect( self._onSettingsDialogAboutToExecute) core.uiSettingsManager().dialogAccepted.disconnect( self._onDocumentChanged) if self._dock: self._dock.closed.disconnect(self._onDockClosed) self._dock.shown.disconnect(self._onDockShown) self._saveAction.triggered.disconnect(self._dock.onPreviewSave) def _onDocumentChanged(self): """Document or Language changed. Create dock, if necessary """ if self._canPreview(core.workspace().currentDocument()): if not self._dockInstalled: self._createDock() else: if self._dockInstalled: self._removeDock() def _canPreview(self, document): """Check if the given document can be shown in the Preview dock. """ if document is None: return False if document.qutepart.language() in ('Markdown', 'Restructured Text') or \ isHtmlFile(document): return True if canUseCodeChat(document.filePath()): return True if sphinxEnabledForFile(document.filePath()): return True return False def _createDock(self): """Install dock """ # create dock if self._dock is None: from enki.plugins.preview.preview import PreviewDock self._dock = PreviewDock() self._dock.closed.connect(self._onDockClosed) # Disconnected. self._dock.shown.connect(self._onDockShown) # Disconnected. self._saveAction = QAction(QIcon(':enkiicons/save.png'), 'Save Preview as HTML', self._dock) self._saveAction.setShortcut(QKeySequence("Alt+Shift+P")) self._saveAction.triggered.connect( self._dock.onPreviewSave) # Disconnected. core.mainWindow().addDockWidget(Qt.RightDockWidgetArea, self._dock) core.actionManager().addAction("mView/aPreview", self._dock.showAction()) core.actionManager().addAction("mFile/aSavePreview", self._saveAction) self._dockInstalled = True if core.config()['Preview']['Enabled']: self._dock.show() def _onDockClosed(self): """Dock has been closed by user. Change Enabled option """ if core.config()['Preview']['Enabled']: core.config()['Preview']['Enabled'] = False core.config().flush() def _onDockShown(self): """Dock has been shown by user. Change Enabled option """ if not core.config()['Preview']['Enabled']: core.config()['Preview']['Enabled'] = True core.config().flush() def _removeDock(self): """Remove dock from GUI """ core.actionManager().removeAction("mView/aPreview") core.actionManager().removeAction("mFile/aSavePreview") core.mainWindow().removeDockWidget(self._dock) self._dockInstalled = False def _onSettingsDialogAboutToExecute(self, dialog): """The UI settings dialog is about to execute. Install preview-related settings.""" CodeChatSettingsWidget(dialog) SphinxSettingsWidget(dialog) @pyqtSlot() def _setSphinxActionVisibility(self): self._sphinxAction.setVisible(core.config()['Sphinx']['Enabled']) @pyqtSlot() def onSphinxPath(self): core.config()['Sphinx']['ProjectPath'] = os.getcwd() core.config().flush() if core.config()['Preview']['Enabled']: self._dock._scheduleDocumentProcessing()
class PylintViewer(QWidget): " Pylint tab widget " # Limits to colorize the final score BadLimit = 8.5 GoodLimit = 9.5 # Options of providing a report SingleFile = 0 DirectoryFiles = 1 ProjectFiles = 2 SingleBuffer = 3 updatePylintTooltip = pyqtSignal(str) def __init__(self, parent=None): QWidget.__init__(self, parent) self.__reportUUID = "" self.__reportFileName = "" self.__reportOption = -1 self.__reportShown = False self.__report = None self.__widgets = [] # Prepare members for reuse if GlobalData().pylintAvailable: self.__noneLabel = QLabel("\nNo results available") else: self.__noneLabel = QLabel("\nPylint is not available") self.__noneLabel.setAutoFillBackground(True) noneLabelPalette = self.__noneLabel.palette() noneLabelPalette.setColor(QPalette.Background, GlobalData().skin.nolexerPaper) self.__noneLabel.setPalette(noneLabelPalette) self.__noneLabel.setFrameShape(QFrame.StyledPanel) self.__noneLabel.setAlignment(Qt.AlignHCenter) self.__headerFont = self.__noneLabel.font() self.__headerFont.setPointSize(self.__headerFont.pointSize() + 4) self.__noneLabel.setFont(self.__headerFont) self.__createLayout(parent) self.__updateButtonsStatus() self.resizeEvent() return def __createLayout(self, parent): " Creates the toolbar and layout " # Buttons self.printButton = QAction(PixmapCache().getIcon('printer.png'), 'Print', self) #printButton.setShortcut( 'Ctrl+' ) self.printButton.triggered.connect(self.__onPrint) self.printButton.setVisible(False) self.printPreviewButton = QAction( PixmapCache().getIcon('printpreview.png'), 'Print preview', self) #printPreviewButton.setShortcut( 'Ctrl+' ) self.printPreviewButton.triggered.connect(self.__onPrintPreview) self.printPreviewButton.setVisible(False) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.clearButton = QAction(PixmapCache().getIcon('trash.png'), 'Clear', self) self.clearButton.triggered.connect(self.__clear) # The toolbar self.toolbar = QToolBar(self) self.toolbar.setOrientation(Qt.Vertical) self.toolbar.setMovable(False) self.toolbar.setAllowedAreas(Qt.RightToolBarArea) self.toolbar.setIconSize(QSize(16, 16)) self.toolbar.setFixedWidth(28) self.toolbar.setContentsMargins(0, 0, 0, 0) self.toolbar.addAction(self.printPreviewButton) self.toolbar.addAction(self.printButton) self.toolbar.addWidget(spacer) self.toolbar.addAction(self.clearButton) self.__vLayout = QVBoxLayout() self.__vLayout.setContentsMargins(5, 5, 5, 5) self.__vLayout.setSpacing(0) self.__vLayout.setSizeConstraint(QLayout.SetFixedSize) self.__bodyFrame = QFrame(self) # self.__bodyFrame.setFrameShape( QFrame.StyledPanel ) self.__bodyFrame.setFrameShape(QFrame.NoFrame) # self.__bodyFrame.setSizePolicy( QSizePolicy.Maximum, # QSizePolicy.Expanding ) self.__bodyFrame.setLayout(self.__vLayout) self.bodyWidget = QScrollArea(self) self.bodyWidget.setFocusPolicy(Qt.NoFocus) self.bodyWidget.setWidget(self.__bodyFrame) self.bodyWidget.hide() self.__hLayout = QHBoxLayout() self.__hLayout.setContentsMargins(0, 0, 0, 0) self.__hLayout.setSpacing(0) self.__hLayout.addWidget(self.toolbar) self.__hLayout.addWidget(self.__noneLabel) self.__hLayout.addWidget(self.bodyWidget) self.setLayout(self.__hLayout) return def __updateButtonsStatus(self): " Updates the buttons status " self.printButton.setEnabled(self.__reportShown) self.printPreviewButton.setEnabled(self.__reportShown) self.clearButton.setEnabled(self.__reportShown) return def __onPrint(self): " Triggered when the print button is pressed " pass def __onPrintPreview(self): " triggered when the print preview button is pressed " pass def setFocus(self): " Overridden setFocus " self.__vLayout.setFocus() return def __clear(self): " Clears the content of the vertical layout " if not self.__reportShown: return self.__removeAll() self.bodyWidget.hide() self.__noneLabel.show() self.__report = None self.__reportShown = False self.__updateButtonsStatus() self.resizeEvent() self.__updateTooltip() return def __removeAll(self): " Removes all the items from the report " for item in self.__widgets: item.hide() self.__vLayout.removeWidget(item) del item self.__widgets = [] return def __createScoreLabel(self, score, previousScore, showFileName, fileName): " Creates the score label " txt = "Score: " + str(score) if previousScore != "": txt += " / Previous score: " + str(previousScore) if not showFileName: txt += " for " + os.path.basename(fileName) scoreLabel = QLabel(txt) scoreLabel.setFrameShape(QFrame.StyledPanel) scoreLabel.setFont(self.__headerFont) scoreLabel.setAutoFillBackground(True) palette = scoreLabel.palette() if score < self.BadLimit: palette.setColor(QPalette.Background, QColor(255, 127, 127)) palette.setColor(QPalette.Foreground, QColor(0, 0, 0)) elif score > self.GoodLimit: palette.setColor(QPalette.Background, QColor(220, 255, 220)) palette.setColor(QPalette.Foreground, QColor(0, 0, 0)) else: palette.setColor(QPalette.Background, QColor(255, 255, 127)) palette.setColor(QPalette.Foreground, QColor(0, 0, 0)) scoreLabel.setPalette(palette) return scoreLabel @staticmethod def __setTableHeight(table): " Auxiliary function to set the table height " # Height - it is ugly and approximate however I am tired of # calculating the proper height. Why is this so hard, huh? lastRowHeight = table.itemDelegate().lastHeight height = lastRowHeight * (table.topLevelItemCount() + 1) + 10 table.setFixedHeight(height) return @staticmethod def __shouldShowFileName(messages): " Decides if the file name column should be supressed " if len(messages) == 0: return False firstName = messages[0].fileName for index in range(1, len(messages)): if firstName != messages[index].fileName: return True return False def __addErrorsTable(self, messages, showFileName): " Creates the messages table " errTable = QTreeWidget(self.bodyWidget) errTable.setAlternatingRowColors(True) errTable.setRootIsDecorated(False) errTable.setItemsExpandable(False) errTable.setSortingEnabled(True) errTable.setItemDelegate(NoOutlineHeightDelegate(4)) errTable.setUniformRowHeights(True) errTable.itemActivated.connect(self.__errorActivated) headerLabels = ["File name", "Line", "Message ID", "Object", "Message"] errTable.setHeaderLabels(headerLabels) for item in messages: if item.position is None: lineNumber = str(item.lineNumber) else: lineNumber = str(item.lineNumber) + ":" + str(item.position) values = [ item.fileName, lineNumber, item.messageID, item.objectName, item.message ] errTable.addTopLevelItem(ErrorTableItem(values, 1)) # Hide the file name column if required if not showFileName: errTable.setColumnHidden(0, True) # Resizing errTable.header().resizeSections(QHeaderView.ResizeToContents) errTable.header().setStretchLastSection(True) # Sort indicator if showFileName: sortIndex = 0 # By file names else: sortIndex = 1 # By line number because this is from the same file errTable.header().setSortIndicator(sortIndex, Qt.AscendingOrder) errTable.sortItems(sortIndex, errTable.header().sortIndicatorOrder()) # Height self.__setTableHeight(errTable) self.__vLayout.addWidget(errTable) self.__widgets.append(errTable) return def __addSimilarity(self, similarity, titleText): " Adds a similarity " # Label title = QLabel(titleText) title.setFont(self.__headerFont) self.__vLayout.addWidget(title) self.__widgets.append(title) # List of files simTable = QTreeWidget(self.bodyWidget) simTable.setAlternatingRowColors(True) simTable.setRootIsDecorated(False) simTable.setItemsExpandable(False) simTable.setSortingEnabled(False) simTable.setItemDelegate(NoOutlineHeightDelegate(4)) simTable.setUniformRowHeights(True) simTable.itemActivated.connect(self.__similarityActivated) simTable.setHeaderLabels(["File name", "Line"]) for item in similarity.files: values = [item[0], str(item[1])] simTable.addTopLevelItem(QTreeWidgetItem(values)) # Resizing simTable.header().resizeSections(QHeaderView.ResizeToContents) simTable.header().setStretchLastSection(True) # Height self.__setTableHeight(simTable) self.__vLayout.addWidget(simTable) self.__widgets.append(simTable) # The fragment itself if len(similarity.fragment) > 10: # Take first 9 lines text = "\n".join(similarity.fragment[:9]) + "\n ..." toolTip = "\n".join(similarity.fragment) else: text = "\n".join(similarity.fragment) toolTip = "" fragmentLabel = QLabel("<pre>" + self.__htmlEncode(text) + "</pre>") if toolTip != "": fragmentLabel.setToolTip("<pre>" + self.__htmlEncode(toolTip) + "</pre>") palette = fragmentLabel.palette() palette.setColor(QPalette.Background, QColor(250, 250, 175)) palette.setColor(QPalette.Foreground, QColor(0, 0, 0)) fragmentLabel.setPalette(palette) fragmentLabel.setFrameShape(QFrame.StyledPanel) fragmentLabel.setAutoFillBackground(True) labelFont = fragmentLabel.font() labelFont.setFamily(GlobalData().skin.baseMonoFontFace) fragmentLabel.setFont(labelFont) self.__vLayout.addWidget(fragmentLabel) self.__widgets.append(fragmentLabel) return @staticmethod def __htmlEncode(string): " Encodes HTML " return string.replace( "&", "&" ) \ .replace( ">", ">" ) \ .replace( "<", "<" ) def __addSectionSpacer(self): " Adds a fixed height spacer to the VBox layout " spacer = QWidget() spacer.setFixedHeight(10) self.__vLayout.addWidget(spacer) self.__widgets.append(spacer) return def __addGenericTable(self, table): " Adds a generic table to the report " theTable = QTreeWidget(self.bodyWidget) theTable.setAlternatingRowColors(True) theTable.setRootIsDecorated(False) theTable.setItemsExpandable(False) theTable.setSortingEnabled(False) theTable.setItemDelegate(NoOutlineHeightDelegate(4)) theTable.setUniformRowHeights(True) headerLabels = [] for index in range(0, len(table.header)): headerLabels.append(table.header[index]) theTable.setHeaderLabels(headerLabels) for item in table.body: row = [] for index in range(0, len(table.header)): row.append(item[index]) theTable.addTopLevelItem(QTreeWidgetItem(row)) theTable.setFocusPolicy(Qt.NoFocus) # Resizing theTable.header().resizeSections(QHeaderView.ResizeToContents) theTable.header().setStretchLastSection(True) # Height self.__setTableHeight(theTable) self.__vLayout.addWidget(theTable) self.__widgets.append(theTable) return def __addGenericTableTitle(self, table): " Adds a generic table title " tableTitle = QLabel(table.title) tableTitle.setFont(self.__headerFont) self.__vLayout.addWidget(tableTitle) self.__widgets.append(tableTitle) return def __updateTooltip(self): " Generates a signal with appropriate string message " if not self.__reportShown: tooltip = "No results available" elif self.__reportOption == self.DirectoryFiles: tooltip = "Report generated for directory: " + \ self.__reportFileName elif self.__reportOption == self.ProjectFiles: tooltip = "Report generated for the whole project" elif self.__reportOption == self.SingleFile: tooltip = "Report generated for file: " + self.__reportFileName elif self.__reportOption == self.SingleBuffer: tooltip = "Report generated for unsaved file: " + \ self.__reportFileName else: tooltip = "" self.updatePylintTooltip.emit(tooltip) return def showReport(self, lint, reportOption, fileName, uuid): " Shows the pylint results " self.__removeAll() self.__noneLabel.hide() self.__report = lint self.__reportUUID = uuid self.__reportFileName = fileName self.__reportOption = reportOption showFileName = self.__shouldShowFileName(lint.errorMessages) scoreLabel = self.__createScoreLabel(lint.score, lint.previousScore, showFileName, fileName) self.__vLayout.addWidget(scoreLabel) self.__widgets.append(scoreLabel) if len(lint.errorMessages) > 0: self.__addSectionSpacer() self.__addErrorsTable(lint.errorMessages, showFileName) index = 0 for similarity in lint.similarities: self.__addSectionSpacer() self.__addSimilarity(similarity, "Similarity #" + str(index)) index += 1 for table in lint.tables: self.__addSectionSpacer() self.__addGenericTableTitle(table) self.__addGenericTable(table) self.bodyWidget.show() self.bodyWidget.ensureVisible(0, 0, 0, 0) self.__reportShown = True self.__updateButtonsStatus() self.__updateTooltip() # It helps, but why do I have flickering? QApplication.processEvents() self.__resizeBodyFrame() return def __errorActivated(self, item, column): " Handles the double click (or Enter) on the item " linePos = str(item.text(1)) if ":" in linePos: parts = linePos.split(":") lineNumber = int(parts[0]) pos = int(parts[1]) else: lineNumber = int(linePos) pos = 0 if self.__reportOption in [ self.SingleFile, self.DirectoryFiles, self.ProjectFiles ]: fileName = str(item.text(0)) else: # SingleBuffer if self.__reportFileName != "": if os.path.isabs(self.__reportFileName): fileName = self.__reportFileName else: # Could be unsaved buffer, so try to search by the mainWindow = GlobalData().mainWindow widget = mainWindow.getWidgetByUUID(self.__reportUUID) if widget is None: logging.error("The unsaved buffer has been closed") return # The widget was found, so jump to the required editor = widget.getEditor() editor.gotoLine(lineNumber, pos) editor.setFocus() return GlobalData().mainWindow.openFile(fileName, lineNumber, pos) return def __resizeBodyFrame(self): " Resizing the frame to occupy all available width " size = self.bodyWidget.maximumViewportSize() self.__bodyFrame.setMinimumWidth(size.width() - 16) self.__bodyFrame.setMinimumHeight(size.height()) return def showEvent(self, showEv=None): " Called when the widget is shown " self.__resizeBodyFrame() return def resizeEvent(self, resizeEv=None): " Called when the main window gets resized " self.__resizeBodyFrame() return def onFileUpdated(self, fileName, uuid): " Called when a buffer is saved or saved as " if not self.__reportShown: return if self.__reportUUID != uuid: return # Currently shown report is for the saved buffer # File name is expected being absolute self.__reportFileName = fileName self.updatePylintTooltip.emit("Report generated for buffer saved as " + fileName) return def __similarityActivated(self, item, column): " Triggered when a similarity is activated " fileName = str(item.text(0)) lineNumber = int(item.text(1)) GlobalData().mainWindow.openFile(fileName, lineNumber) return
class MainWindow(ui_mainwindow.Ui_MainWindow, QMainWindow): """ Main application window """ def __init__(self): super(MainWindow, self).__init__() self.setupUi(self) self.canvaslayers = [] self.layerbuttons = [] self.project = None self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas)) self.canvas.setCanvasColor(Qt.white) self.canvas.enableAntiAliasing(True) self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor) self.bar = roam.messagebaritems.MessageBar(self.centralwidget) self.actionMap.setVisible(False) self.actionLegend.setVisible(False) pal = QgsPalLabeling() self.canvas.mapRenderer().setLabelingEngine(pal) self.canvas.setFrameStyle(QFrame.NoFrame) self.menuGroup = QActionGroup(self) self.menuGroup.setExclusive(True) self.menuGroup.addAction(self.actionMap) self.menuGroup.addAction(self.actionDataEntry) self.menuGroup.addAction(self.actionLegend) self.menuGroup.addAction(self.actionProject) self.menuGroup.addAction(self.actionSync) self.menuGroup.addAction(self.actionSettings) self.menuGroup.addAction(self.actionGPS) self.menuGroup.triggered.connect(self.updatePage) self.editgroup = QActionGroup(self) self.editgroup.setExclusive(True) self.editgroup.addAction(self.actionPan) self.editgroup.addAction(self.actionZoom_In) self.editgroup.addAction(self.actionZoom_Out) self.editgroup.addAction(self.actionInfo) self.actionLegend.triggered.connect(self.updatelegend) self.actionGPS = GPSAction(":/icons/gps", self.canvas, self) self.projecttoolbar.addAction(self.actionGPS) self.projectwidget.requestOpenProject.connect(self.loadProject) QgsProject.instance().readProject.connect(self._readProject) self.gpswidget.setgps(GPS) self.actionSettings.toggled.connect( self.settingswidget.populateControls) self.actionSettings.toggled.connect(self.settingswidget.readSettings) self.settingswidget.settingsupdated.connect(self.settingsupdated) self.dataentrywidget = DataEntryWidget(self.canvas, self.bar) self.widgetpage.layout().addWidget(self.dataentrywidget) self.dataentrywidget.rejected.connect(self.formrejected) self.dataentrywidget.featuresaved.connect(self.featureSaved) self.dataentrywidget.featuredeleted.connect(self.featuredeleted) self.dataentrywidget.failedsave.connect(self.failSave) self.dataentrywidget.helprequest.connect(self.showhelp) def createSpacer(width=0, height=0): widget = QWidget() widget.setMinimumWidth(width) widget.setMinimumHeight(height) return widget gpsspacewidget = createSpacer(30) sidespacewidget = createSpacer(30) sidespacewidget2 = createSpacer(height=20) sidespacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sidespacewidget2.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.topspaceraction = self.projecttoolbar.insertWidget( self.actionGPS, gpsspacewidget) def createlabel(text): style = """ QLabel { color: #706565; font: 14px "Calibri" ; }""" label = QLabel(text) label.setStyleSheet(style) return label self.projectlabel = createlabel("Project: {project}") self.userlabel = createlabel( "User: {user}".format(user=getpass.getuser())) self.positionlabel = createlabel('') self.gpslabel = createlabel("GPS: Not active") self.statusbar.addWidget(self.projectlabel) self.statusbar.addWidget(self.userlabel) spacer = createSpacer() spacer2 = createSpacer() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) spacer2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.statusbar.addWidget(spacer) self.statusbar.addWidget(self.positionlabel) self.statusbar.addWidget(spacer2) self.statusbar.addWidget(self.gpslabel) self.menutoolbar.insertWidget(self.actionQuit, sidespacewidget2) self.menutoolbar.insertWidget(self.actionProject, sidespacewidget) self.panels = [] self.connectButtons() self.currentfeatureband = QgsRubberBand(self.canvas) self.currentfeatureband.setIconSize(20) self.currentfeatureband.setWidth(10) self.currentfeatureband.setColor(QColor(186, 93, 212, 76)) self.canvas_page.layout().insertWidget(0, self.projecttoolbar) self.dataentryselection = QAction(self.projecttoolbar) self.dataentryaction = self.projecttoolbar.insertAction( self.topspaceraction, self.dataentryselection) self.dataentryselection.triggered.connect(self.selectdataentry) self.centralwidget.layout().addWidget(self.statusbar) self.actionGPSFeature.setProperty('dataentry', True) self.infodock = InfoDock(self.canvas) self.infodock.featureupdated.connect(self.highlightfeature) self.infodock.hide() self.hidedataentry() self.canvas.extentsChanged.connect(self.updatestatuslabel) RoamEvents.openimage.connect(self.openimage) RoamEvents.openurl.connect(self.viewurl) RoamEvents.openfeatureform.connect(self.openForm) RoamEvents.openkeyboard.connect(self.openkeyboard) RoamEvents.selectioncleared.connect(self.clearselection) RoamEvents.editgeometry.connect(self.addforedit) RoamEvents.editgeometry_complete.connect(self.on_geometryedit) RoamEvents.onShowMessage.connect(self.showUIMessage) RoamEvents.selectionchanged.connect(self.highlightselection) RoamEvents.selectionchanged.connect(self.showInfoResults) GPS.gpspostion.connect(self.updatecanvasfromgps) GPS.firstfix.connect(self.gpsfirstfix) GPS.gpsdisconnected.connect(self.gpsdisconnected) self.lastgpsposition = None self.marker = GPSMarker(self.canvas) self.marker.hide() self.legendpage.showmap.connect(self.showmap) self.editfeaturestack = [] self.currentselection = {} def showUIMessage(self, label, message, level=QgsMessageBar.INFO, time=0, extra=''): self.bar.pushMessage(label, message, level, duration=time, extrainfo=extra) def addforedit(self, form, feature): self.editfeaturestack.append((form, feature)) self.loadform(form) actions = self.getcaptureactions() for action in actions: if action.isdefault: action.trigger() break def updatelegend(self): self.legendpage.updatecanvas(self.canvas) def gpsfirstfix(self, postion, gpsinfo): zoomtolocation = roam.config.settings.get('gpszoomonfix', True) if zoomtolocation: self.canvas.zoomScale(1000) def updatecanvasfromgps(self, position, gpsinfo): # Recenter map if we go outside of the 95% of the area if not self.lastgpsposition == position: self.lastposition = position rect = QgsRectangle(position, position) extentlimt = QgsRectangle(self.canvas.extent()) extentlimt.scale(0.95) if not extentlimt.contains(position): self.canvas.setExtent(rect) self.canvas.refresh() self.marker.show() self.marker.setCenter(position) self.gpslabel.setText("GPS: PDOP {} HDOP {} VDOP {}".format( gpsinfo.pdop, gpsinfo.hdop, gpsinfo.vdop)) def gpsdisconnected(self): self.marker.hide() self.gpslabel.setText("GPS Not Active") def openkeyboard(self): if not roam.config.settings.get('keyboard', True): return if sys.platform == 'win32': try: programfiles = os.environ['ProgramW6432'] except KeyError: programfiles = os.environ['ProgramFiles'] cmd = r'{path}\Common Files\Microsoft Shared\ink\TabTip.exe'.format( path=programfiles) try: os.startfile(cmd) except WindowsError: roam.config.settings['keyboard'] = False roam.config.save() else: cmd = 'onboard' Popen(cmd) def selectdataentry(self): forms = self.project.forms formpicker = PickActionDialog(msg="Select data entry form") for form in forms: action = form.createuiaction() valid, failreasons = form.valid if not valid: roam.utils.warning("Form {} failed to load".format(form.label)) roam.utils.warning("Reasons {}".format(failreasons)) action.triggered.connect(partial(self.showformerror, form)) else: action.triggered.connect(partial(self.loadform, form)) formpicker.addAction(action) formpicker.exec_() def showformerror(self, form): pass def viewurl(self, url): """ Open a URL in Roam :param url: :return: """ key = url.toString().lstrip('file://') try: # Hack. Eww fix me. data, imagetype = roam.htmlviewer.images[os.path.basename(key)] pix = QPixmap() if imagetype == 'base64': pix.loadFromData(data) else: pix.load(data) self.openimage(pix) except KeyError: pix = QPixmap() pix.load(key) if pix.isNull(): QDesktopServices.openUrl(url) return self.openimage(pix) def openimage(self, pixmap): viewer = ImageViewer(self.stackedWidget) viewer.resize(self.stackedWidget.size()) viewer.openimage(pixmap) def settingsupdated(self, settings): self.show() self.actionGPS.updateGPSPort() def updatestatuslabel(self): extent = self.canvas.extent() self.positionlabel.setText("Map Center: {}".format( extent.center().toString())) def on_geometryedit(self, form, feature): layer = form.QGISLayer self.reloadselection(layer, updated=[feature]) self.currentfeatureband.setToGeometry(feature.geometry(), layer) def reloadselection(self, layer, deleted=[], updated=[]): """ Reload the selection after features have been updated or deleted. :param layer: :param deleted: :param updated: :return: """ selectedfeatures = self.currentselection[layer] # Update any features that have changed. for updatedfeature in updated: oldfeatures = [ f for f in selectedfeatures if f.id() == updatedfeature.id() ] for feature in oldfeatures: self.currentselection[layer].remove(feature) self.currentselection[layer].append(updatedfeature) # Delete any old ones for deletedid in deleted: oldfeatures = [f for f in selectedfeatures if f.id() == deletedid] for feature in oldfeatures: self.currentselection[layer].remove(feature) RoamEvents.selectionchanged.emit(self.currentselection) def highlightselection(self, results): self.clearselection() for layer, features in results.iteritems(): band = self.selectionbands[layer] band.setColor(QColor(255, 0, 0, 200)) band.setIconSize(20) band.setWidth(2) band.setBrushStyle(Qt.NoBrush) band.reset(layer.geometryType()) for feature in features: band.addGeometry(feature.geometry(), layer) def clearselection(self): # Clear the main selection rubber band self.currentfeatureband.reset() # Clear the rest for band in self.selectionbands.itervalues(): band.reset() self.editfeaturestack = [] def highlightfeature(self, layer, feature, features): self.clearselection() self.highlightselection({layer: features}) self.currentfeatureband.setToGeometry(feature.geometry(), layer) def showmap(self): self.actionMap.setVisible(True) self.actionLegend.setVisible(True) self.actionMap.trigger() def hidedataentry(self): self.actionDataEntry.setVisible(False) def showdataentry(self): self.actionDataEntry.setVisible(True) self.actionDataEntry.trigger() def dataentrychanged(self, index): self.clearCapatureTools() if not index.isValid(): return modelindex = index # modelindex = self.dataentrymodel.index(index, 0) form = modelindex.data(Qt.UserRole + 1) self.dataentryselection.setCurrentIndex(index.row()) self.createCaptureButtons(form) def raiseerror(self, *exinfo): info = traceback.format_exception(*exinfo) item = self.bar.pushError( QApplication.translate( 'MainWindowPy', 'Seems something has gone wrong. Press for more details', None, QApplication.UnicodeUTF8), info) def setMapTool(self, tool, *args): self.canvas.setMapTool(tool) def homeview(self): """ Zoom the mapview canvas to the extents the project was opened at i.e. the default extent. """ self.canvas.setExtent(self.defaultextent) self.canvas.refresh() def connectButtons(self): def connectAction(action, tool): action.toggled.connect(partial(self.setMapTool, tool)) def cursor(name): pix = QPixmap(name) pix = pix.scaled(QSize(24, 24)) return QCursor(pix) self.zoomInTool = QgsMapToolZoom(self.canvas, False) self.zoomOutTool = QgsMapToolZoom(self.canvas, True) self.panTool = PanTool(self.canvas) self.infoTool = InfoTool(self.canvas) connectAction(self.actionZoom_In, self.zoomInTool) connectAction(self.actionZoom_Out, self.zoomOutTool) connectAction(self.actionPan, self.panTool) connectAction(self.actionInfo, self.infoTool) self.zoomInTool.setCursor(cursor(':/icons/in')) self.zoomOutTool.setCursor(cursor(':/icons/out')) self.infoTool.setCursor(cursor(':/icons/info')) self.actionRaster.triggered.connect(self.toggleRasterLayers) self.infoTool.infoResults.connect(RoamEvents.selectionchanged.emit) self.actionHome.triggered.connect(self.homeview) self.actionQuit.triggered.connect(self.exit) def getcaptureactions(self): for action in self.projecttoolbar.actions(): if action.property('dataentry'): yield action def clearCapatureTools(self): captureselected = False for action in self.projecttoolbar.actions(): if action.objectName() == "capture" and action.isChecked(): captureselected = True if action.property('dataentry'): self.projecttoolbar.removeAction(action) return captureselected def createCaptureButtons(self, form): tool = form.getMaptool()(self.canvas) for action in tool.actions: # Create the action here. if action.ismaptool: action.toggled.connect(partial(self.setMapTool, tool)) # Set the action as a data entry button so we can remove it later. action.setProperty("dataentry", True) self.editgroup.addAction(action) self.layerbuttons.append(action) self.projecttoolbar.insertAction(self.topspaceraction, action) action.setChecked(action.isdefault) if hasattr(tool, 'geometryComplete'): add = partial(self.addNewFeature, form) tool.geometryComplete.connect(add) else: tool.finished.connect(self.openForm) tool.error.connect(partial(self.showUIMessage, form.label)) def loadform(self, form): self.clearCapatureTools() self.dataentryselection.setIcon(QIcon(form.icon)) self.dataentryselection.setText(form.icontext) self.createCaptureButtons(form) def clearToolRubberBand(self): """ Clear the rubber band of the active tool if it has one """ tool = self.canvas.mapTool() try: tool.clearBand() except AttributeError: # No clearBand method found, but that's cool. pass def showhelp(self, url): help = HelpPage(self.stackedWidget) help.setHelpPage(url) help.show() def dataentryfinished(self): self.hidedataentry() self.showmap() self.cleartempobjects() self.infodock.refreshcurrent() def featuredeleted(self, layer, featureid): self.dataentryfinished() self.reloadselection(layer, deleted=[featureid]) self.canvas.refresh() def featureSaved(self): self.dataentryfinished() self.canvas.refresh() def failSave(self, messages): self.bar.pushError("Error when saving changes.", messages) def cleartempobjects(self): self.currentfeatureband.reset() self.clearToolRubberBand() def formrejected(self, message, level): self.dataentryfinished() if message: RoamEvents.raisemessage("Form Message", message, level, duration=2) self.cleartempobjects() def openForm(self, form, feature, editmode): """ Open the form that is assigned to the layer """ self.currentfeatureband.setToGeometry(feature.geometry(), form.QGISLayer) self.showdataentry() self.dataentrywidget.openform(feature=feature, form=form, project=self.project, editmode=editmode) def editfeaturegeometry(self, form, feature, newgeometry): layer = form.QGISLayer layer.startEditing() feature.setGeometry(newgeometry) layer.updateFeature(feature) saved = layer.commitChanges() map(roam.utils.error, layer.commitErrors()) self.canvas.refresh() RoamEvents.editgeometry_complete.emit(form, feature) def addNewFeature(self, form, geometry): """ Add a new new feature to the given layer """ layer = form.QGISLayer if layer.geometryType() in [ QGis.WKBMultiLineString, QGis.WKBMultiPoint, QGis.WKBMultiPolygon ]: geometry.convertToMultiType() try: form, feature = self.editfeaturestack.pop() self.editfeaturegeometry(form, feature, newgeometry=geometry) return except IndexError: pass layer = form.QGISLayer fields = layer.pendingFields() feature = QgsFeature(fields) feature.setGeometry(geometry) for index in xrange(fields.count()): pkindexes = layer.dataProvider().pkAttributeIndexes() if index in pkindexes and layer.dataProvider().name( ) == 'spatialite': continue value = layer.dataProvider().defaultValue(index) feature[index] = value self.openForm(form, feature, editmode=False) def exit(self): """ Exit the application. """ QApplication.exit(0) def showInfoResults(self, results): forms = {} for layer in results.keys(): layername = layer.name() if not layername in forms: forms[layername] = list(self.project.formsforlayer(layername)) self.currentselection = results self.infodock.setResults(results, forms) self.infodock.show() def toggleRasterLayers(self): """ Toggle all raster layers on or off. """ if not self.canvaslayers: return #Freeze the canvas to save on UI refresh self.canvas.freeze() for layer in self.canvaslayers: if layer.layer().type() == QgsMapLayer.RasterLayer: layer.setVisible(not layer.isVisible()) # Really!? We have to reload the whole layer set every time? # WAT? self.canvas.setLayerSet(self.canvaslayers) self.canvas.freeze(False) self.canvas.refresh() def missingLayers(self, layers): """ Called when layers have failed to load from the current project """ roam.utils.warning("Missing layers") map(roam.utils.warning, layers) missinglayers = roam.messagebaritems.MissingLayerItem(layers, parent=self.bar) self.bar.pushItem(missinglayers) def loadprojects(self, projects): """ Load the given projects into the project list """ projects = list(projects) self.projectwidget.loadProjectList(projects) self.syncwidget.loadprojects(projects) def updatePage(self, action): """ Update the current stack page based on the current selected action """ page = action.property("page") self.stackedWidget.setCurrentIndex(page) def show(self): """ Override show method. Handles showing the app in fullscreen mode or just maximized """ fullscreen = roam.config.settings.get("fullscreen", False) if fullscreen: self.showFullScreen() else: self.showMaximized() def viewprojects(self): self.stackedWidget.setCurrentIndex(1) @roam.utils.timeit def _readProject(self, doc): """ readProject is called by QgsProject once the map layer has been populated with all the layers """ parser = ProjectParser(doc) canvasnode = parser.canvasnode self.canvas.freeze() self.canvas.mapRenderer().readXML(canvasnode) self.canvaslayers = parser.canvaslayers() self.canvas.setLayerSet(self.canvaslayers) #red = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorRedPart", 255 )[0]; #green = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorGreenPart", 255 )[0]; #blue = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorBluePart", 255 )[0]; #color = QColor(red, green, blue); #self.canvas.setCanvasColor(color) self.canvas.updateScale() self.projectOpened() self.canvas.freeze(False) self.canvas.refresh() GPS.crs = self.canvas.mapRenderer().destinationCrs() self.showmap() @roam.utils.timeit def projectOpened(self): """ Called when a new project is opened in QGIS. """ projectpath = QgsProject.instance().fileName() self.project = Project.from_folder(os.path.dirname(projectpath)) self.projectlabel.setText("Project: {}".format(self.project.name)) try: firstform = self.project.forms[0] self.loadform(self.project.forms[0]) self.dataentryselection.setVisible(True) except IndexError: self.dataentryselection.setVisible(False) # Enable the raster layers button only if the project contains a raster layer. layers = QgsMapLayerRegistry.instance().mapLayers().values() hasrasters = any(layer.type() == QgsMapLayer.RasterLayer for layer in layers) self.actionRaster.setEnabled(hasrasters) self.defaultextent = self.canvas.extent() roam.utils.info("Extent: {}".format(self.defaultextent.toString())) # Show panels for panel in self.project.getPanels(): self.mainwindow.addDockWidget(Qt.BottomDockWidgetArea, panel) self.panels.append(panel) self.infoTool.selectionlayers = self.project.selectlayersmapping() layers = self.project.legendlayersmapping().values() self.legendpage.updateitems(layers) self.actionPan.trigger() #noinspection PyArgumentList @roam.utils.timeit def loadProject(self, project): """ Load a project into the application . """ roam.utils.log(project) roam.utils.log(project.name) roam.utils.log(project.projectfile) roam.utils.log(project.valid) (passed, message) = project.onProjectLoad() if not passed: self.bar.pushMessage("Project load rejected", "Sorry this project couldn't" "be loaded. Click for me details.", QgsMessageBar.WARNING, extrainfo=message) return self.actionMap.trigger() self.closeProject() self.canvas.refresh() self.canvas.repaint() RoamEvents.selectioncleared.emit() # No idea why we have to set this each time. Maybe QGIS deletes it for # some reason. self.badLayerHandler = BadLayerHandler(callback=self.missingLayers) QgsProject.instance().setBadLayerHandler(self.badLayerHandler) self.stackedWidget.setCurrentIndex(3) self.projectloading_label.setText("Project {} Loading".format( project.name)) pixmap = QPixmap(project.splash) w = self.projectimage.width() h = self.projectimage.height() self.projectimage.setPixmap(pixmap.scaled(w, h, Qt.KeepAspectRatio)) QApplication.processEvents() QDir.setCurrent(os.path.dirname(project.projectfile)) fileinfo = QFileInfo(project.projectfile) QgsProject.instance().read(fileinfo) def closeProject(self): """ Close the current open project """ self.clearCapatureTools() self.canvas.freeze() QgsMapLayerRegistry.instance().removeAllMapLayers() self.canvas.clear() self.canvas.freeze(False) for panel in self.panels: self.removeDockWidget(panel) del panel # Remove all the old buttons for action in self.layerbuttons: self.editgroup.removeAction(action) self.panels = [] self.project = None self.dataentrywidget.clear() self.hidedataentry() self.infodock.close()
class DockTitleBar(QToolBar): def __init__(self, parent): QToolBar.__init__(self, parent) assert parent self.dock = parent # a fake spacer widget w = QWidget(self) l = QHBoxLayout(w) l.setMargin(0) l.setSpacing(0) l.addStretch() frame = QFrame() layout = QBoxLayout(QBoxLayout.LeftToRight, frame) layout.setContentsMargins(4, 4, 0, 0) layout.setSpacing(2) self.aDockFrame = self.addWidget(frame) self.__icon = QLabel() layout.addWidget(self.__icon) layout.addWidget(QLabel(self.dock.windowTitle())) self.dock.windowIconChanged.connect(self.__setWindowIcon) # fake spacer item spacer = QWidgetAction(self) spacer.setDefaultWidget(w) self.setMovable(False) self.setFloatable(False) self.setIconSize(QSize(12, 12)) self.aFloat = QAction(self) self.aClose = QAction(self) QToolBar.addAction(self, spacer) self.separator = QToolBar.addSeparator(self) QToolBar.addAction(self, self.aFloat) QToolBar.addAction(self, self.aClose) self.updateStandardIcons() self.dockWidgetFeaturesChanged(self.dock.features()) self.dock.featuresChanged.connect(self.dockWidgetFeaturesChanged) self.aFloat.triggered.connect(self._floatTriggered) self.aClose.triggered.connect(self.dock.close) def addAction(self, action): self.insertAction(self.separator, action) return action def __setWindowIcon(self, icon): if not icon.isNull(): self.__icon.setPixmap(self.dock.windowIcon().pixmap(16)) def __setWindowTitle(self, title): self.__title.setText(title) def paintEvent(self, _): painter = QStylePainter(self) options = QStyleOptionToolBar() # init style options options.initFrom(self.dock) options.rect = self.rect() textRect = self.rect().adjusted(3, 3, 0, 0) msh = self.minimumSizeHint() # need to rotate if vertical state if self.dock.features() & QDockWidget.DockWidgetVerticalTitleBar: painter.rotate(-90) painter.translate(QPointF(-self.rect().height(), 0)) self.transposeSize(options.rect) self.transposeSize(textRect) msh.transpose() # draw toolbar painter.drawControl(QStyle.CE_ToolBar, options) # restore rotation if self.dock.features() & QDockWidget.DockWidgetVerticalTitleBar: painter.rotate(90) @staticmethod def transposeSize(rect): size = rect.size() size.transpose() rect.setSize(size) def updateStandardIcons(self): size = QSize(16, 16) rect = QRect(QPoint(), self.iconSize()) transform = QTransform() transform.rotate(90) pixmap = self.style().standardIcon(QStyle.SP_TitleBarNormalButton, None, self.widgetForAction(self.aFloat)).pixmap(size) rect.moveCenter(pixmap.rect().center()) pixmap = pixmap.copy(rect) self.aFloat.setIcon(QIcon(pixmap)) pixmap = self.style().standardIcon(QStyle.SP_TitleBarCloseButton, None, self.widgetForAction(self.aClose)).pixmap(size) rect.moveCenter(pixmap.rect().center()) pixmap = pixmap.copy(rect) self.aClose.setIcon(QIcon(pixmap)) def event(self, event): if event.type() == QEvent.StyleChange: self.updateStandardIcons() return QToolBar.event(self, event) def minimumSizeHint(self): return QToolBar.sizeHint(self) def sizeHint(self): size = QToolBar.sizeHint(self) fm = QFontMetrics(self.font()) if self.dock.features() & QDockWidget.DockWidgetVerticalTitleBar: size.setHeight(size.height() + fm.width(self.dock.windowTitle())) else: size.setWidth(size.width() + fm.width(self.dock.windowTitle())) return size def _floatTriggered(self): self.dock.setFloating(not self.dock.isFloating()) def dockWidgetFeaturesChanged(self, features): self.aFloat.setVisible(features & QDockWidget.DockWidgetFloatable) self.aClose.setVisible(features & QDockWidget.DockWidgetClosable) # update toolbar orientation if features & QDockWidget.DockWidgetVerticalTitleBar: if self.orientation() & Qt.Vertical: return self.setOrientation(Qt.Vertical) else: if self.orientation() & Qt.Horizontal: return self.setOrientation(Qt.Horizontal) # reorder the actions items = self.actions() items.reverse() self.clear() self.addActions(items)
class RunConsoleTabWidget( QWidget, MainWindowTabWidgetBase ): " IO console tab widget " textEditorZoom = pyqtSignal( int ) settingUpdated = pyqtSignal() def __init__( self, threadID, parent = None ): MainWindowTabWidgetBase.__init__( self ) QWidget.__init__( self, parent ) self.__viewer = RedirectedIOConsole( self ) self.connect( self.__viewer, SIGNAL( 'UserInput' ), self.__onUserInput ) self.__threadID = threadID self.__showstdin = True self.__showstdout = True self.__showstderr = True self.__createLayout() return def threadID( self ): " Provides the thread ID the console linked to " return self.__threadID def __onUserInput( self, userInput ): " Triggered when the user finished input in the redirected IO console " self.emit( SIGNAL( 'UserInput' ), self.__threadID, userInput ) self.__clearButton.setEnabled( True ) return def __createLayout( self ): " Creates the toolbar and layout " # Buttons self.__printButton = QAction( PixmapCache().getIcon( 'printer.png' ), 'Print', self ) self.__printButton.triggered.connect( self.__onPrint ) self.__printButton.setEnabled( False ) self.__printButton.setVisible( False ) self.__printPreviewButton = QAction( PixmapCache().getIcon( 'printpreview.png' ), 'Print preview', self ) self.__printPreviewButton.triggered.connect( self.__onPrintPreview ) self.__printPreviewButton.setEnabled( False ) self.__printPreviewButton.setVisible( False ) # self.__sendUpButton = QAction( PixmapCache().getIcon('sendioup.png'), # 'Send to Main Editing Area', self ) # self.__sendUpButton.triggered.connect( self.__sendUp ) self.__filterMenu = QMenu( self ) self.__filterMenu.aboutToShow.connect( self.__filterAboutToShow ) self.__filterGroup = QActionGroup( self ) self.__filterShowAllAct = self.__filterMenu.addAction( "Show all" ) self.__filterShowAllAct.setCheckable( True ) self.__filterShowAllAct.setActionGroup( self.__filterGroup ) self.__filterShowAllAct.triggered.connect( self.__onFilterShowAll ) self.__filterShowStdoutAct = self.__filterMenu.addAction( "Show stdin and stdout" ) self.__filterShowStdoutAct.setCheckable( True ) self.__filterShowStdoutAct.setActionGroup( self.__filterGroup ) self.__filterShowStdoutAct.triggered.connect( self.__onFilterShowStdout ) self.__filterShowStderrAct = self.__filterMenu.addAction( "Show stdin and stderr" ) self.__filterShowStderrAct.setCheckable( True ) self.__filterShowStderrAct.setActionGroup( self.__filterGroup ) self.__filterShowStderrAct.triggered.connect( self.__onFilterShowStderr ) self.__filterButton = QToolButton( self ) self.__filterButton.setIcon( PixmapCache().getIcon( 'iofilter.png' ) ) self.__filterButton.setToolTip( 'Filtering settings' ) self.__filterButton.setPopupMode( QToolButton.InstantPopup ) self.__filterButton.setMenu( self.__filterMenu ) self.__filterButton.setFocusPolicy( Qt.NoFocus ) self.__settingsMenu = QMenu( self ) self.__settingsMenu.aboutToShow.connect( self.__settingsAboutToShow ) self.__wrapLongLinesAct = self.__settingsMenu.addAction( "Wrap long lines" ) self.__wrapLongLinesAct.setCheckable( True ) self.__wrapLongLinesAct.triggered.connect( self.__onWrapLongLines ) self.__showEOLAct = self.__settingsMenu.addAction( "Show EOL" ) self.__showEOLAct.setCheckable( True ) self.__showEOLAct.triggered.connect( self.__onShowEOL ) self.__showWhitespacesAct = self.__settingsMenu.addAction( "Show whitespaces" ) self.__showWhitespacesAct.setCheckable( True ) self.__showWhitespacesAct.triggered.connect( self.__onShowWhitespaces ) self.__autoscrollAct = self.__settingsMenu.addAction( "Autoscroll" ) self.__autoscrollAct.setCheckable( True ) self.__autoscrollAct.triggered.connect( self.__onAutoscroll ) self.__showMarginAct = self.__settingsMenu.addAction( "Show timestamp margin" ) self.__showMarginAct.setCheckable( True ) self.__showMarginAct.triggered.connect( self.__onShowMargin ) self.__settingsButton = QToolButton( self ) self.__settingsButton.setIcon( PixmapCache().getIcon( 'iosettings.png' ) ) self.__settingsButton.setToolTip( 'View settings' ) self.__settingsButton.setPopupMode( QToolButton.InstantPopup ) self.__settingsButton.setMenu( self.__settingsMenu ) self.__settingsButton.setFocusPolicy( Qt.NoFocus ) fixedSpacer = QWidget() fixedSpacer.setFixedHeight( 8 ) self.__stopButton = QAction( PixmapCache().getIcon( 'runconsolestop.png' ), 'Stop process', self ) self.__stopButton.triggered.connect( self.stop ) self.__stopAndCloseButton = QAction( PixmapCache().getIcon( 'runconsolestopclose.png' ), 'Stop process and close tab', self ) self.__stopAndCloseButton.triggered.connect( self.stopAndClose ) spacer = QWidget() spacer.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Expanding ) self.__clearButton = QAction( PixmapCache().getIcon( 'trash.png' ), 'Clear', self ) self.__clearButton.triggered.connect( self.clear ) # The toolbar toolbar = QToolBar( self ) toolbar.setOrientation( Qt.Vertical ) toolbar.setMovable( False ) toolbar.setAllowedAreas( Qt.RightToolBarArea ) toolbar.setIconSize( QSize( 16, 16 ) ) toolbar.setFixedWidth( 28 ) toolbar.setContentsMargins( 0, 0, 0, 0 ) # toolbar.addAction( self.__sendUpButton ) toolbar.addAction( self.__printPreviewButton ) toolbar.addAction( self.__printButton ) toolbar.addWidget( self.__filterButton ) toolbar.addWidget( self.__settingsButton ) toolbar.addWidget( fixedSpacer ) toolbar.addAction( self.__stopButton ) toolbar.addAction( self.__stopAndCloseButton ) toolbar.addWidget( spacer ) toolbar.addAction( self.__clearButton ) hLayout = QHBoxLayout() hLayout.setContentsMargins( 0, 0, 0, 0 ) hLayout.setSpacing( 0 ) hLayout.addWidget( toolbar ) hLayout.addWidget( self.__viewer ) self.setLayout( hLayout ) return def onZoomReset( self ): " Triggered when the zoom reset button is pressed " if self.__viewer.zoom != 0: self.textEditorZoom.emit( 0 ) return True def onZoomIn( self ): " Triggered when the zoom in button is pressed " if self.__viewer.zoom < 20: self.textEditorZoom.emit( self.__viewer.zoom + 1 ) return True def onZoomOut( self ): " Triggered when the zoom out button is pressed " if self.__viewer.zoom > -10: self.textEditorZoom.emit( self.__viewer.zoom - 1 ) return True def __onPrint( self ): " Triggered when the print button is pressed " pass def __onPrintPreview( self ): " triggered when the print preview button is pressed " pass def setFocus( self ): " Overridden setFocus " self.__viewer.setFocus() return def onOpenImport( self ): " Triggered when Ctrl+I is received " return True def __sendUp( self ): " Triggered when requested to move the console up " return def __filterAboutToShow( self ): " Triggered when filter menu is about to show " showAll = self.__showstdin and \ self.__showstdout and \ self.__showstderr onlyStdout = self.__showstdin and \ self.__showstdout and \ not self.__showstderr onlyStderr = self.__showstdin and \ not self.__showstdout and \ self.__showstderr self.__filterShowAllAct.setChecked( showAll ) self.__filterShowStdoutAct.setChecked( onlyStdout ) self.__filterShowStderrAct.setChecked( onlyStderr ) return def __onFilterShowAll( self ): " Triggered when filter show all is clicked " if self.__showstdin == True and \ self.__showstdout == True and \ self.__showstderr == True: return self.__showstdin = True self.__showstdout = True self.__showstderr = True self.__viewer.renderContent() return def __onFilterShowStdout( self ): " Triggered when filter show stdout only is clicked " if self.__showstdin == True and \ self.__showstdout == True and \ self.__showstderr == False: return self.__showstdin = True self.__showstdout = True self.__showstderr = False self.__viewer.renderContent() return def __onFilterShowStderr( self ): " Triggered when filter show stderr only is clicked " if self.__showstdin == True and \ self.__showstdout == False and \ self.__showstderr == True: return self.__showstdin = True self.__showstdout = False self.__showstderr = True self.__viewer.renderContent() return def __settingsAboutToShow( self ): " Settings menu is about to show " self.__wrapLongLinesAct.setChecked( Settings().ioconsolelinewrap ) self.__showEOLAct.setChecked( Settings().ioconsoleshoweol ) self.__showWhitespacesAct.setChecked( Settings().ioconsoleshowspaces ) self.__autoscrollAct.setChecked( Settings().ioconsoleautoscroll ) self.__showMarginAct.setChecked( Settings().ioconsoleshowmargin ) return def __onWrapLongLines( self ): " Triggered when long lines setting is changed " Settings().ioconsolelinewrap = not Settings().ioconsolelinewrap self.settingUpdated.emit() return def __onShowEOL( self ): " Triggered when show EOL is changed " Settings().ioconsoleshoweol = not Settings().ioconsoleshoweol self.settingUpdated.emit() return def __onShowWhitespaces( self ): " Triggered when show whitespaces is changed " Settings().ioconsoleshowspaces = not Settings().ioconsoleshowspaces self.settingUpdated.emit() return def __onAutoscroll( self ): " Triggered when autoscroll is changed " Settings().ioconsoleautoscroll = not Settings().ioconsoleautoscroll return def __onShowMargin( self ): " Triggered when show margin is changed " Settings().ioconsoleshowmargin = not Settings().ioconsoleshowmargin self.settingUpdated.emit() return def clear( self ): " Triggered when requested to clear the console " self.__viewer.clearAll() return def consoleSettingsUpdated( self ): " Triggered when one of the consoles updated a common setting " if Settings().ioconsolelinewrap: self.__viewer.setWrapMode( QsciScintilla.WrapWord ) else: self.__viewer.setWrapMode( QsciScintilla.WrapNone ) self.__viewer.setEolVisibility( Settings().ioconsoleshoweol ) if Settings().ioconsoleshowspaces: self.__viewer.setWhitespaceVisibility( QsciScintilla.WsVisible ) else: self.__viewer.setWhitespaceVisibility( QsciScintilla.WsInvisible ) self.__viewer.setTimestampMarginWidth() return def resizeEvent( self, event ): QWidget.resizeEvent( self, event ) return def onPylint( self ): return True def onPymetrics( self ): return True def onRunScript( self, action = False ): return True def onRunScriptSettings( self ): return True def onProfileScript( self, action = False ): return True def onProfileScriptSettings( self ): return True def onImportDgm( self, action = None ): return True def onImportDgmTuned( self ): return True def shouldAcceptFocus( self ): return True def writeFile( self, fileName ): " Writes the text to a file " return self.__viewer.writeFile( fileName ) def updateModificationTime( self, fileName ): return def appendIDEMessage( self, text ): " Appends an IDE message " self.__viewer.appendIDEMessage( text ) return def appendStdoutMessage( self, text ): " Appends an stdout message " self.__viewer.appendStdoutMessage( text ) return def appendStderrMessage( self, text ): " Appends an stderr message " self.__viewer.appendStderrMessage( text ) return def hiddenMessage( self, msg ): " Returns True if the message should not be shown " if msg.msgType == IOConsoleMsg.STDERR_MESSAGE and \ not self.__showstderr: return True if msg.msgType == IOConsoleMsg.STDOUT_MESSAGE and \ not self.__showstdout: return True return False def zoomTo( self, zoomValue ): " Sets the new zoom value " self.__viewer.zoomTo( zoomValue ) self.__viewer.setTimestampMarginWidth() return def rawInput( self, prompt, echo ): " Triggered when raw input is requested " echo = int( echo ) if echo == 0: self.__viewer.inputEcho = False else: self.__viewer.inputEcho = True if prompt: self.appendStdoutMessage( prompt ) self.__clearButton.setEnabled( False ) self.__viewer.switchMode( self.__viewer.MODE_INPUT ) return def sessionStopped( self ): " Triggered when redirecting session is stopped " self.__viewer.switchMode( self.__viewer.MODE_OUTPUT ) self.__clearButton.setEnabled( True ) return def stop( self ): " Triggered when the user requesed to stop the process " self.emit( SIGNAL( 'KillIOConsoleProcess' ), self.__threadID ) return def stopAndClose( self ): " Triggered when the user requested to stop the process and close console " self.stop() self.close() return def close( self ): " Triggered when the console should be closed " self.emit( SIGNAL( 'CloseIOConsole' ), self.__threadID ) return def scriptFinished( self ): " Triggered when the script process finished " self.__stopButton.setEnabled( False ) self.__stopAndCloseButton.setToolTip( "Close tab" ) self.__viewer.switchMode( self.__viewer.MODE_OUTPUT ) self.__clearButton.setEnabled( True ) return # Mandatory interface part is below def getEditor( self ): " Provides the editor widget " return self.__viewer def isModified( self ): " Tells if the file is modified " return False def getRWMode( self ): " Tells if the file is read only " return "IO" def getFileType( self ): " Provides the file type " return TexFileType def setFileType( self, typeToSet ): """ Sets the file type explicitly. It needs e.g. for .cgi files which can change its type """ raise Exception( "Setting a file type is not supported by the " "IO console widget" ) def getType( self ): " Tells the widget type " return MainWindowTabWidgetBase.IOConsole def getLanguage( self ): " Tells the content language " return "IO console" def getFileName( self ): " Tells what file name of the widget content " return "n/a" def setFileName( self, name ): " Sets the file name " raise Exception( "Setting a file name for IO console " "is not applicable" ) def getEol( self ): " Tells the EOL style " return self.__viewer.getEolIndicator() def getLine( self ): " Tells the cursor line " line, _ = self.__viewer.getCursorPosition() return int( line ) def getPos( self ): " Tells the cursor column " _, pos = self.__viewer.getCursorPosition() return int( pos ) def getEncoding( self ): " Tells the content encoding " return self.__viewer.encoding def setEncoding( self, newEncoding ): " Sets the new editor encoding " raise Exception( "Setting encoding is not supported by the " "IO console widget" ) def getShortName( self ): " Tells the display name " return "IO console" def setShortName( self, name ): " Sets the display name " raise Exception( "Setting short name is not supported by the " "IO console widget" )
class DisassemblerResultsWidget( QWidget, MainWindowTabWidgetBase ): " Disassembling results widget " escapePressed = pyqtSignal() textEditorZoom = pyqtSignal( int ) def __init__( self, scriptName, name, code, reportTime, parent = None ): MainWindowTabWidgetBase.__init__( self ) QWidget.__init__( self, parent ) self.__createLayout( scriptName, name, code, reportTime ) return def __createLayout( self, scriptName, name, code, reportTime ): " Creates the toolbar and layout " # Buttons self.__printButton = QAction( PixmapCache().getIcon( 'printer.png' ), 'Print', self ) self.__printButton.triggered.connect( self.__onPrint ) self.__printButton.setEnabled( False ) self.__printButton.setVisible( False ) self.__printPreviewButton = QAction( PixmapCache().getIcon( 'printpreview.png' ), 'Print preview', self ) self.__printPreviewButton.triggered.connect( self.__onPrintPreview ) self.__printPreviewButton.setEnabled( False ) self.__printPreviewButton.setVisible( False ) # Zoom buttons self.__zoomInButton = QAction( PixmapCache().getIcon( 'zoomin.png' ), 'Zoom in (Ctrl+=)', self ) self.__zoomInButton.triggered.connect( self.onZoomIn ) self.__zoomOutButton = QAction( PixmapCache().getIcon( 'zoomout.png' ), 'Zoom out (Ctrl+-)', self ) self.__zoomOutButton.triggered.connect( self.onZoomOut ) self.__zoomResetButton = QAction( PixmapCache().getIcon( 'zoomreset.png' ), 'Zoom reset (Ctrl+0)', self ) self.__zoomResetButton.triggered.connect( self.onZoomReset ) spacer = QWidget() spacer.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Expanding ) # Toolbar toolbar = QToolBar( self ) toolbar.setOrientation( Qt.Vertical ) toolbar.setMovable( False ) toolbar.setAllowedAreas( Qt.RightToolBarArea ) toolbar.setIconSize( QSize( 16, 16 ) ) toolbar.setFixedWidth( 28 ) toolbar.setContentsMargins( 0, 0, 0, 0 ) toolbar.addAction( self.__printPreviewButton ) toolbar.addAction( self.__printButton ) toolbar.addWidget( spacer ) toolbar.addAction( self.__zoomInButton ) toolbar.addAction( self.__zoomOutButton ) toolbar.addAction( self.__zoomResetButton ) summary = QLabel( "<b>Script:</b> " + scriptName + "<br>" "<b>Name:</b> " + name + "<br>" "<b>Disassembled at:</b> " + reportTime ) summary.setFrameStyle( QFrame.StyledPanel ) summary.setAutoFillBackground( True ) summaryPalette = summary.palette() summaryBackground = summaryPalette.color( QPalette.Background ) summaryBackground.setRgb( min( summaryBackground.red() + 30, 255 ), min( summaryBackground.green() + 30, 255 ), min( summaryBackground.blue() + 30, 255 ) ) summaryPalette.setColor( QPalette.Background, summaryBackground ) summary.setPalette( summaryPalette ) self.__text = DisasmWidget( self ) self.__text.setAcceptRichText( False ) self.__text.setLineWrapMode( QTextEdit.NoWrap ) self.__text.setFont( GlobalData().skin.nolexerFont ) self.zoomTo( Settings().zoom ) self.__text.setReadOnly( True ) self.__text.setPlainText( code ) vLayout = QVBoxLayout() vLayout.addWidget( summary ) vLayout.addWidget( self.__text ) hLayout = QHBoxLayout() hLayout.setContentsMargins( 0, 0, 0, 0 ) hLayout.setSpacing( 0 ) hLayout.addLayout( vLayout ) hLayout.addWidget( toolbar ) self.setLayout( hLayout ) return def setFocus( self ): " Overriden setFocus " self.__text.setFocus() return def __onPrint( self ): " Triggered on the 'print' button " pass def __onPrintPreview( self ): " Triggered on the 'print preview' button " pass def onZoomReset( self ): " Triggered when the zoom reset button is pressed " zoom = Settings().zoom if zoom != 0: self.textEditorZoom.emit( 0 ) return True def onZoomIn( self ): " Triggered when the zoom in button is pressed " zoom = Settings().zoom if zoom < 20: self.textEditorZoom.emit( zoom + 1 ) return True def onZoomOut( self ): " Triggered when the zoom out button is pressed " zoom = Settings().zoom if zoom > -10: self.textEditorZoom.emit( zoom - 1 ) return True def keyPressEvent( self, event ): " Handles the key press events " if event.key() == Qt.Key_Escape: self.escapePressed.emit() event.accept() else: QWidget.keyPressEvent( self, event ) return def zoomTo( self, zoomFactor ): " Scales the font in accordance to the given zoom factor " font = QFont( GlobalData().skin.nolexerFont ) newPointSize = font.pointSize() + zoomFactor font.setPointSize( newPointSize ) self.__text.setFont( font ) return # Mandatory interface part is below def isModified( self ): " Tells if the file is modified " return False def getRWMode( self ): " Tells if the file is read only " return "RO" def getType( self ): " Tells the widget type " return MainWindowTabWidgetBase.DisassemblerViewer def getLanguage( self ): " Tells the content language " return "Disassembler" def getFileName( self ): " Tells what file name of the widget content " return "N/A" def setFileName( self, name ): " Sets the file name - not applicable" raise Exception( "Setting a file name for disassembler results is not applicable" ) def getEol( self ): " Tells the EOL style " return "N/A" def getLine( self ): " Tells the cursor line " return "N/A" def getPos( self ): " Tells the cursor column " return "N/A" def getEncoding( self ): " Tells the content encoding " return "N/A" def setEncoding( self, newEncoding ): " Sets the new encoding - not applicable for the disassembler results viewer " return def getShortName( self ): " Tells the display name " return "Disassembling results" def setShortName( self, name ): " Sets the display name - not applicable " raise Exception( "Setting a file name for disassembler results is not applicable" )
class MainUI(remembering.RememberingMainWindow): """Main UI.""" _programs_file = os.path.join(multiplatform.data_dir, 'encuentro.data') def __init__(self, version, app_quit): super(MainUI, self).__init__() self.app_quit = app_quit self.finished = False self.version = version self.setWindowTitle('Encuentro') self.programs_data = data.ProgramsData(self, self._programs_file) self._touch_config() self.downloaders = {} for downtype, dloader_class in all_downloaders.iteritems(): self.downloaders[downtype] = dloader_class() # finish all gui stuff self.big_panel = central_panel.BigPanel(self) self.episodes_list = self.big_panel.episodes self.episodes_download = self.big_panel.downloads_widget self.setCentralWidget(self.big_panel) # the setting of menubar should be almost in the end, because it may # trigger the wizard, which needs big_panel and etc. self.action_play = self.action_download = None self.filter_line = self.filter_cbox = self.needsomething_alert = None self._menubar() systray.show(self) if config.get('autorefresh'): ue = update.UpdateEpisodes(self) ue.background() else: # refresh data if never done before or if last # update was 7 days ago last_refresh = config.get('autorefresh_last_time') if last_refresh is None or (dt.datetime.now() - last_refresh > dt.timedelta(7)): ue = update.UpdateEpisodes(self) ue.background() self.show() self.episodes_download.load_pending() logger.debug("Main UI started ok") def _touch_config(self): """Do some config processing.""" # log the config, but without user and pass safecfg = config.sanitized_config() logger.debug("Configuration loaded: %s", safecfg) # we have a default for download dir if not config.get('downloaddir'): config['downloaddir'] = multiplatform.get_download_dir() # maybe clean some config if self.programs_data.reset_config_from_migration: config['user'] = '' config['password'] = '' config.pop('cols_width', None) config.pop('cols_order', None) config.pop('selected_row', None) def have_config(self): """Return if some config is needed.""" return config.get('user') and config.get('password') def have_metadata(self): """Return if metadata is needed.""" return bool(self.programs_data) def _menubar(self): """Set up the menu bar.""" menubar = self.menuBar() # applications menu menu_appl = menubar.addMenu(u'&Aplicación') icon = self.style().standardIcon(QStyle.SP_BrowserReload) action_reload = QAction(icon, '&Refrescar', self) action_reload.setShortcut('Ctrl+R') action_reload.setToolTip(u'Recarga la lista de programas') action_reload.triggered.connect(self.refresh_episodes) menu_appl.addAction(action_reload) icon = self.style().standardIcon(QStyle.SP_FileDialogDetailedView) action_preferences = QAction(icon, u'&Preferencias', self) action_preferences.triggered.connect(self.open_preferences) action_preferences.setToolTip( u'Configurar distintos parámetros del programa') menu_appl.addAction(action_preferences) menu_appl.addSeparator() icon = self.style().standardIcon(QStyle.SP_MessageBoxInformation) _act = QAction(icon, '&Acerca de', self) _act.triggered.connect(self.open_about_dialog) _act.setToolTip(u'Muestra información de la aplicación') menu_appl.addAction(_act) icon = self.style().standardIcon(QStyle.SP_DialogCloseButton) _act = QAction(icon, '&Salir', self) _act.setShortcut('Ctrl+Q') _act.setToolTip(u'Sale de la aplicación') _act.triggered.connect(self.on_close) menu_appl.addAction(_act) # program menu menu_prog = menubar.addMenu(u'&Programa') icon = self.style().standardIcon(QStyle.SP_ArrowDown) self.action_download = QAction(icon, '&Descargar', self) self.action_download.setShortcut('Ctrl+D') self.action_download.setEnabled(False) self.action_download.setToolTip(TTIP_DOWNLOAD_D) self.action_download.triggered.connect(self.download_episode) menu_prog.addAction(self.action_download) icon = self.style().standardIcon(QStyle.SP_MediaPlay) self.action_play = QAction(icon, '&Reproducir', self) self.action_play.setEnabled(False) self.action_play.setToolTip(TTIP_PLAY_D) self.action_play.triggered.connect(self.on_play_action) menu_prog.addAction(self.action_play) # toolbar for buttons toolbar = self.addToolBar('main') toolbar.addAction(self.action_download) toolbar.addAction(self.action_play) toolbar.addSeparator() toolbar.addAction(action_reload) toolbar.addAction(action_preferences) # filter text and button, to the right spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) toolbar.addWidget(spacer) toolbar.addWidget(QLabel(u"Filtro: ")) self.filter_line = QLineEdit() self.filter_line.setMaximumWidth(150) self.filter_line.textChanged.connect(self.on_filter_changed) toolbar.addWidget(self.filter_line) self.filter_cbox = QCheckBox(u"Sólo descargados") self.filter_cbox.stateChanged.connect(self.on_filter_changed) toolbar.addWidget(self.filter_cbox) # if needed, a warning that stuff needs to be configured icon = self.style().standardIcon(QStyle.SP_MessageBoxWarning) m = u"Necesita configurar algo; haga click aquà para abrir el wizard" self.needsomething_alert = QAction(icon, m, self) self.needsomething_alert.triggered.connect(self._start_wizard) toolbar.addAction(self.needsomething_alert) if not config.get('nowizard'): self._start_wizard() self._review_need_something_indicator() def _start_wizard(self, _=None): """Start the wizard if needed.""" if not self.have_config() or not self.have_metadata(): dlg = wizard.WizardDialog(self) dlg.exec_() self._review_need_something_indicator() def on_filter_changed(self, _): """The filter text has changed, apply it in the episodes list.""" text = self.filter_line.text() cbox = self.filter_cbox.checkState() self.episodes_list.set_filter(text, cbox) # after applying filter, nothing is selected, so check buttons # (easiest way to clean them all) self.check_download_play_buttons() def _review_need_something_indicator(self): """Hide/show/enable/disable different indicators if need sth.""" needsomething = bool(not self.have_config() or not self.have_metadata()) self.needsomething_alert.setVisible(needsomething) def shutdown(self): """Stop everything and quit. This shutdown con be called at any time, even on init, so we have extra precautions about which attributes we have. """ signal.emit('save_state') config.save() self.finished = True programs_data = getattr(self, 'programs_data', None) if programs_data is not None: programs_data.save() downloaders = getattr(self, 'downloaders', {}) for downloader in downloaders.itervalues(): downloader.shutdown() # bye bye self.app_quit() def on_close(self, _): """Close signal.""" if self._should_close(): self.shutdown() def closeEvent(self, event): """All is being closed.""" if self._should_close(): self.shutdown() else: event.ignore() def _should_close(self): """Still time to decide if want to close or not.""" logger.info("Attempt to close the program") pending = self.episodes_download.pending() if not pending: # all fine, save all and quit logger.info("Saving states and quitting") return True logger.debug("Still %d active downloads when trying to quit", pending) # stuff pending m = (u"Hay programas todavÃa en proceso de descarga!\n" u"¿Seguro quiere salir del programa?") QMB = QMessageBox dlg = QMB(u"Guarda!", m, QMB.Question, QMB.Yes, QMB.No, QMB.NoButton) opt = dlg.exec_() if opt != QMB.Yes: logger.info("Quit cancelled") return False # quit anyway, put all downloading and pending episodes to none logger.info("Fixing episodes, saving state and exiting") for program in self.programs_data.values(): state = program.state if state == Status.waiting or state == Status.downloading: program.state = Status.none return True def show_message(self, err_type, text): """Show different messages to the user.""" if self.finished: logger.debug("Ignoring message: %r", text) return logger.debug("Showing a message: %r", text) # error text can be produced by windows, try to to sanitize it if isinstance(text, str): try: text = text.decode("utf8") except UnicodeDecodeError: try: text = text.decode("latin1") except UnicodeDecodeError: text = repr(text) QMB = QMessageBox dlg = QMB(u"Atención: " + err_type, text, QMB.Warning, QMB.Ok, QMB.NoButton, QMB.NoButton) dlg.exec_() def refresh_episodes(self, _=None): """Update and refresh episodes.""" ue = update.UpdateEpisodes(self) ue.interactive() def download_episode(self, _=None): """Download the episode(s).""" episode_ids = self.episodes_list.selected_items() for episode_id in episode_ids: episode = self.programs_data[episode_id] self.queue_download(episode) @defer.inline_callbacks def queue_download(self, episode): """User indicated to download something.""" logger.debug("Download requested of %s", episode) if episode.state != Status.none: logger.debug( "Download denied, episode %s is not in downloadeable " "state.", episode.episode_id) return # queue self.episodes_download.append(episode) self.episodes_list.episode_info.update(episode) self.check_download_play_buttons() if self.episodes_download.downloading: return logger.debug("Downloads: starting") while self.episodes_download.pending(): episode = self.episodes_download.prepare() try: filename, episode = yield self._episode_download(episode) except CancelledError: logger.debug("Got a CancelledError!") self.episodes_download.end(error=u"Cancelado") except BadCredentialsError: logger.debug("Bad credentials error!") msg = (u"Error con las credenciales: hay que configurar " u"usuario y clave correctos") self.show_message('BadCredentialsError', msg) self.episodes_download.end(error=msg) except EncuentroError, e: orig_exc = e.orig_exc msg = "%s(%s)" % (orig_exc, e) err_type = e.__class__.__name__ logger.exception("Custom Encuentro error: %s (%r)", e, orig_exc) notify(err_type, msg) self.episodes_download.end(error=u"Error: " + msg) except Exception, e: logger.exception("Unknown download error: %s (%r)", e, e) err_type = e.__class__.__name__ notify(err_type, str(e)) self.episodes_download.end(error=u"Error: " + str(e)) else:
class FindInFilesViewer(QWidget): " Find in files viewer tab widget " lastEntered = None def __init__(self, parent=None): QWidget.__init__(self, parent) global searchTooltip searchTooltip = Tooltip() self.__reportRegexp = None self.__reportResults = [] self.__reportShown = False self.__bufferChangeconnected = False # Prepare members for reuse self.__noneLabel = QLabel("\nNo results available") self.__noneLabel.setFrameShape(QFrame.StyledPanel) self.__noneLabel.setAlignment(Qt.AlignHCenter) self.__headerFont = self.__noneLabel.font() self.__headerFont.setPointSize(self.__headerFont.pointSize() + 4) self.__noneLabel.setFont(self.__headerFont) self.__noneLabel.setAutoFillBackground(True) noneLabelPalette = self.__noneLabel.palette() noneLabelPalette.setColor(QPalette.Background, GlobalData().skin.nolexerPaper) self.__noneLabel.setPalette(noneLabelPalette) # Keep pylint happy self.printButton = None self.clearButton = None self.printPreviewButton = None self.__createLayout(parent) self.__updateButtonsStatus() GlobalData().project.projectChanged.connect(self.__onProjectChanged) return def __createLayout(self, parent): " Creates the toolbar and layout " # Buttons self.printButton = QAction(PixmapCache().getIcon("printer.png"), "Print", self) # printButton.setShortcut( 'Ctrl+' ) self.printButton.triggered.connect(self.__onPrint) self.printButton.setVisible(False) self.printPreviewButton = QAction(PixmapCache().getIcon("printpreview.png"), "Print preview", self) # printPreviewButton.setShortcut( 'Ctrl+' ) self.printPreviewButton.triggered.connect(self.__onPrintPreview) self.printPreviewButton.setVisible(False) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.clearButton = QAction(PixmapCache().getIcon("trash.png"), "Clear", self) self.clearButton.triggered.connect(self.__clear) # The toolbar self.toolbar = QToolBar(self) self.toolbar.setOrientation(Qt.Vertical) self.toolbar.setMovable(False) self.toolbar.setAllowedAreas(Qt.RightToolBarArea) self.toolbar.setIconSize(QSize(16, 16)) self.toolbar.setFixedWidth(28) self.toolbar.setContentsMargins(0, 0, 0, 0) self.toolbar.addAction(self.printPreviewButton) self.toolbar.addAction(self.printButton) self.toolbar.addWidget(spacer) self.toolbar.addAction(self.clearButton) self.__resultsTree = FindResultsTreeWidget() self.__resultsTree.setAlternatingRowColors(True) self.__resultsTree.setRootIsDecorated(True) self.__resultsTree.setItemsExpandable(True) self.__resultsTree.setUniformRowHeights(True) self.__resultsTree.setItemDelegate(NoOutlineHeightDelegate(4)) headerLabels = ["File name / line", "Text"] self.__resultsTree.setHeaderLabels(headerLabels) self.__resultsTree.itemActivated.connect(self.__resultActivated) self.__resultsTree.itemClicked.connect(self.__resultClicked) self.__resultsTree.setMouseTracking(True) self.__resultsTree.itemEntered.connect(self.__itemEntered) self.__resultsTree.hide() self.__hLayout = QHBoxLayout() self.__hLayout.setContentsMargins(0, 0, 0, 0) self.__hLayout.setSpacing(0) self.__hLayout.addWidget(self.toolbar) self.__hLayout.addWidget(self.__noneLabel) self.__hLayout.addWidget(self.__resultsTree) self.setLayout(self.__hLayout) return def getResultsTree(self): " Provides a reference to the results tree " return self.__resultsTree def __updateButtonsStatus(self): " Updates the buttons status " self.printButton.setEnabled(self.__reportShown) self.printPreviewButton.setEnabled(self.__reportShown) self.clearButton.setEnabled(self.__reportShown) return def __onPrint(self): " Triggered when the print button is pressed " pass def __onPrintPreview(self): " triggered when the print preview button is pressed " pass def setFocus(self): " Overridden setFocus " self.__hLayout.setFocus() return def __onProjectChanged(self, what): " Triggered when a project is changed " if what == CodimensionProject.CompleteProject: self.__clear() return def __clear(self): " Clears the content of the vertical layout " if not self.__reportShown: return # Disconnect the buffer change signal if it is connected if self.__bufferChangeconnected: self.__bufferChangeconnected = False mainWindow = GlobalData().mainWindow editorsManager = mainWindow.editorsManagerWidget.editorsManager editorsManager.bufferModified.disconnect(self.__resultsTree.onBufferModified) self.__resultsTree.resetCache() self.__resultsTree.clear() self.__resultsTree.hide() self.__noneLabel.show() self.__reportRegexp = None self.__reportResults = [] self.__reportShown = False self.__updateButtonsStatus() return def showReport(self, regexp, results): " Shows the find in files results " self.__clear() self.__noneLabel.hide() self.__reportRegexp = regexp self.__reportResults = results # Add the complete information totalMatched = 0 for item in results: matched = len(item.matches) totalMatched += matched if matched == 1: matchText = " (1 match)" else: matchText = " (" + str(matched) + " matches)" columns = [item.fileName, matchText] fileItem = MatchTableFileItem(columns, item.bufferUUID) fileItem.setIcon(0, getFileIcon(detectFileType(item.fileName))) if item.tooltip != "": fileItem.setToolTip(0, item.tooltip) self.__resultsTree.addTopLevelItem(fileItem) # Matches for match in item.matches: columns = [str(match.line), match.text] matchItem = MatchTableItem(columns, match.tooltip) fileItem.addChild(matchItem) fileItem.setExpanded(True) # Update the header with the total number of matches headerLabels = [ "File name / line (total files: " + str(len(results)) + ")", "Text (total matches: " + str(totalMatched) + ")", ] self.__resultsTree.setHeaderLabels(headerLabels) # Resizing the table self.__resultsTree.header().resizeSections(QHeaderView.ResizeToContents) # Show the complete information self.__resultsTree.show() self.__resultsTree.buildCache() self.__reportShown = True self.__updateButtonsStatus() # Connect the buffer change signal if not connected yet if not self.__bufferChangeconnected: self.__bufferChangeconnected = True mainWindow = GlobalData().mainWindow editorsManager = mainWindow.editorsManagerWidget.editorsManager editorsManager.bufferModified.connect(self.__resultsTree.onBufferModified) return def __resultClicked(self, item, column): " Handles the single click " hideSearchTooltip() return def __resultActivated(self, item, column): " Handles the double click (or Enter) on a match " if type(item) == MatchTableItem: fileName = str(item.parent().data(0, Qt.DisplayRole).toString()) lineNumber = int(item.data(0, Qt.DisplayRole).toString()) GlobalData().mainWindow.openFile(fileName, lineNumber) hideSearchTooltip() return def __itemEntered(self, item, column): " Triggered when the mouse cursor entered a row " if type(item) != MatchTableItem: self.lastEntered = item hideSearchTooltip() return if column != 1: # Show the tooltip only for the column with results self.lastEntered = None hideSearchTooltip() return # Memorize the row height for proper tooltip displaying later global cellHeight cellHeight = self.__resultsTree.visualItemRect(item).height() if self.lastEntered != item or not inside: item.itemEntered() self.lastEntered = item return
class QtAction(QtToolkitObject, ProxyAction): """ A Qt implementation of an Enaml ProxyAction. """ #: A reference to the widget created by the proxy. widget = Typed(QAction) #: Cyclic notification guard. This a bitfield of multiple guards. _guard = Int(0) # FIXME Currently, the checked state of the action is lost when # switching from checkable to non-checkable and back again. #-------------------------------------------------------------------------- # Initialization API #-------------------------------------------------------------------------- def create_widget(self): """ Create the underlying QAction object. """ self.widget = QAction(self.parent_widget()) def init_widget(self): """ Initialize the underlying control. """ super(QtAction, self).init_widget() d = self.declaration if d.text: self.set_text(d.text) if d.tool_tip: self.set_tool_tip(d.tool_tip) if d.status_tip: self.set_status_tip(d.status_tip) if d.icon: self.set_icon(d.icon) self.set_checkable(d.checkable) self.set_checked(d.checked) self.set_enabled(d.enabled) self.set_visible(d.visible) self.set_separator(d.separator) widget = self.widget widget.triggered.connect(self.on_triggered) widget.toggled.connect(self.on_toggled) #-------------------------------------------------------------------------- # Signal Handlers #-------------------------------------------------------------------------- def on_triggered(self, checked): """ The signal handler for the 'triggered' signal. """ if not self._guard & CHECKED_GUARD: self.declaration.checked = checked self.declaration.triggered(checked) def on_toggled(self, checked): """ The signal handler for the 'toggled' signal. """ if not self._guard & CHECKED_GUARD: self.declaration.checked = checked self.declaration.toggled(checked) #-------------------------------------------------------------------------- # ProxyAction API #-------------------------------------------------------------------------- def set_text(self, text): """ Set the text on the underlying control. """ widget = self.widget widget.setText(text) parts = text.split('\t') if len(parts) > 1: shortcut = QKeySequence(parts[-1]) widget.setShortcut(shortcut) def set_tool_tip(self, tool_tip): """ Set the tool tip on the underlying control. """ self.widget.setToolTip(tool_tip) def set_status_tip(self, status_tip): """ Set the status tip on the underyling control. """ self.widget.setStatusTip(status_tip) def set_icon(self, icon): """ Set the icon for the action. """ if icon: qicon = get_cached_qicon(icon) else: qicon = QIcon() self.widget.setIcon(qicon) def set_checkable(self, checkable): """ Set the checkable state on the underlying control. """ self.widget.setCheckable(checkable) def set_checked(self, checked): """ Set the checked state on the underlying control. """ self._guard |= CHECKED_GUARD try: self.widget.setChecked(checked) finally: self._guard &= ~CHECKED_GUARD def set_enabled(self, enabled): """ Set the enabled state on the underlying control. """ self.widget.setEnabled(enabled) def set_visible(self, visible): """ Set the visible state on the underlying control. """ self.widget.setVisible(visible) def set_separator(self, separator): """ Set the separator state on the underlying control. """ self.widget.setSeparator(separator)
class IgnoredExceptionsViewer(QWidget): " Implements the client exceptions viewer for a debugger " def __init__(self, parent=None): QWidget.__init__(self, parent) self.__createPopupMenu() self.__createLayout() self.__ignored = [] self.__currentItem = None GlobalData().project.projectChanged.connect(self.__onProjectChanged) if Settings().showIgnoredExcViewer == False: self.__onShowHide(True) return def __createPopupMenu(self): " Creates the popup menu " self.__excptMenu = QMenu() self.__removeMenuItem = self.__excptMenu.addAction( PixmapCache().getIcon('ignexcptdel.png'), "Remove from ignore list", self.__onRemoveFromIgnore) return def __createLayout(self): " Creates the widget layout " verticalLayout = QVBoxLayout(self) verticalLayout.setContentsMargins(0, 0, 0, 0) verticalLayout.setSpacing(0) self.headerFrame = QFrame() self.headerFrame.setFrameStyle(QFrame.StyledPanel) self.headerFrame.setAutoFillBackground(True) headerPalette = self.headerFrame.palette() headerBackground = headerPalette.color(QPalette.Background) headerBackground.setRgb(min(headerBackground.red() + 30, 255), min(headerBackground.green() + 30, 255), min(headerBackground.blue() + 30, 255)) headerPalette.setColor(QPalette.Background, headerBackground) self.headerFrame.setPalette(headerPalette) self.headerFrame.setFixedHeight(24) self.__excptLabel = QLabel("Ignored exception types") expandingSpacer = QSpacerItem(10, 10, QSizePolicy.Expanding) fixedSpacer = QSpacerItem(3, 3) self.__showHideButton = QToolButton() self.__showHideButton.setAutoRaise(True) self.__showHideButton.setIcon(PixmapCache().getIcon('less.png')) self.__showHideButton.setFixedSize(20, 20) self.__showHideButton.setToolTip("Hide ignored exceptions list") self.__showHideButton.setFocusPolicy(Qt.NoFocus) self.__showHideButton.clicked.connect(self.__onShowHide) headerLayout = QHBoxLayout() headerLayout.setContentsMargins(1, 1, 1, 1) headerLayout.addSpacerItem(fixedSpacer) headerLayout.addWidget(self.__excptLabel) headerLayout.addSpacerItem(expandingSpacer) headerLayout.addWidget(self.__showHideButton) self.headerFrame.setLayout(headerLayout) self.exceptionsList = QTreeWidget(self) self.exceptionsList.setSortingEnabled(False) self.exceptionsList.setAlternatingRowColors(True) self.exceptionsList.setRootIsDecorated(False) self.exceptionsList.setItemsExpandable(True) self.exceptionsList.setUniformRowHeights(True) self.exceptionsList.setSelectionMode(QAbstractItemView.SingleSelection) self.exceptionsList.setSelectionBehavior(QAbstractItemView.SelectRows) self.exceptionsList.setItemDelegate(NoOutlineHeightDelegate(4)) self.exceptionsList.setContextMenuPolicy(Qt.CustomContextMenu) self.exceptionsList.customContextMenuRequested.connect( self.__showContextMenu) self.exceptionsList.itemSelectionChanged.connect( self.__onSelectionChanged) self.exceptionsList.setHeaderLabels(["Exception type"]) self.__excTypeEdit = QLineEdit() self.__excTypeEdit.setFixedHeight(26) self.__excTypeEdit.textChanged.connect(self.__onNewFilterChanged) self.__excTypeEdit.returnPressed.connect(self.__onAddExceptionFilter) self.__addButton = QPushButton("Add") # self.__addButton.setFocusPolicy( Qt.NoFocus ) self.__addButton.setEnabled(False) self.__addButton.clicked.connect(self.__onAddExceptionFilter) expandingSpacer2 = QWidget() expandingSpacer2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.__removeButton = QAction(PixmapCache().getIcon('delitem.png'), "Remove selected exception type", self) self.__removeButton.triggered.connect(self.__onRemoveFromIgnore) self.__removeButton.setEnabled(False) fixedSpacer1 = QWidget() fixedSpacer1.setFixedWidth(5) self.__removeAllButton = QAction( PixmapCache().getIcon('ignexcptdelall.png'), "Remove all the exception types", self) self.__removeAllButton.triggered.connect(self.__onRemoveAllFromIgnore) self.__removeAllButton.setEnabled(False) self.toolbar = QToolBar() self.toolbar.setOrientation(Qt.Horizontal) self.toolbar.setMovable(False) self.toolbar.setAllowedAreas(Qt.TopToolBarArea) self.toolbar.setIconSize(QSize(16, 16)) self.toolbar.setFixedHeight(28) self.toolbar.setContentsMargins(0, 0, 0, 0) self.toolbar.addWidget(expandingSpacer2) self.toolbar.addAction(self.__removeButton) self.toolbar.addWidget(fixedSpacer1) self.toolbar.addAction(self.__removeAllButton) addLayout = QHBoxLayout() addLayout.setContentsMargins(1, 1, 1, 1) addLayout.setSpacing(1) addLayout.addWidget(self.__excTypeEdit) addLayout.addWidget(self.__addButton) verticalLayout.addWidget(self.headerFrame) verticalLayout.addWidget(self.toolbar) verticalLayout.addWidget(self.exceptionsList) verticalLayout.addLayout(addLayout) return def clear(self): " Clears the content " self.exceptionsList.clear() self.__excTypeEdit.clear() self.__addButton.setEnabled(False) self.__ignored = [] self.__currentItem = None self.__updateTitle() return def __onShowHide(self, startup=False): " Triggered when show/hide button is clicked " if startup or self.exceptionsList.isVisible(): self.exceptionsList.setVisible(False) self.__excTypeEdit.setVisible(False) self.__addButton.setVisible(False) self.__removeButton.setVisible(False) self.__removeAllButton.setVisible(False) self.__showHideButton.setIcon(PixmapCache().getIcon('more.png')) self.__showHideButton.setToolTip("Show ignored exceptions list") self.__minH = self.minimumHeight() self.__maxH = self.maximumHeight() self.setMinimumHeight(self.headerFrame.height()) self.setMaximumHeight(self.headerFrame.height()) Settings().showIgnoredExcViewer = False else: self.exceptionsList.setVisible(True) self.__excTypeEdit.setVisible(True) self.__addButton.setVisible(True) self.__removeButton.setVisible(True) self.__removeAllButton.setVisible(True) self.__showHideButton.setIcon(PixmapCache().getIcon('less.png')) self.__showHideButton.setToolTip("Hide ignored exceptions list") self.setMinimumHeight(self.__minH) self.setMaximumHeight(self.__maxH) Settings().showIgnoredExcViewer = True return def __onSelectionChanged(self): " Triggered when the current item is changed " selected = list(self.exceptionsList.selectedItems()) if selected: self.__currentItem = selected[0] self.__removeButton.setEnabled(True) else: self.__currentItem = None self.__removeButton.setEnabled(False) return def __showContextMenu(self, coord): " Shows the frames list context menu " contextItem = self.exceptionsList.itemAt(coord) if contextItem is not None: self.__currentItem = contextItem self.__excptMenu.popup(QCursor.pos()) return def __updateTitle(self): " Updates the section title " count = self.exceptionsList.topLevelItemCount() if count == 0: self.__excptLabel.setText("Ignored exception types") else: self.__excptLabel.setText("Ignored exception types (total: " + str(count) + ")") self.__removeAllButton.setEnabled(count != 0) return def __onProjectChanged(self, what): " Triggered when a project is changed " if what != CodimensionProject.CompleteProject: return self.clear() project = GlobalData().project if project.isLoaded(): self.__ignored = list(project.ignoredExcpt) else: self.__ignored = list(Settings().ignoredExceptions) for exceptionType in self.__ignored: item = QTreeWidgetItem(self.exceptionsList) item.setText(0, exceptionType) self.__updateTitle() return def __onNewFilterChanged(self, text): " Triggered when the text is changed " text = str(text).strip() if text == "": self.__addButton.setEnabled(False) return if " " in text: self.__addButton.setEnabled(False) return if text in self.__ignored: self.__addButton.setEnabled(False) return self.__addButton.setEnabled(True) return def __onAddExceptionFilter(self): " Adds an item into the ignored exceptions list " text = self.__excTypeEdit.text().strip() self.addExceptionFilter(text) def addExceptionFilter(self, excType): " Adds a new item into the ignored exceptions list " if excType == "": return if " " in excType: return if excType in self.__ignored: return item = QTreeWidgetItem(self.exceptionsList) item.setText(0, excType) project = GlobalData().project if project.isLoaded(): project.addExceptionFilter(excType) else: Settings().addExceptionFilter(excType) self.__ignored.append(excType) self.__updateTitle() return def __onRemoveFromIgnore(self): " Removes an item from the ignored exception types list " if self.__currentItem is None: return text = self.__currentItem.text(0) # Find the item index and remove it index = 0 while True: if self.exceptionsList.topLevelItem(index).text(0) == text: self.exceptionsList.takeTopLevelItem(index) break index += 1 project = GlobalData().project if project.isLoaded(): project.deleteExceptionFilter(text) else: Settings().deleteExceptionFilter(text) self.__ignored.remove(text) self.__updateTitle() return def __onRemoveAllFromIgnore(self): " Triggered when all the ignored exceptions should be deleted " self.clear() project = GlobalData().project if project.isLoaded(): project.setExceptionFilters([]) else: Settings().setExceptionFilters([]) return def isIgnored(self, exceptionType): " Returns True if this exception type should be ignored " return exceptionType in self.__ignored
class MapWidget(Ui_CanvasWidget, QMainWindow): def __init__(self, parent=None): super(MapWidget, self).__init__(parent) self.setupUi(self) self.snapping = True icon = roam_style.iconsize() self.projecttoolbar.setIconSize(QSize(icon, icon)) self.current_form = None self.last_form = None self.firstshow = True self.layerbuttons = [] self.editfeaturestack = [] self.lastgpsposition = None self.project = None self.gps = None self.gpslogging = None self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas)) self.bridge = QgsLayerTreeMapCanvasBridge( QgsProject.instance().layerTreeRoot(), self.canvas) self.bridge.setAutoSetupOnFirstLayer(False) QgsProject.instance().writeProject.connect(self.bridge.writeProject) QgsProject.instance().readProject.connect(self.bridge.readProject) # self.canvas.setInteractive(False) self.canvas.setCanvasColor(Qt.white) self.canvas.enableAntiAliasing(True) self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor) self.snappingutils = SnappingUtils(self.canvas, self) self.canvas.setSnappingUtils(self.snappingutils) QgsProject.instance().readProject.connect( self.snappingutils.readConfigFromProject) if hasattr(self.canvas, 'setParallelRenderingEnabled'): threadcount = QThread.idealThreadCount() threadcount = 2 if threadcount > 2 else 1 QgsApplication.setMaxThreads(threadcount) self.canvas.setParallelRenderingEnabled(True) pal = QgsPalLabeling() self.canvas.mapRenderer().setLabelingEngine(pal) self.canvas.setFrameStyle(QFrame.NoFrame) self.editgroup = QActionGroup(self) self.editgroup.setExclusive(True) self.editgroup.addAction(self.actionPan) self.editgroup.addAction(self.actionZoom_In) self.editgroup.addAction(self.actionZoom_Out) self.editgroup.addAction(self.actionInfo) self.actionGPS = GPSAction(":/icons/gps", self.canvas, self) self.projecttoolbar.addAction(self.actionGPS) if roam.config.settings.get('north_arrow', False): self.northarrow = NorthArrow(":/icons/north", self.canvas) self.northarrow.setPos(10, 10) self.canvas.scene().addItem(self.northarrow) smallmode = roam.config.settings.get("smallmode", False) self.projecttoolbar.setSmallMode(smallmode) self.scalebar_enabled = roam.config.settings.get('scale_bar', False) if self.scalebar_enabled: self.scalebar = ScaleBarItem(self.canvas) self.canvas.scene().addItem(self.scalebar) self.projecttoolbar.setContextMenuPolicy(Qt.CustomContextMenu) gpsspacewidget = QWidget() gpsspacewidget.setMinimumWidth(30) gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.topspaceraction = self.projecttoolbar.insertWidget( self.actionGPS, gpsspacewidget) self.dataentryselection = QAction(self.projecttoolbar) self.dataentryaction = self.projecttoolbar.insertAction( self.topspaceraction, self.dataentryselection) self.dataentryselection.triggered.connect(self.select_data_entry) self.marker = GPSMarker(self.canvas) self.marker.hide() self.currentfeatureband = CurrentSelection(self.canvas) self.currentfeatureband.setIconSize(30) self.currentfeatureband.setWidth(10) self.currentfeatureband.setColor(QColor(186, 93, 212, 50)) self.currentfeatureband.setOutlineColour(QColor(186, 93, 212)) self.gpsband = QgsRubberBand(self.canvas) self.gpsband.setColor(QColor(165, 111, 212, 75)) self.gpsband.setWidth(5) RoamEvents.editgeometry.connect(self.queue_feature_for_edit) RoamEvents.selectioncleared.connect(self.clear_selection) RoamEvents.selectionchanged.connect(self.highlight_selection) RoamEvents.openfeatureform.connect(self.feature_form_loaded) RoamEvents.sync_complete.connect(self.refresh_map) RoamEvents.snappingChanged.connect(self.snapping_changed) self.snappingbutton = QToolButton() self.snappingbutton.setText("Snapping: On") self.snappingbutton.setAutoRaise(True) self.snappingbutton.pressed.connect(self.toggle_snapping) spacer = QWidget() spacer2 = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) spacer2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.scalewidget = QgsScaleComboBox() self.scalebutton = QToolButton() self.scalebutton.setAutoRaise(True) self.scalebutton.setMaximumHeight(self.statusbar.height()) self.scalebutton.pressed.connect(self.selectscale) self.scalebutton.setText("Scale") self.scalelist = BigList(parent=self.canvas, centeronparent=True, showsave=False) self.scalelist.hide() self.scalelist.setlabel("Map Scale") self.scalelist.setmodel(self.scalewidget.model()) self.scalelist.closewidget.connect(self.scalelist.close) self.scalelist.itemselected.connect(self.update_scale_from_item) self.scalelist.itemselected.connect(self.scalelist.close) self.positionlabel = QLabel('') self.gpslabel = QLabel("GPS: Not active") self.gpslabelposition = QLabel("") self.statusbar.addWidget(self.snappingbutton) self.statusbar.addWidget(spacer2) self.statusbar.addWidget(self.gpslabel) self.statusbar.addWidget(self.gpslabelposition) self.statusbar.addPermanentWidget(self.scalebutton) self.canvas.extentsChanged.connect(self.updatestatuslabel) self.canvas.scaleChanged.connect(self.updatestatuslabel) GPS.gpsposition.connect(self.update_gps_label) GPS.gpsdisconnected.connect(self.gps_disconnected) self.connectButtons() def clear_plugins(self): toolbars = self.findChildren(QToolBar) for toolbar in toolbars: if toolbar.property("plugin_toolbar"): toolbar.unload() self.removeToolBar(toolbar) toolbar.deleteLater() def add_plugins(self, pluginnames): for name in pluginnames: # Get the plugin try: plugin_mod = plugins.loaded_plugins[name] except KeyError: continue if not hasattr(plugin_mod, 'toolbars'): roam.utils.warning( "No toolbars() function found in {}".format(name)) continue toolbars = plugin_mod.toolbars() self.load_plugin_toolbars(toolbars) def load_plugin_toolbars(self, toolbars): for ToolBarClass in toolbars: toolbar = ToolBarClass(plugins.api, self) self.addToolBar(Qt.BottomToolBarArea, toolbar) toolbar.setProperty("plugin_toolbar", True) def snapping_changed(self, snapping): """ Called when the snapping settings have changed. Updates the label in the status bar. :param snapping: """ if snapping: self.snappingbutton.setText("Snapping: On") else: self.snappingbutton.setText("Snapping: Off") def toggle_snapping(self): """ Toggle snapping on or off. """ self.snapping = not self.snapping try: self.canvas.mapTool().toggle_snapping() except AttributeError: pass RoamEvents.snappingChanged.emit(self.snapping) def selectscale(self): """ Show the select scale widget. :return: """ self.scalelist.show() def update_scale_from_item(self, index): """ Update the canvas scale from the selected scale item. :param index: The index of the selected item. """ scale, _ = self.scalewidget.toDouble(index.data(Qt.DisplayRole)) self.canvas.zoomScale(1.0 / scale) def update_gps_label(self, position, gpsinfo): """ Update the GPS label in the status bar with the GPS status. :param position: The current GPS position. :param gpsinfo: The current extra GPS information. """ self.gpslabel.setText( "GPS: PDOP <b>{0:.2f}</b> HDOP <b>{1:.2f}</b> VDOP <b>{2:.2f}</b>" .format(gpsinfo.pdop, gpsinfo.hdop, gpsinfo.vdop)) places = roam.config.settings.get("gpsplaces", 8) self.gpslabelposition.setText( "X <b>{x:.{places}f}</b> Y <b>{y:.{places}f}</b>".format( x=position.x(), y=position.y(), places=places)) def gps_disconnected(self): """ Called when the GPS is disconnected. Updates the label in the status bar with the message. :return: """ self.gpslabel.setText("GPS Not Active") self.gpslabelposition.setText("") def zoom_to_feature(self, feature): box = feature.geometry().boundingBox() xmin, xmax, ymin, ymax = box.xMinimum(), box.xMaximum(), box.yMinimum( ), box.yMaximum() xmin -= 5 xmax += 5 ymin -= 5 ymax += 5 box = QgsRectangle(xmin, ymin, xmax, ymax) self.canvas.setExtent(box) self.canvas.refresh() def updatestatuslabel(self, *args): """ Update the status bar labels when the information has changed. """ extent = self.canvas.extent() self.positionlabel.setText("Map Center: {}".format( extent.center().toString())) scale = 1.0 / self.canvas.scale() scale = self.scalewidget.toString(scale) self.scalebutton.setText(scale) def refresh_map(self): """ Refresh the map """ self.canvas.refresh() def updatescale(self): """ Update the scale of the map with the current scale from the scale widget :return: """ self.canvas.zoomScale(1.0 / self.scalewidget.scale()) def init_qgisproject(self, doc): """ Called when the project file is read for the firs time. :param doc: The XML doc. :return: The current canvas CRS :note: This method is old and needs to be refactored into something else. """ return self.canvas.mapSettings().destinationCrs() def showEvent(self, *args, **kwargs): """ Handle the show event of the of the map widget. We have to do a little hack here to make the QGIS map refresh. """ if QGis.QGIS_VERSION_INT == 20200 and self.firstshow: self.canvas.refresh() self.canvas.repaint() self.firstshow = False def feature_form_loaded(self, form, feature, *args): """ Called when the feature form is loaded. :param form: The Form object. Holds a reference to the forms layer. :param feature: The current capture feature """ self.currentfeatureband.setToGeometry(feature.geometry(), form.QGISLayer) def highlight_selection(self, results): """ Highlight the selection on the canvas. This updates all selected objects based on the result set. :param results: A dict-of-list of layer-features. """ self.clear_selection() for layer, features in results.iteritems(): band = self.selectionbands[layer] band.setColor(QColor(255, 0, 0)) band.setIconSize(25) band.setWidth(5) band.setBrushStyle(Qt.NoBrush) band.reset(layer.geometryType()) band.setZValue(self.currentfeatureband.zValue() - 1) for feature in features: band.addGeometry(feature.geometry(), layer) self.canvas.update() def highlight_active_selection(self, layer, feature, features): """ Update the current active selected feature. :param layer: The layer of the active feature. :param feature: The active feature. :param features: The other features in the set to show as non active selection. :return: """ self.clear_selection() self.highlight_selection({layer: features}) self.currentfeatureband.setToGeometry(feature.geometry(), layer) self.canvas.update() def clear_selection(self): """ Clear the selection from the canvas. Resets all selection rubbber bands. :return: """ # Clear the main selection rubber band self.canvas.scene().update() self.currentfeatureband.reset() # Clear the rest for band in self.selectionbands.itervalues(): band.reset() self.canvas.update() self.editfeaturestack = [] def queue_feature_for_edit(self, form, feature): """ Push a feature on the edit stack so the feature can have the geometry edited. :note: This is a big hack and I don't like it! :param form: The form for the current feature :param feature: The active feature. """ def trigger_default_action(): for action in self.projecttoolbar.actions(): if action.property('dataentry') and action.isdefault: action.trigger() self.canvas.mapTool().setEditMode(True, feature.geometry()) break self.editfeaturestack.append((form, feature)) self.save_current_form() self.load_form(form) trigger_default_action() def save_current_form(self): self.last_form = self.current_form def restore_last_form(self): self.load_form(self.last_form) def clear_temp_objects(self): """ Clear all temp objects from the canvas. :return: """ def clear_tool_band(): """ Clear the rubber band of the active tool if it has one """ tool = self.canvas.mapTool() try: tool.clearBand() except AttributeError: # No clearBand method found, but that's cool. pass self.currentfeatureband.reset() clear_tool_band() def settings_updated(self, settings): """ Called when the settings have been updated in the Roam config. :param settings: A dict of the settings. """ self.actionGPS.updateGPSPort() gpslogging = settings.get('gpslogging', True) if self.gpslogging: self.gpslogging.logging = gpslogging def set_gps(self, gps, logging): """ Set the GPS for the map widget. Connects GPS signals """ self.gps = gps self.gpslogging = logging self.gps.gpsposition.connect(self.gps_update_canvas) self.gps.firstfix.connect(self.gps_first_fix) self.gps.gpsdisconnected.connect(self.gps_disconnected) def gps_update_canvas(self, position, gpsinfo): """ Updates the map canvas based on the GPS position. By default if the GPS is outside the canvas extent the canvas will move to center on the GPS. Can be turned off in settings. :param postion: The current GPS position. :param gpsinfo: The extra GPS information """ # Recenter map if we go outside of the 95% of the area if self.gpslogging.logging: self.gpsband.addPoint(position) self.gpsband.show() if roam.config.settings.get('gpscenter', True): if not self.lastgpsposition == position: self.lastposition = position rect = QgsRectangle(position, position) extentlimt = QgsRectangle(self.canvas.extent()) extentlimt.scale(0.95) if not extentlimt.contains(position): self.zoom_to_location(position) self.marker.show() self.marker.setCenter(position, gpsinfo) def gps_first_fix(self, postion, gpsinfo): """ Called the first time the GPS gets a fix. If set this will zoom to the GPS after the first fix :param postion: The current GPS position. :param gpsinfo: The extra GPS information """ zoomtolocation = roam.config.settings.get('gpszoomonfix', True) if zoomtolocation: self.canvas.zoomScale(1000) self.zoom_to_location(postion) def zoom_to_location(self, position): """ Zoom to ta given position on the map.. """ rect = QgsRectangle(position, position) self.canvas.setExtent(rect) self.canvas.refresh() def gps_disconnected(self): """ Called when the GPS is disconnected """ self.marker.hide() def select_data_entry(self): """ Open the form selection widget to allow the user to pick the active capture form. """ def showformerror(form): pass def actions(): for form in self.project.forms: if not self.form_valid_for_capture(form): continue action = form.createuiaction() valid, failreasons = form.valid if not valid: roam.utils.warning("Form {} failed to load".format( form.label)) roam.utils.warning("Reasons {}".format(failreasons)) action.triggered.connect(partial(showformerror, form)) else: action.triggered.connect(partial(self.load_form, form)) yield action formpicker = PickActionDialog(msg="Select data entry form", wrap=5) formpicker.addactions(actions()) formpicker.exec_() def project_loaded(self, project): """ Called when the project is loaded. Main entry point for a loade project. :param project: The Roam project that has been loaded. """ self.project = project self.actionPan.trigger() firstform = self.first_capture_form() if firstform: self.load_form(firstform) self.dataentryselection.setVisible(True) else: self.dataentryselection.setVisible(False) # Enable the raster layers button only if the project contains a raster layer. layers = QgsMapLayerRegistry.instance().mapLayers().values() hasrasters = any(layer.type() == QgsMapLayer.RasterLayer for layer in layers) self.actionRaster.setEnabled(hasrasters) self.defaultextent = self.canvas.extent() roam.utils.info("Extent: {}".format(self.defaultextent.toString())) self.infoTool.selectionlayers = project.selectlayersmapping() self.canvas.refresh() projectscales, _ = QgsProject.instance().readBoolEntry( "Scales", "/useProjectScales") if projectscales: projectscales, _ = QgsProject.instance().readListEntry( "Scales", "/ScalesList") self.scalewidget.updateScales(projectscales) else: scales = [ "1:50000", "1:25000", "1:10000", "1:5000", "1:2500", "1:1000", "1:500", "1:250", "1:200", "1:100" ] scales = roam.config.settings.get('scales', scales) self.scalewidget.updateScales(scales) if self.scalebar_enabled: self.scalebar.update() self.actionPan.toggle() self.clear_plugins() self.add_plugins(project.enabled_plugins) def setMapTool(self, tool, *args): """ Set the active map tool in the canvas. :param tool: The QgsMapTool to set. """ if tool == self.canvas.mapTool(): return if hasattr(tool, "setSnapping"): tool.setSnapping(self.snapping) self.canvas.setMapTool(tool) def connectButtons(self): """ Connect the default buttons in the interface. Zoom, pan, etc """ def connectAction(action, tool): action.toggled.connect(partial(self.setMapTool, tool)) def cursor(name): pix = QPixmap(name) pix = pix.scaled(QSize(24, 24)) return QCursor(pix) self.zoomInTool = QgsMapToolZoom(self.canvas, False) self.zoomOutTool = QgsMapToolZoom(self.canvas, True) self.panTool = PanTool(self.canvas) self.infoTool = InfoTool(self.canvas) self.infoTool.setAction(self.actionInfo) self.zoomInTool.setAction(self.actionZoom_In) self.zoomOutTool.setAction(self.actionZoom_Out) self.panTool.setAction(self.actionPan) connectAction(self.actionZoom_In, self.zoomInTool) connectAction(self.actionZoom_Out, self.zoomOutTool) connectAction(self.actionPan, self.panTool) connectAction(self.actionInfo, self.infoTool) self.zoomInTool.setCursor(cursor(':/icons/in')) self.zoomOutTool.setCursor(cursor(':/icons/out')) self.infoTool.setCursor(cursor(':/icons/select')) self.actionRaster.triggered.connect(self.toggleRasterLayers) self.actionHome.triggered.connect(self.homeview) def homeview(self): """ Zoom the mapview canvas to the extents the project was opened at i.e. the default extent. """ self.canvas.setExtent(self.defaultextent) self.canvas.refresh() def form_valid_for_capture(self, form): """ Check if the given form is valid for capture. :param form: The form to check. :return: True if valid form for capture """ return form.has_geometry and self.project.layer_can_capture( form.QGISLayer) def first_capture_form(self): """ Return the first valid form for capture. """ for form in self.project.forms: if self.form_valid_for_capture(form): return form def load_form(self, form): """ Load the given form so it's the active one for capture :param form: The form to load """ self.clearCaptureTools() self.dataentryselection.setIcon(QIcon(form.icon)) self.dataentryselection.setText(form.icontext) self.create_capture_buttons(form) self.current_form = form def create_capture_buttons(self, form): """ Create the capture buttons in the toolbar for the given form. :param form: The active form. """ layer = form.QGISLayer tool = form.getMaptool()(self.canvas, form.settings) for action in tool.actions: # Create the action here. if action.ismaptool: action.toggled.connect(partial(self.setMapTool, tool)) # Set the action as a data entry button so we can remove it later. action.setProperty("dataentry", True) self.editgroup.addAction(action) self.layerbuttons.append(action) self.projecttoolbar.insertAction(self.topspaceraction, action) action.setChecked(action.isdefault) if hasattr(tool, 'geometryComplete'): add = partial(self.add_new_feature, form) tool.geometryComplete.connect(add) else: tool.finished.connect(self.openForm) tool.error.connect(self.show_invalid_geometry_message) def show_invalid_geometry_message(self, message): RoamEvents.raisemessage("Invalid geometry capture", message, level=RoamEvents.CRITICAL) def add_new_feature(self, form, geometry): """ Add a new new feature to the given layer """ # TODO Extract into function. # NOTE This function is doing too much, acts as add and also edit. layer = form.QGISLayer if layer.geometryType() in [ QGis.WKBMultiLineString, QGis.WKBMultiPoint, QGis.WKBMultiPolygon ]: geometry.convertToMultiType() try: form, feature = self.editfeaturestack.pop() self.editfeaturegeometry(form, feature, newgeometry=geometry) return except IndexError: pass feature = form.new_feature(geometry=geometry) RoamEvents.load_feature_form(form, feature, editmode=False) def editfeaturegeometry(self, form, feature, newgeometry): # TODO Extract into function. layer = form.QGISLayer layer.startEditing() feature.setGeometry(newgeometry) layer.updateFeature(feature) saved = layer.commitChanges() if not saved: map(roam.utils.error, layer.commitErrors()) self.canvas.refresh() self.currentfeatureband.setToGeometry(feature.geometry(), layer) RoamEvents.editgeometry_complete.emit(form, feature) self.canvas.mapTool().setEditMode(False, None) self.restore_last_form() def clearCaptureTools(self): """ Clear the capture tools from the toolbar. :return: True if the capture button was active at the time of clearing. """ captureselected = False for action in self.projecttoolbar.actions(): if action.objectName() == "capture" and action.isChecked(): captureselected = True if action.property('dataentry'): self.projecttoolbar.removeAction(action) return captureselected def toggleRasterLayers(self): """ Toggle all raster layers on or off. """ # Freeze the canvas to save on UI refresh self.canvas.freeze() tree = QgsProject.instance().layerTreeRoot() for node in tree.findLayers(): if node.layer().type() == QgsMapLayer.RasterLayer: if node.isVisible() == Qt.Checked: state = Qt.Unchecked else: state = Qt.Checked node.setVisible(state) self.canvas.freeze(False) self.canvas.refresh() def cleanup(self): """ Clean up when the project has changed. :return: """ self.bridge.clear() self.gpsband.reset() self.gpsband.hide() self.clear_selection() self.clear_temp_objects() self.clearCaptureTools() self.canvas.freeze() self.canvas.clear() self.canvas.freeze(False) for action in self.layerbuttons: self.editgroup.removeAction(action)
def add_action( self, name, icon_path, text, callback, toggle_flag=False, enabled_flag=True, checkable_flag=False, visible_flag=True, add_to_menu=True, add_to_toolbar=True, status_tip=None, whats_this=None, parent=None): """Add a toolbar icon to the toolbar. :param name: Objectname of the action. Serves also as key for the stored actions. :type name: str :param icon_path: Path to the icon for this action. Can be a resource path (e.g. ':/plugins/foo/bar.png') or a normal file system path. :type icon_path: str :param text: Text that should be shown in menu items for this action. :type text: str :param callback: Function to be called when the action is triggered. :type callback: function :param enabled_flag: A flag indicating if the action should be enabled by default. Defaults to True. :type enabled_flag: bool :param toggle_flag: A flag indicating if the action should connect the toggled or triggered signal by default. Defaults to triggered (False) :type toggle_flag: bool :param checkable_flag: A flag indicating if the action should be checkable by default. Defaults to False. :type checkable: bool :param visible_flag: A flag indicating if the action should be displayed by default. Defaults to True. :type visible: bool :param add_to_menu: Flag indicating whether the action should also be added to the menu. Defaults to True. :type add_to_menu: bool :param add_to_toolbar: Flag indicating whether the action should also be added to the toolbar. Defaults to True. :type add_to_toolbar: bool :param status_tip: Optional text to show in a popup when mouse pointer hovers over the action. :type status_tip: str :param parent: Parent widget for the new action. Defaults None. :type parent: QWidget :param whats_this: Optional text to show in the status bar when the mouse pointer hovers over the action. :returns: The action that was created. Note that the action is also added to self.actions list. :rtype: QAction """ icon = QIcon(icon_path) action = QAction(icon, text, parent) action.setObjectName(name) if toggle_flag: action.toggled.connect(callback) else: action.triggered.connect(callback) action.setEnabled(enabled_flag) action.setCheckable(checkable_flag) action.setVisible(visible_flag) if status_tip is not None: action.setStatusTip(status_tip) if whats_this is not None: action.setWhatsThis(whats_this) if add_to_toolbar: self.toolbar.addAction(action) if add_to_menu: self.iface.addPluginToMenu( self.menu, action) self.actions[name] = action return action
class QMap(): def __init__(self, iface): self.iface = iface self.actions = [] self.panels= [] self.navtoolbar = self.iface.mapNavToolToolBar() self.mainwindow = self.iface.mainWindow() self.iface.projectRead.connect(self.projectOpened) self.iface.initializationCompleted.connect(self.setupUI) self.actionGroup = QActionGroup(self.mainwindow) self.actionGroup.setExclusive(True) self.menuGroup = QActionGroup(self.mainwindow) self.menuGroup.setExclusive(True) self.movetool = MoveTool(self.iface.mapCanvas(), []) self.infotool = InfoTool(self.iface.mapCanvas()) self.infotool.infoResults.connect(self.showInfoResults) self.report = PopDownReport(self.iface.messageBar()) self.dialogprovider = DialogProvider(iface.mapCanvas(), iface) self.dialogprovider.accepted.connect(self.clearToolRubberBand) self.dialogprovider.rejected.connect(self.clearToolRubberBand) self.edittool = EditTool(self.iface.mapCanvas(),[]) self.edittool.finished.connect(self.openForm) self.edittool.featuresfound.connect(self.showFeatureSelection) self.infodock = InfoDock(self.iface.mainWindow()) self.iface.addDockWidget(Qt.RightDockWidgetArea, self.infodock) self.infodock.hide() self.band = QgsRubberBand(self.iface.mapCanvas()) self.band.setIconSize(20) self.band.setWidth(10) self.band.setColor(QColor(186, 93, 212, 76)) def showFeatureSelection(self, features): listUi = ListFeaturesForm(self.mainwindow) listUi.loadFeatureList(features) listUi.openFeatureForm.connect(self.openForm) listUi.exec_() def showInfoResults(self, results): self.infodock.clearResults() self.infodock.setResults(results) self.infodock.show() self.infodock.repaint() @property def _mapLayers(self): return QgsMapLayerRegistry.instance().mapLayers() def clearToolRubberBand(self): tool = self.iface.mapCanvas().mapTool() try: tool.clearBand() except AttributeError: # No clearBand method found, but that's cool. pass def missingLayers(self, layers): def showError(): html = ["<h1>Missing Layers</h1>", "<ul>"] for layer in layers: html.append("<li>{}</li>".format(layer)) html.append("</ul>") self.errorreport.updateHTML("".join(html)) message = "Seems like {} didn't load correctly".format(utils._pluralstring('layer', len(layers))) utils.warning("Missing layers") map(utils.warning, layers) self.widget = self.iface.messageBar().createMessage("Missing Layers", message, QIcon(":/icons/sad")) button = QPushButton(self.widget) button.setCheckable(True) button.setChecked(self.errorreport.isVisible()) button.setText("Show missing layers") button.toggled.connect(showError) button.toggled.connect(functools.partial(self.errorreport.setVisible)) self.widget.destroyed.connect(self.hideReports) self.widget.layout().addWidget(button) self.iface.messageBar().pushWidget(self.widget, QgsMessageBar.WARNING) def excepthook(self, ex_type, value, tb): """ Custom exception hook so that we can handle errors in a nicer way """ where = ''.join(traceback.format_tb(tb)) msg = '{}'.format(value) utils.critical(msg) def showError(): html = """ <html> <body bgcolor="#FFEDED"> <p><b>{}</b></p> <p align="left"><small>{}</small></p> </body> </html> """.format(msg, where) self.errorreport.updateHTML(html) self.widget = self.iface.messageBar().createMessage("oops", "Looks like an error occurred", QIcon(":/icons/sad")) button = QPushButton(self.widget) button.setCheckable(True) button.setChecked(self.errorreport.isVisible()) button.setText("Show error") button.toggled.connect(showError) button.toggled.connect(functools.partial(self.errorreport.setVisible)) self.widget.destroyed.connect(self.hideReports) self.widget.layout().addWidget(button) self.messageBar.pushWidget(self.widget, QgsMessageBar.CRITICAL) def hideReports(self): self.errorreport.setVisible(False) self.report.setVisible(False) def setupUI(self): """ Set up the main QGIS interface items. Called after QGIS has loaded the plugin. """ self.updateAppSize() utils.settings_notify.settings_changed.connect(self.updateAppSize) self.navtoolbar.setMovable(False) self.navtoolbar.setAllowedAreas(Qt.TopToolBarArea) self.mainwindow.insertToolBar(self.toolbar, self.navtoolbar) self.openProjectAction.trigger() def updateAppSize(self): fullscreen = utils.settings.get("fullscreen", False) if fullscreen: self.mainwindow.showFullScreen() else: self.mainwindow.showMaximized() def setMapTool(self, tool): """ Set the current mapview canvas tool tool -- The QgsMapTool to set """ self.iface.mapCanvas().setMapTool(tool) def createToolBars(self): """ Create all the needed toolbars """ self.menutoolbar = QToolBar("Menu", self.mainwindow) self.menutoolbar.setMovable(False) self.menutoolbar.setAllowedAreas(Qt.LeftToolBarArea) self.mainwindow.addToolBar(Qt.LeftToolBarArea, self.menutoolbar) self.toolbar = QToolBar("QMap", self.mainwindow) self.mainwindow.addToolBar(Qt.TopToolBarArea, self.toolbar) self.toolbar.setMovable(False) self.editingtoolbar = FloatingToolBar("Editing", self.toolbar) self.extraaddtoolbar = FloatingToolBar("Extra Add Tools", self.toolbar) self.syncactionstoolbar = FloatingToolBar("Syncing", self.toolbar) self.syncactionstoolbar.setOrientation(Qt.Vertical) def createActions(self): """ Create all the actions """ self.homeAction = (QAction(QIcon(":/icons/zoomfull"), "Default View", self.mainwindow)) self.gpsAction = (GPSAction(QIcon(":/icons/gps"), self.iface.mapCanvas(), self.mainwindow)) self.openProjectAction = (QAction(QIcon(":/icons/open"), "Projects", self.mainwindow)) self.openProjectAction.setCheckable(True) self.configAction = (QAction(QIcon(":/icons/config"), "Settings", self.mainwindow)) self.configAction.setCheckable(True) self.toggleRasterAction = (QAction(QIcon(":/icons/photo"), "Aerial Photos", self.mainwindow)) self.syncAction = QAction(QIcon(":/icons/sync"), "Sync", self.mainwindow) self.syncAction.setVisible(False) self.editattributesaction = QAction(QIcon(":/icons/edit"), "Edit Attributes", self.mainwindow) self.editattributesaction.setCheckable(True) self.editattributesaction.toggled.connect(functools.partial(self.setMapTool, self.edittool)) self.moveaction = QAction(QIcon(":/icons/move"), "Move Feature", self.mainwindow) self.moveaction.setCheckable(True) self.editingmodeaction = QAction(QIcon(":/icons/edittools"), "Edit Tools", self.mainwindow) self.editingmodeaction.setCheckable(True) self.infoaction = QAction(QIcon(":/icons/info"), "Info", self.mainwindow) self.infoaction.setCheckable(True) self.addatgpsaction = QAction(QIcon(":/icons/gpsadd"), "Add at GPS", self.mainwindow) self.edittool.layersupdated.connect(self.editattributesaction.setVisible) self.movetool.layersupdated.connect(self.moveaction.setVisible) self.movetool.layersupdated.connect(self.editingmodeaction.setVisible) def initGui(self): """ Create all the icons and setup the tool bars. Called by QGIS when loading. This is called before setupUI. """ QApplication.setWindowIcon(QIcon(":/branding/logo")) self.mainwindow.findChildren(QMenuBar)[0].setVisible(False) self.mainwindow.setContextMenuPolicy(Qt.PreventContextMenu) self.mainwindow.setWindowTitle("IntraMaps Roam: Mobile Data Collection") # Disable QGIS logging window popups. We do our own logging QgsMessageLog.instance().messageReceived.disconnect() s = """ QToolButton { padding: 6px; color: #4f4f4f; } QToolButton:hover { padding: 6px; background-color: rgb(211, 228, 255); } QToolBar { background: white; } QCheckBox::indicator { width: 40px; height: 40px; } QLabel { color: #4f4f4f; } QDialog { background-color: rgb(255, 255, 255); } QPushButton { border: 1px solid #e1e1e1; padding: 6px; color: #4f4f4f; } QPushButton:hover { border: 1px solid #e1e1e1; padding: 6px; background-color: rgb(211, 228, 255); } QCheckBox { color: #4f4f4f; } QComboBox::drop-down { width: 30px; } QComboBox { border: 1px solid #d3d3d3; } QStackedWidget { background-color: rgb(255, 255, 255); } """ self.mainwindow.setStyleSheet(s) mainwidget = self.mainwindow.centralWidget() mainwidget.setLayout(QGridLayout()) mainwidget.layout().setContentsMargins(0,0,0,0) newlayout = QGridLayout() newlayout.setContentsMargins(0,0,0,0) newlayout.addWidget(self.iface.mapCanvas(), 0,0,2,1) newlayout.addWidget(self.iface.messageBar(), 0,0,1,1) wid = QWidget() wid.setLayout(newlayout) self.stack = QStackedWidget(self.mainwindow) self.messageBar = QgsMessageBar(wid) self.messageBar.setSizePolicy( QSizePolicy.Minimum, QSizePolicy.Fixed ) self.errorreport = PopDownReport(self.messageBar) mainwidget.layout().addWidget(self.stack, 0,0,2,1) mainwidget.layout().addWidget(self.messageBar, 0,0,1,1) self.helppage = HelpPage() helppath = os.path.join(os.path.dirname(__file__) , 'help',"help.html") self.helppage.setHelpPage(helppath) self.settingswidget = SettingsWidget(self.stack) self.projectwidget = ProjectsWidget() self.projectwidget.requestOpenProject.connect(self.loadProject) self.stack.addWidget(wid) self.stack.addWidget(self.projectwidget) self.stack.addWidget(self.helppage) self.stack.addWidget(self.settingswidget) sys.excepthook = self.excepthook def createSpacer(width=30): widget = QWidget() widget.setMinimumWidth(width) return widget self.createToolBars() self.createActions() spacewidget = createSpacer(60) gpsspacewidget = createSpacer() gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.moveaction.toggled.connect(functools.partial(self.setMapTool, self.movetool)) self.infoaction.toggled.connect(functools.partial(self.setMapTool, self.infotool)) showediting = (functools.partial(self.editingtoolbar.showToolbar, self.editingmodeaction, self.moveaction)) self.editingmodeaction.toggled.connect(showediting) self.addatgpsaction.triggered.connect(self.addAtGPS) self.addatgpsaction.setEnabled(self.gpsAction.isConnected) self.gpsAction.gpsfixed.connect(self.addatgpsaction.setEnabled) self.editingtoolbar.addToActionGroup(self.moveaction) self.actionGroup.addAction(self.editingmodeaction) self.actionGroup.addAction(self.editattributesaction) self.actionGroup.addAction(self.infoaction) self.homeAction.triggered.connect(self.zoomToDefaultView) self.openProjectAction.triggered.connect(self.showOpenProjectDialog) self.openProjectAction.triggered.connect(functools.partial(self.stack.setCurrentIndex, 1)) self.configAction.triggered.connect(functools.partial(self.stack.setCurrentIndex, 3)) self.configAction.triggered.connect(self.settingswidget.populateControls) self.configAction.triggered.connect(self.settingswidget.readSettings) self.toggleRasterAction.triggered.connect(self.toggleRasterLayers) self.navtoolbar.insertAction(self.iface.actionZoomIn(), self.iface.actionTouch()) self.navtoolbar.insertAction(self.iface.actionTouch(), self.homeAction) self.navtoolbar.insertAction(self.iface.actionTouch(), self.iface.actionZoomFullExtent()) self.navtoolbar.insertAction(self.homeAction, self.iface.actionZoomFullExtent()) self.navtoolbar.addAction(self.toggleRasterAction) self.navtoolbar.insertWidget(self.iface.actionZoomFullExtent(), spacewidget) self.toolbar.addAction(self.infoaction) self.toolbar.addAction(self.editingmodeaction) self.toolbar.addAction(self.editattributesaction) self.toolbar.addAction(self.syncAction) self.toolbar.addAction(self.gpsAction) self.toolbar.insertWidget(self.syncAction, gpsspacewidget) self.toolbar.insertSeparator(self.gpsAction) self.extraaddtoolbar.addAction(self.addatgpsaction) self.editingtoolbar.addAction(self.moveaction) self.mapview = QAction(QIcon(":/icons/map"), "Map", self.menutoolbar) self.mapview.setCheckable(True) self.mapview.triggered.connect(functools.partial(self.stack.setCurrentIndex, 0)) self.help = QAction(QIcon(":/icons/help"), "Help", self.menutoolbar) self.help.setCheckable(True) self.help.triggered.connect(functools.partial(self.stack.setCurrentIndex, 2)) self.help.setVisible(False) self.projectlabel = QLabel("Project: <br> None") self.projectlabel.setAlignment(Qt.AlignCenter) self.projectlabel.setStyleSheet(""" QLabel { color: #8c8c8c; font: 10px "Calibri" ; }""") self.userlabel = QLabel("User: <br> {user}".format(user=getpass.getuser())) self.userlabel.setAlignment(Qt.AlignCenter) self.userlabel.setStyleSheet(""" QLabel { color: #8c8c8c; font: 10px "Calibri" ; }""") self.quit = QAction(QIcon(":/icons/quit"), "Quit", self.menutoolbar) self.quit.triggered.connect(self.iface.actionExit().trigger) self.menuGroup.addAction(self.mapview) self.menuGroup.addAction(self.openProjectAction) self.menuGroup.addAction(self.help) self.menuGroup.addAction(self.configAction) self.menutoolbar.addAction(self.mapview) self.menutoolbar.addAction(self.openProjectAction) self.menutoolbar.addAction(self.help) self.menutoolbar.addAction(self.configAction) self.menutoolbar.addAction(self.quit) quitspacewidget = createSpacer() quitspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) labelaction = self.menutoolbar.insertWidget(self.configAction, self.userlabel) self.menutoolbar.insertWidget(labelaction, quitspacewidget) self.menutoolbar.insertWidget(labelaction, self.projectlabel) self.setupIcons() self.stack.currentChanged.connect(self.updateUIState) def updateUIState(self, page): """ Update the UI state to reflect the currently selected page in the stacked widget """ def setToolbarsActive(enabled): toolbars = self.mainwindow.findChildren(QToolBar) for toolbar in toolbars: if toolbar == self.menutoolbar: continue toolbar.setEnabled(enabled) def setPanelsVisible(visible): for panel in self.panels: panel.setVisible(visible) ismapview = page == 0 setToolbarsActive(ismapview) setPanelsVisible(ismapview) self.infodock.hide() def addAtGPS(self): """ Add a record at the current GPS location. """ action = self.actionGroup.checkedAction() if not action: return layer = action.data() if not layer: return point = self.gpsAction.position self.addNewFeature(layer=layer, geometry=point) def zoomToDefaultView(self): """ Zoom the mapview canvas to the extents the project was opened at i.e. the default extent. """ self.iface.mapCanvas().setExtent(self.defaultextent) self.iface.mapCanvas().refresh() def toggleRasterLayers(self): """ Toggle all raster layers on or off. """ legend = self.iface.legendInterface() #Freeze the canvas to save on UI refresh self.iface.mapCanvas().freeze() for layer in self._mapLayers.values(): if layer.type() == QgsMapLayer.RasterLayer: isvisible = legend.isLayerVisible(layer) legend.setLayerVisible(layer, not isvisible) self.iface.mapCanvas().freeze(False) self.iface.mapCanvas().refresh() def setupIcons(self): """ Update toolbars to have text and icons, change normal QGIS icons to new style """ toolbars = self.mainwindow.findChildren(QToolBar) for toolbar in toolbars: toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) toolbar.setIconSize(QSize(32, 32)) self.iface.actionTouch().setIconText("Pan") self.iface.actionTouch().setIcon(QIcon(":/icons/pan")) self.iface.actionZoomIn().setIcon(QIcon(":/icons/in")) self.iface.actionZoomOut().setIcon(QIcon(":/icons/out")) self.iface.actionPan().setIcon(QIcon(":/icons/pan")) self.iface.actionZoomFullExtent().setIcon(QIcon(":/icons/home")) self.iface.actionZoomFullExtent().setIconText("Home View") self.actionGroup.addAction(self.iface.actionZoomIn()) self.actionGroup.addAction(self.iface.actionZoomOut()) self.actionGroup.addAction(self.iface.actionTouch()) def projectOpened(self): """ Called when a new project is opened in QGIS. """ for panel in self.panels: self.mainwindow.removeDockWidget(panel) del panel projectpath = QgsProject.instance().fileName() project = QMapProject(os.path.dirname(projectpath), self.iface) self.projectlabel.setText("Project: <br> {}".format(project.name)) self.createFormButtons(projectlayers = project.getConfiguredLayers()) # Enable the raster layers button only if the project contains a raster layer. hasrasters = any(layer.type() for layer in self._mapLayers.values()) self.toggleRasterAction.setEnabled(hasrasters) self.defaultextent = self.iface.mapCanvas().extent() self.connectSyncProviders(project) # Show panels self.panels = list(project.getPanels()) for panel in self.panels: self.mainwindow.addDockWidget(Qt.BottomDockWidgetArea , panel) self.iface.messageBar().popWidget() def captureLayer(self, layer): text = layer.icontext tool = layer.getMaptool(self.iface.mapCanvas()) # Hack until I fix it later if isinstance(tool, PointTool): add = functools.partial(self.addNewFeature, qgslayer) tool.geometryComplete.connect(add) else: tool.finished.connect(self.openForm) tool.error.connect(functools.partial(self.showToolError, text)) action = QAction(QIcon(layer.icon), text, self.mainwindow) action.setData(layer) action.setCheckable(True) action.toggled.connect(functools.partial(self.setMapTool, tool)) self.toolbar.insertAction(self.editingmodeaction, action) if not tool.isEditTool(): # Connect the GPS tools strip to the action pressed event. showgpstools = (functools.partial(self.extraaddtoolbar.showToolbar, action, None)) action.toggled.connect(showgpstools) self.actionGroup.addAction(action) self.actions.append(action) def editLayer(self, layer): self.edittool.addLayer(layer.QGISLayer) self.edittool.searchRadius = 10 def moveLayer(self, layer): self.movetool.addLayer(layer.QGISLayer) def createFormButtons(self, projectlayers): """ Create buttons for each form that is definded """ # Remove all the old buttons for action in self.actions: self.actionGroup.removeAction(action) self.toolbar.removeAction(action) self.edittool.layers = [] self.movetool.layers = [] capabilitityhandlers = { "capture" : self.captureLayer, "edit" : self.editLayer, "move" : self.moveLayer} for layer in projectlayers: try: qgslayer = QgsMapLayerRegistry.instance().mapLayersByName(layer.name)[0] if qgslayer.type() == QgsMapLayer.RasterLayer: utils.log("We can't support raster layers for data entry") continue layer.QGISLayer = qgslayer except IndexError: utils.log("Layer {} not found in project".format(layer.name)) continue for capability in layer.capabilities: try: capabilitityhandlers[capability](layer) except NoMapToolConfigured: utils.log("No map tool configured") continue except ErrorInMapTool as error: self.iface.messageBar().pushMessage("Error configuring map tool", error.message, level=QgsMessageBar.WARNING) continue def showToolError(self, label, message): self.iface.messageBar().pushMessage(label, message, QgsMessageBar.WARNING) def openForm(self, layer, feature): if not layer.isEditable(): layer.startEditing() self.band.setToGeometry(feature.geometry(), layer) self.dialogprovider.openDialog(feature=feature, layer=layer) self.band.reset() def addNewFeature(self, layer, geometry): fields = layer.pendingFields() feature = QgsFeature() feature.setGeometry( geometry ) feature.initAttributes(fields.count()) feature.setFields(fields) for indx in xrange(fields.count()): feature[indx] = layer.dataProvider().defaultValue(indx) self.openForm(layer, feature) def showOpenProjectDialog(self): """ Show the project selection dialog. """ self.stack.setCurrentIndex(1) self.infodock.hide() path = os.path.join(os.path.dirname(__file__), '..' , 'projects/') projects = getProjects(path, self.iface) self.projectwidget.loadProjectList(projects) def loadProject(self, project): """ Load a project into QGIS. """ utils.log(project) utils.log(project.name) utils.log(project.projectfile) utils.log(project.vaild) (passed, message) = project.onProjectLoad() if not passed: QMessageBox.warning(self.mainwindow, "Project Load Rejected", "Project couldn't be loaded because {}".format(message)) return self.mapview.trigger() self.iface.newProject(False) self.iface.mapCanvas().freeze() self.infodock.clearResults() # No idea why we have to set this each time. Maybe QGIS deletes it for # some reason. self.badLayerHandler = BadLayerHandler(callback=self.missingLayers) QgsProject.instance().setBadLayerHandler( self.badLayerHandler ) self.iface.messageBar().pushMessage("Project Loading","", QgsMessageBar.INFO) QApplication.processEvents() fileinfo = QFileInfo(project.projectfile) QgsProject.instance().read(fileinfo) self.iface.mapCanvas().updateScale() self.iface.mapCanvas().freeze(False) self.iface.mapCanvas().refresh() self.mainwindow.setWindowTitle("IntraMaps Roam: Mobile Data Collection") self.iface.projectRead.emit() def unload(self): del self.toolbar def connectSyncProviders(self, project): self.syncactionstoolbar.clear() syncactions = list(project.syncprovders()) # Don't show the sync button if there is no sync providers if not syncactions: self.syncAction.setVisible(False) return self.syncAction.setVisible(True) for provider in syncactions: action = QAction(QIcon(":/icons/sync"), "Sync {}".format(provider.name), self.mainwindow) action.triggered.connect(functools.partial(self.syncProvider, provider)) self.syncactionstoolbar.addAction(action) try: self.syncAction.toggled.disconnect() except TypeError: pass try: self.syncAction.triggered.disconnect() except TypeError: pass if len(syncactions) == 1: # If one provider is set then we just connect the main button. self.syncAction.setCheckable(False) self.syncAction.setText("Sync") self.syncAction.triggered.connect(functools.partial(self.syncProvider, syncactions[0])) else: # the sync button because a sync menu self.syncAction.setCheckable(True) self.syncAction.setText("Sync Menu") showsyncoptions = (functools.partial(self.syncactionstoolbar.showToolbar, self.syncAction, None)) self.syncAction.toggled.connect(showsyncoptions) def syncstarted(self): # Remove the old widget if it's still there. # I don't really like this. Seems hacky. try: self.iface.messageBar().popWidget(self.syncwidget) except RuntimeError: pass except AttributeError: pass self.iface.messageBar().findChildren(QToolButton)[0].setVisible(False) self.syncwidget = self.iface.messageBar().createMessage("Syncing", "Sync in progress", QIcon(":/icons/syncing")) button = QPushButton(self.syncwidget) button.setCheckable(True) button.setText("Status") button.setIcon(QIcon(":/icons/syncinfo")) button.toggled.connect(functools.partial(self.report.setVisible)) pro = QProgressBar() pro.setMaximum(0) pro.setMinimum(0) self.syncwidget.layout().addWidget(pro) self.syncwidget.layout().addWidget(button) self.iface.messageBar().pushWidget(self.syncwidget, QgsMessageBar.INFO) def synccomplete(self): try: self.iface.messageBar().popWidget(self.syncwidget) except RuntimeError: pass stylesheet = ("QgsMessageBar { background-color: rgba(239, 255, 233); border: 0px solid #b9cfe4; } " "QLabel,QTextEdit { color: #057f35; } ") closebutton = self.iface.messageBar().findChildren(QToolButton)[0] closebutton.setVisible(True) closebutton.clicked.connect(functools.partial(self.report.setVisible, False)) self.syncwidget = self.iface.messageBar().createMessage("Syncing", "Sync Complete", QIcon(":/icons/syncdone")) button = QPushButton(self.syncwidget) button.setCheckable(True) button.setChecked(self.report.isVisible()) button.setText("Sync Report") button.setIcon(QIcon(":/icons/syncinfo")) button.toggled.connect(functools.partial(self.report.setVisible)) pro = QProgressBar() pro.setMaximum(100) pro.setValue(100) self.syncwidget.layout().addWidget(pro) self.syncwidget.layout().addWidget(button) self.iface.messageBar().pushWidget(self.syncwidget) self.iface.messageBar().setStyleSheet(stylesheet) self.iface.mapCanvas().refresh() def syncerror(self): try: self.iface.messageBar().popWidget(self.syncwidget) except RuntimeError: pass closebutton = self.iface.messageBar().findChildren(QToolButton)[0] closebutton.setVisible(True) closebutton.clicked.connect(functools.partial(self.report.setVisible, False)) self.syncwidget = self.iface.messageBar().createMessage("Syncing", "Sync Error", QIcon(":/icons/syncfail")) button = QPushButton(self.syncwidget) button.setCheckable(True) button.setChecked(self.report.isVisible()) button.setText("Sync Report") button.setIcon(QIcon(":/icons/syncinfo")) button.toggled.connect(functools.partial(self.report.setVisible)) self.syncwidget.layout().addWidget(button) self.iface.messageBar().pushWidget(self.syncwidget, QgsMessageBar.CRITICAL) self.iface.mapCanvas().refresh() def syncProvider(self, provider): self.syncAction.toggle() provider.syncStarted.connect(functools.partial(self.syncAction.setEnabled, False)) provider.syncStarted.connect(self.syncstarted) provider.syncComplete.connect(self.synccomplete) provider.syncComplete.connect(functools.partial(self.syncAction.setEnabled, True)) provider.syncComplete.connect(functools.partial(self.report.updateHTML)) provider.syncMessage.connect(self.report.updateHTML) provider.syncError.connect(self.report.updateHTML) provider.syncError.connect(self.syncerror) provider.syncError.connect(functools.partial(self.syncAction.setEnabled, True)) provider.startSync()
class DocumentViewManager(QMainWindow): """ MDI area for displaying supporting documents within a given context e.g. supporting documents for a specific household based on the lifetime of the 'SourceDocumentManager' instance. """ def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.setWindowFlags(Qt.Window) self._mdi_area = QMdiArea() self._mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self._mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCentralWidget(self._mdi_area) # set the size of mid_area and DocumentViewManager based on the # screen size. screen = QDesktopWidget().availableGeometry() self._mdi_area.resize(screen.width() - 30, screen.height() - 80) self.resize(self._mdi_area.size()) self._mdi_area.subWindowActivated.connect(self.update_actions) self._viewer_mapper = QSignalMapper(self) self._viewer_mapper.mapped[QWidget].connect(self.set_active_sub_window) win_title = QApplication.translate( "DocumentViewManager", "Document Viewer" ) self.setWindowTitle(win_title) self.setUnifiedTitleAndToolBarOnMac(True) self.statusBar().showMessage( QApplication.translate( "DocumentViewManager", "Ready" ) ) self._doc_viewers = {} self._create_menu_actions() self.update_actions() def center(self): """ Move the Document viewer to the center of the screen. """ # Get the current screens' dimensions... screen = QDesktopWidget().availableGeometry() # ... and get this windows' dimensions mdi_area_size = self.frameGeometry() # The horizontal position hpos = (screen.width() - mdi_area_size.width()) / 2 # vertical position vpos = (screen.height() - mdi_area_size.height()) / 2 # repositions the window self.move(hpos, vpos) def _create_menu_actions(self): self._window_menu = self.menuBar().addMenu( QApplication.translate( "DocumentViewManager","&Windows")) self._close_act = QAction( QApplication.translate("DocumentViewManager", "Cl&ose"), self) self._close_act.setStatusTip( QApplication.translate("DocumentViewManager", "Close the active document viewer")) self._close_act.triggered.connect(self._mdi_area.closeActiveSubWindow) self._close_all_act = QAction(QApplication.translate( "DocumentViewManager", "Close &All"), self ) self._close_all_act.setStatusTip( QApplication.translate("DocumentViewManager", "Close all the document viewers") ) self._close_all_act.triggered.connect( self._mdi_area.closeAllSubWindows ) self._tile_act = QAction(QApplication.translate( "DocumentViewManager", "&Tile"), self ) self._tile_act.setStatusTip( QApplication.translate("DocumentViewManager", "Tile the document viewers")) self._tile_act.triggered.connect(self.tile_windows) self._cascade_act = QAction(QApplication.translate( "DocumentViewManager", "&Cascade"), self) self._cascade_act.setStatusTip(QApplication.translate( "DocumentViewManager", "Cascade the document viewers")) self._cascade_act.triggered.connect(self.cascade_windows) self._next_act = QAction(QApplication.translate( "DocumentViewManager", "Ne&xt"), self) self._next_act.setStatusTip( QApplication.translate( "DocumentViewManager", "Move the focus to the next document viewer" ) ) self._next_act.triggered.connect(self._mdi_area.activateNextSubWindow) self._previous_act = QAction(QApplication.translate( "DocumentViewManager", "Pre&vious"), self) self._previous_act.setStatusTip( QApplication.translate( "DocumentViewManager", "Move the focus to the previous document viewer" ) ) self._previous_act.triggered.connect( self._mdi_area.activatePreviousSubWindow ) self._separator_act = QAction(self) self._separator_act.setSeparator(True) self.update_window_menu() self._window_menu.aboutToShow.connect(self.update_window_menu) def cascade_windows(self): #Cascade document windows self._mdi_area.cascadeSubWindows() def tile_windows(self): #Arrange document windows to occupy the available space in mdi area self._mdi_area.tileSubWindows() def update_actions(self): if self._mdi_area.activeSubWindow(): has_mdi_child = True else: has_mdi_child = False self._close_act.setEnabled(has_mdi_child) self._close_all_act.setEnabled(has_mdi_child) self._tile_act.setEnabled(has_mdi_child) self._cascade_act.setEnabled(has_mdi_child) self._previous_act.setEnabled(has_mdi_child) self._next_act.setEnabled(has_mdi_child) self._separator_act.setVisible(has_mdi_child) def update_window_menu(self): self._window_menu.clear() self._window_menu.addAction(self._close_act) self._window_menu.addAction(self._close_all_act) self._window_menu.addSeparator() self._window_menu.addAction(self._tile_act) self._window_menu.addAction(self._cascade_act) self._window_menu.addSeparator() self._window_menu.addAction(self._next_act) self._window_menu.addAction(self._previous_act) self._window_menu.addAction(self._separator_act) windows = self._mdi_area.subWindowList() self._separator_act.setVisible(len(windows) != 0) for i, window in enumerate(windows): text = "%d. %s" % (i + 1, window.windowTitle()) win_action = self._window_menu.addAction(text) win_action.setCheckable(True) win_action.setChecked(window is self._mdi_area.activeSubWindow()) win_action.triggered.connect(self._viewer_mapper.map) self._viewer_mapper.setMapping(win_action, window) def load_viewer(self, document_widget, visible=True): """ Open a new instance of the viewer or activate an existing one if the document had been previously loaded. :param document_widget: Contains all the necessary information required to load the specific document. :type document_widget: DocumentWidget :param visible: True to show the view manager after the viewer has been loaded, otherwise it will be the responsibility of the caller to enable visibility. :type visible: bool :returns: True if the document was successfully loaded, else False. :rtype: bool """ doc_identifier = document_widget.file_identifier() if doc_identifier in self._doc_viewers: doc_sw = self._doc_viewers[doc_identifier] self._mdi_area.setActiveSubWindow(doc_sw) doc_sw.showNormal() else: doc_viewer = self._create_viewer(document_widget) abs_doc_path = self.absolute_document_path(document_widget) if not QFile.exists(abs_doc_path): msg = QApplication.translate( "DocumentViewManager", "The selected document does not exist." "\nPlease check the supporting documents' " "repository setting." ) QMessageBox.critical( self, QApplication.translate( "DocumentViewManager","Invalid Document" ), msg ) return False doc_viewer.load_document(abs_doc_path) self._doc_viewers[doc_identifier] = doc_viewer self._mdi_area.addSubWindow(doc_viewer) doc_viewer.show() if not self.isVisible() and visible: self.setVisible(True) if self.isMinimized(): self.showNormal() self.center() return True def set_active_sub_window(self, viewer): if viewer: self._mdi_area.setActiveSubWindow(viewer) def absolute_document_path(self, document_widget): """ Build the absolute document path using info from the document widget. :param document_widget: Instance of document widget. :return: Absolute path of the supporting document. :rtype: str """ abs_path = '' file_manager = document_widget.fileManager if not file_manager is None: network_repository = file_manager.networkPath file_id = document_widget.file_identifier() source_entity = document_widget.doc_source_entity() profile_name = current_profile().name doc_type = document_widget.doc_type_value().lower().replace( ' ', '_' ) file_name, file_extension = guess_extension( document_widget.displayName() ) abs_path = network_repository + "/" +profile_name + '/' +\ unicode(source_entity) + "/" + unicode(doc_type) + "/" +\ unicode(file_id) + unicode(file_extension) return abs_path def reset(self): """ Removes all document viewers in the view area. The QCloseEvent sent to each sub-window will decrement the register. """ self._mdi_area.closeAllSubWindows() def _create_viewer(self, document_widget): """ Creates a new instance of a document viewer. :param document_widget: Contains all the necessary information required to load the specific document. :return: Document viewer object :rtype: DocumentViewer """ doc_viewer = DocumentViewer( self._mdi_area, document_widget.file_identifier() ) doc_viewer.setAttribute(Qt.WA_DeleteOnClose) doc_viewer.setWindowTitle( document_widget.displayName() ) # TODO: Incorporate logic for determining # TODO: viewer based on document type ph_viewer = PhotoViewer() # v_layout = QVBoxLayout() # v_layout.addWidget(ph_viewer) # doc_viewer.setLayout(v_layout) doc_viewer.set_view_widget(ph_viewer) doc_viewer.closed.connect(self._on_viewer_closed) return doc_viewer def remove_viewer(self, viewer_id): """ Close and remove the viewer with the specified viewer ID. """ if viewer_id in self._doc_viewers: viewer = self._doc_viewers[viewer_id] self._mdi_area.setActiveSubWindow(viewer) self._mdi_area.closeActiveSubWindow() self._on_viewer_closed(viewer_id) def _on_viewer_closed(self,file_id): """ Slot raised when a document viewer is closed. """ if file_id in self._doc_viewers: del self._doc_viewers[file_id]
class MainWindow(ui_mainwindow.Ui_MainWindow, QMainWindow): """ Main application window """ def __init__(self): super(MainWindow, self).__init__() self.setupUi(self) self.canvaslayers = [] self.layerbuttons = [] self.project = None self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas)) self.canvas.setCanvasColor(Qt.white) self.canvas.enableAntiAliasing(True) self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor) self.bar = roam.messagebaritems.MessageBar(self.centralwidget) self.actionMap.setVisible(False) self.actionLegend.setVisible(False) pal = QgsPalLabeling() self.canvas.mapRenderer().setLabelingEngine(pal) self.canvas.setFrameStyle(QFrame.NoFrame) self.menuGroup = QActionGroup(self) self.menuGroup.setExclusive(True) self.menuGroup.addAction(self.actionMap) self.menuGroup.addAction(self.actionDataEntry) self.menuGroup.addAction(self.actionLegend) self.menuGroup.addAction(self.actionProject) self.menuGroup.addAction(self.actionSync) self.menuGroup.addAction(self.actionSettings) self.menuGroup.addAction(self.actionGPS) self.menuGroup.triggered.connect(self.updatePage) self.editgroup = QActionGroup(self) self.editgroup.setExclusive(True) self.editgroup.addAction(self.actionPan) self.editgroup.addAction(self.actionZoom_In) self.editgroup.addAction(self.actionZoom_Out) self.editgroup.addAction(self.actionInfo) self.actionLegend.triggered.connect(self.updatelegend) self.actionGPS = GPSAction(":/icons/gps", self.canvas, self) self.projecttoolbar.addAction(self.actionGPS) self.projectwidget.requestOpenProject.connect(self.loadProject) QgsProject.instance().readProject.connect(self._readProject) self.gpswidget.setgps(GPS) self.actionSettings.toggled.connect(self.settingswidget.populateControls) self.actionSettings.toggled.connect(self.settingswidget.readSettings) self.settingswidget.settingsupdated.connect(self.settingsupdated) self.dataentrywidget = DataEntryWidget(self.canvas, self.bar) self.widgetpage.layout().addWidget(self.dataentrywidget) self.dataentrywidget.rejected.connect(self.formrejected) self.dataentrywidget.featuresaved.connect(self.featureSaved) self.dataentrywidget.featuredeleted.connect(self.featuredeleted) self.dataentrywidget.failedsave.connect(self.failSave) self.dataentrywidget.helprequest.connect(self.showhelp) def createSpacer(width=0, height=0): widget = QWidget() widget.setMinimumWidth(width) widget.setMinimumHeight(height) return widget gpsspacewidget = createSpacer(30) sidespacewidget = createSpacer(30) sidespacewidget2 = createSpacer(height=20) sidespacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sidespacewidget2.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.topspaceraction = self.projecttoolbar.insertWidget(self.actionGPS, gpsspacewidget) def createlabel(text): style = """ QLabel { color: #706565; font: 14px "Calibri" ; }""" label = QLabel(text) label.setStyleSheet(style) return label self.projectlabel = createlabel("Project: {project}") self.userlabel = createlabel("User: {user}".format(user=getpass.getuser())) self.positionlabel = createlabel('') self.gpslabel = createlabel("GPS: Not active") self.statusbar.addWidget(self.projectlabel) self.statusbar.addWidget(self.userlabel) spacer = createSpacer() spacer2 = createSpacer() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) spacer2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.statusbar.addWidget(spacer) self.statusbar.addWidget(self.positionlabel) self.statusbar.addWidget(spacer2) self.statusbar.addWidget(self.gpslabel) self.menutoolbar.insertWidget(self.actionQuit, sidespacewidget2) self.menutoolbar.insertWidget(self.actionProject, sidespacewidget) self.panels = [] self.connectButtons() self.currentfeatureband = QgsRubberBand(self.canvas) self.currentfeatureband.setIconSize(20) self.currentfeatureband.setWidth(10) self.currentfeatureband.setColor(QColor(186, 93, 212, 76)) self.canvas_page.layout().insertWidget(0, self.projecttoolbar) self.dataentryselection = QAction(self.projecttoolbar) self.dataentryaction = self.projecttoolbar.insertAction(self.topspaceraction, self.dataentryselection) self.dataentryselection.triggered.connect(self.selectdataentry) self.centralwidget.layout().addWidget(self.statusbar) self.actionGPSFeature.setProperty('dataentry', True) self.infodock = InfoDock(self.canvas) self.infodock.featureupdated.connect(self.highlightfeature) self.infodock.hide() self.hidedataentry() self.canvas.extentsChanged.connect(self.updatestatuslabel) RoamEvents.openimage.connect(self.openimage) RoamEvents.openurl.connect(self.viewurl) RoamEvents.openfeatureform.connect(self.openForm) RoamEvents.openkeyboard.connect(self.openkeyboard) RoamEvents.selectioncleared.connect(self.clearselection) RoamEvents.editgeometry.connect(self.addforedit) RoamEvents.editgeometry_complete.connect(self.on_geometryedit) RoamEvents.onShowMessage.connect(self.showUIMessage) RoamEvents.selectionchanged.connect(self.highlightselection) RoamEvents.selectionchanged.connect(self.showInfoResults) GPS.gpspostion.connect(self.updatecanvasfromgps) GPS.firstfix.connect(self.gpsfirstfix) GPS.gpsdisconnected.connect(self.gpsdisconnected) self.lastgpsposition = None self.marker = GPSMarker(self.canvas) self.marker.hide() self.legendpage.showmap.connect(self.showmap) self.editfeaturestack = [] self.currentselection = {} def showUIMessage(self, label, message, level=QgsMessageBar.INFO, time=0, extra=''): self.bar.pushMessage(label, message, level, duration=time, extrainfo=extra) def addforedit(self, form, feature): self.editfeaturestack.append((form, feature)) self.loadform(form) actions = self.getcaptureactions() for action in actions: if action.isdefault: action.trigger() break def updatelegend(self): self.legendpage.updatecanvas(self.canvas) def gpsfirstfix(self, postion, gpsinfo): zoomtolocation = roam.config.settings.get('gpszoomonfix', True) if zoomtolocation: self.canvas.zoomScale(1000) def updatecanvasfromgps(self, position, gpsinfo): # Recenter map if we go outside of the 95% of the area if not self.lastgpsposition == position: self.lastposition = position rect = QgsRectangle(position, position) extentlimt = QgsRectangle(self.canvas.extent()) extentlimt.scale(0.95) if not extentlimt.contains(position): self.canvas.setExtent(rect) self.canvas.refresh() self.marker.show() self.marker.setCenter(position) self.gpslabel.setText("GPS: PDOP {} HDOP {} VDOP {}".format(gpsinfo.pdop, gpsinfo.hdop, gpsinfo.vdop)) def gpsdisconnected(self): self.marker.hide() self.gpslabel.setText("GPS Not Active") def openkeyboard(self): if not roam.config.settings.get('keyboard', True): return if sys.platform == 'win32': try: programfiles = os.environ['ProgramW6432'] except KeyError: programfiles = os.environ['ProgramFiles'] cmd = r'{path}\Common Files\Microsoft Shared\ink\TabTip.exe'.format(path=programfiles) try: os.startfile(cmd) except WindowsError: roam.config.settings['keyboard'] = False roam.config.save() else: cmd = 'onboard' Popen(cmd) def selectdataentry(self): forms = self.project.forms formpicker = PickActionDialog(msg="Select data entry form") for form in forms: action = form.createuiaction() valid, failreasons = form.valid if not valid: roam.utils.warning("Form {} failed to load".format(form.label)) roam.utils.warning("Reasons {}".format(failreasons)) action.triggered.connect(partial(self.showformerror, form)) else: action.triggered.connect(partial(self.loadform, form)) formpicker.addAction(action) formpicker.exec_() def showformerror(self, form): pass def viewurl(self, url): """ Open a URL in Roam :param url: :return: """ key = url.toString().lstrip('file://') try: # Hack. Eww fix me. data, imagetype = roam.htmlviewer.images[os.path.basename(key)] pix = QPixmap() if imagetype == 'base64': pix.loadFromData(data) else: pix.load(data) self.openimage(pix) except KeyError: pix = QPixmap() pix.load(key) if pix.isNull(): QDesktopServices.openUrl(url) return self.openimage(pix) def openimage(self, pixmap): viewer = ImageViewer(self.stackedWidget) viewer.resize(self.stackedWidget.size()) viewer.openimage(pixmap) def settingsupdated(self, settings): self.show() self.actionGPS.updateGPSPort() def updatestatuslabel(self): extent = self.canvas.extent() self.positionlabel.setText("Map Center: {}".format(extent.center().toString())) def on_geometryedit(self, form, feature): layer = form.QGISLayer self.reloadselection(layer, updated=[feature]) self.currentfeatureband.setToGeometry(feature.geometry(), layer) def reloadselection(self, layer, deleted=[], updated=[]): """ Reload the selection after features have been updated or deleted. :param layer: :param deleted: :param updated: :return: """ selectedfeatures = self.currentselection[layer] # Update any features that have changed. for updatedfeature in updated: oldfeatures = [f for f in selectedfeatures if f.id() == updatedfeature.id()] for feature in oldfeatures: self.currentselection[layer].remove(feature) self.currentselection[layer].append(updatedfeature) # Delete any old ones for deletedid in deleted: oldfeatures = [f for f in selectedfeatures if f.id() == deletedid] for feature in oldfeatures: self.currentselection[layer].remove(feature) RoamEvents.selectionchanged.emit(self.currentselection) def highlightselection(self, results): self.clearselection() for layer, features in results.iteritems(): band = self.selectionbands[layer] band.setColor(QColor(255, 0, 0, 200)) band.setIconSize(20) band.setWidth(2) band.setBrushStyle(Qt.NoBrush) band.reset(layer.geometryType()) for feature in features: band.addGeometry(feature.geometry(), layer) def clearselection(self): # Clear the main selection rubber band self.currentfeatureband.reset() # Clear the rest for band in self.selectionbands.itervalues(): band.reset() self.editfeaturestack = [] def highlightfeature(self, layer, feature, features): self.clearselection() self.highlightselection({layer: features}) self.currentfeatureband.setToGeometry(feature.geometry(), layer) def showmap(self): self.actionMap.setVisible(True) self.actionLegend.setVisible(True) self.actionMap.trigger() def hidedataentry(self): self.actionDataEntry.setVisible(False) def showdataentry(self): self.actionDataEntry.setVisible(True) self.actionDataEntry.trigger() def dataentrychanged(self, index): self.clearCapatureTools() if not index.isValid(): return modelindex = index # modelindex = self.dataentrymodel.index(index, 0) form = modelindex.data(Qt.UserRole + 1) self.dataentryselection.setCurrentIndex(index.row()) self.createCaptureButtons(form) def raiseerror(self, *exinfo): info = traceback.format_exception(*exinfo) item = self.bar.pushError(QApplication.translate('MainWindowPy','Seems something has gone wrong. Press for more details', None, QApplication.UnicodeUTF8), info) def setMapTool(self, tool, *args): self.canvas.setMapTool(tool) def homeview(self): """ Zoom the mapview canvas to the extents the project was opened at i.e. the default extent. """ self.canvas.setExtent(self.defaultextent) self.canvas.refresh() def connectButtons(self): def connectAction(action, tool): action.toggled.connect(partial(self.setMapTool, tool)) def cursor(name): pix = QPixmap(name) pix = pix.scaled(QSize(24,24)) return QCursor(pix) self.zoomInTool = QgsMapToolZoom(self.canvas, False) self.zoomOutTool = QgsMapToolZoom(self.canvas, True) self.panTool = PanTool(self.canvas) self.infoTool = InfoTool(self.canvas) connectAction(self.actionZoom_In, self.zoomInTool) connectAction(self.actionZoom_Out, self.zoomOutTool) connectAction(self.actionPan, self.panTool) connectAction(self.actionInfo, self.infoTool) self.zoomInTool.setCursor(cursor(':/icons/in')) self.zoomOutTool.setCursor(cursor(':/icons/out')) self.infoTool.setCursor(cursor(':/icons/info')) self.actionRaster.triggered.connect(self.toggleRasterLayers) self.infoTool.infoResults.connect(RoamEvents.selectionchanged.emit) self.actionHome.triggered.connect(self.homeview) self.actionQuit.triggered.connect(self.exit) def getcaptureactions(self): for action in self.projecttoolbar.actions(): if action.property('dataentry'): yield action def clearCapatureTools(self): captureselected = False for action in self.projecttoolbar.actions(): if action.objectName() == "capture" and action.isChecked(): captureselected = True if action.property('dataentry'): self.projecttoolbar.removeAction(action) return captureselected def createCaptureButtons(self, form): tool = form.getMaptool()(self.canvas) for action in tool.actions: # Create the action here. if action.ismaptool: action.toggled.connect(partial(self.setMapTool, tool)) # Set the action as a data entry button so we can remove it later. action.setProperty("dataentry", True) self.editgroup.addAction(action) self.layerbuttons.append(action) self.projecttoolbar.insertAction(self.topspaceraction, action) action.setChecked(action.isdefault) if hasattr(tool, 'geometryComplete'): add = partial(self.addNewFeature, form) tool.geometryComplete.connect(add) else: tool.finished.connect(self.openForm) tool.error.connect(partial(self.showUIMessage, form.label)) def loadform(self, form): self.clearCapatureTools() self.dataentryselection.setIcon(QIcon(form.icon)) self.dataentryselection.setText(form.icontext) self.createCaptureButtons(form) def clearToolRubberBand(self): """ Clear the rubber band of the active tool if it has one """ tool = self.canvas.mapTool() try: tool.clearBand() except AttributeError: # No clearBand method found, but that's cool. pass def showhelp(self, url): help = HelpPage(self.stackedWidget) help.setHelpPage(url) help.show() def dataentryfinished(self): self.hidedataentry() self.showmap() self.cleartempobjects() self.infodock.refreshcurrent() def featuredeleted(self, layer, featureid): self.dataentryfinished() self.reloadselection(layer, deleted=[featureid]) self.canvas.refresh() def featureSaved(self): self.dataentryfinished() self.canvas.refresh() def failSave(self, messages): self.bar.pushError("Error when saving changes.", messages) def cleartempobjects(self): self.currentfeatureband.reset() self.clearToolRubberBand() def formrejected(self, message, level): self.dataentryfinished() if message: RoamEvents.raisemessage("Form Message", message, level, duration=2) self.cleartempobjects() def openForm(self, form, feature, editmode): """ Open the form that is assigned to the layer """ self.currentfeatureband.setToGeometry(feature.geometry(), form.QGISLayer) self.showdataentry() self.dataentrywidget.openform(feature=feature, form=form, project=self.project, editmode=editmode) def editfeaturegeometry(self, form, feature, newgeometry): layer = form.QGISLayer layer.startEditing() feature.setGeometry(newgeometry) layer.updateFeature(feature) saved = layer.commitChanges() map(roam.utils.error, layer.commitErrors()) self.canvas.refresh() RoamEvents.editgeometry_complete.emit(form, feature) def addNewFeature(self, form, geometry): """ Add a new new feature to the given layer """ layer = form.QGISLayer if layer.geometryType() in [QGis.WKBMultiLineString, QGis.WKBMultiPoint, QGis.WKBMultiPolygon]: geometry.convertToMultiType() try: form, feature = self.editfeaturestack.pop() self.editfeaturegeometry(form, feature, newgeometry=geometry) return except IndexError: pass layer = form.QGISLayer fields = layer.pendingFields() feature = QgsFeature(fields) feature.setGeometry(geometry) for index in xrange(fields.count()): pkindexes = layer.dataProvider().pkAttributeIndexes() if index in pkindexes and layer.dataProvider().name() == 'spatialite': continue value = layer.dataProvider().defaultValue(index) feature[index] = value self.openForm(form, feature, editmode=False) def exit(self): """ Exit the application. """ QApplication.exit(0) def showInfoResults(self, results): forms = {} for layer in results.keys(): layername = layer.name() if not layername in forms: forms[layername] = list(self.project.formsforlayer(layername)) self.currentselection = results self.infodock.setResults(results, forms) self.infodock.show() def toggleRasterLayers(self): """ Toggle all raster layers on or off. """ if not self.canvaslayers: return #Freeze the canvas to save on UI refresh self.canvas.freeze() for layer in self.canvaslayers: if layer.layer().type() == QgsMapLayer.RasterLayer: layer.setVisible(not layer.isVisible()) # Really!? We have to reload the whole layer set every time? # WAT? self.canvas.setLayerSet(self.canvaslayers) self.canvas.freeze(False) self.canvas.refresh() def missingLayers(self, layers): """ Called when layers have failed to load from the current project """ roam.utils.warning("Missing layers") map(roam.utils.warning, layers) missinglayers = roam.messagebaritems.MissingLayerItem(layers, parent=self.bar) self.bar.pushItem(missinglayers) def loadprojects(self, projects): """ Load the given projects into the project list """ projects = list(projects) self.projectwidget.loadProjectList(projects) self.syncwidget.loadprojects(projects) def updatePage(self, action): """ Update the current stack page based on the current selected action """ page = action.property("page") self.stackedWidget.setCurrentIndex(page) def show(self): """ Override show method. Handles showing the app in fullscreen mode or just maximized """ fullscreen = roam.config.settings.get("fullscreen", False) if fullscreen: self.showFullScreen() else: self.showMaximized() def viewprojects(self): self.stackedWidget.setCurrentIndex(1) @roam.utils.timeit def _readProject(self, doc): """ readProject is called by QgsProject once the map layer has been populated with all the layers """ parser = ProjectParser(doc) canvasnode = parser.canvasnode self.canvas.freeze() self.canvas.mapRenderer().readXML(canvasnode) self.canvaslayers = parser.canvaslayers() self.canvas.setLayerSet(self.canvaslayers) #red = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorRedPart", 255 )[0]; #green = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorGreenPart", 255 )[0]; #blue = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorBluePart", 255 )[0]; #color = QColor(red, green, blue); #self.canvas.setCanvasColor(color) self.canvas.updateScale() self.projectOpened() self.canvas.freeze(False) self.canvas.refresh() GPS.crs = self.canvas.mapRenderer().destinationCrs() self.showmap() @roam.utils.timeit def projectOpened(self): """ Called when a new project is opened in QGIS. """ projectpath = QgsProject.instance().fileName() self.project = Project.from_folder(os.path.dirname(projectpath)) self.projectlabel.setText("Project: {}".format(self.project.name)) try: firstform = self.project.forms[0] self.loadform(self.project.forms[0]) self.dataentryselection.setVisible(True) except IndexError: self.dataentryselection.setVisible(False) # Enable the raster layers button only if the project contains a raster layer. layers = QgsMapLayerRegistry.instance().mapLayers().values() hasrasters = any(layer.type() == QgsMapLayer.RasterLayer for layer in layers) self.actionRaster.setEnabled(hasrasters) self.defaultextent = self.canvas.extent() roam.utils.info("Extent: {}".format(self.defaultextent.toString())) # Show panels for panel in self.project.getPanels(): self.mainwindow.addDockWidget(Qt.BottomDockWidgetArea, panel) self.panels.append(panel) self.infoTool.selectionlayers = self.project.selectlayersmapping() layers = self.project.legendlayersmapping().values() self.legendpage.updateitems(layers) self.actionPan.trigger() #noinspection PyArgumentList @roam.utils.timeit def loadProject(self, project): """ Load a project into the application . """ roam.utils.log(project) roam.utils.log(project.name) roam.utils.log(project.projectfile) roam.utils.log(project.valid) (passed, message) = project.onProjectLoad() if not passed: self.bar.pushMessage("Project load rejected", "Sorry this project couldn't" "be loaded. Click for me details.", QgsMessageBar.WARNING, extrainfo=message) return self.actionMap.trigger() self.closeProject() self.canvas.refresh() self.canvas.repaint() RoamEvents.selectioncleared.emit() # No idea why we have to set this each time. Maybe QGIS deletes it for # some reason. self.badLayerHandler = BadLayerHandler(callback=self.missingLayers) QgsProject.instance().setBadLayerHandler(self.badLayerHandler) self.stackedWidget.setCurrentIndex(3) self.projectloading_label.setText("Project {} Loading".format(project.name)) pixmap = QPixmap(project.splash) w = self.projectimage.width() h = self.projectimage.height() self.projectimage.setPixmap(pixmap.scaled(w,h, Qt.KeepAspectRatio)) QApplication.processEvents() QDir.setCurrent(os.path.dirname(project.projectfile)) fileinfo = QFileInfo(project.projectfile) QgsProject.instance().read(fileinfo) def closeProject(self): """ Close the current open project """ self.clearCapatureTools() self.canvas.freeze() QgsMapLayerRegistry.instance().removeAllMapLayers() self.canvas.clear() self.canvas.freeze(False) for panel in self.panels: self.removeDockWidget(panel) del panel # Remove all the old buttons for action in self.layerbuttons: self.editgroup.removeAction(action) self.panels = [] self.project = None self.dataentrywidget.clear() self.hidedataentry() self.infodock.close()
class PymetricsViewer( QWidget ): " Pymetrics tab widget " # Limits to colorize the McCabe score LittleRiskLimit = 10 ModerateRiskLimit = 20 HighRiskLimit = 50 # Options of providing a report SingleFile = 0 DirectoryFiles = 1 ProjectFiles = 2 SingleBuffer = 3 def __init__( self, parent = None ): QWidget.__init__( self, parent ) self.__reportUUID = "" self.__reportFileName = "" self.__reportOption = -1 self.__reportShown = False self.__report = None # Prepare members for reuse self.__noneLabel = QLabel( "\nNo results available" ) self.__noneLabel.setFrameShape( QFrame.StyledPanel ) self.__noneLabel.setAlignment( Qt.AlignHCenter ) self.__headerFont = self.__noneLabel.font() self.__headerFont.setPointSize( self.__headerFont.pointSize() + 4 ) self.__noneLabel.setFont( self.__headerFont ) self.__noneLabel.setAutoFillBackground( True ) noneLabelPalette = self.__noneLabel.palette() noneLabelPalette.setColor( QPalette.Background, GlobalData().skin.nolexerPaper ) self.__noneLabel.setPalette( noneLabelPalette ) self.__createLayout( parent ) self.__updateButtonsStatus() return def __createLayout( self, parent ): " Creates the toolbar and layout " # Buttons self.__mcCabeButton = QAction( PixmapCache().getIcon( 'tableview.png' ), 'Switch to McCabe only table view', self ) self.__mcCabeButton.setCheckable( True ) self.connect( self.__mcCabeButton, SIGNAL( 'toggled(bool)' ), self.__onMcCabe ) self.printButton = QAction( PixmapCache().getIcon( 'printer.png' ), 'Print', self ) #printButton.setShortcut( 'Ctrl+' ) self.connect( self.printButton, SIGNAL( 'triggered()' ), self.__onPrint ) self.printButton.setVisible( False ) self.printPreviewButton = QAction( PixmapCache().getIcon( 'printpreview.png' ), 'Print preview', self ) #printPreviewButton.setShortcut( 'Ctrl+' ) self.connect( self.printPreviewButton, SIGNAL( 'triggered()' ), self.__onPrintPreview ) self.printPreviewButton.setVisible( False ) spacer = QWidget() spacer.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Expanding ) self.clearButton = QAction( PixmapCache().getIcon( 'trash.png' ), 'Clear', self ) self.connect( self.clearButton, SIGNAL( 'triggered()' ), self.__clear ) # The toolbar self.toolbar = QToolBar( self ) self.toolbar.setOrientation( Qt.Vertical ) self.toolbar.setMovable( False ) self.toolbar.setAllowedAreas( Qt.RightToolBarArea ) self.toolbar.setIconSize( QSize( 16, 16 ) ) self.toolbar.setFixedWidth( 28 ) self.toolbar.setContentsMargins( 0, 0, 0, 0 ) self.toolbar.addAction( self.__mcCabeButton ) self.toolbar.addAction( self.printPreviewButton ) self.toolbar.addAction( self.printButton ) self.toolbar.addWidget( spacer ) self.toolbar.addAction( self.clearButton ) self.__totalResultsTree = QTreeWidget() self.__totalResultsTree.setAlternatingRowColors( True ) self.__totalResultsTree.setRootIsDecorated( True ) self.__totalResultsTree.setItemsExpandable( True ) self.__totalResultsTree.setUniformRowHeights( True ) self.__totalResultsTree.setItemDelegate( NoOutlineHeightDelegate( 4 ) ) headerLabels = [ "Path / name", "Value", "" ] self.__totalResultsTree.setHeaderLabels( headerLabels ) self.connect( self.__totalResultsTree, SIGNAL( "itemActivated(QTreeWidgetItem *, int)" ), self.__allItemActivated ) self.connect( self.__totalResultsTree, SIGNAL( "itemExpanded(QTreeWidgetItem *)" ), self.__onResultsExpanded ) self.__totalResultsTree.setColumnHidden( 2, True ) self.__totalResultsTree.hide() self.__mcCabeTable = QTreeWidget() self.__mcCabeTable.setAlternatingRowColors( True ) self.__mcCabeTable.setRootIsDecorated( False ) self.__mcCabeTable.setItemsExpandable( False ) self.__mcCabeTable.setSortingEnabled( True ) self.__mcCabeTable.setItemDelegate( NoOutlineHeightDelegate( 4 ) ) self.__mcCabeTable.setUniformRowHeights( True ) headerLabels = [ "", "File name", "Object", "McCabe Complexity" ] self.__mcCabeTable.setHeaderLabels( headerLabels ) self.connect( self.__mcCabeTable, SIGNAL( "itemActivated(QTreeWidgetItem *, int)" ), self.__mcCabeActivated ) self.__mcCabeTable.hide() self.__hLayout = QHBoxLayout() self.__hLayout.setContentsMargins( 0, 0, 0, 0 ) self.__hLayout.setSpacing( 0 ) self.__hLayout.addWidget( self.toolbar ) self.__hLayout.addWidget( self.__noneLabel ) self.__hLayout.addWidget( self.__totalResultsTree ) self.__hLayout.addWidget( self.__mcCabeTable ) self.setLayout( self.__hLayout ) return def getTotalResultsWidget( self ): " Provides a reference to the total results widget " return self.__totalResultsTree def getMcCabeResultsWidget( self ): " Provides a reference to the McCabe results widget " return self.__mcCabeTable def __updateButtonsStatus( self ): " Updates the buttons status " self.__mcCabeButton.setEnabled( self.__reportShown ) self.printButton.setEnabled( self.__reportShown ) self.printPreviewButton.setEnabled( self.__reportShown ) self.clearButton.setEnabled( self.__reportShown ) return def __onResultsExpanded( self, item ): " An item has been expanded, so the column width should be adjusted " self.__totalResultsTree.header().resizeSections( QHeaderView.ResizeToContents ) return def __onPrint( self ): " Triggered when the print button is pressed " pass def __onPrintPreview( self ): " triggered when the print preview button is pressed " pass def __onMcCabe( self, state ): " Triggered when the metrics view is switched " if not self.__reportShown: return if state: self.__totalResultsTree.hide() self.__mcCabeTable.show() self.__mcCabeButton.setIcon( PixmapCache().getIcon( 'treeview.png' ) ) self.__mcCabeButton.setToolTip( "Switch to complete " "results tree view" ) else: self.__mcCabeTable.hide() self.__totalResultsTree.show() self.__mcCabeButton.setIcon( PixmapCache().getIcon( 'tableview.png' ) ) self.__mcCabeButton.setToolTip( "Switch to McCabe only table view" ) return def setFocus( self ): " Overridden setFocus " self.__hLayout.setFocus() return def __clear( self ): " Clears the content of the vertical layout " if not self.__reportShown: return self.__totalResultsTree.clear() self.__totalResultsTree.hide() self.__mcCabeTable.clear() self.__mcCabeTable.hide() self.__noneLabel.show() self.__report = None self.__reportShown = False self.__updateButtonsStatus() # self.resizeEvent() self.__mcCabeButton.setIcon( PixmapCache().getIcon( 'tableview.png' ) ) self.__mcCabeButton.setToolTip( "Switch to McCabe only table view" ) self.__mcCabeButton.setChecked( False ) self.__updateTooltip() return def __updateTooltip( self ): " Generates a signal with appropriate string message " if not self.__reportShown: tooltip = "No metrics available" elif self.__reportOption == self.DirectoryFiles: tooltip = "Metrics generated for directory: " + \ self.__reportFileName elif self.__reportOption == self.ProjectFiles: tooltip = "Metrics generated for the whole project" elif self.__reportOption == self.SingleFile: tooltip = "Metrics generated for file: " + self.__reportFileName elif self.__reportOption == self.SingleBuffer: tooltip = "Metrics generated for unsaved file: " + \ self.__reportFileName else: tooltip = "" self.emit( SIGNAL( 'updatePymetricsTooltip' ), tooltip ) return @staticmethod def __shouldShowFileName( table, column ): " Checks if the file name is the same " size = table.topLevelItemCount() if size == 0: return False index = size - 1 firstName = table.topLevelItem( index ).text( column ) index -= 1 while index >= 0: if table.topLevelItem( index ).text( column ) != firstName: return True index -= 1 return False def showReport( self, metrics, reportOption, fileName, uuid ): " Shows the pymetrics results " self.__clear() self.__noneLabel.hide() self.__report = metrics self.__reportUUID = uuid self.__reportFileName = fileName self.__reportOption = reportOption if len( metrics.report ) > 1: accumulatedBasic = self.__accumulateBasicMetrics() accItem = QTreeWidgetItem( [ "Cumulative basic metrics" ] ) self.__totalResultsTree.addTopLevelItem( accItem ) for key in accumulatedBasic: bmItem = [ BasicMetrics.metricsOfInterest[ key ], splitThousands( str( accumulatedBasic[ key ] ) ) ] basicMetric = QTreeWidgetItem( bmItem ) accItem.addChild( basicMetric ) # Add the complete information for fileName in metrics.report: if reportOption == self.SingleBuffer: fileItem = QTreeWidgetItem( [ "Editor buffer" ] ) else: fileItem = QTreeWidgetItem( [ fileName ] ) info = GlobalData().briefModinfoCache.get( fileName ) if info.docstring is not None: fileItem.setToolTip( 0, info.docstring.text ) else: fileItem.setToolTip( 0, "" ) self.__totalResultsTree.addTopLevelItem( fileItem ) # Messages part messages = metrics.report[ fileName ].messages if len( messages ) > 0: messagesItem = QTreeWidgetItem( [ "Messages" ] ) fileItem.addChild( messagesItem ) for message in messages: mItem = [ message, "", "E" ] messagesItem.addChild( QTreeWidgetItem( mItem ) ) # Basic metrics part basicItem = QTreeWidgetItem( [ "Basic metrics" ] ) fileItem.addChild( basicItem ) basic = metrics.report[ fileName ].basicMetrics for key in basic.metrics: bmItem = [ BasicMetrics.metricsOfInterest[ key ], str( basic.metrics[ key ] ) ] basicMetric = QTreeWidgetItem( bmItem ) basicItem.addChild( basicMetric ) # McCabe part mccabeItem = QTreeWidgetItem( [ "McCabe metrics" ] ) fileItem.addChild( mccabeItem ) mccabe = metrics.report[ fileName ].mcCabeMetrics.metrics for objName in mccabe: objItem = [ objName, str( mccabe[ objName ] ), "M" ] mccabeMetric = QTreeWidgetItem( objItem ) mccabeItem.addChild( mccabeMetric ) # COCOMO 2 part cocomo = [ "COCOMO 2", str( metrics.report[ fileName ].cocomo2Metrics.value ) ] cocomoItem = QTreeWidgetItem( cocomo ) fileItem.addChild( cocomoItem ) # Resizing the table self.__totalResultsTree.header().resizeSections( QHeaderView.ResizeToContents ) # Add McCabe complexity information for fileName in metrics.report: mccabe = metrics.report[ fileName ].mcCabeMetrics.metrics for objName in mccabe: values = [ "", fileName, objName, str( mccabe[ objName ] ) ] self.__mcCabeTable.addTopLevelItem( McCabeTableItem( values ) ) if not self.__shouldShowFileName( self.__mcCabeTable, 1 ): self.__mcCabeTable.setColumnHidden( 1, True ) # Resizing and sorting the table self.__mcCabeTable.header().setSortIndicator( 3, Qt.DescendingOrder ) self.__mcCabeTable.sortItems( 3, self.__mcCabeTable.header().sortIndicatorOrder() ) self.__mcCabeTable.header().resizeSections( QHeaderView.ResizeToContents ) # Show the complete information self.__mcCabeTable.hide() self.__totalResultsTree.show() self.__reportShown = True self.__updateButtonsStatus() self.__updateTooltip() # It helps, but why do I have flickering? QApplication.processEvents() return def __accumulateBasicMetrics( self ): " Accumulates basic metrics for all the processed files " basic = {} for fileName in self.__report.report: singleBasic = self.__report.report[ fileName ].basicMetrics.metrics for key in singleBasic: if not key.startswith( 'num' ): continue if key in basic: basic[ key ] += int( singleBasic[ key ] ) else: basic[ key ] = int( singleBasic[ key ] ) return basic def __mcCabeActivated( self, item, column ): " Handles the double click (or Enter) on the mccabe table item " objName = str( item.text( 2 ) ) if self.__reportOption == self.SingleBuffer: if os.path.isabs( self.__reportFileName ): fileName = self.__reportFileName else: fileName = "" else: fileName = str( item.text( 1 ) ) self.__onMcCabeObject( objName, fileName ) return def __allItemActivated( self, item, column ): " Handles the double click (or Enter) in the total results tree " # We process only the error messages and McCabe items hiddenColumnText = str( item.text( 2 ) ) if not hiddenColumnText in [ "M", "E" ]: return fileName = self.__getTreeItemFileName( item ) lineNumber = 0 if hiddenColumnText == "M": # This is McCabe item objName = str( item.text( 0 ) ) self.__onMcCabeObject( objName, fileName ) return elif hiddenColumnText == "E": # This is an error message message = str( item.text( 0 ) ) pos = message.find( "at line" ) if pos == -1: logging.error( "Unknown format of the message. " "Please inform the developers." ) return parts = message[ pos: ].split() try: lineNumber = int( parts[ 2 ].replace( ',', '' ) ) except: logging.error( "Unknown format of the message. " "Please inform the developers." ) return if fileName == "": # This is an unsaved buffer, try to find the editor by UUID mainWindow = GlobalData().mainWindow widget = mainWindow.getWidgetByUUID( self.__reportUUID ) if widget is None: logging.error( "The unsaved buffer has been closed" ) return # The widget was found, so jump to the required editor = widget.getEditor() editor.gotoLine( lineNumber ) editor.setFocus() return GlobalData().mainWindow.openFile( fileName, lineNumber ) return def __getTreeItemFileName( self, item ): " Identifies the tree view item file name " if self.__reportOption == self.SingleBuffer: if os.path.isabs( self.__reportFileName ): return self.__reportFileName return "" # The file name is always two levels up fileItem = item.parent().parent() return str( fileItem.text( 0 ) ) def __onMcCabeObject( self, objName, fileName ): " Called when the user activated McCabe item " info = None mainWindow = GlobalData().mainWindow widget = mainWindow.getWidgetByUUID( self.__reportUUID ) if widget is None: if fileName == "": logging.error( "The unsaved buffer has been closed" ) return # No widget, but we know the file name info = getBriefModuleInfoFromFile( fileName ) else: # The widget was found editor = widget.getEditor() # The editor content has been modified, so re-parse the buffer info = getBriefModuleInfoFromMemory( editor.text() ) parts = objName.split( '.' ) currentIndex = 0 functionsContainer = info.functions classesContainer = info.classes line = -1 if objName == "__main__" and len( parts ) == 1: # Special case - global file scope line = 1 currentIndex = 1 while currentIndex < len( parts ): found = False for func in functionsContainer: if func.name == parts[ currentIndex ]: if currentIndex == len( parts ) - 1: # Found, jump to the line line = func.line break functionsContainer = func.functions classesContainer = func.classes found = True break if line != -1: break if found: currentIndex += 1 continue for klass in classesContainer: if klass.name == parts[ currentIndex ]: if currentIndex == len( parts ) - 1: # Found, jump to the line line = klass.line break functionsContainer = klass.functions classesContainer = klass.classes found = True if line != -1: break if found: currentIndex += 1 continue # Not found logging.error( "Cannot find the " + objName ) return # Here we have the line number if widget is None: GlobalData().mainWindow.openFile( fileName, line ) else: editor = widget.getEditor() editor.gotoLine( line ) editor.setFocus() return def onFileUpdated( self, fileName, uuid ): " Called when a buffer is saved or saved as " if not self.__reportShown: return if self.__reportUUID != uuid: return # Currently shown report is for the saved buffer # File name is expected being absolute self.__reportFileName = fileName self.emit( SIGNAL( 'updatePymetricsTooltip' ), "Metrics generated for buffer saved as " + fileName ) return
class QMap(): def __init__(self, iface): self.iface = iface self.actions = [] self.panels= [] self.navtoolbar = self.iface.mapNavToolToolBar() self.mainwindow = self.iface.mainWindow() self.iface.projectRead.connect(self.projectOpened) self.iface.initializationCompleted.connect(self.setupUI) self.actionGroup = QActionGroup(self.mainwindow) self.actionGroup.setExclusive(True) self.menuGroup = QActionGroup(self.mainwindow) self.menuGroup.setExclusive(True) self.movetool = MoveTool(self.iface.mapCanvas(), []) self.report = PopDownReport(self.iface.messageBar()) self.dialogprovider = DialogProvider(iface.mapCanvas(), iface) self.dialogprovider.accepted.connect(self.clearToolRubberBand) self.dialogprovider.rejected.connect(self.clearToolRubberBand) self.edittool = EditTool(self.iface.mapCanvas(),[]) self.edittool.finished.connect(self.openForm) @property def _mapLayers(self): return QgsMapLayerRegistry.instance().mapLayers() def clearToolRubberBand(self): tool = self.iface.mapCanvas().mapTool() try: tool.clearBand() except AttributeError: # No clearBand method found, but that's cool. pass def missingLayers(self, layers): def showError(): html = ["<h1>Missing Layers</h1>", "<ul>"] for layer in layers: html.append("<li>{}</li>".format(layer)) html.append("</ul>") self.errorreport.updateHTML("".join(html)) message = "Seems like {} didn't load correctly".format(utils._pluralstring('layer', len(layers))) utils.warning("Missing layers") map(utils.warning, layers) self.widget = self.messageBar.createMessage("Missing Layers", message, QIcon(":/icons/sad")) button = QPushButton(self.widget) button.setCheckable(True) button.setChecked(self.errorreport.isVisible()) button.setText("Show missing layers") button.toggled.connect(showError) button.toggled.connect(functools.partial(self.errorreport.setVisible)) self.widget.destroyed.connect(self.hideReports) self.widget.layout().addWidget(button) self.messageBar.pushWidget(self.widget, QgsMessageBar.WARNING) def excepthook(self, ex_type, value, tb): """ Custom exception hook so that we can handle errors in a nicer way """ where = ''.join(traceback.format_tb(tb)) msg = '{}'.format(value) utils.critical(msg) def showError(): html = """ <html> <body bgcolor="#FFEDED"> <p><b>{}</b></p> <p align="left"><small>{}</small></p> </body> </html> """.format(msg, where) self.errorreport.updateHTML(html) self.widget = self.messageBar.createMessage("oops", "Looks like an error occurred", QIcon(":/icons/sad")) button = QPushButton(self.widget) button.setCheckable(True) button.setChecked(self.errorreport.isVisible()) button.setText("Show error") button.toggled.connect(showError) button.toggled.connect(functools.partial(self.errorreport.setVisible)) self.widget.destroyed.connect(self.hideReports) self.widget.layout().addWidget(button) self.messageBar.pushWidget(self.widget, QgsMessageBar.CRITICAL) def hideReports(self): self.errorreport.setVisible(False) self.report.setVisible(False) def setupUI(self): """ Set up the main QGIS interface items. Called after QGIS has loaded the plugin. """ fullscreen = utils.settings["fullscreen"] if fullscreen: self.mainwindow.showFullScreen() else: self.mainwindow.showMaximized() self.navtoolbar.setMovable(False) self.navtoolbar.setAllowedAreas(Qt.TopToolBarArea) self.mainwindow.insertToolBar(self.toolbar, self.navtoolbar) self.openProjectAction.trigger() def setMapTool(self, tool): """ Set the current mapview canvas tool tool -- The QgsMapTool to set """ self.iface.mapCanvas().setMapTool(tool) def createToolBars(self): """ Create all the needed toolbars """ self.menutoolbar = QToolBar("Menu", self.mainwindow) self.menutoolbar.setMovable(False) self.menutoolbar.setAllowedAreas(Qt.LeftToolBarArea) self.mainwindow.addToolBar(Qt.LeftToolBarArea, self.menutoolbar) self.toolbar = QToolBar("QMap", self.mainwindow) self.mainwindow.addToolBar(Qt.TopToolBarArea, self.toolbar) self.toolbar.setMovable(False) self.editingtoolbar = FloatingToolBar("Editing", self.toolbar) self.extraaddtoolbar = FloatingToolBar("Extra Add Tools", self.toolbar) self.syncactionstoolbar = FloatingToolBar("Syncing", self.toolbar) self.syncactionstoolbar.setOrientation(Qt.Vertical) def createActions(self): """ Create all the actions """ self.homeAction = (QAction(QIcon(":/icons/zoomfull"), "Default View", self.mainwindow)) self.gpsAction = (GPSAction(QIcon(":/icons/gps"), self.iface.mapCanvas(), self.mainwindow)) self.openProjectAction = (QAction(QIcon(":/icons/open"), "Projects", self.mainwindow)) self.openProjectAction.setCheckable(True) self.toggleRasterAction = (QAction(QIcon(":/icons/photo"), "Aerial Photos", self.mainwindow)) self.syncAction = QAction(QIcon(":/icons/sync"), "Sync", self.mainwindow) self.syncAction.setVisible(False) self.editattributesaction = QAction(QIcon(":/icons/edit"), "Edit Attributes", self.mainwindow) self.editattributesaction.setCheckable(True) self.editattributesaction.toggled.connect(functools.partial(self.setMapTool, self.edittool)) self.moveaction = QAction(QIcon(":/icons/move"), "Move Feature", self.mainwindow) self.moveaction.setCheckable(True) self.editingmodeaction = QAction(QIcon(":/icons/edittools"), "Editing Tools", self.mainwindow) self.editingmodeaction.setCheckable(True) self.addatgpsaction = QAction(QIcon(":/icons/gpsadd"), "Add at GPS", self.mainwindow) self.edittool.layersupdated.connect(self.editattributesaction.setVisible) self.movetool.layersupdated.connect(self.moveaction.setVisible) self.edittool.layersupdated.connect(self.updateEditTools) self.movetool.layersupdated.connect(self.updateEditTools) def updateEditTools(self, *args): """ Show or hide the Editing Tools button based on the sub tools. """ if self.edittool.layers and self.movetool.layers: self.editingmodeaction.setVisible(True) else: self.editingmodeaction.setVisible(False) def initGui(self): """ Create all the icons and setup the tool bars. Called by QGIS when loading. This is called before setupUI. """ QApplication.setWindowIcon(QIcon(":/branding/logo")) self.mainwindow.findChildren(QMenuBar)[0].setVisible(False) self.mainwindow.setContextMenuPolicy(Qt.PreventContextMenu) self.mainwindow.setWindowTitle("IntraMaps Roam: Mobile Data Collection") # Disable QGIS logging window popups. We do our own logging QgsMessageLog.instance().messageReceived.disconnect() s = """ QToolButton { padding: 6px; color: #4f4f4f; } QToolButton:hover { padding: 6px; background-color: rgb(211, 228, 255); } QToolBar { background: white; } QCheckBox::indicator { width: 40px; height: 40px; } QLabel { color: #4f4f4f; } QDialog { background-color: rgb(255, 255, 255); } QPushButton { border: 1px solid #e1e1e1; padding: 6px; color: #4f4f4f; } QPushButton:hover { border: 1px solid #e1e1e1; padding: 6px; background-color: rgb(211, 228, 255); } QCheckBox { color: #4f4f4f; } QComboBox::drop-down { width: 30px; } """ self.mainwindow.setStyleSheet(s) mainwidget = self.mainwindow.centralWidget() mainwidget.setLayout(QGridLayout()) mainwidget.layout().setContentsMargins(0,0,0,0) newlayout = QGridLayout() newlayout.setContentsMargins(0,0,0,0) newlayout.addWidget(self.iface.mapCanvas(), 0,0,2,1) newlayout.addWidget(self.iface.messageBar(), 0,0,1,1) wid = QWidget() wid.setLayout(newlayout) self.stack = QStackedWidget() self.messageBar = QgsMessageBar(wid) self.messageBar.setSizePolicy( QSizePolicy.Minimum, QSizePolicy.Fixed ) self.errorreport = PopDownReport(self.messageBar) mainwidget.layout().addWidget(self.stack, 0,0,2,1) mainwidget.layout().addWidget(self.messageBar, 0,0,1,1) self.helppage = HelpPage() helppath = os.path.join(os.path.dirname(__file__) , 'help',"help.html") self.helppage.setHelpPage(helppath) self.projectwidget = ProjectsWidget() self.projectwidget.requestOpenProject.connect(self.loadProject) self.stack.addWidget(wid) self.stack.addWidget(self.projectwidget) self.stack.addWidget(self.helppage) sys.excepthook = self.excepthook def createSpacer(width=30): widget = QWidget() widget.setMinimumWidth(width) return widget self.createToolBars() self.createActions() spacewidget = createSpacer(60) gpsspacewidget = createSpacer() gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.moveaction.toggled.connect(functools.partial(self.setMapTool, self.movetool)) showediting = (functools.partial(self.editingtoolbar.showToolbar, self.editingmodeaction, self.editattributesaction)) self.editingmodeaction.toggled.connect(showediting) self.addatgpsaction.triggered.connect(self.addAtGPS) self.addatgpsaction.setEnabled(self.gpsAction.isConnected) self.gpsAction.gpsfixed.connect(self.addatgpsaction.setEnabled) self.editingtoolbar.addToActionGroup(self.editattributesaction) self.editingtoolbar.addToActionGroup(self.moveaction) self.actionGroup.addAction(self.editingmodeaction) self.homeAction.triggered.connect(self.zoomToDefaultView) self.openProjectAction.triggered.connect(self.showOpenProjectDialog) self.openProjectAction.triggered.connect(functools.partial(self.stack.setCurrentIndex, 1)) self.toggleRasterAction.triggered.connect(self.toggleRasterLayers) self.navtoolbar.insertAction(self.iface.actionZoomIn(), self.iface.actionTouch()) self.navtoolbar.insertAction(self.iface.actionTouch(), self.homeAction) self.navtoolbar.insertAction(self.iface.actionTouch(), self.iface.actionZoomFullExtent()) self.navtoolbar.insertAction(self.homeAction, self.iface.actionZoomFullExtent()) self.navtoolbar.addAction(self.toggleRasterAction) self.navtoolbar.insertWidget(self.iface.actionZoomFullExtent(), spacewidget) self.toolbar.addAction(self.editingmodeaction) self.toolbar.addAction(self.syncAction) self.toolbar.addAction(self.gpsAction) self.toolbar.insertWidget(self.syncAction, gpsspacewidget) self.toolbar.insertSeparator(self.gpsAction) self.extraaddtoolbar.addAction(self.addatgpsaction) self.editingtoolbar.addAction(self.editattributesaction) self.editingtoolbar.addAction(self.moveaction) self.mapview = QAction(QIcon(":/icons/map"), "Map", self.menutoolbar) self.mapview.setCheckable(True) self.mapview.triggered.connect(functools.partial(self.stack.setCurrentIndex, 0)) self.help = QAction(QIcon(":/icons/help"), "Help", self.menutoolbar) self.help.setCheckable(True) self.help.triggered.connect(functools.partial(self.stack.setCurrentIndex, 2)) self.help.setVisible(False) self.userlabel = QLabel("Current User <br> {user}".format(user=getpass.getuser())) self.userlabel.setAlignment(Qt.AlignCenter) self.userlabel.setStyleSheet(""" QLabel { color: #8c8c8c; font: 10px "Calibri" ; }""") self.quit = QAction(QIcon(":/icons/quit"), "Quit", self.menutoolbar) self.quit.triggered.connect(self.iface.actionExit().trigger) self.menuGroup.addAction(self.mapview) self.menuGroup.addAction(self.openProjectAction) self.menuGroup.addAction(self.help) self.menutoolbar.addAction(self.mapview) self.menutoolbar.addAction(self.openProjectAction) self.menutoolbar.addAction(self.help) self.menutoolbar.addAction(self.quit) quitspacewidget = createSpacer() quitspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) labelaction = self.menutoolbar.insertWidget(self.quit, self.userlabel) self.menutoolbar.insertWidget(labelaction, quitspacewidget) self.setupIcons() self.stack.currentChanged.connect(self.updateUIState) def updateUIState(self, page): """ Update the UI state to reflect the currently selected page in the stacked widget """ def setToolbarsActive(enabled): toolbars = self.mainwindow.findChildren(QToolBar) for toolbar in toolbars: if toolbar == self.menutoolbar: continue toolbar.setEnabled(enabled) def setPanelsVisible(visible): for panel in self.panels: panel.setVisible(visible) ismapview = page == 0 setToolbarsActive(ismapview) setPanelsVisible(ismapview) def addAtGPS(self): """ Add a record at the current GPS location. """ action = self.actionGroup.checkedAction() if not action: return layer = action.data() if not layer: return point = self.gpsAction.position self.addNewFeature(layer=layer, geometry=point) def zoomToDefaultView(self): """ Zoom the mapview canvas to the extents the project was opened at i.e. the default extent. """ self.iface.mapCanvas().setExtent(self.defaultextent) self.iface.mapCanvas().refresh() def toggleRasterLayers(self): """ Toggle all raster layers on or off. """ legend = self.iface.legendInterface() #Freeze the canvas to save on UI refresh self.iface.mapCanvas().freeze() for layer in self._mapLayers.values(): if layer.type() == QgsMapLayer.RasterLayer: isvisible = legend.isLayerVisible(layer) legend.setLayerVisible(layer, not isvisible) self.iface.mapCanvas().freeze(False) self.iface.mapCanvas().refresh() def setupIcons(self): """ Update toolbars to have text and icons, change normal QGIS icons to new style """ toolbars = self.mainwindow.findChildren(QToolBar) for toolbar in toolbars: toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) toolbar.setIconSize(QSize(32, 32)) self.iface.actionTouch().setIconText("Pan") self.iface.actionTouch().setIcon(QIcon(":/icons/pan")) self.iface.actionZoomIn().setIcon(QIcon(":/icons/in")) self.iface.actionZoomOut().setIcon(QIcon(":/icons/out")) self.iface.actionPan().setIcon(QIcon(":/icons/pan")) self.iface.actionZoomFullExtent().setIcon(QIcon(":/icons/home")) self.iface.actionZoomFullExtent().setIconText("Home View") self.actionGroup.addAction(self.iface.actionZoomIn()) self.actionGroup.addAction(self.iface.actionZoomOut()) self.actionGroup.addAction(self.iface.actionTouch()) def projectOpened(self): """ Called when a new project is opened in QGIS. """ for panel in self.panels: self.mainwindow.removeDockWidget(panel) del panel projectpath = QgsProject.instance().fileName() project = QMapProject(os.path.dirname(projectpath), self.iface) self.createFormButtons(projectlayers = project.getConfiguredLayers()) # Enable the raster layers button only if the project contains a raster layer. hasrasters = any(layer.type() for layer in self._mapLayers.values()) self.toggleRasterAction.setEnabled(hasrasters) self.defaultextent = self.iface.mapCanvas().extent() self.connectSyncProviders(project) # Show panels self.panels = list(project.getPanels()) for panel in self.panels: self.mainwindow.addDockWidget(Qt.BottomDockWidgetArea , panel) self.iface.messageBar().popWidget() def createFormButtons(self, projectlayers): """ Create buttons for each form that is definded """ # Remove all the old buttons for action in self.actions: self.actionGroup.removeAction(action) self.toolbar.removeAction(action) self.edittool.layers = [] self.movetool.layers = [] for layer in projectlayers: try: qgslayer = QgsMapLayerRegistry.instance().mapLayersByName(layer.name)[0] if qgslayer.type() == QgsMapLayer.RasterLayer: utils.log("We can't support raster layers for data entry") continue layer.QGISLayer = qgslayer except KeyError: utils.log("Layer not found in project") continue if 'capture' in layer.capabilities: text = layer.icontext try: tool = layer.getMaptool(self.iface.mapCanvas()) except NoMapToolConfigured: utils.log("No map tool configured") continue except ErrorInMapTool as error: self.messageBar.pushMessage("Error configuring map tool", error.message, level=QgsMessageBar.WARNING) continue # Hack until I fix it later if isinstance(tool, PointTool): add = functools.partial(self.addNewFeature, qgslayer) tool.geometryComplete.connect(add) else: tool.finished.connect(self.openForm) action = QAction(QIcon(layer.icon), text, self.mainwindow) action.setData(layer) action.setCheckable(True) action.toggled.connect(functools.partial(self.setMapTool, tool)) self.toolbar.insertAction(self.editingmodeaction, action) if not tool.isEditTool(): # Connect the GPS tools strip to the action pressed event. showgpstools = (functools.partial(self.extraaddtoolbar.showToolbar, action, None)) action.toggled.connect(showgpstools) self.actionGroup.addAction(action) self.actions.append(action) if 'edit' in layer.capabilities: # TODO Use snapping options from project radius = (QgsTolerance.toleranceInMapUnits( 10, qgslayer, self.iface.mapCanvas().mapRenderer(), QgsTolerance.Pixels)) self.edittool.addLayer(qgslayer) self.edittool.searchRadius = radius if 'move' in layer.capabilities: self.movetool.addLayer(qgslayer) def openForm(self, layer, feature): if not layer.isEditable(): layer.startEditing() self.dialogprovider.openDialog(feature=feature, layer=layer) def addNewFeature(self, layer, geometry): fields = layer.pendingFields() if not layer.isEditable(): layer.startEditing() feature = QgsFeature() feature.setGeometry( geometry ) feature.initAttributes(fields.count()) feature.setFields(fields) for indx in xrange(fields.count()): feature[indx] = layer.dataProvider().defaultValue(indx) self.dialogprovider.openDialog(feature=feature, layer=layer) def showOpenProjectDialog(self): """ Show the project selection dialog. """ self.stack.setCurrentIndex(1) path = os.path.join(os.path.dirname(__file__), '..' , 'projects/') projects = getProjects(path, self.iface) self.projectwidget.loadProjectList(projects) def loadProject(self, project): """ Load a project into QGIS. """ utils.log(project) utils.log(project.name) utils.log(project.projectfile) utils.log(project.vaild) (passed, message) = project.onProjectLoad() if not passed: QMessageBox.warning(self.mainwindow, "Project Load Rejected", "Project couldn't be loaded because {}".format(message)) return self.mapview.trigger() self.iface.newProject(False) self.iface.mapCanvas().freeze() fileinfo = QFileInfo(project.projectfile) self.badLayerHandler = BadLayerHandler(callback=self.missingLayers) QgsProject.instance().setBadLayerHandler( self.badLayerHandler ) self.iface.messageBar().pushMessage("Project Loading","", QgsMessageBar.INFO) QgsProject.instance().read(fileinfo) self.iface.mapCanvas().updateScale() self.iface.mapCanvas().freeze(False) self.iface.mapCanvas().refresh() self.mainwindow.setWindowTitle("IntraMaps Roam: Mobile Data Collection") self.iface.projectRead.emit() def unload(self): del self.toolbar def connectSyncProviders(self, project): self.syncactionstoolbar.clear() syncactions = list(project.getSyncProviders()) # Don't show the sync button if there is no sync providers if not syncactions: self.syncAction.setVisible(False) return self.syncAction.setVisible(True) for provider in syncactions: action = QAction(QIcon(":/icons/sync"), "Sync {}".format(provider.name), self.mainwindow) action.triggered.connect(functools.partial(self.syncProvider, provider)) self.syncactionstoolbar.addAction(action) try: self.syncAction.toggled.disconnect() except TypeError: pass try: self.syncAction.triggered.disconnect() except TypeError: pass if len(syncactions) == 1: # If one provider is set then we just connect the main button. self.syncAction.setCheckable(False) self.syncAction.setText("Sync") self.syncAction.triggered.connect(functools.partial(self.syncProvider, syncactions[0])) else: # the sync button because a sync menu self.syncAction.setCheckable(True) self.syncAction.setText("Sync Menu") showsyncoptions = (functools.partial(self.syncactionstoolbar.showToolbar, self.syncAction, None)) self.syncAction.toggled.connect(showsyncoptions) def syncstarted(self): # Remove the old widget if it's still there. # I don't really like this. Seems hacky. try: self.iface.messageBar().popWidget(self.syncwidget) except RuntimeError: pass except AttributeError: pass self.iface.messageBar().findChildren(QToolButton)[0].setVisible(False) self.syncwidget = self.iface.messageBar().createMessage("Syncing", "Sync in progress", QIcon(":/icons/syncing")) button = QPushButton(self.syncwidget) button.setCheckable(True) button.setText("Status") button.setIcon(QIcon(":/icons/syncinfo")) button.toggled.connect(functools.partial(self.report.setVisible)) pro = QProgressBar() pro.setMaximum(0) pro.setMinimum(0) self.syncwidget.layout().addWidget(pro) self.syncwidget.layout().addWidget(button) self.iface.messageBar().pushWidget(self.syncwidget, QgsMessageBar.INFO) def synccomplete(self): try: self.iface.messageBar().popWidget(self.syncwidget) except RuntimeError: pass stylesheet = ("QgsMessageBar { background-color: rgba(239, 255, 233); border: 0px solid #b9cfe4; } " "QLabel,QTextEdit { color: #057f35; } ") closebutton = self.iface.messageBar().findChildren(QToolButton)[0] closebutton.setVisible(True) closebutton.clicked.connect(functools.partial(self.report.setVisible, False)) self.syncwidget = self.iface.messageBar().createMessage("Syncing", "Sync Complete", QIcon(":/icons/syncdone")) button = QPushButton(self.syncwidget) button.setCheckable(True) button.setChecked(self.report.isVisible()) button.setText("Sync Report") button.setIcon(QIcon(":/icons/syncinfo")) button.toggled.connect(functools.partial(self.report.setVisible)) pro = QProgressBar() pro.setMaximum(100) pro.setValue(100) self.syncwidget.layout().addWidget(pro) self.syncwidget.layout().addWidget(button) self.iface.messageBar().pushWidget(self.syncwidget) self.iface.messageBar().setStyleSheet(stylesheet) self.iface.mapCanvas().refresh() def syncerror(self): try: self.iface.messageBar().popWidget(self.syncwidget) except RuntimeError: pass closebutton = self.iface.messageBar().findChildren(QToolButton)[0] closebutton.setVisible(True) closebutton.clicked.connect(functools.partial(self.report.setVisible, False)) self.syncwidget = self.iface.messageBar().createMessage("Syncing", "Sync Error", QIcon(":/icons/syncfail")) button = QPushButton(self.syncwidget) button.setCheckable(True) button.setChecked(self.report.isVisible()) button.setText("Sync Report") button.setIcon(QIcon(":/icons/syncinfo")) button.toggled.connect(functools.partial(self.report.setVisible)) self.syncwidget.layout().addWidget(button) self.iface.messageBar().pushWidget(self.syncwidget, QgsMessageBar.CRITICAL) self.iface.mapCanvas().refresh() def syncProvider(self, provider): self.syncAction.toggle() provider.syncStarted.connect(functools.partial(self.syncAction.setEnabled, False)) provider.syncStarted.connect(self.syncstarted) provider.syncComplete.connect(self.synccomplete) provider.syncComplete.connect(functools.partial(self.syncAction.setEnabled, True)) provider.syncComplete.connect(functools.partial(self.report.updateHTML)) provider.syncMessage.connect(self.report.updateHTML) provider.syncError.connect(self.report.updateHTML) provider.syncError.connect(self.syncerror) provider.syncComplete.connect(functools.partial(self.syncAction.setEnabled, True)) provider.startSync()
class IgnoredExceptionsViewer( QWidget ): " Implements the client exceptions viewer for a debugger " def __init__( self, parent = None ): QWidget.__init__( self, parent ) self.__createPopupMenu() self.__createLayout() self.__ignored = [] self.__currentItem = None GlobalData().project.projectChanged.connect( self.__onProjectChanged ) if Settings().showIgnoredExcViewer == False: self.__onShowHide( True ) return def __createPopupMenu( self ): " Creates the popup menu " self.__excptMenu = QMenu() self.__removeMenuItem = self.__excptMenu.addAction( PixmapCache().getIcon( 'ignexcptdel.png' ), "Remove from ignore list", self.__onRemoveFromIgnore ) return def __createLayout( self ): " Creates the widget layout " verticalLayout = QVBoxLayout( self ) verticalLayout.setContentsMargins( 0, 0, 0, 0 ) verticalLayout.setSpacing( 0 ) self.headerFrame = QFrame() self.headerFrame.setFrameStyle( QFrame.StyledPanel ) self.headerFrame.setAutoFillBackground( True ) headerPalette = self.headerFrame.palette() headerBackground = headerPalette.color( QPalette.Background ) headerBackground.setRgb( min( headerBackground.red() + 30, 255 ), min( headerBackground.green() + 30, 255 ), min( headerBackground.blue() + 30, 255 ) ) headerPalette.setColor( QPalette.Background, headerBackground ) self.headerFrame.setPalette( headerPalette ) self.headerFrame.setFixedHeight( 24 ) self.__excptLabel = QLabel( "Ignored exception types" ) expandingSpacer = QSpacerItem( 10, 10, QSizePolicy.Expanding ) fixedSpacer = QSpacerItem( 3, 3 ) self.__showHideButton = QToolButton() self.__showHideButton.setAutoRaise( True ) self.__showHideButton.setIcon( PixmapCache().getIcon( 'less.png' ) ) self.__showHideButton.setFixedSize( 20, 20 ) self.__showHideButton.setToolTip( "Hide ignored exceptions list" ) self.__showHideButton.setFocusPolicy( Qt.NoFocus ) self.__showHideButton.clicked.connect( self.__onShowHide ) headerLayout = QHBoxLayout() headerLayout.setContentsMargins( 1, 1, 1, 1 ) headerLayout.addSpacerItem( fixedSpacer ) headerLayout.addWidget( self.__excptLabel ) headerLayout.addSpacerItem( expandingSpacer ) headerLayout.addWidget( self.__showHideButton ) self.headerFrame.setLayout( headerLayout ) self.exceptionsList = QTreeWidget( self ) self.exceptionsList.setSortingEnabled( False ) self.exceptionsList.setAlternatingRowColors( True ) self.exceptionsList.setRootIsDecorated( False ) self.exceptionsList.setItemsExpandable( True ) self.exceptionsList.setUniformRowHeights( True ) self.exceptionsList.setSelectionMode( QAbstractItemView.SingleSelection ) self.exceptionsList.setSelectionBehavior( QAbstractItemView.SelectRows ) self.exceptionsList.setItemDelegate( NoOutlineHeightDelegate( 4 ) ) self.exceptionsList.setContextMenuPolicy( Qt.CustomContextMenu ) self.exceptionsList.customContextMenuRequested.connect( self.__showContextMenu ) self.exceptionsList.itemSelectionChanged.connect( self.__onSelectionChanged ) self.exceptionsList.setHeaderLabels( [ "Exception type" ] ) self.__excTypeEdit = QLineEdit() self.__excTypeEdit.setFixedHeight( 26 ) self.__excTypeEdit.textChanged.connect( self.__onNewFilterChanged ) self.__excTypeEdit.returnPressed.connect( self.__onAddExceptionFilter ) self.__addButton = QPushButton( "Add" ) # self.__addButton.setFocusPolicy( Qt.NoFocus ) self.__addButton.setEnabled( False ) self.__addButton.clicked.connect( self.__onAddExceptionFilter ) expandingSpacer2 = QWidget() expandingSpacer2.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Expanding ) self.__removeButton = QAction( PixmapCache().getIcon( 'delitem.png' ), "Remove selected exception type", self ) self.__removeButton.triggered.connect( self.__onRemoveFromIgnore ) self.__removeButton.setEnabled( False ) fixedSpacer1 = QWidget() fixedSpacer1.setFixedWidth( 5 ) self.__removeAllButton = QAction( PixmapCache().getIcon( 'ignexcptdelall.png' ), "Remove all the exception types", self ) self.__removeAllButton.triggered.connect( self.__onRemoveAllFromIgnore ) self.__removeAllButton.setEnabled( False ) self.toolbar = QToolBar() self.toolbar.setOrientation( Qt.Horizontal ) self.toolbar.setMovable( False ) self.toolbar.setAllowedAreas( Qt.TopToolBarArea ) self.toolbar.setIconSize( QSize( 16, 16 ) ) self.toolbar.setFixedHeight( 28 ) self.toolbar.setContentsMargins( 0, 0, 0, 0 ) self.toolbar.addWidget( expandingSpacer2 ) self.toolbar.addAction( self.__removeButton ) self.toolbar.addWidget( fixedSpacer1 ) self.toolbar.addAction( self.__removeAllButton ) addLayout = QHBoxLayout() addLayout.setContentsMargins( 1, 1, 1, 1 ) addLayout.setSpacing( 1 ) addLayout.addWidget( self.__excTypeEdit ) addLayout.addWidget( self.__addButton ) verticalLayout.addWidget( self.headerFrame ) verticalLayout.addWidget( self.toolbar ) verticalLayout.addWidget( self.exceptionsList ) verticalLayout.addLayout( addLayout ) return def clear( self ): " Clears the content " self.exceptionsList.clear() self.__excTypeEdit.clear() self.__addButton.setEnabled( False ) self.__ignored = [] self.__currentItem = None self.__updateTitle() return def __onShowHide( self, startup = False ): " Triggered when show/hide button is clicked " if startup or self.exceptionsList.isVisible(): self.exceptionsList.setVisible( False ) self.__excTypeEdit.setVisible( False ) self.__addButton.setVisible( False ) self.__removeButton.setVisible( False ) self.__removeAllButton.setVisible( False ) self.__showHideButton.setIcon( PixmapCache().getIcon( 'more.png' ) ) self.__showHideButton.setToolTip( "Show ignored exceptions list" ) self.__minH = self.minimumHeight() self.__maxH = self.maximumHeight() self.setMinimumHeight( self.headerFrame.height() ) self.setMaximumHeight( self.headerFrame.height() ) Settings().showIgnoredExcViewer = False else: self.exceptionsList.setVisible( True ) self.__excTypeEdit.setVisible( True ) self.__addButton.setVisible( True ) self.__removeButton.setVisible( True ) self.__removeAllButton.setVisible( True ) self.__showHideButton.setIcon( PixmapCache().getIcon( 'less.png' ) ) self.__showHideButton.setToolTip( "Hide ignored exceptions list" ) self.setMinimumHeight( self.__minH ) self.setMaximumHeight( self.__maxH ) Settings().showIgnoredExcViewer = True return def __onSelectionChanged( self ): " Triggered when the current item is changed " selected = list( self.exceptionsList.selectedItems() ) if selected: self.__currentItem = selected[ 0 ] self.__removeButton.setEnabled( True ) else: self.__currentItem = None self.__removeButton.setEnabled( False ) return def __showContextMenu( self, coord ): " Shows the frames list context menu " contextItem = self.exceptionsList.itemAt( coord ) if contextItem is not None: self.__currentItem = contextItem self.__excptMenu.popup( QCursor.pos() ) return def __updateTitle( self ): " Updates the section title " count = self.exceptionsList.topLevelItemCount() if count == 0: self.__excptLabel.setText( "Ignored exception types" ) else: self.__excptLabel.setText( "Ignored exception types (total: " + str( count ) + ")" ) self.__removeAllButton.setEnabled( count != 0 ) return def __onProjectChanged( self, what ): " Triggered when a project is changed " if what != CodimensionProject.CompleteProject: return self.clear() project = GlobalData().project if project.isLoaded(): self.__ignored = list( project.ignoredExcpt ) else: self.__ignored = list( Settings().ignoredExceptions ) for exceptionType in self.__ignored: item = QTreeWidgetItem( self.exceptionsList ) item.setText( 0, exceptionType ) self.__updateTitle() return def __onNewFilterChanged( self, text ): " Triggered when the text is changed " text = str( text ).strip() if text == "": self.__addButton.setEnabled( False ) return if " " in text: self.__addButton.setEnabled( False ) return if text in self.__ignored: self.__addButton.setEnabled( False ) return self.__addButton.setEnabled( True ) return def __onAddExceptionFilter( self ): " Adds an item into the ignored exceptions list " text = self.__excTypeEdit.text().strip() self.addExceptionFilter( text ) def addExceptionFilter( self, excType ): " Adds a new item into the ignored exceptions list " if excType == "": return if " " in excType: return if excType in self.__ignored: return item = QTreeWidgetItem( self.exceptionsList ) item.setText( 0, excType ) project = GlobalData().project if project.isLoaded(): project.addExceptionFilter( excType ) else: Settings().addExceptionFilter( excType ) self.__ignored.append( excType ) self.__updateTitle() return def __onRemoveFromIgnore( self ): " Removes an item from the ignored exception types list " if self.__currentItem is None: return text = self.__currentItem.text( 0 ) # Find the item index and remove it index = 0 while True: if self.exceptionsList.topLevelItem( index ).text( 0 ) == text: self.exceptionsList.takeTopLevelItem( index ) break index += 1 project = GlobalData().project if project.isLoaded(): project.deleteExceptionFilter( text ) else: Settings().deleteExceptionFilter( text ) self.__ignored.remove( text ) self.__updateTitle() return def __onRemoveAllFromIgnore( self ): " Triggered when all the ignored exceptions should be deleted " self.clear() project = GlobalData().project if project.isLoaded(): project.setExceptionFilters( [] ) else: Settings().setExceptionFilters( [] ) return def isIgnored( self, exceptionType ): " Returns True if this exception type should be ignored " return exceptionType in self.__ignored
class PymetricsViewer(QWidget): " Pymetrics tab widget " # Limits to colorize the McCabe score LittleRiskLimit = 10 ModerateRiskLimit = 20 HighRiskLimit = 50 # Options of providing a report SingleFile = 0 DirectoryFiles = 1 ProjectFiles = 2 SingleBuffer = 3 def __init__(self, parent=None): QWidget.__init__(self, parent) self.__reportUUID = "" self.__reportFileName = "" self.__reportOption = -1 self.__reportShown = False self.__report = None # Prepare members for reuse self.__noneLabel = QLabel("\nNo results available") self.__noneLabel.setFrameShape(QFrame.StyledPanel) self.__noneLabel.setAlignment(Qt.AlignHCenter) self.__headerFont = self.__noneLabel.font() self.__headerFont.setPointSize(self.__headerFont.pointSize() + 4) self.__noneLabel.setFont(self.__headerFont) self.__noneLabel.setAutoFillBackground(True) noneLabelPalette = self.__noneLabel.palette() noneLabelPalette.setColor(QPalette.Background, GlobalData().skin.nolexerPaper) self.__noneLabel.setPalette(noneLabelPalette) self.__createLayout(parent) self.__updateButtonsStatus() return def __createLayout(self, parent): " Creates the toolbar and layout " # Buttons self.__mcCabeButton = QAction(PixmapCache().getIcon('tableview.png'), 'Switch to McCabe only table view', self) self.__mcCabeButton.setCheckable(True) self.connect(self.__mcCabeButton, SIGNAL('toggled(bool)'), self.__onMcCabe) self.printButton = QAction(PixmapCache().getIcon('printer.png'), 'Print', self) #printButton.setShortcut( 'Ctrl+' ) self.connect(self.printButton, SIGNAL('triggered()'), self.__onPrint) self.printButton.setVisible(False) self.printPreviewButton = QAction( PixmapCache().getIcon('printpreview.png'), 'Print preview', self) #printPreviewButton.setShortcut( 'Ctrl+' ) self.connect(self.printPreviewButton, SIGNAL('triggered()'), self.__onPrintPreview) self.printPreviewButton.setVisible(False) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.clearButton = QAction(PixmapCache().getIcon('trash.png'), 'Clear', self) self.connect(self.clearButton, SIGNAL('triggered()'), self.__clear) # The toolbar self.toolbar = QToolBar(self) self.toolbar.setOrientation(Qt.Vertical) self.toolbar.setMovable(False) self.toolbar.setAllowedAreas(Qt.RightToolBarArea) self.toolbar.setIconSize(QSize(16, 16)) self.toolbar.setFixedWidth(28) self.toolbar.setContentsMargins(0, 0, 0, 0) self.toolbar.addAction(self.__mcCabeButton) self.toolbar.addAction(self.printPreviewButton) self.toolbar.addAction(self.printButton) self.toolbar.addWidget(spacer) self.toolbar.addAction(self.clearButton) self.__totalResultsTree = QTreeWidget() self.__totalResultsTree.setAlternatingRowColors(True) self.__totalResultsTree.setRootIsDecorated(True) self.__totalResultsTree.setItemsExpandable(True) self.__totalResultsTree.setUniformRowHeights(True) self.__totalResultsTree.setItemDelegate(NoOutlineHeightDelegate(4)) headerLabels = ["Path / name", "Value", ""] self.__totalResultsTree.setHeaderLabels(headerLabels) self.connect(self.__totalResultsTree, SIGNAL("itemActivated(QTreeWidgetItem *, int)"), self.__allItemActivated) self.connect(self.__totalResultsTree, SIGNAL("itemExpanded(QTreeWidgetItem *)"), self.__onResultsExpanded) self.__totalResultsTree.setColumnHidden(2, True) self.__totalResultsTree.hide() self.__mcCabeTable = QTreeWidget() self.__mcCabeTable.setAlternatingRowColors(True) self.__mcCabeTable.setRootIsDecorated(False) self.__mcCabeTable.setItemsExpandable(False) self.__mcCabeTable.setSortingEnabled(True) self.__mcCabeTable.setItemDelegate(NoOutlineHeightDelegate(4)) self.__mcCabeTable.setUniformRowHeights(True) headerLabels = ["", "File name", "Object", "McCabe Complexity"] self.__mcCabeTable.setHeaderLabels(headerLabels) self.connect(self.__mcCabeTable, SIGNAL("itemActivated(QTreeWidgetItem *, int)"), self.__mcCabeActivated) self.__mcCabeTable.hide() self.__hLayout = QHBoxLayout() self.__hLayout.setContentsMargins(0, 0, 0, 0) self.__hLayout.setSpacing(0) self.__hLayout.addWidget(self.toolbar) self.__hLayout.addWidget(self.__noneLabel) self.__hLayout.addWidget(self.__totalResultsTree) self.__hLayout.addWidget(self.__mcCabeTable) self.setLayout(self.__hLayout) return def getTotalResultsWidget(self): " Provides a reference to the total results widget " return self.__totalResultsTree def getMcCabeResultsWidget(self): " Provides a reference to the McCabe results widget " return self.__mcCabeTable def __updateButtonsStatus(self): " Updates the buttons status " self.__mcCabeButton.setEnabled(self.__reportShown) self.printButton.setEnabled(self.__reportShown) self.printPreviewButton.setEnabled(self.__reportShown) self.clearButton.setEnabled(self.__reportShown) return def __onResultsExpanded(self, item): " An item has been expanded, so the column width should be adjusted " self.__totalResultsTree.header().resizeSections( QHeaderView.ResizeToContents) return def __onPrint(self): " Triggered when the print button is pressed " pass def __onPrintPreview(self): " triggered when the print preview button is pressed " pass def __onMcCabe(self, state): " Triggered when the metrics view is switched " if not self.__reportShown: return if state: self.__totalResultsTree.hide() self.__mcCabeTable.show() self.__mcCabeButton.setIcon(PixmapCache().getIcon('treeview.png')) self.__mcCabeButton.setToolTip("Switch to complete " "results tree view") else: self.__mcCabeTable.hide() self.__totalResultsTree.show() self.__mcCabeButton.setIcon(PixmapCache().getIcon('tableview.png')) self.__mcCabeButton.setToolTip("Switch to McCabe only table view") return def setFocus(self): " Overridden setFocus " self.__hLayout.setFocus() return def __clear(self): " Clears the content of the vertical layout " if not self.__reportShown: return self.__totalResultsTree.clear() self.__totalResultsTree.hide() self.__mcCabeTable.clear() self.__mcCabeTable.hide() self.__noneLabel.show() self.__report = None self.__reportShown = False self.__updateButtonsStatus() # self.resizeEvent() self.__mcCabeButton.setIcon(PixmapCache().getIcon('tableview.png')) self.__mcCabeButton.setToolTip("Switch to McCabe only table view") self.__mcCabeButton.setChecked(False) self.__updateTooltip() return def __updateTooltip(self): " Generates a signal with appropriate string message " if not self.__reportShown: tooltip = "No metrics available" elif self.__reportOption == self.DirectoryFiles: tooltip = "Metrics generated for directory: " + \ self.__reportFileName elif self.__reportOption == self.ProjectFiles: tooltip = "Metrics generated for the whole project" elif self.__reportOption == self.SingleFile: tooltip = "Metrics generated for file: " + self.__reportFileName elif self.__reportOption == self.SingleBuffer: tooltip = "Metrics generated for unsaved file: " + \ self.__reportFileName else: tooltip = "" self.emit(SIGNAL('updatePymetricsTooltip'), tooltip) return @staticmethod def __shouldShowFileName(table, column): " Checks if the file name is the same " size = table.topLevelItemCount() if size == 0: return False index = size - 1 firstName = table.topLevelItem(index).text(column) index -= 1 while index >= 0: if table.topLevelItem(index).text(column) != firstName: return True index -= 1 return False def showReport(self, metrics, reportOption, fileName, uuid): " Shows the pymetrics results " self.__clear() self.__noneLabel.hide() self.__report = metrics self.__reportUUID = uuid self.__reportFileName = fileName self.__reportOption = reportOption if len(metrics.report) > 1: accumulatedBasic = self.__accumulateBasicMetrics() accItem = QTreeWidgetItem(["Cumulative basic metrics"]) self.__totalResultsTree.addTopLevelItem(accItem) for key in accumulatedBasic: bmItem = [ BasicMetrics.metricsOfInterest[key], splitThousands(str(accumulatedBasic[key])) ] basicMetric = QTreeWidgetItem(bmItem) accItem.addChild(basicMetric) # Add the complete information for fileName in metrics.report: if reportOption == self.SingleBuffer: fileItem = QTreeWidgetItem(["Editor buffer"]) else: fileItem = QTreeWidgetItem([fileName]) info = GlobalData().briefModinfoCache.get(fileName) if info.docstring is not None: fileItem.setToolTip(0, info.docstring.text) else: fileItem.setToolTip(0, "") self.__totalResultsTree.addTopLevelItem(fileItem) # Messages part messages = metrics.report[fileName].messages if len(messages) > 0: messagesItem = QTreeWidgetItem(["Messages"]) fileItem.addChild(messagesItem) for message in messages: mItem = [message, "", "E"] messagesItem.addChild(QTreeWidgetItem(mItem)) # Basic metrics part basicItem = QTreeWidgetItem(["Basic metrics"]) fileItem.addChild(basicItem) basic = metrics.report[fileName].basicMetrics for key in basic.metrics: bmItem = [ BasicMetrics.metricsOfInterest[key], str(basic.metrics[key]) ] basicMetric = QTreeWidgetItem(bmItem) basicItem.addChild(basicMetric) # McCabe part mccabeItem = QTreeWidgetItem(["McCabe metrics"]) fileItem.addChild(mccabeItem) mccabe = metrics.report[fileName].mcCabeMetrics.metrics for objName in mccabe: objItem = [objName, str(mccabe[objName]), "M"] mccabeMetric = QTreeWidgetItem(objItem) mccabeItem.addChild(mccabeMetric) # COCOMO 2 part cocomo = [ "COCOMO 2", str(metrics.report[fileName].cocomo2Metrics.value) ] cocomoItem = QTreeWidgetItem(cocomo) fileItem.addChild(cocomoItem) # Resizing the table self.__totalResultsTree.header().resizeSections( QHeaderView.ResizeToContents) # Add McCabe complexity information for fileName in metrics.report: mccabe = metrics.report[fileName].mcCabeMetrics.metrics for objName in mccabe: values = ["", fileName, objName, str(mccabe[objName])] self.__mcCabeTable.addTopLevelItem(McCabeTableItem(values)) if not self.__shouldShowFileName(self.__mcCabeTable, 1): self.__mcCabeTable.setColumnHidden(1, True) # Resizing and sorting the table self.__mcCabeTable.header().setSortIndicator(3, Qt.DescendingOrder) self.__mcCabeTable.sortItems( 3, self.__mcCabeTable.header().sortIndicatorOrder()) self.__mcCabeTable.header().resizeSections( QHeaderView.ResizeToContents) # Show the complete information self.__mcCabeTable.hide() self.__totalResultsTree.show() self.__reportShown = True self.__updateButtonsStatus() self.__updateTooltip() # It helps, but why do I have flickering? QApplication.processEvents() return def __accumulateBasicMetrics(self): " Accumulates basic metrics for all the processed files " basic = {} for fileName in self.__report.report: singleBasic = self.__report.report[fileName].basicMetrics.metrics for key in singleBasic: if not key.startswith('num'): continue if key in basic: basic[key] += int(singleBasic[key]) else: basic[key] = int(singleBasic[key]) return basic def __mcCabeActivated(self, item, column): " Handles the double click (or Enter) on the mccabe table item " objName = str(item.text(2)) if self.__reportOption == self.SingleBuffer: if os.path.isabs(self.__reportFileName): fileName = self.__reportFileName else: fileName = "" else: fileName = str(item.text(1)) self.__onMcCabeObject(objName, fileName) return def __allItemActivated(self, item, column): " Handles the double click (or Enter) in the total results tree " # We process only the error messages and McCabe items hiddenColumnText = str(item.text(2)) if not hiddenColumnText in ["M", "E"]: return fileName = self.__getTreeItemFileName(item) lineNumber = 0 if hiddenColumnText == "M": # This is McCabe item objName = str(item.text(0)) self.__onMcCabeObject(objName, fileName) return elif hiddenColumnText == "E": # This is an error message message = str(item.text(0)) pos = message.find("at line") if pos == -1: logging.error("Unknown format of the message. " "Please inform the developers.") return parts = message[pos:].split() try: lineNumber = int(parts[2].replace(',', '')) except: logging.error("Unknown format of the message. " "Please inform the developers.") return if fileName == "": # This is an unsaved buffer, try to find the editor by UUID mainWindow = GlobalData().mainWindow widget = mainWindow.getWidgetByUUID(self.__reportUUID) if widget is None: logging.error("The unsaved buffer has been closed") return # The widget was found, so jump to the required editor = widget.getEditor() editor.gotoLine(lineNumber) editor.setFocus() return GlobalData().mainWindow.openFile(fileName, lineNumber) return def __getTreeItemFileName(self, item): " Identifies the tree view item file name " if self.__reportOption == self.SingleBuffer: if os.path.isabs(self.__reportFileName): return self.__reportFileName return "" # The file name is always two levels up fileItem = item.parent().parent() return str(fileItem.text(0)) def __onMcCabeObject(self, objName, fileName): " Called when the user activated McCabe item " info = None mainWindow = GlobalData().mainWindow widget = mainWindow.getWidgetByUUID(self.__reportUUID) if widget is None: if fileName == "": logging.error("The unsaved buffer has been closed") return # No widget, but we know the file name info = getBriefModuleInfoFromFile(fileName) else: # The widget was found editor = widget.getEditor() # The editor content has been modified, so re-parse the buffer info = getBriefModuleInfoFromMemory(editor.text()) parts = objName.split('.') currentIndex = 0 functionsContainer = info.functions classesContainer = info.classes line = -1 if objName == "__main__" and len(parts) == 1: # Special case - global file scope line = 1 currentIndex = 1 while currentIndex < len(parts): found = False for func in functionsContainer: if func.name == parts[currentIndex]: if currentIndex == len(parts) - 1: # Found, jump to the line line = func.line break functionsContainer = func.functions classesContainer = func.classes found = True break if line != -1: break if found: currentIndex += 1 continue for klass in classesContainer: if klass.name == parts[currentIndex]: if currentIndex == len(parts) - 1: # Found, jump to the line line = klass.line break functionsContainer = klass.functions classesContainer = klass.classes found = True if line != -1: break if found: currentIndex += 1 continue # Not found logging.error("Cannot find the " + objName) return # Here we have the line number if widget is None: GlobalData().mainWindow.openFile(fileName, line) else: editor = widget.getEditor() editor.gotoLine(line) editor.setFocus() return def onFileUpdated(self, fileName, uuid): " Called when a buffer is saved or saved as " if not self.__reportShown: return if self.__reportUUID != uuid: return # Currently shown report is for the saved buffer # File name is expected being absolute self.__reportFileName = fileName self.emit(SIGNAL('updatePymetricsTooltip'), "Metrics generated for buffer saved as " + fileName) return
class FindInFilesViewer(QWidget): " Find in files viewer tab widget " lastEntered = None def __init__(self, parent=None): QWidget.__init__(self, parent) global searchTooltip searchTooltip = Tooltip() self.__reportRegexp = None self.__reportResults = [] self.__reportShown = False self.__bufferChangeconnected = False # Prepare members for reuse self.__noneLabel = QLabel("\nNo results available") self.__noneLabel.setFrameShape(QFrame.StyledPanel) self.__noneLabel.setAlignment(Qt.AlignHCenter) self.__headerFont = self.__noneLabel.font() self.__headerFont.setPointSize(self.__headerFont.pointSize() + 4) self.__noneLabel.setFont(self.__headerFont) self.__noneLabel.setAutoFillBackground(True) noneLabelPalette = self.__noneLabel.palette() noneLabelPalette.setColor(QPalette.Background, GlobalData().skin.nolexerPaper) self.__noneLabel.setPalette(noneLabelPalette) # Keep pylint happy self.printButton = None self.clearButton = None self.printPreviewButton = None self.__createLayout(parent) self.__updateButtonsStatus() GlobalData().project.projectChanged.connect(self.__onProjectChanged) return def __createLayout(self, parent): " Creates the toolbar and layout " # Buttons self.printButton = QAction(PixmapCache().getIcon('printer.png'), 'Print', self) #printButton.setShortcut( 'Ctrl+' ) self.printButton.triggered.connect(self.__onPrint) self.printButton.setVisible(False) self.printPreviewButton = QAction( PixmapCache().getIcon('printpreview.png'), 'Print preview', self) #printPreviewButton.setShortcut( 'Ctrl+' ) self.printPreviewButton.triggered.connect(self.__onPrintPreview) self.printPreviewButton.setVisible(False) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.clearButton = QAction(PixmapCache().getIcon('trash.png'), 'Clear', self) self.clearButton.triggered.connect(self.__clear) # The toolbar self.toolbar = QToolBar(self) self.toolbar.setOrientation(Qt.Vertical) self.toolbar.setMovable(False) self.toolbar.setAllowedAreas(Qt.RightToolBarArea) self.toolbar.setIconSize(QSize(16, 16)) self.toolbar.setFixedWidth(28) self.toolbar.setContentsMargins(0, 0, 0, 0) self.toolbar.addAction(self.printPreviewButton) self.toolbar.addAction(self.printButton) self.toolbar.addWidget(spacer) self.toolbar.addAction(self.clearButton) self.__resultsTree = FindResultsTreeWidget() self.__resultsTree.setAlternatingRowColors(True) self.__resultsTree.setRootIsDecorated(True) self.__resultsTree.setItemsExpandable(True) self.__resultsTree.setUniformRowHeights(True) self.__resultsTree.setItemDelegate(NoOutlineHeightDelegate(4)) headerLabels = ["File name / line", "Text"] self.__resultsTree.setHeaderLabels(headerLabels) self.__resultsTree.itemActivated.connect(self.__resultActivated) self.__resultsTree.itemClicked.connect(self.__resultClicked) self.__resultsTree.setMouseTracking(True) self.__resultsTree.itemEntered.connect(self.__itemEntered) self.__resultsTree.hide() self.__hLayout = QHBoxLayout() self.__hLayout.setContentsMargins(0, 0, 0, 0) self.__hLayout.setSpacing(0) self.__hLayout.addWidget(self.toolbar) self.__hLayout.addWidget(self.__noneLabel) self.__hLayout.addWidget(self.__resultsTree) self.setLayout(self.__hLayout) return def getResultsTree(self): " Provides a reference to the results tree " return self.__resultsTree def __updateButtonsStatus(self): " Updates the buttons status " self.printButton.setEnabled(self.__reportShown) self.printPreviewButton.setEnabled(self.__reportShown) self.clearButton.setEnabled(self.__reportShown) return def __onPrint(self): " Triggered when the print button is pressed " pass def __onPrintPreview(self): " triggered when the print preview button is pressed " pass def setFocus(self): " Overridden setFocus " self.__hLayout.setFocus() return def __onProjectChanged(self, what): " Triggered when a project is changed " if what == CodimensionProject.CompleteProject: self.__clear() return def __clear(self): " Clears the content of the vertical layout " if not self.__reportShown: return # Disconnect the buffer change signal if it is connected if self.__bufferChangeconnected: self.__bufferChangeconnected = False mainWindow = GlobalData().mainWindow editorsManager = mainWindow.editorsManagerWidget.editorsManager editorsManager.bufferModified.disconnect( self.__resultsTree.onBufferModified) self.__resultsTree.resetCache() self.__resultsTree.clear() self.__resultsTree.hide() self.__noneLabel.show() self.__reportRegexp = None self.__reportResults = [] self.__reportShown = False self.__updateButtonsStatus() return def showReport(self, regexp, results): " Shows the find in files results " self.__clear() self.__noneLabel.hide() self.__reportRegexp = regexp self.__reportResults = results # Add the complete information totalMatched = 0 for item in results: matched = len(item.matches) totalMatched += matched if matched == 1: matchText = " (1 match)" else: matchText = " (" + str(matched) + " matches)" columns = [item.fileName, matchText] fileItem = MatchTableFileItem(columns, item.bufferUUID) fileItem.setIcon(0, getFileIcon(detectFileType(item.fileName))) if item.tooltip != "": fileItem.setToolTip(0, item.tooltip) self.__resultsTree.addTopLevelItem(fileItem) # Matches for match in item.matches: columns = [str(match.line), match.text] matchItem = MatchTableItem(columns, match.tooltip) fileItem.addChild(matchItem) fileItem.setExpanded(True) # Update the header with the total number of matches headerLabels = [ "File name / line (total files: " + str(len(results)) + ")", "Text (total matches: " + str(totalMatched) + ")" ] self.__resultsTree.setHeaderLabels(headerLabels) # Resizing the table self.__resultsTree.header().resizeSections( QHeaderView.ResizeToContents) # Show the complete information self.__resultsTree.show() self.__resultsTree.buildCache() self.__reportShown = True self.__updateButtonsStatus() # Connect the buffer change signal if not connected yet if not self.__bufferChangeconnected: self.__bufferChangeconnected = True mainWindow = GlobalData().mainWindow editorsManager = mainWindow.editorsManagerWidget.editorsManager editorsManager.bufferModified.connect( self.__resultsTree.onBufferModified) return def __resultClicked(self, item, column): " Handles the single click " hideSearchTooltip() return def __resultActivated(self, item, column): " Handles the double click (or Enter) on a match " if type(item) == MatchTableItem: fileName = str(item.parent().data(0, Qt.DisplayRole).toString()) lineNumber = int(item.data(0, Qt.DisplayRole).toString()) GlobalData().mainWindow.openFile(fileName, lineNumber) hideSearchTooltip() return def __itemEntered(self, item, column): " Triggered when the mouse cursor entered a row " if type(item) != MatchTableItem: self.lastEntered = item hideSearchTooltip() return if column != 1: # Show the tooltip only for the column with results self.lastEntered = None hideSearchTooltip() return # Memorize the row height for proper tooltip displaying later global cellHeight cellHeight = self.__resultsTree.visualItemRect(item).height() if self.lastEntered != item or not inside: item.itemEntered() self.lastEntered = item return
class DockTitleBar(QToolBar): def __init__(self, parent): QToolBar.__init__(self, parent) assert parent self.dock = parent # a fake spacer widget w = QWidget(self) l = QHBoxLayout(w) l.setMargin(0) l.setSpacing(0) l.addStretch() frame = QFrame() layout = QBoxLayout(QBoxLayout.LeftToRight, frame) layout.setContentsMargins(4, 4, 0, 0) layout.setSpacing(2) self.aDockFrame = self.addWidget(frame) self.__icon = QLabel() layout.addWidget(self.__icon) layout.addWidget(QLabel(self.dock.windowTitle())) self.dock.windowIconChanged.connect(self.__setWindowIcon) # fake spacer item spacer = QWidgetAction(self) spacer.setDefaultWidget(w) self.setMovable(False) self.setFloatable(False) self.setIconSize(QSize(12, 12)) self.aFloat = QAction(self) self.aClose = QAction(self) QToolBar.addAction(self, spacer) self.separator = QToolBar.addSeparator(self) QToolBar.addAction(self, self.aFloat) QToolBar.addAction(self, self.aClose) self.updateStandardIcons() self.dockWidgetFeaturesChanged(self.dock.features()) self.dock.featuresChanged.connect(self.dockWidgetFeaturesChanged) self.aFloat.triggered.connect(self._floatTriggered) self.aClose.triggered.connect(self.dock.close) def addAction(self, action): self.insertAction(self.separator, action) return action def __setWindowIcon(self, icon): if not icon.isNull(): self.__icon.setPixmap(self.dock.windowIcon().pixmap(16)) def __setWindowTitle(self, title): self.__title.setText(title) def paintEvent(self, _): painter = QStylePainter(self) options = QStyleOptionToolBar() # init style options options.initFrom(self.dock) options.rect = self.rect() textRect = self.rect().adjusted(3, 3, 0, 0) msh = self.minimumSizeHint() # need to rotate if vertical state if self.dock.features() & QDockWidget.DockWidgetVerticalTitleBar: painter.rotate(-90) painter.translate(QPointF(-self.rect().height(), 0)) self.transposeSize(options.rect) self.transposeSize(textRect) msh.transpose() # draw toolbar painter.drawControl(QStyle.CE_ToolBar, options) # restore rotation if self.dock.features() & QDockWidget.DockWidgetVerticalTitleBar: painter.rotate(90) @staticmethod def transposeSize(rect): size = rect.size() size.transpose() rect.setSize(size) def updateStandardIcons(self): size = QSize(16, 16) rect = QRect(QPoint(), self.iconSize()) transform = QTransform() transform.rotate(90) pixmap = self.style().standardIcon( QStyle.SP_TitleBarNormalButton, None, self.widgetForAction(self.aFloat)).pixmap(size) rect.moveCenter(pixmap.rect().center()) pixmap = pixmap.copy(rect) self.aFloat.setIcon(QIcon(pixmap)) pixmap = self.style().standardIcon(QStyle.SP_TitleBarCloseButton, None, self.widgetForAction( self.aClose)).pixmap(size) rect.moveCenter(pixmap.rect().center()) pixmap = pixmap.copy(rect) self.aClose.setIcon(QIcon(pixmap)) def event(self, event): if event.type() == QEvent.StyleChange: self.updateStandardIcons() return QToolBar.event(self, event) def minimumSizeHint(self): return QToolBar.sizeHint(self) def sizeHint(self): size = QToolBar.sizeHint(self) fm = QFontMetrics(self.font()) if self.dock.features() & QDockWidget.DockWidgetVerticalTitleBar: size.setHeight(size.height() + fm.width(self.dock.windowTitle())) else: size.setWidth(size.width() + fm.width(self.dock.windowTitle())) return size def _floatTriggered(self): self.dock.setFloating(not self.dock.isFloating()) def dockWidgetFeaturesChanged(self, features): self.aFloat.setVisible(features & QDockWidget.DockWidgetFloatable) self.aClose.setVisible(features & QDockWidget.DockWidgetClosable) # update toolbar orientation if features & QDockWidget.DockWidgetVerticalTitleBar: if self.orientation() & Qt.Vertical: return self.setOrientation(Qt.Vertical) else: if self.orientation() & Qt.Horizontal: return self.setOrientation(Qt.Horizontal) # reorder the actions items = self.actions() items.reverse() self.clear() self.addActions(items)
class IOConsoleTabWidget(QWidget, MainWindowTabWidgetBase): " IO console tab widget " textEditorZoom = pyqtSignal(int) settingUpdated = pyqtSignal() def __init__(self, parent): MainWindowTabWidgetBase.__init__(self) QWidget.__init__(self, parent) self.__viewer = RedirectedIOConsole(self) self.connect(self.__viewer, SIGNAL('UserInput'), self.__onUserInput) self.__createLayout() return def __onUserInput(self, userInput): " Triggered when the user finished input in the redirected IO console " self.emit(SIGNAL('UserInput'), userInput) self.__clearButton.setEnabled(True) return def __createLayout(self): " Creates the toolbar and layout " # Buttons self.__printButton = QAction(PixmapCache().getIcon('printer.png'), 'Print', self) self.__printButton.triggered.connect(self.__onPrint) self.__printButton.setEnabled(False) self.__printButton.setVisible(False) self.__printPreviewButton = QAction( PixmapCache().getIcon('printpreview.png'), 'Print preview', self) self.__printPreviewButton.triggered.connect(self.__onPrintPreview) self.__printPreviewButton.setEnabled(False) self.__printPreviewButton.setVisible(False) # self.__sendUpButton = QAction( PixmapCache().getIcon('sendioup.png'), # 'Send to Main Editing Area', self ) # self.__sendUpButton.triggered.connect( self.__sendUp ) self.__filterMenu = QMenu(self) self.__filterMenu.aboutToShow.connect(self.__filterAboutToShow) self.__filterGroup = QActionGroup(self) self.__filterShowAllAct = self.__filterMenu.addAction("Show all") self.__filterShowAllAct.setCheckable(True) self.__filterShowAllAct.setActionGroup(self.__filterGroup) self.__filterShowAllAct.triggered.connect(self.__onFilterShowAll) self.__filterShowStdoutAct = self.__filterMenu.addAction( "Show stdin and stdout") self.__filterShowStdoutAct.setCheckable(True) self.__filterShowStdoutAct.setActionGroup(self.__filterGroup) self.__filterShowStdoutAct.triggered.connect(self.__onFilterShowStdout) self.__filterShowStderrAct = self.__filterMenu.addAction( "Show stdin and stderr") self.__filterShowStderrAct.setCheckable(True) self.__filterShowStderrAct.setActionGroup(self.__filterGroup) self.__filterShowStderrAct.triggered.connect(self.__onFilterShowStderr) self.__filterButton = QToolButton(self) self.__filterButton.setIcon(PixmapCache().getIcon('iofilter.png')) self.__filterButton.setToolTip('Filtering settings') self.__filterButton.setPopupMode(QToolButton.InstantPopup) self.__filterButton.setMenu(self.__filterMenu) self.__filterButton.setFocusPolicy(Qt.NoFocus) self.__settingsMenu = QMenu(self) self.__settingsMenu.aboutToShow.connect(self.__settingsAboutToShow) self.__wrapLongLinesAct = self.__settingsMenu.addAction( "Wrap long lines") self.__wrapLongLinesAct.setCheckable(True) self.__wrapLongLinesAct.triggered.connect(self.__onWrapLongLines) self.__showEOLAct = self.__settingsMenu.addAction("Show EOL") self.__showEOLAct.setCheckable(True) self.__showEOLAct.triggered.connect(self.__onShowEOL) self.__showWhitespacesAct = self.__settingsMenu.addAction( "Show whitespaces") self.__showWhitespacesAct.setCheckable(True) self.__showWhitespacesAct.triggered.connect(self.__onShowWhitespaces) self.__autoscrollAct = self.__settingsMenu.addAction("Autoscroll") self.__autoscrollAct.setCheckable(True) self.__autoscrollAct.triggered.connect(self.__onAutoscroll) self.__showMarginAct = self.__settingsMenu.addAction( "Show timestamp margin") self.__showMarginAct.setCheckable(True) self.__showMarginAct.triggered.connect(self.__onShowMargin) self.__settingsButton = QToolButton(self) self.__settingsButton.setIcon(PixmapCache().getIcon('iosettings.png')) self.__settingsButton.setToolTip('View settings') self.__settingsButton.setPopupMode(QToolButton.InstantPopup) self.__settingsButton.setMenu(self.__settingsMenu) self.__settingsButton.setFocusPolicy(Qt.NoFocus) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.__clearButton = QAction(PixmapCache().getIcon('trash.png'), 'Clear', self) self.__clearButton.triggered.connect(self.clear) # The toolbar toolbar = QToolBar(self) toolbar.setOrientation(Qt.Vertical) toolbar.setMovable(False) toolbar.setAllowedAreas(Qt.RightToolBarArea) toolbar.setIconSize(QSize(16, 16)) toolbar.setFixedWidth(28) toolbar.setContentsMargins(0, 0, 0, 0) # toolbar.addAction( self.__sendUpButton ) toolbar.addAction(self.__printPreviewButton) toolbar.addAction(self.__printButton) toolbar.addWidget(self.__filterButton) toolbar.addWidget(self.__settingsButton) toolbar.addWidget(spacer) toolbar.addAction(self.__clearButton) hLayout = QHBoxLayout() hLayout.setContentsMargins(0, 0, 0, 0) hLayout.setSpacing(0) hLayout.addWidget(toolbar) hLayout.addWidget(self.__viewer) self.setLayout(hLayout) return def onZoomReset(self): " Triggered when the zoom reset button is pressed " if self.__viewer.zoom != 0: self.textEditorZoom.emit(0) return True def onZoomIn(self): " Triggered when the zoom in button is pressed " if self.__viewer.zoom < 20: self.textEditorZoom.emit(self.__viewer.zoom + 1) return True def onZoomOut(self): " Triggered when the zoom out button is pressed " if self.__viewer.zoom > -10: self.textEditorZoom.emit(self.__viewer.zoom - 1) return True def __onPrint(self): " Triggered when the print button is pressed " pass def __onPrintPreview(self): " triggered when the print preview button is pressed " pass def setFocus(self): " Overridden setFocus " self.__viewer.setFocus() return def onOpenImport(self): " Triggered when Ctrl+I is received " return True def __sendUp(self): " Triggered when requested to move the console up " return def __filterAboutToShow(self): " Triggered when filter menu is about to show " showAll = Settings().ioconsoleshowstdin and \ Settings().ioconsoleshowstdout and \ Settings().ioconsoleshowstderr onlyStdout = Settings().ioconsoleshowstdin and \ Settings().ioconsoleshowstdout and \ not Settings().ioconsoleshowstderr onlyStderr = Settings().ioconsoleshowstdin and \ not Settings().ioconsoleshowstdout and \ Settings().ioconsoleshowstderr self.__filterShowAllAct.setChecked(showAll) self.__filterShowStdoutAct.setChecked(onlyStdout) self.__filterShowStderrAct.setChecked(onlyStderr) return def __onFilterShowAll(self): " Triggered when filter show all is clicked " if Settings().ioconsoleshowstdin == True and \ Settings().ioconsoleshowstdout == True and \ Settings().ioconsoleshowstderr == True: return Settings().ioconsoleshowstdin = True Settings().ioconsoleshowstdout = True Settings().ioconsoleshowstderr = True self.__viewer.renderContent() return def __onFilterShowStdout(self): " Triggered when filter show stdout only is clicked " if Settings().ioconsoleshowstdin == True and \ Settings().ioconsoleshowstdout == True and \ Settings().ioconsoleshowstderr == False: return Settings().ioconsoleshowstdin = True Settings().ioconsoleshowstdout = True Settings().ioconsoleshowstderr = False self.__viewer.renderContent() return def __onFilterShowStderr(self): " Triggered when filter show stderr only is clicked " if Settings().ioconsoleshowstdin == True and \ Settings().ioconsoleshowstdout == False and \ Settings().ioconsoleshowstderr == True: return Settings().ioconsoleshowstdin = True Settings().ioconsoleshowstdout = False Settings().ioconsoleshowstderr = True self.__viewer.renderContent() return def __settingsAboutToShow(self): " Settings menu is about to show " self.__wrapLongLinesAct.setChecked(Settings().ioconsolelinewrap) self.__showEOLAct.setChecked(Settings().ioconsoleshoweol) self.__showWhitespacesAct.setChecked(Settings().ioconsoleshowspaces) self.__autoscrollAct.setChecked(Settings().ioconsoleautoscroll) self.__showMarginAct.setChecked(Settings().ioconsoleshowmargin) return def __onWrapLongLines(self): " Triggered when long lines setting is changed " Settings().ioconsolelinewrap = not Settings().ioconsolelinewrap self.settingUpdated.emit() return def __onShowEOL(self): " Triggered when show EOL is changed " Settings().ioconsoleshoweol = not Settings().ioconsoleshoweol self.settingUpdated.emit() return def __onShowWhitespaces(self): " Triggered when show whitespaces is changed " Settings().ioconsoleshowspaces = not Settings().ioconsoleshowspaces self.settingUpdated.emit() return def __onAutoscroll(self): " Triggered when autoscroll is changed " Settings().ioconsoleautoscroll = not Settings().ioconsoleautoscroll return def __onShowMargin(self): " Triggered when show margin is changed " Settings().ioconsoleshowmargin = not Settings().ioconsoleshowmargin self.settingUpdated.emit() return def clear(self): " Triggered when requested to clear the console " self.__viewer.clearAll() return def consoleSettingsUpdated(self): " Triggered when one of the consoles updated a common setting " if Settings().ioconsolelinewrap: self.__viewer.setWrapMode(QsciScintilla.WrapWord) else: self.__viewer.setWrapMode(QsciScintilla.WrapNone) self.__viewer.setEolVisibility(Settings().ioconsoleshoweol) if Settings().ioconsoleshowspaces: self.__viewer.setWhitespaceVisibility(QsciScintilla.WsVisible) else: self.__viewer.setWhitespaceVisibility(QsciScintilla.WsInvisible) self.__viewer.setTimestampMarginWidth() return def resizeEvent(self, event): QWidget.resizeEvent(self, event) return def onPylint(self): return True def onPymetrics(self): return True def onRunScript(self, action=False): return True def onRunScriptSettings(self): return True def onProfileScript(self, action=False): return True def onProfileScriptSettings(self): return True def onImportDgm(self, action=None): return True def onImportDgmTuned(self): return True def shouldAcceptFocus(self): return True def onNavigationBar(self): pass def writeFile(self, fileName): " Writes the text to a file " return self.__viewer.writeFile(fileName) def updateModificationTime(self, fileName): return def appendIDEMessage(self, text): " Appends an IDE message " self.__viewer.appendIDEMessage(text) return def appendStdoutMessage(self, text): " Appends an stdout message " self.__viewer.appendStdoutMessage(text) return def appendStderrMessage(self, text): " Appends an stderr message " self.__viewer.appendStderrMessage(text) return def hiddenMessage(self, msg): " Returns True if the message should not be shown " if msg.msgType == IOConsoleMsg.STDERR_MESSAGE and \ not Settings().ioconsoleshowstderr: return True if msg.msgType == IOConsoleMsg.STDOUT_MESSAGE and \ not Settings().ioconsoleshowstdout: return True return False def zoomTo(self, zoomValue): " Sets the new zoom value " self.__viewer.zoomTo(zoomValue) self.__viewer.setTimestampMarginWidth() return def rawInput(self, prompt, echo): " Triggered when raw input is requested " echo = int(echo) if echo == 0: self.__viewer.inputEcho = False else: self.__viewer.inputEcho = True if prompt: self.appendStdoutMessage(prompt) self.__clearButton.setEnabled(False) self.__viewer.switchMode(self.__viewer.MODE_INPUT) return def sessionStopped(self): " Triggered when redirecting session is stopped " self.__viewer.switchMode(self.__viewer.MODE_OUTPUT) self.__clearButton.setEnabled(True) return # Mandatory interface part is below def getEditor(self): " Provides the editor widget " return self.__viewer def isModified(self): " Tells if the file is modified " return False def getRWMode(self): " Tells if the file is read only " return "IO" def getFileType(self): " Provides the file type " return TexFileType def setFileType(self, typeToSet): """ Sets the file type explicitly. It needs e.g. for .cgi files which can change its type """ raise Exception("Setting a file type is not supported by the " "IO console widget") def getType(self): " Tells the widget type " return MainWindowTabWidgetBase.IOConsole def getLanguage(self): " Tells the content language " return "IO console" def getFileName(self): " Tells what file name of the widget content " return "n/a" def setFileName(self, name): " Sets the file name " raise Exception("Setting a file name for IO console " "is not applicable") def getEol(self): " Tells the EOL style " return self.__viewer.getEolIndicator() def getLine(self): " Tells the cursor line " line, _ = self.__viewer.getCursorPosition() return int(line) def getPos(self): " Tells the cursor column " _, pos = self.__viewer.getCursorPosition() return int(pos) def getEncoding(self): " Tells the content encoding " return self.__viewer.encoding def setEncoding(self, newEncoding): " Sets the new editor encoding " raise Exception("Setting encoding is not supported by the " "IO console widget") def getShortName(self): " Tells the display name " return "IO console" def setShortName(self, name): " Sets the display name " raise Exception("Setting short name is not supported by the " "IO console widget")
class MapWidget(Ui_CanvasWidget, QMainWindow): def __init__(self, parent=None): super(MapWidget, self).__init__(parent) self.setupUi(self) self.firstshow = True self.layerbuttons = [] self.editfeaturestack = [] self.lastgpsposition = None self.project = None self.gps = None self.gpslogging = None self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas)) self.canvas.setCanvasColor(Qt.white) self.canvas.enableAntiAliasing(True) self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor) if hasattr(self.canvas, 'setParallelRenderingEnabled'): self.canvas.setParallelRenderingEnabled(True) pal = QgsPalLabeling() self.canvas.mapRenderer().setLabelingEngine(pal) self.canvas.setFrameStyle(QFrame.NoFrame) self.editgroup = QActionGroup(self) self.editgroup.setExclusive(True) self.editgroup.addAction(self.actionPan) self.editgroup.addAction(self.actionZoom_In) self.editgroup.addAction(self.actionZoom_Out) self.editgroup.addAction(self.actionInfo) self.actionGPS = GPSAction(":/icons/gps", self.canvas, self) self.projecttoolbar.addAction(self.actionGPS) gpsspacewidget = QWidget() gpsspacewidget.setMinimumWidth(30) gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.topspaceraction = self.projecttoolbar.insertWidget( self.actionGPS, gpsspacewidget) self.dataentryselection = QAction(self.projecttoolbar) self.dataentryaction = self.projecttoolbar.insertAction( self.topspaceraction, self.dataentryselection) self.dataentryselection.triggered.connect(self.select_data_entry) self.marker = GPSMarker(self.canvas) self.marker.hide() self.currentfeatureband = QgsRubberBand(self.canvas) self.currentfeatureband.setIconSize(20) self.currentfeatureband.setWidth(10) self.currentfeatureband.setColor(QColor(186, 93, 212, 76)) self.gpsband = QgsRubberBand(self.canvas) self.gpsband.setColor(QColor(0, 0, 212, 76)) self.gpsband.setWidth(5) RoamEvents.editgeometry.connect(self.queue_feature_for_edit) RoamEvents.selectioncleared.connect(self.clear_selection) RoamEvents.selectionchanged.connect(self.highlight_selection) RoamEvents.featureformloaded.connect(self.feature_form_loaded) self.connectButtons() def init_qgisproject(self, doc): parser = ProjectParser(doc) canvasnode = parser.canvasnode self.canvas.freeze() self.canvas.mapRenderer().readXML(canvasnode) self.canvaslayers = parser.canvaslayers() self.canvas.setLayerSet(self.canvaslayers) #red = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorRedPart", 255 )[0]; #green = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorGreenPart", 255 )[0]; #blue = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorBluePart", 255 )[0]; #color = QColor(red, green, blue); #self.canvas.setCanvasColor(color) self.canvas.updateScale() return self.canvas.mapRenderer().destinationCrs() def showEvent(self, *args, **kwargs): if self.firstshow: self.canvas.refresh() self.canvas.repaint() self.firstshow = False def feature_form_loaded(self, form, feature, project, editmode): self.currentfeatureband.setToGeometry(feature.geometry(), form.QGISLayer) def highlight_selection(self, results): self.clear_selection() for layer, features in results.iteritems(): band = self.selectionbands[layer] band.setColor(QColor(255, 0, 0, 200)) band.setIconSize(20) band.setWidth(2) band.setBrushStyle(Qt.NoBrush) band.reset(layer.geometryType()) for feature in features: band.addGeometry(feature.geometry(), layer) def highlight_active_selection(self, layer, feature, features): self.clear_selection() self.highlight_selection({layer: features}) self.currentfeatureband.setToGeometry(feature.geometry(), layer) def clear_selection(self): # Clear the main selection rubber band self.currentfeatureband.reset() # Clear the rest for band in self.selectionbands.itervalues(): band.reset() self.editfeaturestack = [] def queue_feature_for_edit(self, form, feature): def trigger_default_action(): for action in self.projecttoolbar.actions(): if action.property('dataentry') and action.isdefault: action.trigger() break self.editfeaturestack.append((form, feature)) self.load_form(form) trigger_default_action() def clear_temp_objects(self): def clear_tool_band(): """ Clear the rubber band of the active tool if it has one """ tool = self.canvas.mapTool() try: tool.clearBand() except AttributeError: # No clearBand method found, but that's cool. pass self.currentfeatureband.reset() clear_tool_band() def settings_updated(self, settings): self.actionGPS.updateGPSPort() gpslogging = settings.get('gpslogging', True) if self.gpslogging: self.gpslogging.logging = gpslogging def set_gps(self, gps, logging): self.gps = gps self.gpslogging = logging self.gps.gpsposition.connect(self.gps_update_canvas) self.gps.firstfix.connect(self.gps_first_fix) self.gps.gpsdisconnected.connect(self.gps_disconnected) def gps_update_canvas(self, position, gpsinfo): # Recenter map if we go outside of the 95% of the area if self.gpslogging.logging: self.gpsband.addPoint(position) self.gpsband.show() if roam.config.settings.get('gpscenter', True): if not self.lastgpsposition == position: self.lastposition = position rect = QgsRectangle(position, position) extentlimt = QgsRectangle(self.canvas.extent()) extentlimt.scale(0.95) if not extentlimt.contains(position): self.zoom_to_location(position) self.marker.show() self.marker.setCenter(position) def gps_first_fix(self, postion, gpsinfo): zoomtolocation = roam.config.settings.get('gpszoomonfix', True) if zoomtolocation: self.canvas.zoomScale(1000) self.zoom_to_location(postion) def zoom_to_location(self, position): rect = QgsRectangle(position, position) self.canvas.setExtent(rect) self.canvas.refresh() def gps_disconnected(self): self.marker.hide() def select_data_entry(self): def showformerror(form): pass def actions(): for form in self.project.forms: action = form.createuiaction() valid, failreasons = form.valid if not valid: roam.utils.warning("Form {} failed to load".format( form.label)) roam.utils.warning("Reasons {}".format(failreasons)) action.triggered.connect(partial(showformerror, form)) else: action.triggered.connect(partial(self.load_form, form)) yield action formpicker = PickActionDialog(msg="Select data entry form") formpicker.addactions(actions()) formpicker.exec_() def project_loaded(self, project): self.project = project self.actionPan.trigger() try: firstform = project.forms[0] self.load_form(firstform) self.dataentryselection.setVisible(True) except IndexError: self.dataentryselection.setVisible(False) # Enable the raster layers button only if the project contains a raster layer. layers = QgsMapLayerRegistry.instance().mapLayers().values() hasrasters = any(layer.type() == QgsMapLayer.RasterLayer for layer in layers) self.actionRaster.setEnabled(hasrasters) self.defaultextent = self.canvas.extent() roam.utils.info("Extent: {}".format(self.defaultextent.toString())) self.infoTool.selectionlayers = project.selectlayersmapping() self.canvas.freeze(False) self.canvas.refresh() def setMapTool(self, tool, *args): self.canvas.setMapTool(tool) def connectButtons(self): def connectAction(action, tool): action.toggled.connect(partial(self.setMapTool, tool)) def cursor(name): pix = QPixmap(name) pix = pix.scaled(QSize(24, 24)) return QCursor(pix) self.zoomInTool = QgsMapToolZoom(self.canvas, False) self.zoomOutTool = QgsMapToolZoom(self.canvas, True) self.panTool = PanTool(self.canvas) self.infoTool = InfoTool(self.canvas) connectAction(self.actionZoom_In, self.zoomInTool) connectAction(self.actionZoom_Out, self.zoomOutTool) connectAction(self.actionPan, self.panTool) connectAction(self.actionInfo, self.infoTool) self.zoomInTool.setCursor(cursor(':/icons/in')) self.zoomOutTool.setCursor(cursor(':/icons/out')) self.infoTool.setCursor(cursor(':/icons/info')) self.actionRaster.triggered.connect(self.toggleRasterLayers) self.infoTool.infoResults.connect(RoamEvents.selectionchanged.emit) self.actionHome.triggered.connect(self.homeview) def homeview(self): """ Zoom the mapview canvas to the extents the project was opened at i.e. the default extent. """ self.canvas.setExtent(self.defaultextent) self.canvas.refresh() def load_form(self, form): self.clearCapatureTools() self.dataentryselection.setIcon(QIcon(form.icon)) self.dataentryselection.setText(form.icontext) self.create_capture_buttons(form) def create_capture_buttons(self, form): tool = form.getMaptool()(self.canvas) for action in tool.actions: # Create the action here. if action.ismaptool: action.toggled.connect(partial(self.setMapTool, tool)) # Set the action as a data entry button so we can remove it later. action.setProperty("dataentry", True) self.editgroup.addAction(action) self.layerbuttons.append(action) self.projecttoolbar.insertAction(self.topspaceraction, action) action.setChecked(action.isdefault) if hasattr(tool, 'geometryComplete'): add = partial(self.add_new_feature, form) tool.geometryComplete.connect(add) else: tool.finished.connect(self.openForm) tool.error.connect(partial(self.showUIMessage, form.label)) def add_new_feature(self, form, geometry): """ Add a new new feature to the given layer """ # TODO Extract into function. # NOTE This function is doing too much, acts as add and also edit. layer = form.QGISLayer if layer.geometryType() in [ QGis.WKBMultiLineString, QGis.WKBMultiPoint, QGis.WKBMultiPolygon ]: geometry.convertToMultiType() try: form, feature = self.editfeaturestack.pop() self.editfeaturegeometry(form, feature, newgeometry=geometry) return except IndexError: pass layer = form.QGISLayer fields = layer.pendingFields() feature = QgsFeature(fields) feature.setGeometry(geometry) for index in xrange(fields.count()): pkindexes = layer.dataProvider().pkAttributeIndexes() if index in pkindexes and layer.dataProvider().name( ) == 'spatialite': continue value = layer.dataProvider().defaultValue(index) feature[index] = value RoamEvents.open_feature_form(form, feature, editmode=False) def editfeaturegeometry(self, form, feature, newgeometry): # TODO Extract into function. layer = form.QGISLayer layer.startEditing() feature.setGeometry(newgeometry) layer.updateFeature(feature) saved = layer.commitChanges() if not saved: map(roam.utils.error, layer.commitErrors()) self.canvas.refresh() self.currentfeatureband.setToGeometry(feature.geometry(), layer) RoamEvents.editgeometry_complete.emit(form, feature) def clearCapatureTools(self): captureselected = False for action in self.projecttoolbar.actions(): if action.objectName() == "capture" and action.isChecked(): captureselected = True if action.property('dataentry'): self.projecttoolbar.removeAction(action) return captureselected def toggleRasterLayers(self): """ Toggle all raster layers on or off. """ if not self.canvaslayers: return #Freeze the canvas to save on UI refresh self.canvas.freeze() for layer in self.canvaslayers: if layer.layer().type() == QgsMapLayer.RasterLayer: layer.setVisible(not layer.isVisible()) # Really!? We have to reload the whole layer set every time? # WAT? self.canvas.setLayerSet(self.canvaslayers) self.canvas.freeze(False) self.canvas.refresh() def cleanup(self): self.gpsband.reset() self.gpsband.hide() self.clear_selection() self.clear_temp_objects() self.clearCapatureTools() self.canvas.freeze() self.canvas.clear() self.canvas.freeze(False) for action in self.layerbuttons: self.editgroup.removeAction(action)
class MapWidget(Ui_CanvasWidget, QMainWindow): def __init__(self, parent=None): super(MapWidget, self).__init__(parent) self.setupUi(self) icon = roam_style.iconsize() self.projecttoolbar.setIconSize(QSize(icon, icon)) self.firstshow = True self.layerbuttons = [] self.editfeaturestack = [] self.lastgpsposition = None self.project = None self.gps = None self.gpslogging = None self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas)) self.bridge = QgsLayerTreeMapCanvasBridge(QgsProject.instance().layerTreeRoot(), self.canvas) self.bridge.setAutoSetupOnFirstLayer(False) QgsProject.instance().writeProject.connect(self.bridge.writeProject) QgsProject.instance().readProject.connect(self.bridge.readProject) # self.canvas.setInteractive(False) self.canvas.setCanvasColor(Qt.white) self.canvas.enableAntiAliasing(True) self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor) self.snapping = SnappingUtils(self.canvas, self) self.canvas.setSnappingUtils(self.snapping) QgsProject.instance().readProject.connect(self.snapping.readConfigFromProject) if hasattr(self.canvas, 'setParallelRenderingEnabled'): threadcount = QThread.idealThreadCount() threadcount = 2 if threadcount > 2 else 1 QgsApplication.setMaxThreads(threadcount) self.canvas.setParallelRenderingEnabled(True) pal = QgsPalLabeling() self.canvas.mapRenderer().setLabelingEngine(pal) self.canvas.setFrameStyle(QFrame.NoFrame) self.editgroup = QActionGroup(self) self.editgroup.setExclusive(True) self.editgroup.addAction(self.actionPan) self.editgroup.addAction(self.actionZoom_In) self.editgroup.addAction(self.actionZoom_Out) self.editgroup.addAction(self.actionInfo) self.actionGPS = GPSAction(":/icons/gps", self.canvas, self) self.projecttoolbar.addAction(self.actionGPS) if roam.config.settings.get('north_arrow', False): self.northarrow = NorthArrow(":/icons/north", self.canvas) self.northarrow.setPos(10, 10) self.canvas.scene().addItem(self.northarrow) self.scalebar_enabled = roam.config.settings.get('scale_bar', False) if self.scalebar_enabled: self.scalebar = ScaleBarItem(self.canvas) self.canvas.scene().addItem(self.scalebar) self.projecttoolbar.setContextMenuPolicy(Qt.CustomContextMenu) gpsspacewidget = QWidget() gpsspacewidget.setMinimumWidth(30) gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.topspaceraction = self.projecttoolbar.insertWidget(self.actionGPS, gpsspacewidget) self.dataentryselection = QAction(self.projecttoolbar) self.dataentryaction = self.projecttoolbar.insertAction(self.topspaceraction, self.dataentryselection) self.dataentryselection.triggered.connect(self.select_data_entry) self.marker = GPSMarker(self.canvas) self.marker.hide() self.currentfeatureband = CurrentSelection(self.canvas) self.currentfeatureband.setIconSize(30) self.currentfeatureband.setWidth(10) self.currentfeatureband.setColor(QColor(186, 93, 212, 50)) self.currentfeatureband.setOutlineColour(QColor(186, 93, 212)) self.gpsband = QgsRubberBand(self.canvas) self.gpsband.setColor(QColor(165, 111, 212, 75)) self.gpsband.setWidth(5) RoamEvents.editgeometry.connect(self.queue_feature_for_edit) RoamEvents.selectioncleared.connect(self.clear_selection) RoamEvents.selectionchanged.connect(self.highlight_selection) RoamEvents.openfeatureform.connect(self.feature_form_loaded) RoamEvents.sync_complete.connect(self.refresh_map) RoamEvents.snappingChanged.connect(self.snapping_changed) self.snappingbutton = QToolButton() self.snappingbutton.setText("Snapping: On") self.snappingbutton.setAutoRaise(True) self.snappingbutton.pressed.connect(self.toggle_snapping) spacer = QWidget() spacer2 = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) spacer2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.scalewidget = QgsScaleComboBox() self.scalebutton = QToolButton() self.scalebutton.setAutoRaise(True) self.scalebutton.setMaximumHeight(self.statusbar.height()) self.scalebutton.pressed.connect(self.selectscale) self.scalebutton.setText("Scale") self.scalelist = BigList(parent=self.canvas, centeronparent=True, showsave=False) self.scalelist.hide() self.scalelist.setlabel("Map Scale") self.scalelist.setmodel(self.scalewidget.model()) self.scalelist.closewidget.connect(self.scalelist.close) self.scalelist.itemselected.connect(self.update_scale_from_item) self.scalelist.itemselected.connect(self.scalelist.close) self.positionlabel = QLabel('') self.gpslabel = QLabel("GPS: Not active") self.statusbar.addWidget(self.snappingbutton) self.statusbar.addWidget(spacer2) self.statusbar.addWidget(self.gpslabel) self.statusbar.addPermanentWidget(self.scalebutton) self.canvas.extentsChanged.connect(self.updatestatuslabel) self.canvas.scaleChanged.connect(self.updatestatuslabel) GPS.gpsposition.connect(self.update_gps_label) GPS.gpsdisconnected.connect(self.gps_disconnected) self.connectButtons() def clear_plugins(self): toolbars = self.findChildren(QToolBar) for toolbar in toolbars: if toolbar.property("plugin_toolbar"): self.removeToolBar(toolbar) toolbar.deleteLater() def add_plugins(self, pluginnames): for name in pluginnames: # Get the plugin try: plugin_mod = plugins.loaded_plugins[name] except KeyError: continue if not hasattr(plugin_mod, 'toolbars'): roam.utils.warning("No toolbars() function found in {}".format(name)) continue toolbars = plugin_mod.toolbars() self.load_plugin_toolbars(toolbars) def load_plugin_toolbars(self, toolbars): for ToolBarClass in toolbars: toolbar = ToolBarClass(plugins.api, self) self.addToolBar(Qt.BottomToolBarArea, toolbar) toolbar.setProperty("plugin_toolbar", True) def snapping_changed(self, snapping): """ Called when the snapping settings have changed. Updates the label in the status bar. :param snapping: """ if snapping: self.snappingbutton.setText("Snapping: On") else: self.snappingbutton.setText("Snapping: Off") def toggle_snapping(self): """ Toggle snapping on or off. """ snap = not snapping global snapping snapping = snap RoamEvents.snappingChanged.emit(snapping) def selectscale(self): """ Show the select scale widget. :return: """ self.scalelist.show() def update_scale_from_item(self, index): """ Update the canvas scale from the selected scale item. :param index: The index of the selected item. """ scale, _ = self.scalewidget.toDouble(index.data(Qt.DisplayRole)) self.canvas.zoomScale(1.0 / scale) def update_gps_label(self, position, gpsinfo): """ Update the GPS label in the status bar with the GPS status. :param position: The current GPS position. :param gpsinfo: The current extra GPS information. """ self.gpslabel.setText("GPS: PDOP {0:.2f} HDOP {1:.2f} VDOP {2:.2f}".format(gpsinfo.pdop, gpsinfo.hdop, gpsinfo.vdop)) def gps_disconnected(self): """ Called when the GPS is disconnected. Updates the label in the status bar with the message. :return: """ self.gpslabel.setText("GPS Not Active") def updatestatuslabel(self, *args): """ Update the status bar labels when the information has changed. """ extent = self.canvas.extent() self.positionlabel.setText("Map Center: {}".format(extent.center().toString())) scale = 1.0 / self.canvas.scale() scale = self.scalewidget.toString(scale) self.scalebutton.setText(scale) def refresh_map(self): """ Refresh the map """ self.canvas.refresh() def updatescale(self): """ Update the scale of the map with the current scale from the scale widget :return: """ self.canvas.zoomScale(1.0 / self.scalewidget.scale()) def init_qgisproject(self, doc): """ Called when the project file is read for the firs time. :param doc: The XML doc. :return: The current canvas CRS :note: This method is old and needs to be refactored into something else. """ return self.canvas.mapSettings().destinationCrs() def showEvent(self, *args, **kwargs): """ Handle the show event of the of the map widget. We have to do a little hack here to make the QGIS map refresh. """ if QGis.QGIS_VERSION_INT == 20200 and self.firstshow: self.canvas.refresh() self.canvas.repaint() self.firstshow = False def feature_form_loaded(self, form, feature, *args): """ Called when the feature form is loaded. :param form: The Form object. Holds a reference to the forms layer. :param feature: The current capture feature """ self.currentfeatureband.setToGeometry(feature.geometry(), form.QGISLayer) def highlight_selection(self, results): """ Highlight the selection on the canvas. This updates all selected objects based on the result set. :param results: A dict-of-list of layer-features. """ self.clear_selection() for layer, features in results.iteritems(): band = self.selectionbands[layer] band.setColor(QColor(255, 0, 0)) band.setIconSize(25) band.setWidth(5) band.setBrushStyle(Qt.NoBrush) band.reset(layer.geometryType()) band.setZValue(self.currentfeatureband.zValue() - 1) for feature in features: band.addGeometry(feature.geometry(), layer) self.canvas.update() def highlight_active_selection(self, layer, feature, features): """ Update the current active selected feature. :param layer: The layer of the active feature. :param feature: The active feature. :param features: The other features in the set to show as non active selection. :return: """ self.clear_selection() self.highlight_selection({layer: features}) self.currentfeatureband.setToGeometry(feature.geometry(), layer) self.canvas.update() def clear_selection(self): """ Clear the selection from the canvas. Resets all selection rubbber bands. :return: """ # Clear the main selection rubber band self.canvas.scene().update() self.currentfeatureband.reset() # Clear the rest for band in self.selectionbands.itervalues(): band.reset() self.canvas.update() self.editfeaturestack = [] def queue_feature_for_edit(self, form, feature): """ Push a feature on the edit stack so the feature can have the geometry edited. :note: This is a big hack and I don't like it! :param form: The form for the current feature :param feature: The active feature. """ def trigger_default_action(): for action in self.projecttoolbar.actions(): if action.property('dataentry') and action.isdefault: action.trigger() self.canvas.mapTool().setEditMode(True, feature.geometry()) break self.editfeaturestack.append((form, feature)) self.load_form(form) trigger_default_action() def clear_temp_objects(self): """ Clear all temp objects from the canvas. :return: """ def clear_tool_band(): """ Clear the rubber band of the active tool if it has one """ tool = self.canvas.mapTool() try: tool.clearBand() except AttributeError: # No clearBand method found, but that's cool. pass self.currentfeatureband.reset() clear_tool_band() def settings_updated(self, settings): """ Called when the settings have been updated in the Roam config. :param settings: A dict of the settings. """ self.actionGPS.updateGPSPort() gpslogging = settings.get('gpslogging', True) if self.gpslogging: self.gpslogging.logging = gpslogging def set_gps(self, gps, logging): """ Set the GPS for the map widget. Connects GPS signals """ self.gps = gps self.gpslogging = logging self.gps.gpsposition.connect(self.gps_update_canvas) self.gps.firstfix.connect(self.gps_first_fix) self.gps.gpsdisconnected.connect(self.gps_disconnected) def gps_update_canvas(self, position, gpsinfo): """ Updates the map canvas based on the GPS position. By default if the GPS is outside the canvas extent the canvas will move to center on the GPS. Can be turned off in settings. :param postion: The current GPS position. :param gpsinfo: The extra GPS information """ # Recenter map if we go outside of the 95% of the area if self.gpslogging.logging: self.gpsband.addPoint(position) self.gpsband.show() if roam.config.settings.get('gpscenter', True): if not self.lastgpsposition == position: self.lastposition = position rect = QgsRectangle(position, position) extentlimt = QgsRectangle(self.canvas.extent()) extentlimt.scale(0.95) if not extentlimt.contains(position): self.zoom_to_location(position) self.marker.show() self.marker.setCenter(position, gpsinfo) def gps_first_fix(self, postion, gpsinfo): """ Called the first time the GPS gets a fix. If set this will zoom to the GPS after the first fix :param postion: The current GPS position. :param gpsinfo: The extra GPS information """ zoomtolocation = roam.config.settings.get('gpszoomonfix', True) if zoomtolocation: self.canvas.zoomScale(1000) self.zoom_to_location(postion) def zoom_to_location(self, position): """ Zoom to ta given position on the map.. """ rect = QgsRectangle(position, position) self.canvas.setExtent(rect) self.canvas.refresh() def gps_disconnected(self): """ Called when the GPS is disconnected """ self.marker.hide() def select_data_entry(self): """ Open the form selection widget to allow the user to pick the active capture form. """ def showformerror(form): pass def actions(): for form in self.project.forms: if not self.form_valid_for_capture(form): continue action = form.createuiaction() valid, failreasons = form.valid if not valid: roam.utils.warning("Form {} failed to load".format(form.label)) roam.utils.warning("Reasons {}".format(failreasons)) action.triggered.connect(partial(showformerror, form)) else: action.triggered.connect(partial(self.load_form, form)) yield action formpicker = PickActionDialog(msg="Select data entry form", wrap=5) formpicker.addactions(actions()) formpicker.exec_() def project_loaded(self, project): """ Called when the project is loaded. Main entry point for a loade project. :param project: The Roam project that has been loaded. """ self.project = project self.actionPan.trigger() firstform = self.first_capture_form() if firstform: self.load_form(firstform) self.dataentryselection.setVisible(True) else: self.dataentryselection.setVisible(False) # Enable the raster layers button only if the project contains a raster layer. layers = QgsMapLayerRegistry.instance().mapLayers().values() hasrasters = any(layer.type() == QgsMapLayer.RasterLayer for layer in layers) self.actionRaster.setEnabled(hasrasters) self.defaultextent = self.canvas.extent() roam.utils.info("Extent: {}".format(self.defaultextent.toString())) self.infoTool.selectionlayers = project.selectlayersmapping() self.canvas.refresh() projectscales, _ = QgsProject.instance().readBoolEntry("Scales", "/useProjectScales") if projectscales: projectscales, _ = QgsProject.instance().readListEntry("Scales", "/ScalesList") self.scalewidget.updateScales(projectscales) else: scales = ["1:50000", "1:25000", "1:10000", "1:5000", "1:2500", "1:1000", "1:500", "1:250", "1:200", "1:100"] scales = roam.config.settings.get('scales', scales) self.scalewidget.updateScales(scales) if self.scalebar_enabled: self.scalebar.update() self.actionPan.toggle() self.clear_plugins() self.add_plugins(project.enabled_plugins) def setMapTool(self, tool, *args): """ Set the active map tool in the canvas. :param tool: The QgsMapTool to set. """ if tool == self.canvas.mapTool(): return if hasattr(tool, "setSnapping"): tool.setSnapping(snapping) self.canvas.setMapTool(tool) def connectButtons(self): """ Connect the default buttons in the interface. Zoom, pan, etc """ def connectAction(action, tool): action.toggled.connect(partial(self.setMapTool, tool)) def cursor(name): pix = QPixmap(name) pix = pix.scaled(QSize(24, 24)) return QCursor(pix) self.zoomInTool = QgsMapToolZoom(self.canvas, False) self.zoomOutTool = QgsMapToolZoom(self.canvas, True) self.panTool = PanTool(self.canvas) self.infoTool = InfoTool(self.canvas) connectAction(self.actionZoom_In, self.zoomInTool) connectAction(self.actionZoom_Out, self.zoomOutTool) connectAction(self.actionPan, self.panTool) connectAction(self.actionInfo, self.infoTool) self.zoomInTool.setCursor(cursor(':/icons/in')) self.zoomOutTool.setCursor(cursor(':/icons/out')) self.infoTool.setCursor(cursor(':/icons/select')) self.actionRaster.triggered.connect(self.toggleRasterLayers) self.actionHome.triggered.connect(self.homeview) def homeview(self): """ Zoom the mapview canvas to the extents the project was opened at i.e. the default extent. """ self.canvas.setExtent(self.defaultextent) self.canvas.refresh() def form_valid_for_capture(self, form): """ Check if the given form is valid for capture. :param form: The form to check. :return: True if valid form for capture """ return form.has_geometry and self.project.layer_can_capture(form.QGISLayer) def first_capture_form(self): """ Return the first valid form for capture. """ for form in self.project.forms: if self.form_valid_for_capture(form): return form def load_form(self, form): """ Load the given form so it's the active one for capture :param form: The form to load """ self.clearCapatureTools() self.dataentryselection.setIcon(QIcon(form.icon)) self.dataentryselection.setText(form.icontext) self.create_capture_buttons(form) def create_capture_buttons(self, form): """ Create the capture buttons in the toolbar for the given form. :param form: The active form. """ layer = form.QGISLayer tool = form.getMaptool()(self.canvas) for action in tool.actions: # Create the action here. if action.ismaptool: action.toggled.connect(partial(self.setMapTool, tool)) # Set the action as a data entry button so we can remove it later. action.setProperty("dataentry", True) self.editgroup.addAction(action) self.layerbuttons.append(action) self.projecttoolbar.insertAction(self.topspaceraction, action) action.setChecked(action.isdefault) if hasattr(tool, 'geometryComplete'): add = partial(self.add_new_feature, form) tool.geometryComplete.connect(add) else: tool.finished.connect(self.openForm) tool.error.connect(partial(self.showUIMessage, form.label)) def add_new_feature(self, form, geometry): """ Add a new new feature to the given layer """ # TODO Extract into function. # NOTE This function is doing too much, acts as add and also edit. layer = form.QGISLayer if layer.geometryType() in [QGis.WKBMultiLineString, QGis.WKBMultiPoint, QGis.WKBMultiPolygon]: geometry.convertToMultiType() try: form, feature = self.editfeaturestack.pop() self.editfeaturegeometry(form, feature, newgeometry=geometry) return except IndexError: pass feature = form.new_feature(geometry=geometry) RoamEvents.load_feature_form(form, feature, editmode=False) def editfeaturegeometry(self, form, feature, newgeometry): # TODO Extract into function. layer = form.QGISLayer layer.startEditing() feature.setGeometry(newgeometry) layer.updateFeature(feature) saved = layer.commitChanges() if not saved: map(roam.utils.error, layer.commitErrors()) self.canvas.refresh() self.currentfeatureband.setToGeometry(feature.geometry(), layer) RoamEvents.editgeometry_complete.emit(form, feature) self.canvas.mapTool().setEditMode(False, None) def clearCapatureTools(self): """ Clear the capture tools from the toolbar. :return: True if the capture button was active at the time of clearing. """ captureselected = False for action in self.projecttoolbar.actions(): if action.objectName() == "capture" and action.isChecked(): captureselected = True if action.property('dataentry'): self.projecttoolbar.removeAction(action) return captureselected def toggleRasterLayers(self): """ Toggle all raster layers on or off. """ # Freeze the canvas to save on UI refresh self.canvas.freeze() tree = QgsProject.instance().layerTreeRoot() for node in tree.findLayers(): if node.layer().type() == QgsMapLayer.RasterLayer: if node.isVisible() == Qt.Checked: state = Qt.Unchecked else: state = Qt.Checked node.setVisible(state) self.canvas.freeze(False) self.canvas.refresh() def cleanup(self): """ Clean up when the project has changed. :return: """ self.bridge.clear() self.gpsband.reset() self.gpsband.hide() self.clear_selection() self.clear_temp_objects() self.clearCapatureTools() self.canvas.freeze() self.canvas.clear() self.canvas.freeze(False) for action in self.layerbuttons: self.editgroup.removeAction(action)
class quickFinder(QObject): name = u"&Quick Finder" actions = None toolbar = None finders = {} loadingIcon = None def __init__(self, iface): QObject.__init__(self) self.iface = iface self.actions = {} self.finders = {} self.settings = MySettings() self._initFinders() self.iface.projectRead.connect(self._reloadFinders) self.iface.newProjectCreated.connect(self._reloadFinders) # translation environment self.plugin_dir = os.path.dirname(__file__) locale = QSettings().value("locale/userLocale")[0:2] localePath = os.path.join(self.plugin_dir, 'i18n', 'quickfinder_{0}.qm'.format(locale)) if os.path.exists(localePath): self.translator = QTranslator() self.translator.load(localePath) QCoreApplication.installTranslator(self.translator) def initGui(self): self.actions['showSettings'] = QAction( QIcon(":/plugins/quickfinder/icons/settings.svg"), self.tr(u"&Settings"), self.iface.mainWindow()) self.actions['showSettings'].triggered.connect(self.showSettings) self.iface.addPluginToMenu(self.name, self.actions['showSettings']) self.actions['help'] = QAction( QIcon(":/plugins/quickfinder/icons/help.svg"), self.tr("Help"), self.iface.mainWindow()) self.actions['help'].triggered.connect( lambda: QDesktopServices().openUrl( QUrl("http://3nids.github.io/quickfinder"))) self.iface.addPluginToMenu(self.name, self.actions['help']) self._initToolbar() self.rubber = QgsRubberBand(self.iface.mapCanvas()) self.rubber.setColor(QColor(255, 255, 50, 200)) self.rubber.setIcon(self.rubber.ICON_CIRCLE) self.rubber.setIconSize(15) self.rubber.setWidth(4) self.rubber.setBrushStyle(Qt.NoBrush) def unload(self): """ Unload plugin """ for key in self.finders.keys(): self.finders[key].close() for action in self.actions.itervalues(): self.iface.removePluginMenu(self.name, action) if self.toolbar: del self.toolbar if self.rubber: self.iface.mapCanvas().scene().removeItem(self.rubber) del self.rubber def _initToolbar(self): """setup the plugin toolbar.""" self.toolbar = self.iface.addToolBar(self.name) self.toolbar.setObjectName('mQuickFinderToolBar') self.searchAction = QAction(QIcon(":/plugins/quickfinder/icons/magnifier13.svg"), self.tr("Search"), self.toolbar) self.stopAction = QAction( QIcon(":/plugins/quickfinder/icons/wrong2.svg"), self.tr("Cancel"), self.toolbar) self.finderBox = FinderBox(self.finders, self.iface, self.toolbar) self.finderBox.searchStarted.connect(self.searchStarted) self.finderBox.searchFinished.connect(self.searchFinished) self.finderBoxAction = self.toolbar.addWidget(self.finderBox) self.finderBoxAction.setVisible(True) self.searchAction.triggered.connect(self.finderBox.search) self.toolbar.addAction(self.searchAction) self.stopAction.setVisible(False) self.stopAction.triggered.connect(self.finderBox.stop) self.toolbar.addAction(self.stopAction) self.toolbar.setVisible(True) def _initFinders(self): self.finders['geomapfish'] = GeomapfishFinder(self) self.finders['osm'] = OsmFinder(self) self.finders['project'] = ProjectFinder(self) for key in self.finders.keys(): self.finders[key].message.connect(self.displayMessage) self.refreshProject() def _reloadFinders(self): for key in self.finders.keys(): self.finders[key].close() self.finders[key].reload() self.refreshProject() @pyqtSlot(str, QgsMessageBar.MessageLevel) def displayMessage(self, message, level): self.iface.messageBar().pushMessage("QuickFinder", message, level) def showSettings(self): if ConfigurationDialog().exec_(): self._reloadFinders() def searchStarted(self): self.searchAction.setVisible(False) self.stopAction.setVisible(True) def searchFinished(self): self.searchAction.setVisible(True) self.stopAction.setVisible(False) def refreshProject(self): if not self.finders['project'].activated: return if not self.settings.value("refreshAuto"): return nDays = self.settings.value("refreshDelay") # do not ask more ofen than 3 days askLimit = min(3, nDays) recentlyAsked = self.settings.value("refreshLastAsked") >= nDaysAgoIsoDate(askLimit) if recentlyAsked: return threshDate = nDaysAgoIsoDate(nDays) uptodate = True for search in self.finders['project'].searches.values(): if search.dateEvaluated <= threshDate: uptodate = False break if uptodate: return self.settings.setValue("refreshLastAsked", nDaysAgoIsoDate(0)) ret = QMessageBox(QMessageBox.Warning, "Quick Finder", QCoreApplication.translate("Auto Refresh", "Some searches are outdated. Do you want to refresh them ?"), QMessageBox.Cancel | QMessageBox.Yes).exec_() if ret == QMessageBox.Yes: RefreshDialog(self.finders['project']).exec_()
class RecentFiles(): """Keeps track of last opened files.""" def __init__(self, parent=None): """Constructor""" self.parent = parent self.max_recent = 5 self.recent_files = [] self.recent_actions = [] self.action_clear_history = QAction(self.parent) self.create() self.update() def create(self): """Creates menu actions.""" for i in range(self.max_recent): action = QAction(self.parent) action.setIcon(QIcon(QPixmap(":/icons/action_rom.png"))) self.recent_actions.append(action) self.recent_actions[i].setVisible(False) self.parent.connect(self.recent_actions[i], SIGNAL("triggered()"), self.parent.file_open) self.parent.menuRecent.addAction(self.recent_actions[i]) self.parent.menuRecent.addSeparator() self.action_clear_history.setText("&Clear history") self.action_clear_history.setEnabled(False) self.action_clear_history.setVisible(True) self.action_clear_history.setIcon( QIcon(QPixmap(":/icons/action_clear.png"))) self.parent.connect(self.action_clear_history, SIGNAL("triggered()"), self.clear) self.parent.menuRecent.addAction(self.action_clear_history) def update(self): """Updates list of recent files.""" self.recent_files = self.parent.settings.qset.value("recent_files", []) num_files = min(len(self.recent_files), self.max_recent) for i in range(num_files): text = QFileInfo(self.recent_files[i]).fileName() self.recent_actions[i].setText(text) self.recent_actions[i].setData(self.recent_files[i]) self.recent_actions[i].setVisible(True) self.recent_actions[i].setToolTip( QFileInfo(self.recent_files[i]).filePath()) for j in range(num_files, self.max_recent): self.recent_actions[j].setVisible(False) self.action_clear_history.setEnabled((num_files > 0)) def add(self, filepath): """Adds file to recent files list.""" if filepath in self.recent_files: self.recent_files.remove(filepath) self.recent_files.insert(0, filepath) while len(self.recent_files) > 5: self.recent_files.pop(len(self.recent_files) - 1) self.parent.settings.qset.setValue("recent_files", self.recent_files) self.update() def clear(self): """Clears list of recent files.""" self.parent.settings.qset.remove("recent_files") self.update()
class PylintViewer( QWidget ): " Pylint tab widget " # Limits to colorize the final score BadLimit = 8.5 GoodLimit = 9.5 # Options of providing a report SingleFile = 0 DirectoryFiles = 1 ProjectFiles = 2 SingleBuffer = 3 updatePylintTooltip = pyqtSignal( str ) def __init__( self, parent = None ): QWidget.__init__( self, parent ) self.__reportUUID = "" self.__reportFileName = "" self.__reportOption = -1 self.__reportShown = False self.__report = None self.__widgets = [] # Prepare members for reuse if GlobalData().pylintAvailable: self.__noneLabel = QLabel( "\nNo results available" ) else: self.__noneLabel = QLabel( "\nPylint is not available" ) self.__noneLabel.setAutoFillBackground( True ) noneLabelPalette = self.__noneLabel.palette() noneLabelPalette.setColor( QPalette.Background, GlobalData().skin.nolexerPaper ) self.__noneLabel.setPalette( noneLabelPalette ) self.__noneLabel.setFrameShape( QFrame.StyledPanel ) self.__noneLabel.setAlignment( Qt.AlignHCenter ) self.__headerFont = self.__noneLabel.font() self.__headerFont.setPointSize( self.__headerFont.pointSize() + 4 ) self.__noneLabel.setFont( self.__headerFont ) self.__createLayout( parent ) self.__updateButtonsStatus() self.resizeEvent() return def __createLayout( self, parent ): " Creates the toolbar and layout " # Buttons self.printButton = QAction( PixmapCache().getIcon( 'printer.png' ), 'Print', self ) #printButton.setShortcut( 'Ctrl+' ) self.printButton.triggered.connect( self.__onPrint ) self.printButton.setVisible( False ) self.printPreviewButton = QAction( PixmapCache().getIcon( 'printpreview.png' ), 'Print preview', self ) #printPreviewButton.setShortcut( 'Ctrl+' ) self.printPreviewButton.triggered.connect( self.__onPrintPreview ) self.printPreviewButton.setVisible( False ) spacer = QWidget() spacer.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Expanding ) self.clearButton = QAction( PixmapCache().getIcon( 'trash.png' ), 'Clear', self ) self.clearButton.triggered.connect( self.__clear ) # The toolbar self.toolbar = QToolBar( self ) self.toolbar.setOrientation( Qt.Vertical ) self.toolbar.setMovable( False ) self.toolbar.setAllowedAreas( Qt.RightToolBarArea ) self.toolbar.setIconSize( QSize( 16, 16 ) ) self.toolbar.setFixedWidth( 28 ) self.toolbar.setContentsMargins( 0, 0, 0, 0 ) self.toolbar.addAction( self.printPreviewButton ) self.toolbar.addAction( self.printButton ) self.toolbar.addWidget( spacer ) self.toolbar.addAction( self.clearButton ) self.__vLayout = QVBoxLayout() self.__vLayout.setContentsMargins( 5, 5, 5, 5 ) self.__vLayout.setSpacing( 0 ) self.__vLayout.setSizeConstraint( QLayout.SetFixedSize ) self.__bodyFrame = QFrame( self ) # self.__bodyFrame.setFrameShape( QFrame.StyledPanel ) self.__bodyFrame.setFrameShape( QFrame.NoFrame ) # self.__bodyFrame.setSizePolicy( QSizePolicy.Maximum, # QSizePolicy.Expanding ) self.__bodyFrame.setLayout( self.__vLayout ) self.bodyWidget = QScrollArea( self ) self.bodyWidget.setFocusPolicy( Qt.NoFocus ) self.bodyWidget.setWidget( self.__bodyFrame ) self.bodyWidget.hide() self.__hLayout = QHBoxLayout() self.__hLayout.setContentsMargins( 0, 0, 0, 0 ) self.__hLayout.setSpacing( 0 ) self.__hLayout.addWidget( self.toolbar ) self.__hLayout.addWidget( self.__noneLabel ) self.__hLayout.addWidget( self.bodyWidget ) self.setLayout( self.__hLayout ) return def __updateButtonsStatus( self ): " Updates the buttons status " self.printButton.setEnabled( self.__reportShown ) self.printPreviewButton.setEnabled( self.__reportShown ) self.clearButton.setEnabled( self.__reportShown ) return def __onPrint( self ): " Triggered when the print button is pressed " pass def __onPrintPreview( self ): " triggered when the print preview button is pressed " pass def setFocus( self ): " Overridden setFocus " self.__vLayout.setFocus() return def __clear( self ): " Clears the content of the vertical layout " if not self.__reportShown: return self.__removeAll() self.bodyWidget.hide() self.__noneLabel.show() self.__report = None self.__reportShown = False self.__updateButtonsStatus() self.resizeEvent() self.__updateTooltip() return def __removeAll( self ): " Removes all the items from the report " for item in self.__widgets: item.hide() self.__vLayout.removeWidget( item ) del item self.__widgets = [] return def __createScoreLabel( self, score, previousScore, showFileName, fileName ): " Creates the score label " txt = "Score: " + str( score ) if previousScore != "": txt += " / Previous score: " + str( previousScore ) if not showFileName: txt += " for " + os.path.basename( fileName ) scoreLabel = QLabel( txt ) scoreLabel.setFrameShape( QFrame.StyledPanel ) scoreLabel.setFont( self.__headerFont ) scoreLabel.setAutoFillBackground( True ) palette = scoreLabel.palette() if score < self.BadLimit: palette.setColor( QPalette.Background, QColor( 255, 127, 127 ) ) palette.setColor( QPalette.Foreground, QColor( 0, 0, 0 ) ) elif score > self.GoodLimit: palette.setColor( QPalette.Background, QColor( 220, 255, 220 ) ) palette.setColor( QPalette.Foreground, QColor( 0, 0, 0 ) ) else: palette.setColor( QPalette.Background, QColor( 255, 255, 127 ) ) palette.setColor( QPalette.Foreground, QColor( 0, 0, 0 ) ) scoreLabel.setPalette( palette ) return scoreLabel @staticmethod def __setTableHeight( table ): " Auxiliary function to set the table height " # Height - it is ugly and approximate however I am tired of # calculating the proper height. Why is this so hard, huh? lastRowHeight = table.itemDelegate().lastHeight height = lastRowHeight * ( table.topLevelItemCount() + 1 ) + 10 table.setFixedHeight( height ) return @staticmethod def __shouldShowFileName( messages ): " Decides if the file name column should be supressed " if len( messages ) == 0: return False firstName = messages[ 0 ].fileName for index in range( 1, len( messages ) ): if firstName != messages[ index ].fileName: return True return False def __addErrorsTable( self, messages, showFileName ): " Creates the messages table " errTable = QTreeWidget( self.bodyWidget ) errTable.setAlternatingRowColors( True ) errTable.setRootIsDecorated( False ) errTable.setItemsExpandable( False ) errTable.setSortingEnabled( True ) errTable.setItemDelegate( NoOutlineHeightDelegate( 4 ) ) errTable.setUniformRowHeights( True ) errTable.itemActivated.connect( self.__errorActivated ) headerLabels = [ "File name", "Line", "Message ID", "Object", "Message" ] errTable.setHeaderLabels( headerLabels ) for item in messages: if item.position is None: lineNumber = str( item.lineNumber ) else: lineNumber = str( item.lineNumber ) + ":" + str( item.position ) values = [ item.fileName, lineNumber, item.messageID, item.objectName, item.message ] errTable.addTopLevelItem( ErrorTableItem( values, 1 ) ) # Hide the file name column if required if not showFileName: errTable.setColumnHidden( 0, True ) # Resizing errTable.header().resizeSections( QHeaderView.ResizeToContents ) errTable.header().setStretchLastSection( True ) # Sort indicator if showFileName: sortIndex = 0 # By file names else: sortIndex = 1 # By line number because this is from the same file errTable.header().setSortIndicator( sortIndex, Qt.AscendingOrder ) errTable.sortItems( sortIndex, errTable.header().sortIndicatorOrder() ) # Height self.__setTableHeight( errTable ) self.__vLayout.addWidget( errTable ) self.__widgets.append( errTable ) return def __addSimilarity( self, similarity, titleText ): " Adds a similarity " # Label title = QLabel( titleText ) title.setFont( self.__headerFont ) self.__vLayout.addWidget( title ) self.__widgets.append( title ) # List of files simTable = QTreeWidget( self.bodyWidget ) simTable.setAlternatingRowColors( True ) simTable.setRootIsDecorated( False ) simTable.setItemsExpandable( False ) simTable.setSortingEnabled( False ) simTable.setItemDelegate( NoOutlineHeightDelegate( 4 ) ) simTable.setUniformRowHeights( True ) simTable.itemActivated.connect( self.__similarityActivated ) simTable.setHeaderLabels( [ "File name", "Line" ] ) for item in similarity.files: values = [ item[ 0 ], str( item[ 1 ] ) ] simTable.addTopLevelItem( QTreeWidgetItem( values ) ) # Resizing simTable.header().resizeSections( QHeaderView.ResizeToContents ) simTable.header().setStretchLastSection( True ) # Height self.__setTableHeight( simTable ) self.__vLayout.addWidget( simTable ) self.__widgets.append( simTable ) # The fragment itself if len( similarity.fragment ) > 10: # Take first 9 lines text = "\n".join( similarity.fragment[ : 9 ] ) + "\n ..." toolTip = "\n".join( similarity.fragment ) else: text = "\n".join( similarity.fragment ) toolTip = "" fragmentLabel = QLabel( "<pre>" + self.__htmlEncode( text ) + "</pre>" ) if toolTip != "": fragmentLabel.setToolTip( "<pre>" + self.__htmlEncode( toolTip ) + "</pre>" ) palette = fragmentLabel.palette() palette.setColor( QPalette.Background, QColor( 250, 250, 175 ) ) palette.setColor( QPalette.Foreground, QColor( 0, 0, 0 ) ) fragmentLabel.setPalette( palette ) fragmentLabel.setFrameShape( QFrame.StyledPanel ) fragmentLabel.setAutoFillBackground( True ) labelFont = fragmentLabel.font() labelFont.setFamily( GlobalData().skin.baseMonoFontFace ) fragmentLabel.setFont( labelFont ) self.__vLayout.addWidget( fragmentLabel ) self.__widgets.append( fragmentLabel ) return @staticmethod def __htmlEncode( string ): " Encodes HTML " return string.replace( "&", "&" ) \ .replace( ">", ">" ) \ .replace( "<", "<" ) def __addSectionSpacer( self ): " Adds a fixed height spacer to the VBox layout " spacer = QWidget() spacer.setFixedHeight( 10 ) self.__vLayout.addWidget( spacer ) self.__widgets.append( spacer ) return def __addGenericTable( self, table ): " Adds a generic table to the report " theTable = QTreeWidget( self.bodyWidget ) theTable.setAlternatingRowColors( True ) theTable.setRootIsDecorated( False ) theTable.setItemsExpandable( False ) theTable.setSortingEnabled( False ) theTable.setItemDelegate( NoOutlineHeightDelegate( 4 ) ) theTable.setUniformRowHeights( True ) headerLabels = [] for index in range( 0, len( table.header ) ): headerLabels.append( table.header[ index ] ) theTable.setHeaderLabels( headerLabels ) for item in table.body: row = [] for index in range( 0, len( table.header ) ): row.append( item[ index ] ) theTable.addTopLevelItem( QTreeWidgetItem( row ) ) theTable.setFocusPolicy( Qt.NoFocus ) # Resizing theTable.header().resizeSections( QHeaderView.ResizeToContents ) theTable.header().setStretchLastSection( True ) # Height self.__setTableHeight( theTable ) self.__vLayout.addWidget( theTable ) self.__widgets.append( theTable ) return def __addGenericTableTitle( self, table ): " Adds a generic table title " tableTitle = QLabel( table.title ) tableTitle.setFont( self.__headerFont ) self.__vLayout.addWidget( tableTitle ) self.__widgets.append( tableTitle ) return def __updateTooltip( self ): " Generates a signal with appropriate string message " if not self.__reportShown: tooltip = "No results available" elif self.__reportOption == self.DirectoryFiles: tooltip = "Report generated for directory: " + \ self.__reportFileName elif self.__reportOption == self.ProjectFiles: tooltip = "Report generated for the whole project" elif self.__reportOption == self.SingleFile: tooltip = "Report generated for file: " + self.__reportFileName elif self.__reportOption == self.SingleBuffer: tooltip = "Report generated for unsaved file: " + \ self.__reportFileName else: tooltip = "" self.updatePylintTooltip.emit( tooltip ) return def showReport( self, lint, reportOption, fileName, uuid ): " Shows the pylint results " self.__removeAll() self.__noneLabel.hide() self.__report = lint self.__reportUUID = uuid self.__reportFileName = fileName self.__reportOption = reportOption showFileName = self.__shouldShowFileName( lint.errorMessages ) scoreLabel = self.__createScoreLabel( lint.score, lint.previousScore, showFileName, fileName ) self.__vLayout.addWidget( scoreLabel ) self.__widgets.append( scoreLabel ) if len( lint.errorMessages ) > 0: self.__addSectionSpacer() self.__addErrorsTable( lint.errorMessages, showFileName ) index = 0 for similarity in lint.similarities: self.__addSectionSpacer() self.__addSimilarity( similarity, "Similarity #" + str( index ) ) index += 1 for table in lint.tables: self.__addSectionSpacer() self.__addGenericTableTitle( table ) self.__addGenericTable( table ) self.bodyWidget.show() self.bodyWidget.ensureVisible( 0, 0, 0, 0 ) self.__reportShown = True self.__updateButtonsStatus() self.__updateTooltip() # It helps, but why do I have flickering? QApplication.processEvents() self.__resizeBodyFrame() return def __errorActivated( self, item, column ): " Handles the double click (or Enter) on the item " linePos = str( item.text( 1 ) ) if ":" in linePos: parts = linePos.split( ":" ) lineNumber = int( parts[ 0 ] ) pos = int( parts[ 1 ] ) else: lineNumber = int( linePos ) pos = 0 if self.__reportOption in [ self.SingleFile, self.DirectoryFiles, self.ProjectFiles ]: fileName = str( item.text( 0 ) ) else: # SingleBuffer if self.__reportFileName != "": if os.path.isabs( self.__reportFileName ): fileName = self.__reportFileName else: # Could be unsaved buffer, so try to search by the mainWindow = GlobalData().mainWindow widget = mainWindow.getWidgetByUUID( self.__reportUUID ) if widget is None: logging.error( "The unsaved buffer has been closed" ) return # The widget was found, so jump to the required editor = widget.getEditor() editor.gotoLine( lineNumber, pos ) editor.setFocus() return GlobalData().mainWindow.openFile( fileName, lineNumber, pos ) return def __resizeBodyFrame( self ): " Resizing the frame to occupy all available width " size = self.bodyWidget.maximumViewportSize() self.__bodyFrame.setMinimumWidth( size.width() - 16 ) self.__bodyFrame.setMinimumHeight( size.height() ) return def showEvent( self, showEv = None ): " Called when the widget is shown " self.__resizeBodyFrame() return def resizeEvent( self, resizeEv = None ): " Called when the main window gets resized " self.__resizeBodyFrame() return def onFileUpdated( self, fileName, uuid ): " Called when a buffer is saved or saved as " if not self.__reportShown: return if self.__reportUUID != uuid: return # Currently shown report is for the saved buffer # File name is expected being absolute self.__reportFileName = fileName self.updatePylintTooltip.emit( "Report generated for buffer saved as " + fileName ) return def __similarityActivated( self, item, column ): " Triggered when a similarity is activated " fileName = str( item.text( 0 ) ) lineNumber = int( item.text( 1 ) ) GlobalData().mainWindow.openFile( fileName, lineNumber ) return
def __init__(self, argv): QMainWindow.__init__(self) self.setWindowTitle("SimSo: Real-Time Scheduling Simulator") # Possible actions: style = qApp.style() # New self._newAction = QAction( style.standardIcon(QStyle.SP_FileDialogNewFolder), '&New', None) self._newAction.setShortcut(Qt.CTRL + Qt.Key_N) self._newAction.triggered.connect(self.fileNew) # Open self._openAction = QAction( style.standardIcon(QStyle.SP_DialogOpenButton), '&Open', None) self._openAction.setShortcut(Qt.CTRL + Qt.Key_O) self._openAction.triggered.connect(self.fileOpen) # Save self._saveAction = QAction( style.standardIcon(QStyle.SP_DialogSaveButton), '&Save', None) self._saveAction.setShortcut(Qt.CTRL + Qt.Key_S) self._saveAction.triggered.connect(self.fileSave) # Save As self._saveAsAction = QAction( style.standardIcon(QStyle.SP_DialogSaveButton), 'Save &As', None) self._saveAsAction.setShortcut(Qt.CTRL + Qt.SHIFT + Qt.Key_S) self._saveAsAction.triggered.connect(self.fileSaveAs) # Run self._runAction = QAction(style.standardIcon(QStyle.SP_MediaPlay), '&Run', None) self._runAction.setShortcut(Qt.CTRL + Qt.Key_R) self._runAction.triggered.connect(self.fileRun) # Show Model data self._modelAction = QAction('&Model data', None) self._modelAction.setShortcut(Qt.CTRL + Qt.Key_M) #self._ganttAction.setCheckable(True) self._modelAction.triggered.connect(self.showModelWindow) #Â Show Gantt self._ganttAction = QAction('&Gantt', None) self._ganttAction.setShortcut(Qt.CTRL + Qt.Key_G) self._ganttAction.setEnabled(False) #self._ganttAction.setCheckable(True) self._ganttAction.triggered.connect(self.showGantt) #Â Show Results self._metricsAction = QAction('&Results', None) self._metricsAction.setShortcut(Qt.CTRL + Qt.Key_I) self._metricsAction.setEnabled(False) #self._metricsAction.setCheckable(True) self._metricsAction.triggered.connect(self.showResults) # Show Doc self._docAction = QAction('&Documentation', None) self._docAction.triggered.connect(self.showDocumentation) self._aboutAction = QAction('&About SimSo', None) self._aboutAction.triggered.connect(self.showAbout) # Recent files self._recentFileActions = [] for i in range(5): act = QAction(self) act.setVisible(False) act.triggered.connect(self.openRecentFile) self._recentFileActions.append(act) # File Menu: file_menu = QMenu('&File', self) file_menu.addAction(self._newAction) file_menu.addAction(self._openAction) file_menu.addAction(self._saveAction) file_menu.addAction(self._saveAsAction) file_menu.addAction(self._runAction) file_menu.addSeparator() for act in self._recentFileActions: file_menu.addAction(act) file_menu.addSeparator() file_menu.addAction('&Quit', self.fileQuit, Qt.CTRL + Qt.Key_Q) self.updateRecentFileActions() # View Menu: view_menu = QMenu('&View', self) view_menu.addAction(self._modelAction) view_menu.addAction(self._ganttAction) view_menu.addAction(self._metricsAction) # Help Menu: help_menu = QMenu('&Help', self) help_menu.addAction(self._docAction) help_menu.addAction(self._aboutAction) # Add menus to menuBar: self.menuBar().addMenu(file_menu) self.menuBar().addMenu(view_menu) self.menuBar().addMenu(help_menu) # ToolBar: self.toolBar = QToolBar("Main ToolBar") self.addToolBar(self.toolBar) self.toolBar.addAction(self._newAction) self.toolBar.addAction(self._openAction) self.toolBar.addAction(self._saveAction) self.toolBar.addAction(self._runAction) self.toolBar.addAction(self._ganttAction) self.toolBar.addAction(self._metricsAction) # Tab: self.main_tab = QTabWidget() self.main_tab.setTabsClosable(True) self.main_tab.setMovable(True) self.main_tab.tabCloseRequested.connect(self.tabCloseRequested) self.main_tab.currentChanged.connect(self.tabChanged) self.setCentralWidget(self.main_tab) # Init statusBar: self.statusBar().showMessage("", 2000) self._documentation = None if argv: for arg in argv: try: self.open_file(arg) except Exception as e: print(e) else: self.fileNew()
class MainUI(remembering.RememberingMainWindow): """Main UI.""" _programs_file = os.path.join(multiplatform.data_dir, 'encuentro.data') def __init__(self, version, app_quit): super(MainUI, self).__init__() self.app_quit = app_quit self.finished = False self.version = version self.setWindowTitle('Encuentro') self.programs_data = data.ProgramsData(self, self._programs_file) self._touch_config() self.downloaders = {} for downtype, dloader_class in all_downloaders.iteritems(): self.downloaders[downtype] = dloader_class() # finish all gui stuff self.big_panel = central_panel.BigPanel(self) self.episodes_list = self.big_panel.episodes self.episodes_download = self.big_panel.downloads_widget self.setCentralWidget(self.big_panel) # the setting of menubar should be almost in the end, because it may # trigger the wizard, which needs big_panel and etc. self.action_play = self.action_download = None self.filter_line = self.filter_cbox = self.needsomething_alert = None self._menubar() systray.show(self) if config.get('autorefresh'): ue = update.UpdateEpisodes(self) ue.background() else: # refresh data if never done before or if last # update was 7 days ago last_refresh = config.get('autorefresh_last_time') if last_refresh is None or ( dt.datetime.now() - last_refresh > dt.timedelta(7)): ue = update.UpdateEpisodes(self) ue.background() self.show() self.episodes_download.load_pending() logger.debug("Main UI started ok") def _touch_config(self): """Do some config processing.""" # log the config, but without user and pass safecfg = config.sanitized_config() logger.debug("Configuration loaded: %s", safecfg) # we have a default for download dir if not config.get('downloaddir'): config['downloaddir'] = multiplatform.get_download_dir() # maybe clean some config if self.programs_data.reset_config_from_migration: config['user'] = '' config['password'] = '' config.pop('cols_width', None) config.pop('cols_order', None) config.pop('selected_row', None) def have_config(self): """Return if some config is needed.""" return config.get('user') and config.get('password') def have_metadata(self): """Return if metadata is needed.""" return bool(self.programs_data) def _menubar(self): """Set up the menu bar.""" menubar = self.menuBar() # applications menu menu_appl = menubar.addMenu(u'&Aplicación') icon = self.style().standardIcon(QStyle.SP_BrowserReload) action_reload = QAction(icon, '&Refrescar', self) action_reload.setShortcut('Ctrl+R') action_reload.setToolTip(u'Recarga la lista de programas') action_reload.triggered.connect(self.refresh_episodes) menu_appl.addAction(action_reload) icon = self.style().standardIcon(QStyle.SP_FileDialogDetailedView) action_preferences = QAction(icon, u'&Preferencias', self) action_preferences.triggered.connect(self.open_preferences) action_preferences.setToolTip( u'Configurar distintos parámetros del programa') menu_appl.addAction(action_preferences) menu_appl.addSeparator() icon = self.style().standardIcon(QStyle.SP_MessageBoxInformation) _act = QAction(icon, '&Acerca de', self) _act.triggered.connect(self.open_about_dialog) _act.setToolTip(u'Muestra información de la aplicación') menu_appl.addAction(_act) icon = self.style().standardIcon(QStyle.SP_DialogCloseButton) _act = QAction(icon, '&Salir', self) _act.setShortcut('Ctrl+Q') _act.setToolTip(u'Sale de la aplicación') _act.triggered.connect(self.on_close) menu_appl.addAction(_act) # program menu menu_prog = menubar.addMenu(u'&Programa') icon = self.style().standardIcon(QStyle.SP_ArrowDown) self.action_download = QAction(icon, '&Descargar', self) self.action_download.setShortcut('Ctrl+D') self.action_download.setEnabled(False) self.action_download.setToolTip(TTIP_DOWNLOAD_D) self.action_download.triggered.connect(self.download_episode) menu_prog.addAction(self.action_download) icon = self.style().standardIcon(QStyle.SP_MediaPlay) self.action_play = QAction(icon, '&Reproducir', self) self.action_play.setEnabled(False) self.action_play.setToolTip(TTIP_PLAY_D) self.action_play.triggered.connect(self.on_play_action) menu_prog.addAction(self.action_play) # toolbar for buttons toolbar = self.addToolBar('main') toolbar.addAction(self.action_download) toolbar.addAction(self.action_play) toolbar.addSeparator() toolbar.addAction(action_reload) toolbar.addAction(action_preferences) # filter text and button, to the right spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) toolbar.addWidget(spacer) toolbar.addWidget(QLabel(u"Filtro: ")) self.filter_line = QLineEdit() self.filter_line.setMaximumWidth(150) self.filter_line.textChanged.connect(self.on_filter_changed) toolbar.addWidget(self.filter_line) self.filter_cbox = QCheckBox(u"Sólo descargados") self.filter_cbox.stateChanged.connect(self.on_filter_changed) toolbar.addWidget(self.filter_cbox) # if needed, a warning that stuff needs to be configured icon = self.style().standardIcon(QStyle.SP_MessageBoxWarning) m = u"Necesita configurar algo; haga click aquà para abrir el wizard" self.needsomething_alert = QAction(icon, m, self) self.needsomething_alert.triggered.connect(self._start_wizard) toolbar.addAction(self.needsomething_alert) if not config.get('nowizard'): self._start_wizard() self._review_need_something_indicator() def _start_wizard(self, _=None): """Start the wizard if needed.""" if not self.have_config() or not self.have_metadata(): dlg = wizard.WizardDialog(self) dlg.exec_() self._review_need_something_indicator() def on_filter_changed(self, _): """The filter text has changed, apply it in the episodes list.""" text = self.filter_line.text() cbox = self.filter_cbox.checkState() self.episodes_list.set_filter(text, cbox) # after applying filter, nothing is selected, so check buttons # (easiest way to clean them all) self.check_download_play_buttons() def _review_need_something_indicator(self): """Hide/show/enable/disable different indicators if need sth.""" needsomething = bool(not self.have_config() or not self.have_metadata()) self.needsomething_alert.setVisible(needsomething) def shutdown(self): """Stop everything and quit. This shutdown con be called at any time, even on init, so we have extra precautions about which attributes we have. """ signal.emit('save_state') config.save() self.finished = True programs_data = getattr(self, 'programs_data', None) if programs_data is not None: programs_data.save() downloaders = getattr(self, 'downloaders', {}) for downloader in downloaders.itervalues(): downloader.shutdown() # bye bye self.app_quit() def on_close(self, _): """Close signal.""" if self._should_close(): self.shutdown() def closeEvent(self, event): """All is being closed.""" if self._should_close(): self.shutdown() else: event.ignore() def _should_close(self): """Still time to decide if want to close or not.""" logger.info("Attempt to close the program") pending = self.episodes_download.pending() if not pending: # all fine, save all and quit logger.info("Saving states and quitting") return True logger.debug("Still %d active downloads when trying to quit", pending) # stuff pending m = (u"Hay programas todavÃa en proceso de descarga!\n" u"¿Seguro quiere salir del programa?") QMB = QMessageBox dlg = QMB(u"Guarda!", m, QMB.Question, QMB.Yes, QMB.No, QMB.NoButton) opt = dlg.exec_() if opt != QMB.Yes: logger.info("Quit cancelled") return False # quit anyway, put all downloading and pending episodes to none logger.info("Fixing episodes, saving state and exiting") for program in self.programs_data.values(): state = program.state if state == Status.waiting or state == Status.downloading: program.state = Status.none return True def show_message(self, err_type, text): """Show different messages to the user.""" if self.finished: logger.debug("Ignoring message: %r", text) return logger.debug("Showing a message: %r", text) # error text can be produced by windows, try to to sanitize it if isinstance(text, str): try: text = text.decode("utf8") except UnicodeDecodeError: try: text = text.decode("latin1") except UnicodeDecodeError: text = repr(text) QMB = QMessageBox dlg = QMB(u"Atención: " + err_type, text, QMB.Warning, QMB.Ok, QMB.NoButton, QMB.NoButton) dlg.exec_() def refresh_episodes(self, _=None): """Update and refresh episodes.""" ue = update.UpdateEpisodes(self) ue.interactive() def download_episode(self, _=None): """Download the episode(s).""" episode_ids = self.episodes_list.selected_items() for episode_id in episode_ids: episode = self.programs_data[episode_id] self.queue_download(episode) @defer.inline_callbacks def queue_download(self, episode): """User indicated to download something.""" logger.debug("Download requested of %s", episode) if episode.state != Status.none: logger.debug("Download denied, episode %s is not in downloadeable " "state.", episode.episode_id) return # queue self.episodes_download.append(episode) self.episodes_list.episode_info.update(episode) self.check_download_play_buttons() if self.episodes_download.downloading: return logger.debug("Downloads: starting") while self.episodes_download.pending(): episode = self.episodes_download.prepare() try: filename, episode = yield self._episode_download(episode) except CancelledError: logger.debug("Got a CancelledError!") self.episodes_download.end(error=u"Cancelado") except BadCredentialsError: logger.debug("Bad credentials error!") msg = (u"Error con las credenciales: hay que configurar " u"usuario y clave correctos") self.show_message('BadCredentialsError', msg) self.episodes_download.end(error=msg) except EncuentroError, e: orig_exc = e.orig_exc msg = "%s(%s)" % (orig_exc, e) err_type = e.__class__.__name__ logger.exception("Custom Encuentro error: %s (%r)", e, orig_exc) notify(err_type, msg) self.episodes_download.end(error=u"Error: " + msg) except Exception, e: logger.exception("Unknown download error: %s (%r)", e, e) err_type = e.__class__.__name__ notify(err_type, str(e)) self.episodes_download.end(error=u"Error: " + str(e)) else:
class DisassemblerResultsWidget(QWidget, MainWindowTabWidgetBase): " Disassembling results widget " escapePressed = pyqtSignal() textEditorZoom = pyqtSignal(int) def __init__(self, scriptName, name, code, reportTime, parent=None): MainWindowTabWidgetBase.__init__(self) QWidget.__init__(self, parent) self.__createLayout(scriptName, name, code, reportTime) return def __createLayout(self, scriptName, name, code, reportTime): " Creates the toolbar and layout " # Buttons self.__printButton = QAction(PixmapCache().getIcon('printer.png'), 'Print', self) self.__printButton.triggered.connect(self.__onPrint) self.__printButton.setEnabled(False) self.__printButton.setVisible(False) self.__printPreviewButton = QAction( PixmapCache().getIcon('printpreview.png'), 'Print preview', self) self.__printPreviewButton.triggered.connect(self.__onPrintPreview) self.__printPreviewButton.setEnabled(False) self.__printPreviewButton.setVisible(False) # Zoom buttons self.__zoomInButton = QAction(PixmapCache().getIcon('zoomin.png'), 'Zoom in (Ctrl+=)', self) self.__zoomInButton.triggered.connect(self.onZoomIn) self.__zoomOutButton = QAction(PixmapCache().getIcon('zoomout.png'), 'Zoom out (Ctrl+-)', self) self.__zoomOutButton.triggered.connect(self.onZoomOut) self.__zoomResetButton = QAction( PixmapCache().getIcon('zoomreset.png'), 'Zoom reset (Ctrl+0)', self) self.__zoomResetButton.triggered.connect(self.onZoomReset) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) # Toolbar toolbar = QToolBar(self) toolbar.setOrientation(Qt.Vertical) toolbar.setMovable(False) toolbar.setAllowedAreas(Qt.RightToolBarArea) toolbar.setIconSize(QSize(16, 16)) toolbar.setFixedWidth(28) toolbar.setContentsMargins(0, 0, 0, 0) toolbar.addAction(self.__printPreviewButton) toolbar.addAction(self.__printButton) toolbar.addWidget(spacer) toolbar.addAction(self.__zoomInButton) toolbar.addAction(self.__zoomOutButton) toolbar.addAction(self.__zoomResetButton) summary = QLabel("<b>Script:</b> " + scriptName + "<br>" "<b>Name:</b> " + name + "<br>" "<b>Disassembled at:</b> " + reportTime) summary.setFrameStyle(QFrame.StyledPanel) summary.setAutoFillBackground(True) summaryPalette = summary.palette() summaryBackground = summaryPalette.color(QPalette.Background) summaryBackground.setRgb(min(summaryBackground.red() + 30, 255), min(summaryBackground.green() + 30, 255), min(summaryBackground.blue() + 30, 255)) summaryPalette.setColor(QPalette.Background, summaryBackground) summary.setPalette(summaryPalette) self.__text = DisasmWidget(self) self.__text.setAcceptRichText(False) self.__text.setLineWrapMode(QTextEdit.NoWrap) self.__text.setFont(GlobalData().skin.nolexerFont) self.zoomTo(Settings().zoom) self.__text.setReadOnly(True) self.__text.setPlainText(code) vLayout = QVBoxLayout() vLayout.addWidget(summary) vLayout.addWidget(self.__text) hLayout = QHBoxLayout() hLayout.setContentsMargins(0, 0, 0, 0) hLayout.setSpacing(0) hLayout.addLayout(vLayout) hLayout.addWidget(toolbar) self.setLayout(hLayout) return def setFocus(self): " Overriden setFocus " self.__text.setFocus() return def __onPrint(self): " Triggered on the 'print' button " pass def __onPrintPreview(self): " Triggered on the 'print preview' button " pass def onZoomReset(self): " Triggered when the zoom reset button is pressed " zoom = Settings().zoom if zoom != 0: self.textEditorZoom.emit(0) return True def onZoomIn(self): " Triggered when the zoom in button is pressed " zoom = Settings().zoom if zoom < 20: self.textEditorZoom.emit(zoom + 1) return True def onZoomOut(self): " Triggered when the zoom out button is pressed " zoom = Settings().zoom if zoom > -10: self.textEditorZoom.emit(zoom - 1) return True def keyPressEvent(self, event): " Handles the key press events " if event.key() == Qt.Key_Escape: self.escapePressed.emit() event.accept() else: QWidget.keyPressEvent(self, event) return def zoomTo(self, zoomFactor): " Scales the font in accordance to the given zoom factor " font = QFont(GlobalData().skin.nolexerFont) newPointSize = font.pointSize() + zoomFactor font.setPointSize(newPointSize) self.__text.setFont(font) return # Mandatory interface part is below def isModified(self): " Tells if the file is modified " return False def getRWMode(self): " Tells if the file is read only " return "RO" def getType(self): " Tells the widget type " return MainWindowTabWidgetBase.DisassemblerViewer def getLanguage(self): " Tells the content language " return "Disassembler" def getFileName(self): " Tells what file name of the widget content " return "N/A" def setFileName(self, name): " Sets the file name - not applicable" raise Exception( "Setting a file name for disassembler results is not applicable") def getEol(self): " Tells the EOL style " return "N/A" def getLine(self): " Tells the cursor line " return "N/A" def getPos(self): " Tells the cursor column " return "N/A" def getEncoding(self): " Tells the content encoding " return "N/A" def setEncoding(self, newEncoding): " Sets the new encoding - not applicable for the disassembler results viewer " return def getShortName(self): " Tells the display name " return "Disassembling results" def setShortName(self, name): " Sets the display name - not applicable " raise Exception( "Setting a file name for disassembler results is not applicable")
class QuickFinder(QObject): name = u"&Quick Finder" actions = None toolbar = None finders = {} loadingIcon = None def __init__(self, iface): QObject.__init__(self) self.iface = iface self.actions = {} self.finders = {} self.settings = MySettings() self.rubber = None self._init_finders() self.iface.projectRead.connect(self._reload_finders) self.iface.newProjectCreated.connect(self._reload_finders) # translation environment self.plugin_dir = os.path.dirname(__file__) locale = QSettings().value("locale/userLocale")[0:2] localePath = os.path.join(self.plugin_dir, 'i18n', 'quickfinder_{0}.qm'.format(locale)) if os.path.exists(localePath): self.translator = QTranslator() self.translator.load(localePath) QCoreApplication.installTranslator(self.translator) def initGui(self): self.actions['showSettings'] = QAction( QIcon(":/plugins/quickfinder/icons/settings.svg"), self.tr(u"&Settings"), self.iface.mainWindow()) self.actions['showSettings'].triggered.connect(self.show_settings) self.iface.addPluginToMenu(self.name, self.actions['showSettings']) self.actions['help'] = QAction( QIcon(":/plugins/quickfinder/icons/help.svg"), self.tr("Help"), self.iface.mainWindow()) self.actions['help'].triggered.connect( lambda: QDesktopServices().openUrl( QUrl("http://3nids.github.io/quickfinder"))) self.iface.addPluginToMenu(self.name, self.actions['help']) self._init_toolbar() self.rubber = QgsRubberBand(self.iface.mapCanvas()) self.rubber.setColor(QColor(255, 255, 50, 200)) self.rubber.setIcon(self.rubber.ICON_CIRCLE) self.rubber.setIconSize(15) self.rubber.setWidth(4) self.rubber.setBrushStyle(Qt.NoBrush) def unload(self): """ Unload plugin """ for key in self.finders.keys(): self.finders[key].close() for action in self.actions.itervalues(): self.iface.removePluginMenu(self.name, action) if self.toolbar: del self.toolbar if self.rubber: self.iface.mapCanvas().scene().removeItem(self.rubber) del self.rubber def _init_toolbar(self): """setup the plugin toolbar.""" self.toolbar = self.iface.addToolBar(self.name) self.toolbar.setObjectName('mQuickFinderToolBar') self.search_action = QAction(QIcon(":/plugins/quickfinder/icons/magnifier13.svg"), self.tr("Search"), self.toolbar) self.stop_action = QAction(QIcon(":/plugins/quickfinder/icons/wrong2.svg"), self.tr("Cancel"), self.toolbar) self.finder_box = FinderBox(self.finders, self.iface, self.toolbar) self.finder_box.search_started.connect(self.search_started) self.finder_box.search_finished.connect(self.search_finished) self.finder_box_action = self.toolbar.addWidget(self.finder_box) self.finder_box_action.setVisible(True) self.search_action.triggered.connect(self.finder_box.search) self.toolbar.addAction(self.search_action) self.stop_action.setVisible(False) self.stop_action.triggered.connect(self.finder_box.stop) self.toolbar.addAction(self.stop_action) self.toolbar.setVisible(True) def _init_finders(self): self.finders['geomapfish'] = GeomapfishFinder(self) self.finders['osm'] = OsmFinder(self) self.finders['project'] = ProjectFinder(self) for key in self.finders.keys(): self.finders[key].message.connect(self.display_message) self.refresh_project() def _reload_finders(self): for key in self.finders.keys(): self.finders[key].close() self.finders[key].reload() self.refresh_project() @pyqtSlot(str, QgsMessageBar.MessageLevel) def display_message(self, message, level): self.iface.messageBar().pushMessage("QuickFinder", message, level) def show_settings(self): if ConfigurationDialog().exec_(): self._reload_finders() def search_started(self): self.search_action.setVisible(False) self.stop_action.setVisible(True) def search_finished(self): self.search_action.setVisible(True) self.stop_action.setVisible(False) def refresh_project(self): if not self.finders['project'].activated: return if not self.settings.value("refreshAuto"): return n_days = self.settings.value("refreshDelay") # do not ask more ofen than 3 days ask_limit = min(3, n_days) recently_asked = self.settings.value("refreshLastAsked") >= n_days_ago_iso_date(ask_limit) if recently_asked: return thresh_date = n_days_ago_iso_date(n_days) uptodate = True for search in self.finders['project'].searches.values(): if search.dateEvaluated <= thresh_date: uptodate = False break if uptodate: return self.settings.setValue("refreshLastAsked", n_days_ago_iso_date(0)) ret = QMessageBox(QMessageBox.Warning, "Quick Finder", QCoreApplication.translate("Auto Refresh", "Some searches are outdated. Do you want to refresh them ?"), QMessageBox.Cancel | QMessageBox.Yes).exec_() if ret == QMessageBox.Yes: RefreshDialog(self.finders['project']).exec_()
class DateRangeSelector(QObject): def __init__(self, mainwindow, view): QObject.__init__(self) self.mainwindow = mainwindow self.view = view self.model = mainwindow.model.daterange_selector self.model.view = self self._setupUi() def _setupUi(self): # Create actions self.actionNext = QAction(tr("Next"), self) self.actionNext.setShortcut("Ctrl+Alt+]") self.actionNext.triggered.connect(self.model.select_next_date_range) self.actionPrevious = QAction(tr("Previous"), self) self.actionPrevious.setShortcut("Ctrl+Alt+[") self.actionPrevious.triggered.connect(self.model.select_prev_date_range) self.actionToday = QAction(tr("Today"), self) self.actionToday.setShortcut("Ctrl+Alt+T") self.actionToday.triggered.connect(self.model.select_today_date_range) self.actionChangeToMonth = QAction(tr("Month"), self) self.actionChangeToMonth.setShortcut("Ctrl+Alt+1") self.actionChangeToMonth.triggered.connect(self.model.select_month_range) self.actionChangeToQuarter = QAction(tr("Quarter"), self) self.actionChangeToQuarter.setShortcut("Ctrl+Alt+2") self.actionChangeToQuarter.triggered.connect(self.model.select_quarter_range) self.actionChangeToYear = QAction(tr("Year"), self) self.actionChangeToYear.setShortcut("Ctrl+Alt+3") self.actionChangeToYear.triggered.connect(self.model.select_year_range) self.actionChangeToYearToDate = QAction(tr("Year To Date"), self) self.actionChangeToYearToDate.setShortcut("Ctrl+Alt+4") self.actionChangeToYearToDate.triggered.connect(self.model.select_year_to_date_range) self.actionChangeToRunningYear = QAction(tr("Running Year"), self) self.actionChangeToRunningYear.setShortcut("Ctrl+Alt+5") self.actionChangeToRunningYear.triggered.connect(self.model.select_running_year_range) self.actionChangeToAllTransactions = QAction(tr("All Transactions"), self) self.actionChangeToAllTransactions.setShortcut("Ctrl+Alt+6") self.actionChangeToAllTransactions.triggered.connect(self.model.select_all_transactions_range) self.actionChangeToCustom = QAction(tr("Custom..."), self) self.actionChangeToCustom.setShortcut("Ctrl+Alt+7") self.actionChangeToCustom.triggered.connect(self.model.select_custom_date_range) self.actionChangeToCustom1 = QAction("Custom1", self) self.actionChangeToCustom1.setShortcut("Ctrl+Alt+8") self.actionChangeToCustom1.setVisible(False) self.actionChangeToCustom1.triggered.connect(self.custom1Triggered) self.actionChangeToCustom2 = QAction("Custom2", self) self.actionChangeToCustom2.setShortcut("Ctrl+Alt+9") self.actionChangeToCustom2.setVisible(False) self.actionChangeToCustom2.triggered.connect(self.custom2Triggered) self.actionChangeToCustom3 = QAction("Custom3", self) self.actionChangeToCustom3.setShortcut("Ctrl+Alt+0") self.actionChangeToCustom3.setVisible(False) self.actionChangeToCustom3.triggered.connect(self.custom3Triggered) # set typeButton menu menu = QMenu(self.view.typeButton) menu.addAction(self.actionChangeToMonth) menu.addAction(self.actionChangeToQuarter) menu.addAction(self.actionChangeToYear) menu.addAction(self.actionChangeToYearToDate) menu.addAction(self.actionChangeToRunningYear) menu.addAction(self.actionChangeToAllTransactions) menu.addAction(self.actionChangeToCustom) menu.addAction(self.actionChangeToCustom1) menu.addAction(self.actionChangeToCustom2) menu.addAction(self.actionChangeToCustom3) self.view.typeButton.setMenu(menu) # set mainwindow's date range menu m = self.mainwindow.menuDateRange m.addAction(self.actionChangeToMonth) m.addAction(self.actionChangeToQuarter) m.addAction(self.actionChangeToYear) m.addAction(self.actionChangeToYearToDate) m.addAction(self.actionChangeToRunningYear) m.addAction(self.actionChangeToAllTransactions) m.addAction(self.actionChangeToCustom) m.addAction(self.actionChangeToCustom1) m.addAction(self.actionChangeToCustom2) m.addAction(self.actionChangeToCustom3) m.addAction(self.actionPrevious) m.addAction(self.actionNext) m.addAction(self.actionToday) # bind prev/next button self.view.prevButton.clicked.connect(self.model.select_prev_date_range) self.view.nextButton.clicked.connect(self.model.select_next_date_range) #--- Event Handlers def custom1Triggered(self): self.model.select_saved_range(0) def custom2Triggered(self): self.model.select_saved_range(1) def custom3Triggered(self): self.model.select_saved_range(2) #--- model --> view def animate_backward(self): # I didn't find a way to have a nice fading effect like we do on the Cocoa side in Qt. # The animation framework seems to be mainly for the QGraphicsScene framework. Since QWidget # doesn't have an opacity property, we're kind of screwed. However, since the date range # widget now displays the dates of the current range, this animation is less important than # it used to be. pass def animate_forward(self): pass def refresh(self): self.view.typeButton.setText(self.model.display) canNavigateDateRange = self.model.can_navigate self.actionNext.setEnabled(canNavigateDateRange) self.actionPrevious.setEnabled(canNavigateDateRange) self.actionToday.setEnabled(canNavigateDateRange) self.view.prevButton.setEnabled(canNavigateDateRange) self.view.nextButton.setEnabled(canNavigateDateRange) def refresh_custom_ranges(self): customActions = [ self.actionChangeToCustom1, self.actionChangeToCustom2, self.actionChangeToCustom3 ] for i, name in enumerate(self.model.custom_range_names): action = customActions[i] if name is not None: action.setText(name) action.setVisible(True) else: action.setVisible(False)
class FileOutlineViewer( QWidget ): """ The file outline viewer widget """ def __init__( self, editorsManager, parent = None ): QWidget.__init__( self, parent ) self.__editorsManager = editorsManager self.__mainWindow = parent self.__editorsManager.currentChanged.connect( self.__onTabChanged ) self.connect( self.__editorsManager, SIGNAL( "tabClosed" ), self.__onTabClosed ) self.connect( self.__editorsManager, SIGNAL( 'bufferSavedAs' ), self.__onSavedBufferAs ) self.connect( self.__editorsManager, SIGNAL( 'fileTypeChanged' ), self.__onFileTypeChanged ) self.__outlineBrowsers = {} # UUID -> OutlineAttributes self.__currentUUID = None self.__updateTimer = QTimer( self ) self.__updateTimer.setSingleShot( True ) self.__updateTimer.timeout.connect( self.__updateView ) self.findButton = None self.outlineViewer = None self.toolbar = None self.__createLayout() self.__modifiedFormat = Settings().modifiedFormat # create the context menu self.__menu = QMenu( self ) self.__findMenuItem = self.__menu.addAction( PixmapCache().getIcon( 'findusage.png' ), 'Find where used', self.__findWhereUsed ) return def setTooltips( self, switchOn ): " Sets the tooltips mode " for key in self.__outlineBrowsers: self.__outlineBrowsers[ key ].browser.setTooltips( switchOn ) return def __connectOutlineBrowser( self, browser ): " Connects a new buffer signals " browser.setContextMenuPolicy( Qt.CustomContextMenu ) browser.customContextMenuRequested.connect( self.__handleShowContextMenu ) self.connect( browser, SIGNAL( "selectionChanged" ), self.__selectionChanged ) return def __createLayout( self ): " Helper to create the viewer layout " # Toolbar part - buttons self.findButton = QAction( PixmapCache().getIcon( 'findusage.png' ), 'Find where highlighted item is used', self ) self.findButton.setVisible( False ) self.findButton.triggered.connect( self.__findWhereUsed ) self.showParsingErrorsButton = QAction( PixmapCache().getIcon( 'showparsingerrors.png' ), 'Show lexer/parser errors', self ) self.showParsingErrorsButton.triggered.connect( self.__showParserError ) self.showParsingErrorsButton.setEnabled( False ) self.toolbar = QToolBar( self ) self.toolbar.setMovable( False ) self.toolbar.setAllowedAreas( Qt.TopToolBarArea ) self.toolbar.setIconSize( QSize( 16, 16 ) ) self.toolbar.setFixedHeight( 28 ) self.toolbar.setContentsMargins( 0, 0, 0, 0 ) self.toolbar.addAction( self.findButton ) self.toolbar.addAction( self.showParsingErrorsButton ) # Prepare members for reuse self.__noneLabel = QLabel( "\nNot a python file" ) self.__noneLabel.setFrameShape( QFrame.StyledPanel ) self.__noneLabel.setAlignment( Qt.AlignHCenter ) headerFont = self.__noneLabel.font() headerFont.setPointSize( headerFont.pointSize() + 2 ) self.__noneLabel.setFont( headerFont ) self.__noneLabel.setAutoFillBackground( True ) noneLabelPalette = self.__noneLabel.palette() noneLabelPalette.setColor( QPalette.Background, GlobalData().skin.nolexerPaper ) self.__noneLabel.setPalette( noneLabelPalette ) self.__layout = QVBoxLayout() self.__layout.setContentsMargins( 0, 0, 0, 0 ) self.__layout.setSpacing( 0 ) self.__layout.addWidget( self.toolbar ) self.__layout.addWidget( self.__noneLabel ) self.setLayout( self.__layout ) return def __selectionChanged( self, index ): " Handles the changed selection " if index is None: self.__outlineBrowsers[ self.__currentUUID ].contentItem = None else: self.__outlineBrowsers[ self.__currentUUID ].contentItem = \ self.__outlineBrowsers[ self.__currentUUID ].browser.model().item( index ) self.__updateButtons() return def __handleShowContextMenu( self, coord ): """ Show the context menu """ browser = self.__outlineBrowsers[ self.__currentUUID ].browser index = browser.indexAt( coord ) if not index.isValid(): return # This will update the contextItem self.__selectionChanged( index ) contextItem = self.__outlineBrowsers[ self.__currentUUID ].contentItem if contextItem is None: return self.__findMenuItem.setEnabled( self.findButton.isEnabled() ) self.__menu.popup( QCursor.pos() ) return def __goToDefinition( self ): " Jump to definition context menu handler " contextItem = self.__outlineBrowsers[ self.__currentUUID ].contentItem if contextItem is not None: self.__outlineBrowsers[ self.__currentUUID ].browser.openItem( contextItem ) return def __findWhereUsed( self ): """ Find where used context menu handler """ contextItem = self.__outlineBrowsers[ self.__currentUUID ].contentItem if contextItem is not None: GlobalData().mainWindow.findWhereUsed( contextItem.getPath(), contextItem.sourceObj ) return def __updateButtons( self ): " Updates the toolbar buttons depending on what is selected " self.findButton.setEnabled( False ) contextItem = self.__outlineBrowsers[ self.__currentUUID ].contentItem if contextItem is None: return if contextItem.itemType in [ FunctionItemType, ClassItemType, AttributeItemType, GlobalItemType ]: self.findButton.setEnabled( True ) return def __onTabChanged( self, index ): " Triggered when another tab becomes active " # If the timer is still active that means the tab was switched before # the handler had a chance to work. Therefore update the previous tab # first if so. if self.__updateTimer.isActive(): self.__updateTimer.stop() self.__updateView() # Now, switch the outline browser to the new tab if index == -1: widget = self.__editorsManager.currentWidget() else: widget = self.__editorsManager.getWidgetByIndex( index ) if widget is None: if self.__currentUUID is not None: self.__outlineBrowsers[ self.__currentUUID ].browser.hide() self.__currentUUID = None self.__noneLabel.show() self.showParsingErrorsButton.setEnabled( False ) return if widget.getType() not in [ MainWindowTabWidgetBase.PlainTextEditor, MainWindowTabWidgetBase.VCSAnnotateViewer ]: if self.__currentUUID is not None: self.__outlineBrowsers[ self.__currentUUID ].browser.hide() self.__currentUUID = None self.__noneLabel.show() self.showParsingErrorsButton.setEnabled( False ) return # This is text editor, detect the file type if widget.getFileType() not in [ PythonFileType, Python3FileType ]: if self.__currentUUID is not None: self.__outlineBrowsers[ self.__currentUUID ].browser.hide() self.__currentUUID = None self.__noneLabel.show() self.showParsingErrorsButton.setEnabled( False ) return # This is a python file, check if we already have the parsed info in # the cache uuid = widget.getUUID() if uuid in self.__outlineBrowsers: # We have it, hide the current and show the existed if self.__currentUUID is not None: self.__outlineBrowsers[ self.__currentUUID ].browser.hide() self.__currentUUID = None else: self.__noneLabel.hide() self.__currentUUID = uuid self.__outlineBrowsers[ self.__currentUUID ].browser.show() info = self.__outlineBrowsers[ self.__currentUUID ].info self.showParsingErrorsButton.setEnabled( info.isOK != True ) return # It is first time we are here, create a new editor = widget.getEditor() editor.SCEN_CHANGE.connect( self.__onBufferChanged ) editor.cursorPositionChanged.connect( self.__cursorPositionChanged ) info = getBriefModuleInfoFromMemory( editor.text() ) self.showParsingErrorsButton.setEnabled( info.isOK != True ) shortFileName = widget.getShortName() browser = OutlineBrowser( uuid, shortFileName, info, self ) browser.setHeaderHighlight( info.isOK != True ) self.__connectOutlineBrowser( browser ) self.__layout.addWidget( browser ) if self.__currentUUID is not None: self.__outlineBrowsers[ self.__currentUUID ].browser.hide() self.__currentUUID = None else: self.__noneLabel.hide() self.__currentUUID = uuid attributes = OutlineAttributes() attributes.browser = browser attributes.contextItem = None attributes.info = info attributes.shortFileName = shortFileName attributes.changed = False self.__outlineBrowsers[ self.__currentUUID ] = attributes self.__outlineBrowsers[ self.__currentUUID ].browser.show() return def getCurrentUsedInfo( self ): " Provides the info used to show the current outline window " if self.__currentUUID in self.__outlineBrowsers: return self.__outlineBrowsers[ self.__currentUUID ].info return None def __cursorPositionChanged( self, xpos, ypos ): " Triggered when a cursor position is changed " if self.__updateTimer.isActive(): # If a file is very large and the cursor is moved # straight after changes this will delay the update till # the real pause. self.__updateTimer.stop() self.__updateTimer.start( 1500 ) return def __onBufferChanged( self ): " Triggered when a change in the buffer is identified " if self.__currentUUID is None: return widget = self.__editorsManager.getWidgetByUUID( self.__currentUUID ) if widget is None: return if widget.getEditor().ignoreBufferChangedSignal: return if self.__mainWindow.debugMode: return self.__updateTimer.stop() if self.__currentUUID in self.__outlineBrowsers: if self.__outlineBrowsers[ self.__currentUUID ].changed == False: self.__outlineBrowsers[ self.__currentUUID ].changed = True browser = self.__outlineBrowsers[ self.__currentUUID ].browser fName = self.__outlineBrowsers[ self.__currentUUID ].shortFileName title = self.__modifiedFormat % fName browser.model().sourceModel().updateRootData( 0, title ) self.__updateTimer.start( 1500 ) return def __updateView( self ): " Updates the view when a file is changed " self.__updateTimer.stop() info = self.getCurrentBufferInfo() if info is None: return self.showParsingErrorsButton.setEnabled( info.isOK != True ) browser = self.__outlineBrowsers[ self.__currentUUID ].browser fName = self.__outlineBrowsers[ self.__currentUUID ].shortFileName browser.setHeaderHighlight( info.isOK != True ) if not info.isOK: title = self.__modifiedFormat % fName browser.model().sourceModel().updateRootData( 0, title ) return browser.model().sourceModel().updateRootData( 0, fName ) self.__outlineBrowsers[ self.__currentUUID ].changed = False browser.updateFileItem( browser.model().sourceModel().rootItem, info ) self.__outlineBrowsers[ self.__currentUUID ].info = info return def getCurrentBufferInfo( self ): " Provides the current buffer parsed info " if self.__currentUUID is None: return None widget = self.__editorsManager.getWidgetByUUID( self.__currentUUID ) if widget is None: return None editor = widget.getEditor() info = getBriefModuleInfoFromMemory( editor.text() ) return info def __onTabClosed( self, uuid ): " Triggered when a tab is closed " if uuid in self.__outlineBrowsers: del self.__outlineBrowsers[ uuid ] return def __onSavedBufferAs( self, fileName, uuid ): " Triggered when a file is saved with a new name " if uuid in self.__outlineBrowsers: baseName = os.path.basename( fileName ) if detectFileType( fileName ) not in [ PythonFileType, Python3FileType ]: # It's not a python file anymore if uuid == self.__currentUUID: self.__outlineBrowsers[ uuid ].browser.hide() self.__noneLabel.show() self.__currentUUID = None del self.__outlineBrowsers[ uuid ] self.showParsingErrorsButton.setEnabled( False ) self.findButton.setEnabled( False ) return # Still python file with a different name browser = self.__outlineBrowsers[ uuid ].browser self.__outlineBrowsers[ uuid ].shortFileName = baseName if self.__outlineBrowsers[ uuid ].changed: title = self.__modifiedFormat % baseName else: title = baseName browser.model().sourceModel().updateRootData( 0, title ) return def __onFileTypeChanged( self, fileName, uuid, newFileType ): " Triggered when the current buffer file type is changed, e.g. .cgi " if newFileType in [ PythonFileType, Python3FileType ]: # The file became a python one if uuid not in self.__outlineBrowsers: self.__onTabChanged( -1 ) else: if uuid in self.__outlineBrowsers: # It's not a python file any more if uuid == self.__currentUUID: self.__outlineBrowsers[ uuid ].browser.hide() self.__noneLabel.show() self.__currentUUID = None del self.__outlineBrowsers[ uuid ] self.showParsingErrorsButton.setEnabled( False ) self.findButton.setEnabled( False ) return def __showParserError( self ): " Shows the parser errors window " if self.__currentUUID is None: return try: fName = self.__outlineBrowsers[ self.__currentUUID ].shortFileName widget = self.__editorsManager.getWidgetByUUID( self.__currentUUID ) if widget is None: return editor = widget.getEditor() info = getBriefModuleInfoFromMemory( editor.text() ) dialog = ParserErrorsDialog( fName, info ) dialog.exec_() except Exception, ex: logging.error( str( ex ) ) return