class ProjectsWidget(Ui_ListModules, QWidget): requestOpenProject = pyqtSignal(object) def __init__(self, parent = None): super(ProjectsWidget, self).__init__(parent) self.setupUi(self) self.flickcharm = FlickCharm() self.flickcharm.activateOn(self.moduleList) self.moduleList.itemClicked.connect(self.openProject) def loadProjectList(self, projects): self.moduleList.clear() for project in projects: if not project.valid: roam.utils.warning("Project {} is invalid because {}".format(project.name, project.error)) continue item = QListWidgetItem(self.moduleList, QListWidgetItem.UserType) item.setData(QListWidgetItem.UserType, project) item.setSizeHint(QSize(150, 150)) projectwidget = ProjectWidget(self.moduleList) projectwidget.image = QPixmap(project.splash) projectwidget.name = project.name projectwidget.description = project.description projectwidget.version = project.version self.moduleList.addItem(item) self.moduleList.setItemWidget(item, projectwidget) def openProject(self, item): # self.setDisabled(True) project = item.data(QListWidgetItem.UserType) self.selectedProject = project self.requestOpenProject.emit(project)
class HelpPage(helppage_widget, helppage_base): def __init__(self, parent=None): super(HelpPage, self).__init__(parent) self.setupUi(self) if self.parent(): self.parent().installEventFilter(self) self.charm = FlickCharm() self.charm.activateOn(self.webView) self.webView.linkClicked.connect(RoamEvents.openurl.emit) self.webView.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) def eventFilter(self, object, event): if event.type() == QEvent.Resize: self.setsize() elif event.type() == QEvent.MouseButtonPress: self.close() return object.eventFilter(object, event) def setsize(self): width = self.parent().width() * 50 / 100 self.resize(width, self.parent().height()) self.move(self.parent().width() - self.width() -1, 1) def show(self): super(HelpPage, self).show() self.setsize() self.setFocus(Qt.PopupFocusReason) def setHelpPage(self, helppath): self.webView.load(QUrl.fromLocalFile(helppath))
class HelpPage(helppage_widget, helppage_base): def __init__(self, parent=None): super(HelpPage, self).__init__(parent) self.setupUi(self) if self.parent(): self.parent().installEventFilter(self) self.charm = FlickCharm() self.charm.activateOn(self.webView) self.webView.linkClicked.connect(RoamEvents.openurl.emit) self.webView.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) def eventFilter(self, object, event): if event.type() == QEvent.Resize: self.setsize() elif event.type() == QEvent.MouseButtonPress: self.close() return object.eventFilter(object, event) def setsize(self): width = self.parent().width() * 50 / 100 self.resize(width, self.parent().height()) self.move(self.parent().width() - self.width() - 1, 1) def show(self): super(HelpPage, self).show() self.setsize() self.setFocus(Qt.PopupFocusReason) def setHelpPage(self, helppath): self.webView.load(QUrl.fromLocalFile(helppath))
class ProjectsWidget(modules_widget, modules_base): requestOpenProject = pyqtSignal(object) def __init__(self, parent = None): super(ProjectsWidget, self).__init__(parent) self.setupUi(self) self.flickcharm = FlickCharm() self.flickcharm.activateOn(self.moduleList) self.moduleList.itemClicked.connect(self.openProject) def loadProjectList(self, projects): self.moduleList.clear() for project in projects: if not project.valid: continue item = QListWidgetItem(self.moduleList, QListWidgetItem.UserType) item.setData(QListWidgetItem.UserType, project) item.setSizeHint(QSize(150, 150)) projectwidget = ProjectWidget(self.moduleList) projectwidget.image = QPixmap(project.splash) projectwidget.name = project.name projectwidget.description = project.description projectwidget.version = project.version self.moduleList.addItem(item) self.moduleList.setItemWidget(item, projectwidget) def openProject(self, item): # self.setDisabled(True) project = item.data(QListWidgetItem.UserType) self.selectedProject = project self.requestOpenProject.emit(project)
class BigList(Ui_BigList, QWidget): itemselected = pyqtSignal(QModelIndex) closewidget = pyqtSignal() def __init__(self, parent=None): super(BigList, self).__init__(parent) self.setupUi(self) self.listView.clicked.connect(self.selected) self.closebutton.pressed.connect(self.closewidget.emit) self._index = None self.charm = FlickCharm() self.charm.activateOn(self.listView) def selected(self, index): self._index = index self.itemselected.emit(index) def setmodel(self, model): self.listView.setModel(model) def setlabel(self, fieldname): self.fieldnameLabel.setText(fieldname) def currentindex(self): return self._index def setcurrentindex(self, index): if index is None: index = QModelIndex() if isinstance(index, int): index = self.listView.model().index(index, 0) self.listView.setCurrentIndex(index)
class BigList(Ui_BigList, QWidget): itemselected = pyqtSignal(QModelIndex) closewidget = pyqtSignal() savewidget = pyqtSignal() def __init__(self, parent=None, centeronparent=False, showsave=True): super(BigList, self).__init__(parent) self.setupUi(self) self.centeronparent = centeronparent self.listView.clicked.connect(self.selected) self.saveButton.pressed.connect(self.savewidget.emit) self.closebutton.pressed.connect(self.closewidget.emit) self._index = None self.search.textEdited.connect(self.set_filter) self.filtermodel = QSortFilterProxyModel() self.filtermodel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.listView.setModel(self.filtermodel) self.listView.setWordWrap(True) self.charm = FlickCharm() self.charm.activateOn(self.listView) self.saveButton.setVisible(showsave) def set_filter(self, text): self.filtermodel.setFilterRegExp(text + ".*") def selected(self, index): self._index = index self._index = self.filtermodel.mapToSource(index) self.itemselected.emit(self._index) def setmodel(self, model): self.filtermodel.setSourceModel(model) def setlabel(self, fieldname): self.fieldnameLabel.setText(fieldname) def currentindex(self): return self._index def setcurrentindex(self, index): if index is None: index = QModelIndex() if isinstance(index, int): index = self.listView.model().index(index, 0) self.listView.setCurrentIndex(index) def show(self): super(BigList, self).show() if self.centeronparent: width = self.parent().width() height = self.parent().height() self.move(width / 4, 0) self.resize(QSize(width /2, height))
class BigList(Ui_BigList, QWidget): itemselected = pyqtSignal(QModelIndex) closewidget = pyqtSignal() savewidget = pyqtSignal() def __init__(self, parent=None, centeronparent=False, showsave=True): super(BigList, self).__init__(parent) self.setupUi(self) self.centeronparent = centeronparent self.listView.clicked.connect(self.selected) self.saveButton.pressed.connect(self.savewidget.emit) self.closebutton.pressed.connect(self.closewidget.emit) self._index = None self.search.textEdited.connect(self.set_filter) self.filtermodel = QSortFilterProxyModel() self.filtermodel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.listView.setModel(self.filtermodel) self.listView.setWordWrap(True) self.charm = FlickCharm() self.charm.activateOn(self.listView) self.saveButton.setVisible(showsave) def set_filter(self, text): self.filtermodel.setFilterRegExp(text + ".*") def selected(self, index): self._index = index self._index = self.filtermodel.mapToSource(index) self.itemselected.emit(self._index) def setmodel(self, model): self.filtermodel.setSourceModel(model) def setlabel(self, fieldname): self.fieldnameLabel.setText(fieldname) def currentindex(self): return self._index def setcurrentindex(self, index): if index is None: index = QModelIndex() if isinstance(index, int): index = self.listView.model().index(index, 0) self.listView.setCurrentIndex(index) def show(self): super(BigList, self).show() if self.centeronparent: width = self.parent().width() height = self.parent().height() self.move(width / 4, 0) self.resize(QSize(width / 2, height))
class ProjectsWidget(Ui_ListModules, QWidget): requestOpenProject = pyqtSignal(object) def __init__(self, parent = None): super(ProjectsWidget, self).__init__(parent) self.setupUi(self) self.flickcharm = FlickCharm() self.flickcharm.activateOn(self.moduleList) self.moduleList.itemClicked.connect(self.openProject) self.projectitems = {} def loadProjectList(self, projects): self.moduleList.clear() self.projectitems.clear() for project in projects: if not project.valid: roam.utils.warning("Project {} is invalid because {}".format(project.name, project.error)) continue item = QListWidgetItem(self.moduleList, QListWidgetItem.UserType) item.setData(QListWidgetItem.UserType, project) item.setSizeHint(QSize(150, 150)) projectwidget = ProjectWidget(project, self.moduleList) projectwidget.image = QPixmap(project.splash) projectwidget.name = project.name projectwidget.description = project.description projectwidget.version = project.version self.moduleList.addItem(item) self.moduleList.setItemWidget(item, projectwidget) self.projectitems[project] = projectwidget def openProject(self, item): # self.setDisabled(True) project = item.data(QListWidgetItem.UserType) self.selectedProject = project self.requestOpenProject.emit(project) self.set_open_project(project) def set_open_project(self, currentproject): for project, widget in self.projectitems.iteritems(): if currentproject: showclose = currentproject == project else: showclose = False widget.show_close(showclose)
class BigList(Ui_BigList, QWidget): itemselected = pyqtSignal(QModelIndex) closewidget = pyqtSignal() savewidget = pyqtSignal() def __init__(self, parent=None): super(BigList, self).__init__(parent) self.setupUi(self) self.listView.clicked.connect(self.selected) self.saveButton.pressed.connect(self.savewidget.emit) self.closebutton.pressed.connect(self.closewidget.emit) self._index = None self.search.textEdited.connect(self.set_filter) self.filtermodel = QSortFilterProxyModel() self.filtermodel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.listView.setModel(self.filtermodel) self.charm = FlickCharm() self.charm.activateOn(self.listView) def set_filter(self, text): self.filtermodel.setFilterRegExp(text + ".*") def selected(self, index): self._index = index self._index = self.filtermodel.mapToSource(index) self.itemselected.emit(self._index) def setmodel(self, model): self.filtermodel.setSourceModel(model) def setlabel(self, fieldname): self.fieldnameLabel.setText(fieldname) def currentindex(self): return self._index def setcurrentindex(self, index): if index is None: index = QModelIndex() if isinstance(index, int): index = self.listView.model().index(index, 0) self.listView.setCurrentIndex(index)
class BigList(Ui_BigList, QWidget): itemselected = pyqtSignal(QModelIndex) def __init__(self, parent=None): super(BigList, self).__init__(parent) self.setupUi(self) self.listView.clicked.connect(self.itemselected) self.charm = FlickCharm() self.charm.activateOn(self.listView) def setmodel(self, model): self.listView.setModel(model) def setlabel(self, fieldname): self.fieldnameLabel.setText(fieldname) def show(self): super(BigList, self).show() width = self.parent().width() height = self.parent().height() self.move(width / 4, 0) self.resize(QSize(width / 2, height))
class FeatureFormWidget(Ui_Form, QWidget): # Raise the cancel event, takes a reason and a level cancel = pyqtSignal(str, int) featuresaved = pyqtSignal() featuredeleted = pyqtSignal() def __init__(self, parent=None): super(FeatureFormWidget, self).__init__(parent) self.setupUi(self) toolbar = QToolBar() size = QSize(48, 48) toolbar.setIconSize(size) style = Qt.ToolButtonTextUnderIcon toolbar.setToolButtonStyle(style) self.actionDelete = toolbar.addAction(QIcon(":/icons/delete"), "Delete") self.actionDelete.triggered.connect(self.delete_feature) label = 'Required fields marked in <b style="background-color:rgba(255, 221, 48,150)">yellow</b>' self.missingfieldsLabel = QLabel(label) self.missingfieldsLabel.hide() self.missingfieldaction = toolbar.addWidget(self.missingfieldsLabel) titlespacer = QWidget() titlespacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) toolbar.addWidget(titlespacer) self.titlellabel = QLabel(label) self.titlellabel.setProperty("headerlabel", True) self.titlelabelaction = toolbar.addWidget(self.titlellabel) spacer = QWidget() spacer2 = QWidget() spacer2.setMinimumWidth(20) spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) spacer2.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) toolbar.addWidget(spacer) self.actionCancel = toolbar.addAction(QIcon(":/icons/cancel"), "Cancel") toolbar.addWidget(spacer2) self.actionSave = toolbar.addAction(QIcon(":/icons/save"), "Save") self.actionSave.triggered.connect(self.save_feature) self.layout().insertWidget(0, toolbar) self.flickwidget = FlickCharm() self.flickwidget.activateOn(self.scrollArea) self.featureform = None self.values = {} self.config = {} self.feature = None def set_featureform(self, featureform): """ Note: There can only be one feature form. If you need to show another one make a new FeatureFormWidget """ self.featureform = featureform self.titlellabel.setText(self.featureform.windowTitle()) self.featureform.formvalidation.connect(self._update_validation) self.featureform.helprequest.connect(functools.partial(RoamEvents.helprequest.emit, self)) self.featureform.showlargewidget.connect(RoamEvents.show_widget.emit) self.featureform.enablesave.connect(self.actionSave.setEnabled) self.featureform.enablesave.connect(self.actionSave.setVisible) self.featureform.rejected.connect(self.cancel.emit) self.featureformarea.layout().addWidget(self.featureform) def delete_feature(self): try: msg = self.featureform.deletemessage except AttributeError: msg = 'Do you really want to delete this feature?' box = DeleteFeatureDialog(msg) if not box.exec_(): return try: self.featureform.delete() except featureform.DeleteFeatureException as ex: RoamEvents.raisemessage(*ex.error) self.featureform.featuredeleted(self.feature) self.featuredeleted.emit() def save_feature(self): try: self.featureform.save() except featureform.MissingValuesException as ex: RoamEvents.raisemessage(*ex.error) return except featureform.FeatureSaveException as ex: RoamEvents.raisemessage(*ex.error) self.featuresaved.emit() RoamEvents.featuresaved.emit() def set_config(self, config): self.config = config editmode = config['editmode'] allowsave = config.get('allowsave', True) self.feature = config.get('feature', None) self.featureform.feature = self.feature self.featureform.editingmode = editmode self.actionDelete.setVisible(editmode) self.actionSave.setEnabled(allowsave) def _update_validation(self, passed): # Show the error if there is missing fields self.missingfieldaction.setVisible(not passed) def bind_values(self, values): self.values = values self.featureform.bindvalues(values) def after_load(self): self.featureform.loaded() def before_load(self): self.featureform.load(self.config['feature'], self.config['layers'], self.values)
class FeatureFormWidget(Ui_Form, QWidget): # Raise the cancel event, takes a reason and a level canceled = pyqtSignal(str, int) featuresaved = pyqtSignal() featuredeleted = pyqtSignal() def __init__(self, parent=None): super(FeatureFormWidget, self).__init__(parent) self.setupUi(self) toolbar = QToolBar() size = QSize(48, 48) toolbar.setIconSize(size) style = Qt.ToolButtonTextUnderIcon toolbar.setToolButtonStyle(style) self.actionDelete = toolbar.addAction(QIcon(":/icons/delete"), "Delete") self.actionDelete.triggered.connect(self.delete_feature) label = '<b style="color:red">*</b> Required fields' self.missingfieldsLabel = QLabel(label) self.missingfieldsLabel.hide() self.missingfieldaction = toolbar.addWidget(self.missingfieldsLabel) titlespacer = QWidget() titlespacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) toolbar.addWidget(titlespacer) self.titlellabel = QLabel(label) self.titlellabel.setProperty("headerlabel", True) self.titlelabelaction = toolbar.addWidget(self.titlellabel) spacer = QWidget() spacer2 = QWidget() spacer2.setMinimumWidth(40) spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) spacer2.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) toolbar.addWidget(spacer) self.actionCancel = toolbar.addAction(QIcon(":/icons/cancel"), "Cancel") toolbar.addWidget(spacer2) self.actionSave = toolbar.addAction(QIcon(":/icons/save"), "Save") self.actionSave.triggered.connect(self.save_feature) self.layout().setContentsMargins(0, 3, 0, 3) self.layout().insertWidget(0, toolbar) self.actiontoolbar = QToolBar() self.actiontoolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.actiontoolbar.addWidget(spacer) self.layout().insertWidget(1, self.actiontoolbar) self.flickwidget = FlickCharm() self.flickwidget.activateOn(self.scrollArea) self.featureform = None self.values = {} self.config = {} self.feature = None def set_featureform(self, featureform): """ Note: There can only be one feature form. If you need to show another one make a new FeatureFormWidget """ self.featureform = featureform self.titlellabel.setText(self.featureform.windowTitle()) self.featureform.formvalidation.connect(self._update_validation) self.featureform.helprequest.connect( functools.partial(RoamEvents.helprequest.emit, self)) self.featureform.showlargewidget.connect(RoamEvents.show_widget.emit) self.featureform.enablesave.connect(self.actionSave.setEnabled) self.featureform.enablesave.connect(self.actionSave.setVisible) self.featureform.rejected.connect(self.canceled.emit) self.featureform.accepted.connect(self.featuresaved) actions = self.featureform.form_actions() if actions: for action in actions: self.actiontoolbar.addAction(action) else: self.actiontoolbar.hide() self.featureform.setContentsMargins(0, 0, 0, 0) self.featureformarea.layout().addWidget(self.featureform) def delete_feature(self): try: msg = self.featureform.deletemessage except AttributeError: msg = 'Do you really want to delete this feature?' box = DeleteFeatureDialog(msg) if not box.exec_(): return try: self.featureform.delete() except featureform.DeleteFeatureException as ex: RoamEvents.raisemessage(*ex.error) self.featureform.featuredeleted(self.feature) self.featuredeleted.emit() def feature_saved(self): self.featuresaved.emit() RoamEvents.featuresaved.emit() def save_feature(self): try: self.featureform.save() except featureform.MissingValuesException as ex: RoamEvents.raisemessage(*ex.error) return except featureform.FeatureSaveException as ex: RoamEvents.raisemessage(*ex.error) self.feature_saved() def set_config(self, config): self.config = config editmode = config['editmode'] allowsave = config.get('allowsave', True) self.feature = config.get('feature', None) tools = config.get('tools', []) candelete = True if tools: candelete = "delete" in tools self.featureform.feature = self.feature self.featureform.editingmode = editmode self.actionDelete.setVisible(editmode and candelete) self.actionSave.setEnabled(allowsave) def _update_validation(self, passed): # Show the error if there is missing fields self.missingfieldaction.setVisible(not passed) def bind_values(self, values): self.values = values self.featureform.bindvalues(values) def after_load(self): self.featureform.loaded() def before_load(self): self.featureform.load(self.config['feature'], self.config['layers'], self.values)
class ProjectsWidget(Ui_ListModules, QWidget): requestOpenProject = pyqtSignal(object) projectUpdate = pyqtSignal(object, object) search_for_updates = pyqtSignal() projectInstall = pyqtSignal(dict) def __init__(self, parent=None): super(ProjectsWidget, self).__init__(parent) self.setupUi(self) self.serverurl = None self.flickcharm = FlickCharm() self.flickcharm.activateOn(self.moduleList) self.moduleList.itemClicked.connect(self.openProject) self.projectitems = {} self.project_base = None def showEvent(self, event): self.search_for_updates.emit() def loadProjectList(self, projects): self.moduleList.clear() self.projectitems.clear() for project in projects: if not project.valid: roam.utils.warning("Project {} is invalid because {}".format(project.name, project.error)) continue self.add_new_item(project.name, project, is_new=False) def show_new_updateable(self, updateprojects, newprojects): print updateprojects, newprojects for project, info in updateprojects: item = self.projectitems[project.name] widget = self.item_widget(item) widget.serverversion = info['version'] for info in newprojects: if info['name'] in self.projectitems: self.update_item(info['name'], info) else: self.add_new_item(info['name'], info, is_new=True) def update_item(self, name, info): item = self.projectitems[name] item.setData(QListWidgetItem.UserType, info) widget = self.moduleList.itemWidget(item) widget.project = info def add_new_item(self, name, project, is_new=False): item = QListWidgetItem(self.moduleList, QListWidgetItem.UserType) item.setData(QListWidgetItem.UserType, project) item.setSizeHint(QSize(150, 150)) projectwidget = ProjectWidget(self.moduleList, project, is_new=is_new) projectwidget.install_project.connect(self.projectInstall.emit) projectwidget.update_project.connect(self.projectUpdate.emit) self.moduleList.addItem(item) self.moduleList.setItemWidget(item, projectwidget) self.projectitems[name] = item def item_widget(self, item): return self.moduleList.itemWidget(item) def openProject(self, item): # self.setDisabled(True) project = self.item_widget(item).project if isinstance(project, dict): return self.selectedProject = project self.requestOpenProject.emit(project) self.set_open_project(project) def set_open_project(self, currentproject): for project, item in self.projectitems.iteritems(): widget = self.item_widget(item) project = widget.project if currentproject and not isinstance(project, dict): showclose = currentproject.basefolder == project.basefolder else: showclose = False widget.show_close(showclose) def project_installed(self, projectname): project = roam.project.Project.from_folder(os.path.join(self.project_base, projectname)) item = self.projectitems[project.basefolder] widget = self.item_widget(item) widget.project = project widget.is_new = False widget.update_status("installed") def update_project_status(self, projectname, status): item = self.projectitems[projectname] widget = self.item_widget(item) widget.update_status(status)
class InfoDock(infodock_widget, QWidget): featureupdated = pyqtSignal(object, object, list) resultscleared = pyqtSignal() def __init__(self, parent): super(InfoDock, self).__init__(parent) self.setupUi(self) self.forms = {} self.charm = FlickCharm() self.charm.activateOn(self.attributesView) self.layerList.currentRowChanged.connect(self.layerIndexChanged) self.attributesView.linkClicked.connect(self.handle_link) self.attributesView.page().setLinkDelegationPolicy( QWebPage.DelegateAllLinks) action = self.attributesView.pageAction(QWebPage.Copy) action.setShortcut(QKeySequence.Copy) self.grabGesture(Qt.SwipeGesture) self.setAttribute(Qt.WA_AcceptTouchEvents) self.editButton.pressed.connect(self.openform) self.editGeomButton.pressed.connect(self.editgeom) self.parent().installEventFilter(self) self.project = None self.startwidth = self.width() self.expaned = False self.layerList.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.expandAction = QAction(QIcon(":/icons/expand"), "Expand Panel", self) self.expandAction.triggered.connect(self.change_expanded_state) self.navigateAction = QAction(QIcon(":/icons/navigate"), "Navigate To..", self) self.navigateAction.triggered.connect(self._navigate_to_selection) self.moreActionsButton.pressed.connect(self._show_more_actions) self.navwidget.mousePressEvent = self._sink self.bottomWidget.mousePressEvent = self._sink self.navwidget.mouseReleaseEvent = self._sink self.bottomWidget.mouseReleaseEvent = self._sink self.navwidget.mouseMoveEvent = self._sink self.bottomWidget.mouseMoveEvent = self._sink self.deleteFeatureButton.pressed.connect(self.delete_feature) self.deleteFeatureButton.setCheckable(False) self.quickInspectButton.hide() self.quickInspectButton.pressed.connect(self.quick_inspect) self.nextButton.pressed.connect(self.pagenext) self.prevButton.pressed.connect(self.pageback) RoamEvents.selectioncleared.connect(self.clearResults) RoamEvents.editgeometry_complete.connect(self.refreshcurrent) def _navigate_to_selection(self): feature = self.selection.feature geom = feature.geometry() point = geom.centroid().asPoint() if GPS.waypoint == point: GPS.waypoint = None else: GPS.waypoint = point def _show_more_actions(self): dlg = PickActionDialog() self.navigateAction.setEnabled(GPS.isConnected) if not GPS.isConnected: self.navigateAction.setText("Navigate To.. (No GPS)") else: self.navigateAction.setText("Navigate To..") dlg.addactions([self.expandAction, self.navigateAction]) dlg.exec_() def delete_feature(self): cursor = self.selection RoamEvents.delete_feature(cursor.form, cursor.feature) def handle_link(self, url): if url.toString().endswith("/back"): self.pageback() elif url.toString().endswith("/next"): self.pagenext() else: RoamEvents.openurl.emit(url) def _sink(self, event): return def change_expanded_state(self): if self.expaned: self._collapse() else: self._expand() def mousePressEvent(self, event): pos = self.mapToParent(event.pos()) newevent = QMouseEvent(event.type(), pos, event.button(), event.buttons(), event.modifiers()) self.parent().mousePressEvent(newevent) def mouseReleaseEvent(self, event): pos = self.mapToParent(event.pos()) newevent = QMouseEvent(event.type(), pos, event.button(), event.buttons(), event.modifiers()) self.parent().mouseReleaseEvent(newevent) def mouseMoveEvent(self, event): pos = self.mapToParent(event.pos()) newevent = QMouseEvent(event.type(), pos, event.button(), event.buttons(), event.modifiers()) self.parent().mouseMoveEvent(newevent) def _expand(self): self.resize(self.parent().width() - 10, self.parent().height()) self.move(10, 0) self.expaned = True self.expandAction.setText("Collapse Panel") def _collapse(self): self.resize(self.startwidth, self.parent().height()) self.move(self.parent().width() - self.startwidth, 0) self.expaned = False self.expandAction.setText("Expand Panel") def eventFilter(self, object, event): if event.type() == QEvent.Resize: self._collapse() return super(InfoDock, self).eventFilter(object, event) def close(self): RoamEvents.selectioncleared.emit() super(InfoDock, self).close() def event(self, event): if event.type() == QEvent.Gesture: gesture = event.gesture(Qt.SwipeGesture) if gesture: self.pagenext() return QWidget.event(self, event) @property def selection(self): item = self.layerList.item(self.layerList.currentRow()) if not item: return cursor = item.data(Qt.UserRole) return cursor def openform(self): cursor = self.selection tools = self.project.layer_tools(cursor.layer) if 'inspection' in tools: config = tools['inspection'] form, feature = self.get_inspection_config(cursor.feature, config) editmode = False else: form = cursor.form feature = cursor.feature editmode = True RoamEvents.load_feature_form(form, feature, editmode) def quick_inspect(self): cursor = self.selection tools = self.project.layer_tools(cursor.layer) config = tools['inspection'] form, feature = self.get_inspection_config(cursor.feature, config) editmode = False form.suppressform = True RoamEvents.load_feature_form(form, feature, editmode) # Leaking state is leaking. But this is what we have for now. form.suppressform = False def get_inspection_config(self, current_feature, config): form = config['form'] newform = self.project.form_by_name(form) if config.get('mode', "copy").lower() == 'copy': geom = current_feature.geometry() mappings = config.get('field_mapping', {}) data = {} for fieldfrom, fieldto in mappings.iteritems(): data[fieldto] = current_feature[fieldfrom] newgeom = QgsGeometry(geom) newfeature = newform.new_feature(geometry=newgeom, data=data) return newform, newfeature else: raise NotImplementedError("Only copy mode supported currently") def editgeom(self): cursor = self.selection RoamEvents.editgeometry.emit(cursor.form, cursor.feature) self.editGeomButton.setEnabled(False) self.deleteFeatureButton.setEnabled(False) def pageback(self): cursor = self.selection cursor.back() self.update(cursor) def pagenext(self): cursor = self.selection cursor.next() self.update(cursor) def layerIndexChanged(self, index): item = self.layerList.item(index) if not item: return cursor = item.data(Qt.UserRole) self.update(cursor) def setResults(self, results, forms, project): lastrow = self.layerList.currentRow() if lastrow == -1: lastrow = 0 self.clearResults() self.forms = forms self.project = project for layer, features in results.iteritems(): if features: self._addResult(layer, features) self.layerList.setCurrentRow(lastrow) self.layerList.setMinimumWidth( self.layerList.sizeHintForColumn(0) + 20) size = 0 for n in range(self.layerList.count()): size += self.layerList.sizeHintForRow(n) self.layerList.setMinimumHeight(size) self.layerList.setMaximumHeight(size) self.navwidget.show() def show(self): if self.layerList.count() > 0: super(InfoDock, self).show() else: self.hide() def _addResult(self, layer, features): layername = layer.name() forms = self.forms.get(layername, []) if not forms: item = QListWidgetItem(QIcon(), layername, self.layerList) item.setData(Qt.UserRole, FeatureCursor(layer, features)) return for form in forms: selectname = self.project.selectlayer_name(form.layername) if selectname == layername: itemtext = "{} \n ({})".format(layername, form.label) else: itemtext = selectname icon = QIcon(form.icon) item = QListWidgetItem(icon, itemtext, self.layerList) item.setData(Qt.UserRole, FeatureCursor(layer, features, form)) def refreshcurrent(self): self.update(self.selection) def update(self, cursor): if cursor is None: return try: feature = cursor.feature except NoFeature as ex: utils.exception(ex) return form = cursor.form layer = cursor.layer clear_image_cache() self.countLabel.setText(str(cursor)) info1, results = self.generate_info("info1", self.project, layer, feature.id(), feature, countlabel=str(cursor)) info2, _ = self.generate_info("info2", self.project, layer, feature.id(), feature, lastresults=results[0]) if form: name = "{}".format(layer.name(), form.label) else: name = layer.name() info = dict(TITLE=name, INFO1=info1, INFO2=info2) html = updateTemplate(info, infotemplate) self.attributesView.setHtml(html, templates.baseurl) tools = self.project.layer_tools(layer) hasform = not form is None print tools editattributes = 'edit_attributes' in tools or 'inspection' in tools or hasform print editattributes editgeom = 'edit_geom' in tools and hasform deletefeature = 'delete' in tools and hasform self.deleteFeatureButton.setVisible(deletefeature) self.quickInspectButton.setVisible('inspection' in tools) self.editButton.setVisible(editattributes) self.editGeomButton.setVisible(editgeom) self.featureupdated.emit(layer, feature, cursor.features) def generate_info(self, infoblock, project, layer, mapkey, feature, countlabel=None, lastresults=None): infoblockdef = project.info_query(infoblock, layer.name()) isinfo1 = infoblock == "info1" if not infoblockdef: if isinfo1: infoblockdef = {} infoblockdef['type'] = 'feature' else: return None, [] if isinfo1: caption = infoblockdef.get('caption', "Record") else: caption = infoblockdef.get('caption', "Related Record") results = [] error = None infotype = infoblockdef.get('type', 'feature') if infotype == 'sql': try: queryresults = self.results_from_query(infoblockdef, layer, feature, mapkey, lastresults=lastresults) if isinfo1 and not queryresults: # If there is no results from the query and we are a info 1 block we grab from the feature. results.append(self.results_from_feature(feature)) else: results = queryresults except database.DatabaseException as ex: RoamEvents.raisemessage("Query Error", ex.message, 3) utils.error(ex.message) if not isinfo1: error = "<b> Error: {} <b>".format(ex.msg) else: results.append(self.results_from_feature(feature)) elif infotype == 'feature': featuredata = self.results_from_feature(feature) excludedfields = infoblockdef.get('hidden', []) for field in excludedfields: try: del featuredata[field] except KeyError: pass results.append(featuredata) else: return None, [] blocks = [] for count, result in enumerate(results, start=1): if isinfo1 and count == 1: countblock = countblocktemplate.substitute(count=countlabel) else: countblock = '' fields = result.keys() attributes = result.values() rows = generate_rows(fields, attributes, imagepath=self.project.image_folder) try: caption = caption.format(**dict(zip(fields, attributes))) except KeyError: pass blocks.append( updateTemplate( dict(ROWS=rows, HEADER=caption, CONTROLS=countblock), infoblocktemplate)) if error: return error, [] return '<br>'.join(blocks), results def results_from_feature(self, feature): attributes = feature.attributes() fields = [field.name().lower() for field in feature.fields()] return OrderedDict(zip(fields, attributes)) def results_from_query(self, infoblockdef, layer, feature, mapkey, lastresults=None): def get_key(): try: keycolumn = infoblockdef['mapping']['mapkey'] if keycolumn == 'from_info1': if 'mapkey' in lastresults: return lastresults['mapkey'] else: return [] else: return feature[keycolumn] except KeyError: return mapkey def get_layer(): connection = infoblockdef.get('connection', "from_layer") if isinstance(connection, dict): return layer_by_name(connection['layer']) elif connection == "from_layer": return layer else: raise NotImplementedError( "{} is not a supported connection type".format(connection)) if not lastresults: lastresults = {} sql = infoblockdef['query'] layer = get_layer() db = database.Database.fromLayer(layer) mapkey = get_key() attributes = values_from_feature(feature, safe_names=True) attributes['mapkey'] = mapkey # Run the SQL text though the QGIS expression engine first. sql = QgsExpression.replaceExpressionText(sql, feature, layer) results = db.query(sql, **attributes) results = list(results) return results def clearResults(self): self.layerList.clear() self.attributesView.setHtml('') self.editButton.setVisible(False) self.editGeomButton.setEnabled(True) self.editButton.setEnabled(True) self.deleteFeatureButton.setEnabled(True) self.navwidget.hide()
class SearchPlugin(widget, base, Page): title = "Search" icon = resolve("search.svg") def __init__(self, api, parent=None): super(SearchPlugin, self).__init__(parent) self.setupUi(self) self.api = api self.project = None self.dbpath = None self.flickcharm = FlickCharm() self.flickcharm.activateOn(self.resultsView) self.searchbox.textChanged.connect(self.search) self.searchbox.installEventFilter(self) self.clearButton.pressed.connect(self.searchbox.clear) self.resultsView.itemClicked.connect(self.jump_to) self.rebuildLabel.linkActivated.connect(self.rebuild_index) self.fuzzyCheck.stateChanged.connect(self.fuzzy_changed) self.indexbuilder = None self.indexthread = None def fuzzy_changed(self, state): self.search(self.searchbox.text()) def index_built(self, dbpath, timing): self.dbpath = dbpath self.resultsView.clear() self.searchbox.setEnabled(True) print "Index built in: {} seconds".format(timing) def eventFilter(self, object, event): if event.type() == QEvent.FocusIn: RoamEvents.openkeyboard.emit() return False @property def db(self): db = sqlite3.connect(self.dbpath) db.create_function("rank", 1, make_rank_func((1., .1, 0, 0))) return db def project_loaded(self, project): self.project = project self.build_index(project) def rebuild_index(self): self.build_index(self.project) def build_index(self, project): self.searchbox.setEnabled(False) self.resultsView.setEnabled(False) self.resultsView.addItem("building search index...") validformat, settings = valid_search_settings(project.settings) if not validformat: RoamEvents.raisemessage("Searching", "Invalid search config.", level=1) self.searchbox.hide() self.resultsView.clear() self.resultsView.addItem("Invalid search config found") return self.indexthread = QThread() path = os.path.join(os.environ['APPDATA'], "roam", project.name) roam.utils.info("Search index path: {0}".format(path)) if not os.path.exists(path): os.makedirs(path) self.indexbuilder = IndexBuilder(path, settings) self.indexbuilder.moveToThread(self.indexthread) QgsMapLayerRegistry.instance().removeAll.connect(self.indexthread.quit) self.indexbuilder.indexBuilt.connect(self.index_built) self.indexbuilder.finished.connect(self.indexthread.quit) self.indexthread.started.connect(self.indexbuilder.build_index) self.indexthread.finished.connect(self.indexbuilder.quit) self.indexthread.start() def search(self, text): db = self.db c = db.cursor() self.resultsView.clear() self.resultsView.setEnabled(False) if not text: return if self.fuzzyCheck.isChecked(): search = "* ".join(text.split()) + "*" else: search = text query = c.execute("""SELECT layer, featureid, snippet(search, '[',']') as snippet FROM search JOIN featureinfo on search.docid = featureinfo.id WHERE search match '{}' LIMIT 100""".format(search)).fetchall() for layer, featureid, snippet in query: item = QListWidgetItem() text = "{}\n {}".format(layer, snippet.replace('\n', ' ')) item.setText(text) item.setData(Qt.UserRole, (layer, featureid, snippet)) self.resultsView.addItem(item) self.resultsView.setEnabled(True) if self.resultsView.count() == 0: self.resultsView.addItem("No Results") self.resultsView.setEnabled(False) db.close() def jump_to(self, item): data = item.data(32) if not data: return layername, fid = data[0], data[1] layer = roam.api.utils.layer_by_name(layername) feature = layer.getFeatures(QgsFeatureRequest(fid)).next() 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) box.grow(20) self.api.mainwindow.canvas.setExtent(box) self.api.mainwindow.canvas.refresh() self.api.mainwindow.showmap() RoamEvents.selectionchanged.emit({layer: [feature]})
class ProjectsWidget(Ui_ListModules, QWidget): requestOpenProject = pyqtSignal(object) projectUpdate = pyqtSignal(object) search_for_updates = pyqtSignal() projectInstall = pyqtSignal(dict) def __init__(self, parent=None): super(ProjectsWidget, self).__init__(parent) self.setupUi(self) self.serverurl = None self.flickcharm = FlickCharm() self.flickcharm.activateOn(self.moduleList) self.moduleList.itemClicked.connect(self.openProject) self.projectitems = {} self.project_base = None self.progressFrame.hide() def showEvent(self, event): self.search_for_updates.emit() def loadProjectList(self, projects): self.moduleList.clear() self.projectitems.clear() for project in projects: if not project.valid: roam.utils.warning("Project {0} is invalid because {1}".format(project.name, project.error)) self.add_new_item(project.id, project, is_new=False, is_valid=project.valid) def show_new_updateable(self, updateprojects, newprojects): print updateprojects, newprojects for info in updateprojects: projectid = info['projectid'] item = self.projectitems[projectid] widget = self.item_widget(item) widget.serverversion = info['version'] widget.serverinfo = info for info in newprojects: projectid = info['projectid'] if info['name'] in self.projectitems: self.update_item(projectid, info) else: self.add_new_item(projectid, info, is_new=True) def update_item(self, projectid, info): item = self.projectitems[projectid] item.setData(QListWidgetItem.UserType, info) widget = self.moduleList.itemWidget(item) widget.project = info widget.serverinfo = info def add_new_item(self, projectid, project, is_new=False, is_valid=True): item = QListWidgetItem(self.moduleList, QListWidgetItem.UserType) item.setData(QListWidgetItem.UserType, project) item.setSizeHint(QSize(150, 150)) if not is_valid: item.setFlags(item.flags() & ~Qt.ItemIsEnabled) projectwidget = ProjectWidget(self.moduleList, project, is_new=is_new) projectwidget.install_project.connect(self.projectInstall.emit) projectwidget.update_project.connect(self.projectUpdate.emit) projectwidget.setEnabled(is_valid) projectwidget.serverinfo = project self.moduleList.addItem(item) self.moduleList.setItemWidget(item, projectwidget) self.projectitems[projectid] = item def item_widget(self, item): return self.moduleList.itemWidget(item) def openProject(self, item): # self.setDisabled(True) project = self.item_widget(item).project if isinstance(project, dict): return if not project.valid: return self.selectedProject = project self.requestOpenProject.emit(project) self.set_open_project(project) def set_open_project(self, currentproject): for projectid, item in self.projectitems.iteritems(): widget = self.item_widget(item) if widget.is_new: continue project = widget.project if currentproject is None: widget.show_close(False) else: widget.show_close(currentproject.id == project.id) def project_installed(self, projectname): project = roam.project.Project.from_folder(os.path.join(self.project_base, projectname)) item = self.projectitems[project.basefolder] widget = self.item_widget(item) widget.project = project widget.is_new = False widget.update_status("installed") def update_project_status(self, projectname, status): if status.lower() in ["complete", 'installed']: self.progressFrame.hide() else: self.progressFrame.show() self.statusLabel.setText("{}: {}".format(projectname, status)) try: item = self.projectitems[projectname] widget = self.item_widget(item) widget.update_status(status) except KeyError: pass
class InfoDock(infodock_widget, QWidget): featureupdated = pyqtSignal(object, object, list) resultscleared = pyqtSignal() def __init__(self, parent): super(InfoDock, self).__init__(parent) self.setupUi(self) self.forms = {} self.charm = FlickCharm() self.charm.activateOn(self.attributesView) self.layerList.currentRowChanged.connect(self.layerIndexChanged) self.attributesView.linkClicked.connect(self.handle_link) self.attributesView.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) action = self.attributesView.pageAction(QWebPage.Copy) action.setShortcut(QKeySequence.Copy) self.grabGesture(Qt.SwipeGesture) self.setAttribute(Qt.WA_AcceptTouchEvents) self.editButton.pressed.connect(self.openform) self.editGeomButton.pressed.connect(self.editgeom) self.parent().installEventFilter(self) self.project = None self.startwidth = self.width() self.expaned = False self.layerList.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.expandAction = QAction(QIcon(":/icons/expand"), "Expand Panel", self) self.expandAction.triggered.connect(self.change_expanded_state) self.navigateAction = QAction(QIcon(":/icons/navigate"), "Navigate To..", self) self.navigateAction.triggered.connect(self._navigate_to_selection) self.moreActionsButton.pressed.connect(self._show_more_actions) self.navwidget.mousePressEvent = self._sink self.bottomWidget.mousePressEvent = self._sink self.navwidget.mouseReleaseEvent = self._sink self.bottomWidget.mouseReleaseEvent = self._sink self.navwidget.mouseMoveEvent = self._sink self.bottomWidget.mouseMoveEvent = self._sink self.deleteFeatureButton.pressed.connect(self.delete_feature) self.deleteFeatureButton.setCheckable(False) self.nextButton.pressed.connect(self.pagenext) self.prevButton.pressed.connect(self.pageback) RoamEvents.selectioncleared.connect(self.clearResults) RoamEvents.editgeometry_complete.connect(self.refreshcurrent) def _navigate_to_selection(self): feature = self.selection.feature geom = feature.geometry() point = geom.centroid().asPoint() if GPS.waypoint == point: GPS.waypoint = None else: GPS.waypoint = point def _show_more_actions(self): dlg = PickActionDialog() self.navigateAction.setEnabled(GPS.isConnected) if not GPS.isConnected: self.navigateAction.setText("Navigate To.. (No GPS)") else: self.navigateAction.setText("Navigate To..") dlg.addactions([self.expandAction, self.navigateAction]) dlg.exec_() def delete_feature(self): cursor = self.selection RoamEvents.delete_feature(cursor.form, cursor.feature) def handle_link(self, url): if url.toString().endswith("/back"): self.pageback() elif url.toString().endswith("/next"): self.pagenext() else: RoamEvents.openurl.emit(url) def _sink(self, event): return def change_expanded_state(self): if self.expaned: self._collapse() else: self._expand() def mousePressEvent(self, event): pos = self.mapToParent(event.pos()) newevent = QMouseEvent(event.type(), pos, event.button(), event.buttons(), event.modifiers()) self.parent().mousePressEvent(newevent) def mouseReleaseEvent(self, event): pos = self.mapToParent(event.pos()) newevent = QMouseEvent(event.type(), pos, event.button(), event.buttons(), event.modifiers()) self.parent().mouseReleaseEvent(newevent) def mouseMoveEvent(self, event): pos = self.mapToParent(event.pos()) newevent = QMouseEvent(event.type(), pos, event.button(), event.buttons(), event.modifiers()) self.parent().mouseMoveEvent(newevent) def _expand(self): self.resize(self.parent().width() - 10, self.parent().height()) self.move(10, 0) self.expaned = True def _collapse(self): self.resize(self.startwidth, self.parent().height()) self.move(self.parent().width() - self.startwidth, 0) self.expaned = False def eventFilter(self, object, event): if event.type() == QEvent.Resize: self._collapse() return super(InfoDock, self).eventFilter(object, event) def close(self): RoamEvents.selectioncleared.emit() super(InfoDock, self).close() def event(self, event): if event.type() == QEvent.Gesture: gesture = event.gesture(Qt.SwipeGesture) if gesture: self.pagenext() return QWidget.event(self, event) @property def selection(self): item = self.layerList.item(self.layerList.currentRow()) if not item: return cursor = item.data(Qt.UserRole) return cursor def openform(self): cursor = self.selection tools = self.project.layer_tools(cursor.layer) if 'inspection' in tools: config = tools['inspection'] form, feature = self.get_inspection_config(cursor.feature, config) editmode = False else: form = cursor.form feature = cursor.feature editmode = True RoamEvents.load_feature_form(form, feature, editmode) def get_inspection_config(self, current_feature, config): form = config['form'] newform = self.project.form_by_name(form) if config.get('mode', "copy").lower() == 'copy': geom = current_feature.geometry() newgeom = QgsGeometry(geom) newfeature = newform.new_feature(geometry=newgeom) mappings = config.get('field_mapping', {}) for fieldfrom, fieldto in mappings.iteritems(): newfeature[fieldto] = current_feature[fieldfrom] return newform, newfeature else: raise NotImplementedError("Only copy mode supported currently") def editgeom(self): cursor = self.selection RoamEvents.editgeometry.emit(cursor.form, cursor.feature) self.editGeomButton.setEnabled(False) self.deleteFeatureButton.setEnabled(False) def pageback(self): cursor = self.selection cursor.back() self.update(cursor) def pagenext(self): cursor = self.selection cursor.next() self.update(cursor) def layerIndexChanged(self, index): item = self.layerList.item(index) if not item: return cursor = item.data(Qt.UserRole) self.update(cursor) def setResults(self, results, forms, project): lastrow = self.layerList.currentRow() if lastrow == -1: lastrow = 0 self.clearResults() self.forms = forms self.project = project for layer, features in results.iteritems(): if features: self._addResult(layer, features) self.layerList.setCurrentRow(lastrow) self.layerList.setMinimumWidth(self.layerList.sizeHintForColumn(0) + 20) size = 0 for n in range(self.layerList.count()): size += self.layerList.sizeHintForRow(n) self.layerList.setMinimumHeight(size) self.layerList.setMaximumHeight(size) self.navwidget.show() def show(self): if self.layerList.count() > 0: super(InfoDock, self).show() else: self.hide() def _addResult(self, layer, features): layername = layer.name() forms = self.forms.get(layername, []) if not forms: item = QListWidgetItem(QIcon(), layername, self.layerList) item.setData(Qt.UserRole, FeatureCursor(layer, features)) return for form in forms: selectname = self.project.selectlayer_name(form.layername) if selectname == layername: itemtext = "{} \n ({})".format(layername, form.label) else: itemtext = selectname icon = QIcon(form.icon) item = QListWidgetItem(icon, itemtext, self.layerList) item.setData(Qt.UserRole, FeatureCursor(layer, features, form)) def refreshcurrent(self): self.update(self.selection) def update(self, cursor): if cursor is None: return try: feature = cursor.feature except NoFeature as ex: utils.exception(ex) return form = cursor.form layer = cursor.layer clear_image_cache() self.countLabel.setText(str(cursor)) info1, results = self.generate_info("info1", self.project, layer, feature.id(), feature, countlabel=str(cursor)) info2, _= self.generate_info("info2", self.project, layer, feature.id(), feature, lastresults=results[0]) if form: name = "{}".format(layer.name(), form.label) else: name = layer.name() info = dict(TITLE=name, INFO1=info1, INFO2=info2) html = updateTemplate(info, infotemplate) self.attributesView.setHtml(html, templates.baseurl) tools = self.project.layer_tools(layer) hasform = not form is None editattributes = 'edit_attributes' in tools or 'inspection' in tools and hasform editgeom = 'edit_geom' in tools and hasform deletefeature = 'delete' in tools and hasform self.deleteFeatureButton.setVisible(deletefeature) self.editButton.setVisible(editattributes) self.editGeomButton.setVisible(editgeom) self.featureupdated.emit(layer, feature, cursor.features) def generate_info(self, infoblock, project, layer, mapkey, feature, countlabel=None, lastresults=None): infoblockdef = project.info_query(infoblock, layer.name()) isinfo1 = infoblock == "info1" if not infoblockdef: if isinfo1: infoblockdef = {} infoblockdef['type'] = 'feature' else: return None, [] if isinfo1: caption = infoblockdef.get('caption', "Record") else: caption = infoblockdef.get('caption', "Related Record") results = [] error = None infotype = infoblockdef.get('type', 'feature') if infotype == 'sql': try: queryresults = self.results_from_query(infoblockdef, layer, feature, mapkey, lastresults=lastresults) if isinfo1 and not queryresults: # If there is no results from the query and we are a info 1 block we grab from the feature. results.append(self.results_from_feature(feature)) else: results = queryresults except database.DatabaseException as ex: if not isinfo1: error = "<b> Error: {} <b>".format(ex.msg) else: results.append(self.results_from_feature(feature)) elif infotype == 'feature': featuredata = self.results_from_feature(feature) excludedfields = infoblockdef.get('hidden', []) for field in excludedfields: try: del featuredata[field] except KeyError: pass results.append(featuredata) else: return None, [] blocks = [] for count, result in enumerate(results, start=1): if isinfo1 and count == 1: countblock = countblocktemplate.substitute(count=countlabel) else: countblock = '' fields = result.keys() attributes = result.values() rows = generate_rows(fields, attributes, imagepath=self.project.image_folder) try: caption = caption.format(**dict(zip(fields, attributes))) except KeyError: pass blocks.append(updateTemplate(dict(ROWS=rows, HEADER=caption, CONTROLS=countblock), infoblocktemplate)) if error: return error, [] return '<br>'.join(blocks), results def results_from_feature(self, feature): attributes = feature.attributes() fields = [field.name().lower() for field in feature.fields()] return OrderedDict(zip(fields, attributes)) def results_from_query(self, infoblockdef, layer, feature, mapkey, lastresults=None): def get_key(): try: keycolumn = infoblockdef['mapping']['mapkey'] if keycolumn == 'from_info1': if 'mapkey' in lastresults: return lastresults['mapkey'] else: return [] else: return feature[keycolumn] except KeyError: return mapkey def get_layer(): connection = infoblockdef.get('connection', "from_layer") if isinstance(connection, dict): return layer_by_name(connection['layer']) elif connection == "from_layer": return layer else: raise NotImplementedError("{} is not a supported connection type".format(connection)) if not lastresults: lastresults = {} sql = infoblockdef['query'] layer = get_layer() db = database.Database.fromLayer(layer) mapkey = get_key() attributes = values_from_feature(feature) results = db.query(sql, mapkey=mapkey, **attributes) results = list(results) return results def clearResults(self): self.layerList.clear() self.attributesView.setHtml('') self.editButton.setVisible(False) self.editGeomButton.setEnabled(True) self.editButton.setEnabled(True) self.deleteFeatureButton.setEnabled(True) self.navwidget.hide()
class SearchPlugin(widget, base, Page): title = "Search" icon = resolve("search.svg") def __init__(self, api, parent=None): super(SearchPlugin, self).__init__(parent) self.setupUi(self) self.api = api self.project = None self.dbpath = None self.flickcharm = FlickCharm() self.flickcharm.activateOn(self.resultsView) self.searchbox.textChanged.connect(self.search) self.searchbox.installEventFilter(self) self.resultsView.itemClicked.connect(self.jump_to) self.rebuildLabel.linkActivated.connect(self.rebuild_index) self.fuzzyCheck.stateChanged.connect(self.fuzzy_changed) self.indexbuilder = None self.indexthread = None def fuzzy_changed(self, state): self.search(self.searchbox.text()) def index_built(self, dbpath, timing): self.dbpath = dbpath self.resultsView.clear() self.searchbox.setEnabled(True) print "Index built in: {} seconds".format(timing) def eventFilter(self, object, event): if event.type() == QEvent.FocusIn: RoamEvents.openkeyboard.emit() return False @property def db(self): db = sqlite3.connect(self.dbpath) db.create_function("rank", 1, make_rank_func((1., .1, 0, 0))) return db def project_loaded(self, project): self.project = project self.build_index(project) def rebuild_index(self): self.build_index(self.project) def build_index(self, project): self.searchbox.setEnabled(False) self.resultsView.setEnabled(False) self.resultsView.addItem( "Just let me build the search index first....") validformat, settings = valid_search_settings(project.settings) if not validformat: RoamEvents.raisemessage("Searching", "Invalid search config.", level=1) self.searchbox.hide() self.resultsView.clear() self.resultsView.addItem("Invalid search config found") return self.indexthread = QThread() self.indexbuilder = IndexBuilder(project.folder, settings) self.indexbuilder.moveToThread(self.indexthread) QgsMapLayerRegistry.instance().removeAll.connect(self.indexthread.quit) self.indexbuilder.indexBuilt.connect(self.index_built) self.indexbuilder.finished.connect(self.indexthread.quit) self.indexthread.started.connect(self.indexbuilder.build_index) self.indexthread.finished.connect(self.indexbuilder.quit) self.indexthread.start() def search(self, text): db = self.db c = db.cursor() self.resultsView.clear() self.resultsView.setEnabled(False) if not text: return if self.fuzzyCheck.isChecked(): search = "* ".join(text.split()) + "*" else: search = text query = c.execute( """SELECT layer, featureid, snippet(search, '[',']') as snippet FROM search JOIN featureinfo on search.docid = featureinfo.id WHERE search match '{}' LIMIT 100""".format( search)).fetchall() for layer, featureid, snippet in query: item = QListWidgetItem() text = "{}\n {}".format(layer, snippet.replace('\n', ' ')) item.setText(text) item.setData(Qt.UserRole, (layer, featureid, snippet)) self.resultsView.addItem(item) self.resultsView.setEnabled(True) if self.resultsView.count() == 0: self.resultsView.addItem("No Results") self.resultsView.setEnabled(False) db.close() def jump_to(self, item): data = item.data(32) if not data: return layername, fid = data[0], data[1] layer = roam.api.utils.layer_by_name(layername) feature = layer.getFeatures(QgsFeatureRequest(fid)).next() 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.api.mainwindow.canvas.setExtent(box) self.api.mainwindow.canvas.refresh() self.api.mainwindow.showmap() RoamEvents.selectionchanged.emit({layer: [feature]})
class SyncWidget(sync_widget, sync_base): syncqueue = [] def __init__(self, parent=None): super(SyncWidget, self).__init__(parent) self.setupUi(self) self.syncrunning = False self.syncallButton.hide() self.flickcharm = FlickCharm() self.flickcharm.activateOn(self.synctree) self.flickcharm.activateOn(self.syncstatus) def loadprojects(self, projects): root = self.synctree.invisibleRootItem() for project in projects: print project providers = list(project.syncprovders()) print providers if not providers: continue projectitem = QTreeWidgetItem(root) projectitem.setText(0, project.name) projectitem.setIcon(0, QIcon(project.splash)) for provider in providers: provideritem = QTreeWidgetItem(projectitem) provideritem.setText(0, provider.name) button = QPushButton() button.pressed.connect(partial(self.run, button, provider)) button.setText(provider.name) self.synctree.setItemWidget(provideritem,0, button) self.synctree.expandAll() def updatestatus(self, message): self.syncstatus.append(message) self.syncstatus.ensureCursorVisible() def updatewitherror(self, message): self.updatestatus('<b style="color:red">Error in sync: {}</b>'.format(message)) def updatecomplete(self): self.updatestatus('<b style="color:darkgreen">Sync complete</b>') def runnext(self): try: provider = SyncWidget.syncqueue.pop(0) provider.syncComplete.connect(self.updatecomplete) provider.syncComplete.connect(self.runnext) provider.syncMessage.connect(self.updatestatus) provider.syncError.connect(self.updatewitherror) provider.start() except IndexError: # If we get here we have run out of providers to run return def disconnect(self, provider): try: provider.syncComplete.disconnect() provider.syncMessage.disconnect() provider.syncStarted.disconnect() provider.syncError.disconnect() except TypeError: pass def syncfinished(self, button, provider): self.disconnect(provider) button.setText(provider.name) button.setEnabled(True) self.syncrunning = False def syncstarted(self, button, provider): self.updatestatus('<b style="font-size:large">Sync started for {}</h3>'.format(provider.name)) button.setText('Running') button.setEnabled(False) self.syncrunning = True def run(self, button, provider): provider.syncStarted.connect(partial(self.syncstarted, button, provider)) provider.syncFinished.connect(partial(self.syncfinished, button, provider)) SyncWidget.syncqueue.append(provider) if self.syncrunning: button.setText("Pending") else: self.runnext()
class SyncWidget(Ui_Form, QWidget): syncqueue = [] def __init__(self, parent=None): super(SyncWidget, self).__init__(parent) self.setupUi(self) self.syncrunning = False self.syncallButton.hide() self.flickcharm = FlickCharm() self.flickcharm.activateOn(self.scrollArea) self.flickcharm.activateOn(self.syncstatus) def load_application_sync(self): print "Load application sync" providers = list(roam.syncing.syncprovders()) if not providers: return actionwidget = ActionPickerWidget() actionwidget.setTile("Roam Syncing") for provider in providers: action = QAction(None) action.setText(provider.name) action.setIcon(QIcon(":/icons/sync")) action.triggered.connect(partial(self.run, action, provider)) actionwidget.addAction(action) self.syncwidgets.layout().addWidget(actionwidget) def loadprojects(self, projects): #root = self.synctree.invisibleRootItem() self.load_application_sync() for project in projects: providers = list(project.syncprovders()) if not providers: continue actionwidget = ActionPickerWidget() actionwidget.setTile(project.name) for provider in providers: action = QAction(None) action.setText(provider.name) action.setIcon(QIcon(":/icons/sync")) action.triggered.connect(partial(self.run, action, provider)) actionwidget.addAction(action) self.syncwidgets.layout().addWidget(actionwidget) spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Expanding) self.syncwidgets.layout().addItem(spacerItem) def updatestatus(self, message): self.syncstatus.append(message) self.syncstatus.ensureCursorVisible() def updatewitherror(self, message): self.updatestatus('<b style="color:red">Error in sync: {}</b>'.format(message)) self.updatestatus('') def updatecomplete(self): self.updatestatus('<b style="color:darkgreen">Sync complete</b>') self.updatestatus('') RoamEvents.sync_complete.emit() def runnext(self): try: provider = SyncWidget.syncqueue.pop(0) provider.syncComplete.connect(self.updatecomplete) provider.syncComplete.connect(self.runnext) provider.syncMessage.connect(self.updatestatus) provider.syncError.connect(self.updatewitherror) if provider.closeproject: RoamEvents.closeProject.emit(provider.project) provider.start() except IndexError: # If we get here we have run out of providers to run return def disconnect(self, provider): try: provider.syncComplete.disconnect() provider.syncMessage.disconnect() provider.syncStarted.disconnect() provider.syncError.disconnect() except TypeError: pass def syncfinished(self, action, provider): self.disconnect(provider) action.setText(provider.name) action.setEnabled(True) self.syncrunning = False def syncstarted(self, action, provider): self.updatestatus('<b style="font-size:large">Sync started for {}</h3>'.format(provider.name)) action.setText('Running') action.setEnabled(False) self.syncrunning = True def run(self, action, provider): provider.syncStarted.connect(partial(self.syncstarted, action, provider)) provider.syncFinished.connect(partial(self.syncfinished, action, provider)) SyncWidget.syncqueue.append(provider) if self.syncrunning: action.setText("Pending") else: self.runnext()
class InfoDock(infodock_widget, QWidget): featureupdated = pyqtSignal(object, object, list) resultscleared = pyqtSignal() def __init__(self, parent): super(InfoDock, self).__init__(parent) self.setupUi(self) self.forms = {} self.charm = FlickCharm() self.charm.activateOn(self.attributesView) self.layerList.currentRowChanged.connect(self.layerIndexChanged) self.attributesView.linkClicked.connect(RoamEvents.openurl.emit) self.attributesView.page().setLinkDelegationPolicy( QWebPage.DelegateAllLinks) self.grabGesture(Qt.SwipeGesture) self.setAttribute(Qt.WA_AcceptTouchEvents) self.editButton.pressed.connect(self.openform) self.editGeomButton.pressed.connect(self.editgeom) self.parent().installEventFilter(self) RoamEvents.selectioncleared.connect(self.clearResults) RoamEvents.editgeometry_complete.connect(self.refreshcurrent) def eventFilter(self, object, event): if event.type() == QEvent.Resize: self.resize(self.width(), self.parent().height()) self.move(self.parent().width() - self.width(), 0) return super(InfoDock, self).eventFilter(object, event) def close(self): RoamEvents.selectioncleared.emit() super(InfoDock, self).close() def event(self, event): if event.type() == QEvent.Gesture: gesture = event.gesture(Qt.SwipeGesture) if gesture: self.pagenext() return QWidget.event(self, event) @property def selection(self): item = self.layerList.item(self.layerList.currentRow()) if not item: return cursor = item.data(Qt.UserRole) return cursor def openform(self): cursor = self.selection RoamEvents.openfeatureform.emit(cursor.form, cursor.feature, True) def editgeom(self): cursor = self.selection RoamEvents.editgeometry.emit(cursor.form, cursor.feature) def pageback(self): cursor = self.selection cursor.back() self.update(cursor) def pagenext(self): cursor = self.selection cursor.next() self.update(cursor) def layerIndexChanged(self, index): item = self.layerList.item(index) if not item: return cursor = item.data(Qt.UserRole) self.update(cursor) def setResults(self, results, forms): lastrow = self.layerList.currentRow() if lastrow == -1: lastrow = 0 self.clearResults() self.forms = forms for layer, features in results.iteritems(): if features: self._addResult(layer, features) self.layerList.setCurrentRow(lastrow) self.layerList.setMinimumWidth( self.layerList.sizeHintForColumn(0) + 20) self.navwidget.show() def show(self): if self.layerList.count() > 0: super(InfoDock, self).show() else: self.hide() def _addResult(self, layer, features): layername = layer.name() forms = self.forms.get(layername, []) if not forms: item = QListWidgetItem(QIcon(), layername, self.layerList) item.setData(Qt.UserRole, FeatureCursor(layer, features)) return for form in forms: itemtext = "{} \n ({})".format(layername, form.label) icon = QIcon(form.icon) item = QListWidgetItem(icon, itemtext, self.layerList) item.setData(Qt.UserRole, FeatureCursor(layer, features, form)) def refreshcurrent(self): self.update(self.selection) def update(self, cursor): if cursor is None: return try: feature = cursor.feature except NoFeature as ex: utils.warning(ex) return fields = [field.name() for field in feature.fields()] data = OrderedDict() items = [] for field, value in zip(fields, feature.attributes()): data[field] = value item = u"<tr><th>{0}</th> <td>${{{0}}}</td></tr>".format(field) items.append(item) rowtemple = Template(''.join(items)) rowshtml = updateTemplate(data, rowtemple) form = cursor.form layer = cursor.layer if form: name = "{}".format(layer.name(), form.label) else: name = layer.name() info = dict(TITLE=name, ROWS=rowshtml) html = updateTemplate(info, template) self.countlabel.setText(str(cursor)) self.attributesView.setHtml(html, templates.baseurl) self.editButton.setVisible(not form is None) self.editGeomButton.setVisible(not form is None) self.featureupdated.emit(layer, feature, cursor.features) def clearResults(self): self.layerList.clear() self.attributesView.setHtml('') self.editButton.setVisible(False) self.navwidget.hide()
class DataEntryWidget(dataentry_widget, dataentry_base): """ """ accepted = pyqtSignal() rejected = pyqtSignal(str, int) finished = pyqtSignal() featuresaved = pyqtSignal() featuredeleted = pyqtSignal(object, object) failedsave = pyqtSignal(list) helprequest = pyqtSignal(str) openimage = pyqtSignal(object) def __init__(self, canvas, parent=None): super(DataEntryWidget, self).__init__(parent) self.setupUi(self) self.featureform = None self.feature = None self.fields = None self.project = None self.canvas = canvas self.flickwidget = FlickCharm() self.flickwidget.activateOn(self.scrollArea) toolbar = QToolBar() size = QSize(48, 48) toolbar.setIconSize(size) style = Qt.ToolButtonTextUnderIcon toolbar.setToolButtonStyle(style) self.actionSave.triggered.connect(self.accept) self.actionCancel.triggered.connect(functools.partial(self.formrejected, None)) self.actionDelete.triggered.connect(self.deletefeature) label = 'Required fields marked in <b style="background-color:rgba(255, 221, 48,150)">yellow</b>' self.missingfieldsLabel = QLabel(label) self.missingfieldsLabel.hide() toolbar.addAction(self.actionDelete) self.missingfieldaction = toolbar.addWidget(self.missingfieldsLabel) spacer = QWidget() spacer2 = QWidget() spacer2.setMinimumWidth(20) spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) spacer2.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) toolbar.addWidget(spacer) toolbar.addAction(self.actionCancel) toolbar.addWidget(spacer2) toolbar.addAction(self.actionSave) self.data_entry_page.layout().insertWidget(0, toolbar) def deletefeature(self): try: msg = self.featureform.deletemessage except AttributeError: msg = 'Do you really want to delete this feature?' box = DeleteFeatureDialog(msg, QApplication.activeWindow()) if not box.exec_(): return featureid = self.feature.id() try: userdeleted = self.featureform.deletefeature() if not userdeleted: # If the user didn't add there own feature delete logic # we will just do it for them. layer = self.featureform.form.QGISLayer layer.startEditing() layer.deleteFeature(featureid) saved = layer.commitChanges() if not saved: raise featureform.DeleteFeatureException(layer.commitErrors()) except featureform.DeleteFeatureException as ex: self.failedsave.emit([ex.message]) map(error, ex.message) return self.featureform.featuredeleted(self.feature) self.featuredeleted.emit(self.featureform.form.QGISLayer, featureid) def accept(self): fields = [w['field'] for w in self.featureform.formconfig['widgets']] def updatefeautrefields(feature): for key, value in values.iteritems(): try: if key in fields: feature[key] = field_or_null(value) else: feature[key] = value except KeyError: continue return feature def field_or_null(field): if field == '' or field is None or isinstance(field, QPyNullVariant): return QPyNullVariant(str) return field if not self.featureform.allpassing: RoamEvents.raisemessage("Missing fields", "Some fields are still required.", QgsMessageBar.WARNING, duration=2) return if not self.featureform: return if not self.featureform.accept(): return layer = self.featureform.form.QGISLayer before = QgsFeature(self.feature) before.setFields(self.fields, initAttributes=False) values, savedvalues = self.featureform.getvalues() after = QgsFeature(self.feature) after.setFields(self.fields, initAttributes=False) after = updatefeautrefields(after) layer.startEditing() if after.id() > 0: if self.project.historyenabled(layer): # Mark the old one as history before['status'] = 'H' after['status'] = 'C' after['dateedited'] = QDateTime.currentDateTime() after['editedby'] = getpass.getuser() layer.addFeature(after) layer.updateFeature(before) else: layer.updateFeature(after) else: layer.addFeature(after) featureform.savevalues(layer, savedvalues) saved = layer.commitChanges() if not saved: self.failedsave.emit(layer.commitErrors()) map(error, layer.commitErrors()) else: self.featureform.featuresaved(after, values) self.featuresaved.emit() self.accepted.emit() self.featureform = None def formrejected(self, message=None, level=featureform.RejectedException.WARNING): self.clear() self.rejected.emit(message, level) def formvalidation(self, passed): self.missingfieldaction.setVisible(not passed) def setlargewidget(self, widgettype, lastvalue, callback, config): def cleanup(): self.stackedWidget.setCurrentIndex(0) self.clearcurrentwidget(self.fullscreenwidget) del self.largewidgetwrapper widget = widgettype.createwidget() self.largewidgetwrapper = widgettype.for_widget(widget, None, None, None, None, map=self.canvas) self.largewidgetwrapper.finished.connect(callback) self.largewidgetwrapper.finished.connect(cleanup) self.largewidgetwrapper.cancel.connect(cleanup) self.clearcurrentwidget(self.fullscreenwidget) self.fullscreenwidget.layout().insertWidget(0, widget) self.stackedWidget.setCurrentIndex(1) self.largewidgetwrapper.initWidget(widget) self.largewidgetwrapper.config = config self.largewidgetwrapper.setvalue(lastvalue) def setwidget(self, widget): self.clearcurrentwidget(self.scrollAreaWidgetContents) self.scrollAreaWidgetContents.layout().insertWidget(0, widget) self.stackedWidget.setCurrentIndex(0) def clear(self): self.featureform = None self.clearcurrentwidget(self.fullscreenwidget) self.clearcurrentwidget(self.scrollAreaWidgetContents) def clearcurrentwidget(self, parent): item = parent.layout().itemAt(0) if item and item.widget(): widget = item.widget() widget.setParent(None) widget.deleteLater() def openform(self, form, feature, project, editmode): """ Opens a form for the given feature. """ roam.qgisfunctions.capturegeometry = feature.geometry() defaultvalues = {} layer = form.QGISLayer if not editmode: defaultwidgets = form.widgetswithdefaults() defaultvalues = defaults.default_values(defaultwidgets, feature, layer) defaultvalues.update(featureform.loadsavedvalues(layer)) self.actionDelete.setVisible(editmode) for field, value in defaultvalues.iteritems(): feature[field] = value self.formvalidation(passed=True) self.feature = feature # Hold a reference to the fields because QGIS will let the # go out of scope and we get crashes. Yay! self.fields = self.feature.fields() self.featureform = form.create_featureform(feature, defaultvalues, canvas=self.canvas) self.featureform.editingmode = editmode self.featureform.rejected.connect(self.formrejected) self.featureform.enablesave.connect(self.actionSave.setEnabled) # Call the pre loading events for the form layers = QgsMapLayerRegistry.instance().mapLayers() self.project = project fields = [field.name().lower() for field in self.fields] attributes = feature.attributes() if layer.dataProvider().name() == 'spatialite': pkindexes = layer.dataProvider().pkAttributeIndexes() for index in pkindexes: del fields[index] del attributes[index] values = CaseInsensitiveDict(zip(fields, attributes)) try: self.featureform.load(feature, layers, values) except featureform.RejectedException as rejected: self.formrejected(rejected.message, rejected.level) return self.featureform.formvalidation.connect(self.formvalidation) self.featureform.helprequest.connect(self.helprequest.emit) self.featureform.bindvalues(values) self.featureform.showlargewidget.connect(self.setlargewidget) self.actionSave.setVisible(True) self.setwidget(self.featureform) self.featureform.loaded() RoamEvents.featureformloaded.emit(form, feature, project, editmode)
class DataEntryWidget(dataentry_widget, dataentry_base): """ """ accepted = pyqtSignal() rejected = pyqtSignal(str, int) finished = pyqtSignal() featuresaved = pyqtSignal() featuredeleted = pyqtSignal(object, object) failedsave = pyqtSignal(list) helprequest = pyqtSignal(str) openimage = pyqtSignal(object) def __init__(self, canvas, parent=None): super(DataEntryWidget, self).__init__(parent) self.setupUi(self) self.featureform = None self.feature = None self.fields = None self.project = None self.canvas = canvas self.flickwidget = FlickCharm() self.flickwidget.activateOn(self.scrollArea) toolbar = QToolBar() size = QSize(48, 48) toolbar.setIconSize(size) style = Qt.ToolButtonTextUnderIcon toolbar.setToolButtonStyle(style) self.actionSave.triggered.connect(self.accept) self.actionCancel.triggered.connect( functools.partial(self.formrejected, None)) self.actionDelete.triggered.connect(self.deletefeature) label = 'Required fields marked in <b style="background-color:rgba(255, 221, 48,150)">yellow</b>' self.missingfieldsLabel = QLabel(label) self.missingfieldsLabel.hide() toolbar.addAction(self.actionDelete) self.missingfieldaction = toolbar.addWidget(self.missingfieldsLabel) spacer = QWidget() spacer2 = QWidget() spacer2.setMinimumWidth(20) spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) spacer2.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) toolbar.addWidget(spacer) toolbar.addAction(self.actionCancel) toolbar.addWidget(spacer2) toolbar.addAction(self.actionSave) self.data_entry_page.layout().insertWidget(0, toolbar) def deletefeature(self): try: msg = self.featureform.deletemessage except AttributeError: msg = 'Do you really want to delete this feature?' box = DeleteFeatureDialog(msg, QApplication.activeWindow()) if not box.exec_(): return featureid = self.feature.id() try: userdeleted = self.featureform.deletefeature() if not userdeleted: # If the user didn't add there own feature delete logic # we will just do it for them. layer = self.featureform.form.QGISLayer layer.startEditing() layer.deleteFeature(featureid) saved = layer.commitChanges() if not saved: raise featureform.DeleteFeatureException( layer.commitErrors()) except featureform.DeleteFeatureException as ex: self.failedsave.emit([ex.message]) map(error, ex.message) return self.featureform.featuredeleted(self.feature) self.featuredeleted.emit(self.featureform.form.QGISLayer, featureid) def accept(self): fields = [w['field'] for w in self.featureform.formconfig['widgets']] def updatefeautrefields(feature): for key, value in values.iteritems(): try: if key in fields: feature[key] = field_or_null(value) else: feature[key] = value except KeyError: continue return feature def field_or_null(field): if field == '' or field is None or isinstance( field, QPyNullVariant): return QPyNullVariant(str) return field if not self.featureform.allpassing: RoamEvents.raisemessage("Missing fields", "Some fields are still required.", QgsMessageBar.WARNING, duration=2) return if not self.featureform: return if not self.featureform.accept(): return layer = self.featureform.form.QGISLayer before = QgsFeature(self.feature) before.setFields(self.fields, initAttributes=False) values, savedvalues = self.featureform.getvalues() after = QgsFeature(self.feature) after.setFields(self.fields, initAttributes=False) after = updatefeautrefields(after) layer.startEditing() if after.id() > 0: if self.project.historyenabled(layer): # Mark the old one as history before['status'] = 'H' after['status'] = 'C' after['dateedited'] = QDateTime.currentDateTime() after['editedby'] = getpass.getuser() layer.addFeature(after) layer.updateFeature(before) else: layer.updateFeature(after) else: layer.addFeature(after) featureform.savevalues(layer, savedvalues) saved = layer.commitChanges() if not saved: self.failedsave.emit(layer.commitErrors()) map(error, layer.commitErrors()) else: self.featureform.featuresaved(after, values) self.featuresaved.emit() self.accepted.emit() self.featureform = None def formrejected(self, message=None, level=featureform.RejectedException.WARNING): self.clear() self.rejected.emit(message, level) def formvalidation(self, passed): self.missingfieldaction.setVisible(not passed) def setlargewidget(self, widgettype, lastvalue, callback, config): def cleanup(): self.stackedWidget.setCurrentIndex(0) self.clearcurrentwidget(self.fullscreenwidget) del self.largewidgetwrapper widget = widgettype.createwidget() self.largewidgetwrapper = widgettype.for_widget(widget, None, None, None, None, map=self.canvas) self.largewidgetwrapper.finished.connect(callback) self.largewidgetwrapper.finished.connect(cleanup) self.largewidgetwrapper.cancel.connect(cleanup) self.clearcurrentwidget(self.fullscreenwidget) self.fullscreenwidget.layout().insertWidget(0, widget) self.stackedWidget.setCurrentIndex(1) self.largewidgetwrapper.initWidget(widget) self.largewidgetwrapper.config = config self.largewidgetwrapper.setvalue(lastvalue) def setwidget(self, widget): self.clearcurrentwidget(self.scrollAreaWidgetContents) self.scrollAreaWidgetContents.layout().insertWidget(0, widget) self.stackedWidget.setCurrentIndex(0) def clear(self): self.featureform = None self.clearcurrentwidget(self.fullscreenwidget) self.clearcurrentwidget(self.scrollAreaWidgetContents) def clearcurrentwidget(self, parent): item = parent.layout().itemAt(0) if item and item.widget(): widget = item.widget() widget.setParent(None) widget.deleteLater() def openform(self, form, feature, project, editmode): """ Opens a form for the given feature. """ roam.qgisfunctions.capturegeometry = feature.geometry() defaultvalues = {} layer = form.QGISLayer if not editmode: defaultwidgets = form.widgetswithdefaults() defaultvalues = defaults.default_values(defaultwidgets, feature, layer) defaultvalues.update(featureform.loadsavedvalues(layer)) self.actionDelete.setVisible(editmode) for field, value in defaultvalues.iteritems(): feature[field] = value self.formvalidation(passed=True) self.feature = feature # Hold a reference to the fields because QGIS will let the # go out of scope and we get crashes. Yay! self.fields = self.feature.fields() self.featureform = form.create_featureform(feature, defaultvalues, canvas=self.canvas) self.featureform.editingmode = editmode self.featureform.rejected.connect(self.formrejected) self.featureform.enablesave.connect(self.actionSave.setEnabled) # Call the pre loading events for the form layers = QgsMapLayerRegistry.instance().mapLayers() self.project = project fields = [field.name().lower() for field in self.fields] attributes = feature.attributes() if layer.dataProvider().name() == 'spatialite': pkindexes = layer.dataProvider().pkAttributeIndexes() for index in pkindexes: del fields[index] del attributes[index] values = CaseInsensitiveDict(zip(fields, attributes)) try: self.featureform.load(feature, layers, values) except featureform.RejectedException as rejected: self.formrejected(rejected.message, rejected.level) return self.featureform.formvalidation.connect(self.formvalidation) self.featureform.helprequest.connect(self.helprequest.emit) self.featureform.bindvalues(values) self.featureform.showlargewidget.connect(self.setlargewidget) self.actionSave.setVisible(True) self.setwidget(self.featureform) self.featureform.loaded() RoamEvents.featureformloaded.emit(form, feature, project, editmode)
class SearchPlugin(widget, base, Page): title = "Search" icon = resolve("search.svg") def __init__(self, api, parent=None): super(SearchPlugin, self).__init__(parent) self.setupUi(self) self.api = api self.project = None self.dbpath = None self.flickcharm = FlickCharm() self.flickcharm.activateOn(self.resultsView) self.searchbox.textChanged.connect(self.search) self.searchbox.installEventFilter(self) self.resultsView.itemClicked.connect(self.jump_to) self.rebuildLabel.linkActivated.connect(self.rebuild_index) self.fuzzyCheck.stateChanged.connect(self.fuzzy_changed) self.indexbuilder = None self.indexthread = None def fuzzy_changed(self, state): self.search(self.searchbox.text()) def index_built(self, dbpath, timing): self.dbpath = dbpath self.resultsView.clear() self.searchbox.setEnabled(True) print "Index built in: {} seconds".format(timing) def eventFilter(self, object, event): if event.type() == QEvent.FocusIn: RoamEvents.openkeyboard.emit() return False @property def db(self): db = sqlite3.connect(self.dbpath) db.create_function("rank", 1, make_rank_func((1., .1, 0, 0))) return db def project_loaded(self, project): self.project = project self.build_index(project) def rebuild_index(self): self.build_index(self.project) def build_index(self, project): self.searchbox.setEnabled(False) self.resultsView.setEnabled(False) self.resultsView.addItem("Just let me build the search index first....") self.indexthread = QThread() self.indexbuilder = IndexBuilder(project.folder, project.settings.get("search", {})) self.indexbuilder.moveToThread(self.indexthread) self.indexbuilder.indexBuilt.connect(self.index_built) self.indexbuilder.finished.connect(self.indexthread.quit) self.indexthread.started.connect(self.indexbuilder.build_index) self.indexthread.finished.connect(self.indexbuilder.quit) print "building index" self.indexthread.start() def search(self, text): db = self.db c = db.cursor() self.resultsView.clear() self.resultsView.setEnabled(False) if not text: return if self.fuzzyCheck.isChecked(): search = "* ".join(text.split()) + "*" else: search = text query = c.execute("""SELECT layer, featureid, snippet(search, '[',']') as snippet FROM search JOIN featureinfo on search.docid = featureinfo.id WHERE search match '{}' LIMIT 100""".format(search)).fetchall() for layer, featureid, snippet in query: item = QListWidgetItem() text = "{}\n {}".format(layer, snippet.replace('\n', ' ')) item.setText(text) item.setData(Qt.UserRole, (layer, featureid, snippet)) self.resultsView.addItem(item) self.resultsView.setEnabled(True) if self.resultsView.count() == 0: self.resultsView.addItem("No Results") db.close() def jump_to(self, item): data = item.data(32) layername, fid = data[0], data[1] layer = roam.api.utils.layer_by_name(layername) layer.select(fid) feature = layer.selectedFeatures()[0] self.api.mainwindow.canvas.zoomToSelected(layer) layer.removeSelection() self.api.mainwindow.showmap() RoamEvents.selectionchanged.emit({layer: [feature]})
class InfoDock(infodock_widget, QWidget): requestopenform = pyqtSignal(object, QgsFeature) featureupdated = pyqtSignal(object, object, list) resultscleared = pyqtSignal() def __init__(self, parent): super(InfoDock, self).__init__(parent) self.setupUi(self) self.forms = {} self.charm = FlickCharm() self.charm.activateOn(self.attributesView) self.layerList.currentRowChanged.connect(self.layerIndexChanged) self.attributesView.linkClicked.connect(openimage) self.attributesView.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.grabGesture(Qt.SwipeGesture) self.setAttribute(Qt.WA_AcceptTouchEvents) self.editButton.pressed.connect(self.openform) self.parent().installEventFilter(self) def eventFilter(self, object, event): if event.type() == QEvent.Resize: width = self.parent().width() * 40 / 100 self.resize(width, self.parent().height()) self.move(self.parent().width() - self.width() - 1, 1) return object.eventFilter(object, event) def close(self): self.clearResults() super(InfoDock, self).close() def event(self, event): if event.type() == QEvent.Gesture: gesture = event.gesture(Qt.SwipeGesture) if gesture: self.pagenext() return QWidget.event(self, event) @property def selection(self): item = self.layerList.item(self.layerList.currentRow()) if not item: return cursor = item.data(Qt.UserRole) return cursor def openform(self): cursor = self.selection self.requestopenform.emit(cursor.form, cursor.feature) def pageback(self): cursor = self.selection cursor.back() self.update(cursor) def pagenext(self): cursor = self.selection cursor.next() self.update(cursor) def layerIndexChanged(self, index): item = self.layerList.item(index) if not item: return cursor = item.data(Qt.UserRole) self.update(cursor) def setResults(self, results, forms): self.clearResults() self.forms = forms for layer, features in results.iteritems(): if features: self._addResult(layer, features) self.layerList.setCurrentRow(0) self.layerList.setMinimumWidth(self.layerList.sizeHintForColumn(0) + 20) self.navwidget.show() def show(self): if self.layerList.count() > 0: super(InfoDock, self).show() else: self.hide() def _addResult(self, layer, features): layername = layer.name() forms = self.forms.get(layername, []) if not forms: item = QListWidgetItem(QIcon(), layername, self.layerList) item.setData(Qt.UserRole, FeatureCursor(layer, features)) return for form in forms: itemtext = "{} ({})".format(layername, form.label) icon = QIcon(form.icon) item = QListWidgetItem(icon, itemtext, self.layerList) item.setData(Qt.UserRole, FeatureCursor(layer, features, form)) def update(self, cursor): global image images = {} feature = cursor.feature fields = [field.name() for field in feature.fields()] data = OrderedDict() items = [] for field, value in zip(fields, feature.attributes()): data[field] = value item = "<tr><th>{0}</th> <td>${{{0}}}</td></tr>".format(field) items.append(item) rowtemple = Template("".join(items)) rowshtml = updateTemplate(data, rowtemple) form = cursor.form layer = cursor.layer if form: name = "{}".format(layer.name(), form.label) else: name = layer.name() info = dict(TITLE=name, ROWS=rowshtml) html = updateTemplate(info, template) base = os.path.dirname(os.path.abspath(__file__)) baseurl = QUrl.fromLocalFile(base + "\\") if form: displaytext = form.settings.get("display", None) display = QgsExpression.replaceExpressionText(displaytext, cursor.feature, layer) else: display = str(feature[0]) self.countlabel.setText(str(cursor)) self.displaylabel.setText(display) self.attributesView.setHtml(html, baseurl) self.editButton.setVisible(not form is None) self.featureupdated.emit(layer, cursor.feature, cursor.features) def clearResults(self): self.layerList.clear() self.attributesView.setHtml("") self.editButton.setVisible(False) self.resultscleared.emit() self.navwidget.hide()
class InfoDock(infodock_widget, QWidget): featureupdated = pyqtSignal(object, object, list) resultscleared = pyqtSignal() def __init__(self, parent): super(InfoDock, self).__init__(parent) self.setupUi(self) self.forms = {} self.charm = FlickCharm() self.charm.activateOn(self.attributesView) self.layerList.currentRowChanged.connect(self.layerIndexChanged) self.attributesView.linkClicked.connect(RoamEvents.openurl.emit) self.attributesView.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.grabGesture(Qt.SwipeGesture) self.setAttribute(Qt.WA_AcceptTouchEvents) self.editButton.pressed.connect(self.openform) self.editGeomButton.pressed.connect(self.editgeom) self.parent().installEventFilter(self) self.project = None self.startwidth = self.width() self.expaned = False RoamEvents.selectioncleared.connect(self.clearResults) RoamEvents.editgeometry_complete.connect(self.refreshcurrent) def mouseDoubleClickEvent(self, QMouseEvent): if self.expaned: self._collapse() else: self._expand() def _expand(self): self.resize(self.parent().width() - 10, self.parent().height()) self.move(10, 0) self.expaned = True def _collapse(self): self.resize(self.startwidth, self.parent().height()) self.move(self.parent().width() - self.startwidth, 0) self.expaned = False def eventFilter(self, object, event): if event.type() == QEvent.Resize: self._collapse() return super(InfoDock, self).eventFilter(object, event) def close(self): RoamEvents.selectioncleared.emit() super(InfoDock, self).close() def event(self, event): if event.type() == QEvent.Gesture: gesture = event.gesture(Qt.SwipeGesture) if gesture: self.pagenext() return QWidget.event(self, event) @property def selection(self): item = self.layerList.item(self.layerList.currentRow()) if not item: return cursor = item.data(Qt.UserRole) return cursor def openform(self): cursor = self.selection RoamEvents.load_feature_form(cursor.form, cursor.feature, True) def editgeom(self): cursor = self.selection RoamEvents.editgeometry.emit(cursor.form, cursor.feature) def pageback(self): cursor = self.selection cursor.back() self.update(cursor) def pagenext(self): cursor = self.selection cursor.next() self.update(cursor) def layerIndexChanged(self, index): item = self.layerList.item(index) if not item: return cursor = item.data(Qt.UserRole) self.update(cursor) def setResults(self, results, forms, project): lastrow = self.layerList.currentRow() if lastrow == -1: lastrow = 0 self.clearResults() self.forms = forms self.project = project for layer, features in results.iteritems(): if features: self._addResult(layer, features) self.layerList.setCurrentRow(lastrow) self.layerList.setMinimumWidth(self.layerList.sizeHintForColumn(0) + 20) self.layerList.setMinimumHeight(self.layerList.sizeHintForRow(0) + 20) self.navwidget.show() def show(self): if self.layerList.count() > 0: super(InfoDock, self).show() else: self.hide() def _addResult(self, layer, features): layername = layer.name() forms = self.forms.get(layername, []) if not forms: item = QListWidgetItem(QIcon(), layername, self.layerList) item.setData(Qt.UserRole, FeatureCursor(layer, features)) return for form in forms: selectname = self.project.selectlayer_name(form.layername) print selectname if selectname == layername: itemtext = "{} \n ({})".format(layername, form.label) else: itemtext = selectname icon = QIcon(form.icon) item = QListWidgetItem(icon, itemtext, self.layerList) item.setData(Qt.UserRole, FeatureCursor(layer, features, form)) def refreshcurrent(self): self.update(self.selection) def update(self, cursor): if cursor is None: return try: feature = cursor.feature except NoFeature as ex: utils.warning(ex) return form = cursor.form layer = cursor.layer clear_image_cache() info1, results = self.generate_info("info1", self.project, layer, feature.id(), feature) info2, _= self.generate_info("info2", self.project, layer, feature.id(), feature, lastresults=results[0]) if form: name = "{}".format(layer.name(), form.label) else: name = layer.name() info = dict(TITLE=name, INFO1=info1, INFO2=info2) html = updateTemplate(info, template) self.countlabel.setText(str(cursor)) self.attributesView.setHtml(html, templates.baseurl) tools = self.project.layer_tools(layer) hasform = not form is None editattributes = 'edit_attributes' in tools and hasform editgeom = 'edit_geom' in tools and hasform self.editButton.setVisible(editattributes) self.editGeomButton.setVisible(editgeom) self.featureupdated.emit(layer, feature, cursor.features) def generate_info(self, infoblock, project, layer, mapkey, feature, lastresults=None): info_template = Template(""" <div class="panel panel-default"> <div class="panel-heading text-left"> <h2 class="panel-title" style="font-size:24px">${HEADER}</h2> </div> <div class="panel-body" style="padding:0px"> <table class="table table-condensed"> <col style="width: 35%;"/> <col style="width: 65%;"/> ${ROWS} </table> </div> </div> """) infoblockdef = project.info_query(infoblock, layer.name()) isinfo1 = infoblock == "info1" if isinfo1: header = infoblockdef.get('caption', "Record") else: header = infoblockdef.get('caption', "Related Record") if not infoblockdef: if isinfo1: infoblockdef = {} infoblockdef['type'] = 'feature' else: return None, [] results = [] error = None if infoblockdef['type'] == 'sql': try: queryresults = self.results_from_query(infoblockdef, layer, feature, mapkey, lastresults=lastresults) if isinfo1 and not queryresults: # If there is no results from the query and we are a info 1 block we grab from the feature. results.append(self.results_from_feature(feature)) else: results = queryresults except database.DatabaseException as ex: if not isinfo1: error = "<b> Error: {} <b>".format(ex.msg) else: results.append(self.results_from_feature(feature)) elif infoblockdef['type'] == 'feature': results.append(self.results_from_feature(feature)) else: return None, [] blocks = [] for count, result in enumerate(results, start=1): if not isinfo1: newheader = "{} {} of {}".format(header,count, len(results)) else: newheader = header fields = result.keys() attributes = result.values() rows = generate_rows(fields, attributes) blocks.append(updateTemplate(dict(ROWS=rows, HEADER=newheader), info_template)) if error: return error, [] return '<br>'.join(blocks), results def results_from_feature(self, feature): return values_from_feature(feature) def results_from_query(self, infoblockdef, layer, feature, mapkey, lastresults=None): def get_key(): try: keycolumn = infoblockdef['mapping']['mapkey'] if keycolumn == 'from_info1': if 'mapkey' in lastresults: return lastresults['mapkey'] else: return [] else: return feature[keycolumn] except KeyError: return mapkey def get_layer(): connection = infoblockdef['connection'] if isinstance(connection, dict): return layer_by_name(connection['layer']) elif connection == "from_layer": return layer else: raise NotImplementedError("{} is not a supported connection type".format(connection)) if not lastresults: lastresults = {} sql = infoblockdef['query'] layer = get_layer() db = database.Database.fromLayer(layer) mapkey = get_key() results = db.query(sql, mapkey=mapkey) return list(results) def clearResults(self): self.layerList.clear() self.attributesView.setHtml('') self.editButton.setVisible(False) self.navwidget.hide()
class SyncWidget(Ui_Form, QWidget): syncqueue = [] def __init__(self, parent=None): super(SyncWidget, self).__init__(parent) self.setupUi(self) self.syncrunning = False self.syncallButton.hide() self.flickcharm = FlickCharm() self.flickcharm.activateOn(self.synctree) self.flickcharm.activateOn(self.syncstatus) def loadprojects(self, projects): root = self.synctree.invisibleRootItem() for project in projects: providers = list(project.syncprovders()) if not providers: continue projectitem = QTreeWidgetItem(root) projectitem.setText(0, project.name) projectitem.setIcon(0, QIcon(project.splash)) for provider in providers: provideritem = QTreeWidgetItem(projectitem) provideritem.setText(0, provider.name) button = QPushButton() button.pressed.connect(partial(self.run, button, provider)) button.setText(provider.name) self.synctree.setItemWidget(provideritem,0, button) self.synctree.expandAll() def updatestatus(self, message): self.syncstatus.append(message) self.syncstatus.ensureCursorVisible() def updatewitherror(self, message): self.updatestatus('<b style="color:red">Error in sync: {}</b>'.format(message)) self.updatestatus('') def updatecomplete(self): self.updatestatus('<b style="color:darkgreen">Sync complete</b>') self.updatestatus('') def runnext(self): try: provider = SyncWidget.syncqueue.pop(0) provider.syncComplete.connect(self.updatecomplete) provider.syncComplete.connect(self.runnext) provider.syncMessage.connect(self.updatestatus) provider.syncError.connect(self.updatewitherror) provider.start() except IndexError: # If we get here we have run out of providers to run return def disconnect(self, provider): try: provider.syncComplete.disconnect() provider.syncMessage.disconnect() provider.syncStarted.disconnect() provider.syncError.disconnect() except TypeError: pass def syncfinished(self, button, provider): self.disconnect(provider) button.setText(provider.name) button.setEnabled(True) self.syncrunning = False def syncstarted(self, button, provider): self.updatestatus('<b style="font-size:large">Sync started for {}</h3>'.format(provider.name)) button.setText('Running') button.setEnabled(False) self.syncrunning = True def run(self, button, provider): provider.syncStarted.connect(partial(self.syncstarted, button, provider)) provider.syncFinished.connect(partial(self.syncfinished, button, provider)) SyncWidget.syncqueue.append(provider) if self.syncrunning: button.setText("Pending") else: self.runnext()
class DataEntryWidget(dataentry_widget, dataentry_base): """ """ accepted = pyqtSignal() rejected = pyqtSignal(str) finished = pyqtSignal() featuresaved = pyqtSignal() failedsave = pyqtSignal(list) helprequest = pyqtSignal(str) def __init__(self, canvas, bar, parent=None): super(DataEntryWidget, self).__init__(parent) self.setupUi(self) self.featureform = None self.project = None self.canvas = canvas self.bar = bar self.flickwidget = FlickCharm() self.flickwidget.activateOn(self.scrollArea) toolbar = QToolBar() size = QSize(32, 32) toolbar.setIconSize(size) style = Qt.ToolButtonTextUnderIcon toolbar.setToolButtonStyle(style) self.actionSave.triggered.connect(self.accept) self.actionCancel.triggered.connect(functools.partial(self.reject, None)) label = 'Required fields marked in <b style="background-color:rgba(255, 221, 48,150)">yellow</b>' self.missingfieldsLabel = QLabel(label) self.missingfieldsLabel.hide() self.missingfieldaction = toolbar.addWidget(self.missingfieldsLabel) spacer = QWidget() spacer2 = QWidget() spacer2.setMinimumWidth(20) spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) spacer2.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) toolbar.addWidget(spacer) toolbar.addAction(self.actionSave) toolbar.addWidget(spacer2) toolbar.addAction(self.actionCancel) self.layout().insertWidget(2, toolbar) self.actionSave def accept(self): if not self.featureform.allpassing: self.bar.pushMessage("Missing fields", "Some fields are still required.", QgsMessageBar.WARNING, duration=2) return if not self.featureform: return if not self.featureform.accept(): return layer = self.featureform.form.QGISLayer before, after, savedvalues = self.featureform.unbind() layer.startEditing() if after.id() > 0: if self.project.historyenabled(layer): # Mark the old one as history before['status'] = 'H' after['status'] = 'C' after['dateedited'] = QDateTime.currentDateTime() after['editedby'] = getpass.getuser() layer.addFeature(after) layer.updateFeature(before) else: layer.updateFeature(after) else: layer.addFeature(after) featureform.savevalues(layer, savedvalues) saved = layer.commitChanges() if not saved: self.failedsave.emit(layer.commitErrors()) map(error, layer.commitErrors()) else: self.featuresaved.emit() self.accepted.emit() self.finished.emit() self.featureform = None def reject(self, message): # Tell the form it is rejected if self.featureform: self.featureform.reject(message) def formrejected(self, message=None): self.clearcurrentwidget() self.rejected.emit(message) self.finished.emit() self.featureform = None def formvalidation(self, passed): self.missingfieldaction.setVisible(not passed) def showwidget(self, widget): self.actionSave.setVisible(False) self.setwidget(widget) def setwidget(self, widget): self.clearcurrentwidget() self.scrollAreaWidgetContents.layout().insertWidget(0, widget) def clear(self): self.featureform = None self.clearcurrentwidget() def clearcurrentwidget(self): item = self.scrollAreaWidgetContents.layout().itemAt(0) if item and item.widget(): widget = item.widget() widget.deleteLater() def continueload(self): self.featureform.showwidget.disconnect() self.featureform.loadform.disconnect() self.featureform.formvalidation.connect(self.formvalidation) self.featureform.helprequest.connect(self.helprequest.emit) self.featureform.bind() self.actionSave.setVisible(True) self.setwidget(self.featureform.widget) self.featureform.loaded() def openform(self, form, feature, project): """ Opens a form for the given feature. This method is connected using signals rather then a normal top down method. If the loadform signal is emitted from the FeatureForm the data entry widget will continue to bind and load the form. If rejected is emitted the form will be rejected and the message will be shown to the user. This allows for pre form load checks that allow the form to show pre main form widgets using showwidget. """ defaults = {} editing = feature.id() > 0 if not editing: defaults = getdefaults(form.widgetswithdefaults(), feature, form.QGISLayer, self.canvas) for field, value in defaults.iteritems(): feature[field] = value self.formvalidation(passed=True) self.feature = feature self.featureform = form.create_featureform(feature, defaults) self.featureform.showwidget.connect(self.showwidget) self.featureform.loadform.connect(self.continueload) self.featureform.rejected.connect(self.formrejected) # Call the pre loading evnts for the form layers = iter(QgsMapLayerRegistry.instance().mapLayers()) self.project = project self.featureform.load(feature, layers, editing)
class InfoDock(infodock_widget, QWidget): featureupdated = pyqtSignal(object, object, list) resultscleared = pyqtSignal() def __init__(self, parent): super(InfoDock, self).__init__(parent) self.setupUi(self) self.forms = {} self.charm = FlickCharm() self.charm.activateOn(self.attributesView) self.layerList.currentRowChanged.connect(self.layerIndexChanged) self.attributesView.linkClicked.connect(RoamEvents.openurl.emit) self.attributesView.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.grabGesture(Qt.SwipeGesture) self.setAttribute(Qt.WA_AcceptTouchEvents) self.editButton.pressed.connect(self.openform) self.editGeomButton.pressed.connect(self.editgeom) self.parent().installEventFilter(self) RoamEvents.selectioncleared.connect(self.clearResults) RoamEvents.editgeometry_complete.connect(self.refreshcurrent) def eventFilter(self, object, event): if event.type() == QEvent.Resize: self.resize(self.width(), self.parent().height()) self.move(self.parent().width() - self.width(), 0) return super(InfoDock, self).eventFilter(object, event) def close(self): RoamEvents.selectioncleared.emit() super(InfoDock, self).close() def event(self, event): if event.type() == QEvent.Gesture: gesture = event.gesture(Qt.SwipeGesture) if gesture: self.pagenext() return QWidget.event(self, event) @property def selection(self): item = self.layerList.item(self.layerList.currentRow()) if not item: return cursor = item.data(Qt.UserRole) return cursor def openform(self): cursor = self.selection RoamEvents.openfeatureform.emit(cursor.form, cursor.feature, True) def editgeom(self): cursor = self.selection RoamEvents.editgeometry.emit(cursor.form, cursor.feature) def pageback(self): cursor = self.selection cursor.back() self.update(cursor) def pagenext(self): cursor = self.selection cursor.next() self.update(cursor) def layerIndexChanged(self, index): item = self.layerList.item(index) if not item: return cursor = item.data(Qt.UserRole) self.update(cursor) def setResults(self, results, forms): lastrow = self.layerList.currentRow() if lastrow == -1: lastrow = 0 self.clearResults() self.forms = forms for layer, features in results.iteritems(): if features: self._addResult(layer, features) self.layerList.setCurrentRow(lastrow) self.layerList.setMinimumWidth(self.layerList.sizeHintForColumn(0) + 20) self.navwidget.show() def show(self): if self.layerList.count() > 0: super(InfoDock, self).show() else: self.hide() def _addResult(self, layer, features): layername = layer.name() forms = self.forms.get(layername, []) if not forms: item = QListWidgetItem(QIcon(), layername, self.layerList) item.setData(Qt.UserRole, FeatureCursor(layer, features)) return for form in forms: itemtext = "{} \n ({})".format(layername, form.label) icon = QIcon(form.icon) item = QListWidgetItem(icon, itemtext, self.layerList) item.setData(Qt.UserRole, FeatureCursor(layer, features, form)) def refreshcurrent(self): self.update(self.selection) def update(self, cursor): if cursor is None: return try: feature = cursor.feature except NoFeature as ex: utils.warning(ex) return fields = [field.name() for field in feature.fields()] data = OrderedDict() items = [] for field, value in zip(fields, feature.attributes()): data[field] = value item = u"<tr><th>{0}</th> <td>${{{0}}}</td></tr>".format(field) items.append(item) rowtemple = Template(''.join(items)) rowshtml = updateTemplate(data, rowtemple) form = cursor.form layer = cursor.layer if form: name = "{}".format(layer.name(), form.label) else: name = layer.name() info = dict(TITLE=name, ROWS=rowshtml) html = updateTemplate(info, template) self.countlabel.setText(str(cursor)) self.attributesView.setHtml(html, templates.baseurl) self.editButton.setVisible(not form is None) self.featureupdated.emit(layer, feature, cursor.features) def clearResults(self): self.layerList.clear() self.attributesView.setHtml('') self.editButton.setVisible(False) self.navwidget.hide()