I'm not the QGIS Standalone application expert, but it seems logical to me that you can't access the iface
variable, because it represents the QGIS Dialog, but your are in a standalone application, without the QGIS DIalog by definition.
Side note, in your question, you are referring to QgsMapVectorLayer
and QgsMapVectorRegistry
which doesn't exist. I guess you are talking about QgsVectorLayer
. The QgsMapLayerRegistry
has been removed since QGIS 3: https://gis.stackexchange.com/a/244467/24505 If you are still using QGIS 2, I suggest you to update to QGIS 3.4 which the LTR version.
So to add a layer, you can use addMapLayer
from QgsProject
:
https://qgis.org/pyqgis/master/core/QgsProject.html?highlight=project#qgis.core.QgsProject.addMapLayer
Here is a example of a dialog showing a point.
#!/usr/bin/env python3
from qgis.core import (
QgsApplication, QgsVectorLayer, QgsProject, QgsFeature, QgsGeometry)
from qgis.gui import QgsMapCanvas, QgsLayerTreeMapCanvasBridge
from qgis.PyQt.QtWidgets import QDialog, QVBoxLayout
class MyDialog(QDialog):
def __init__(self, parent):
QDialog.__init__(self)
self.parent = parent
self.setLayout(QVBoxLayout())
project = QgsProject()
canvas = QgsMapCanvas()
bridge = QgsLayerTreeMapCanvasBridge(
project.layerTreeRoot(),
canvas
)
bridge.setCanvasLayers()
self.layout().addWidget(canvas)
layer = QgsVectorLayer(
'Point?'
'crs=epsg:4326&'
'field=id:integer&field=name:string(20)&index=yes',
'layer', 'memory')
feature = QgsFeature()
feature.setGeometry(QgsGeometry.fromWkt('POINT(10 10)'))
feature.setAttributes([1, 'Point'])
layer.startEditing()
layer.addFeature(feature)
layer.commitChanges()
layer.updateExtents()
project.addMapLayer(layer)
canvas.zoomToFullExtent()
self.exec_()
if __name__ == '__main__':
QgsApplication.setPrefixPath('/application/path/to/adapt', True)
application = QgsApplication([], True)
application.initQgis()
dialog = MyDialog(application)
application.exitQgis()
The bridge
makes the link between your QgsProject
and your QgsMapCanvas
. So you when you add a layer in project, it displayed in the map canvas.
I concur that there seems to be an issue with disconnecting the save()
and resetValues()
slots from the accepted
and rejected
signals of the QDialogButtonBox
of QgsAttributeForm
. Indeed, perusing the docs, I saw that the QgsAttributeForm
class has a method disconnectButtonBox()
which should take care of this nicely. Inspecting the c++ source code you can see that this method should simply disconnect both those slots from the button box signals. Unfortunately, I was just not able to get it to work.
Below is an effective workaround which makes use of the QgsAttributeForm.hideButtonBox()
method which does work. The basic idea is that we add two new, separate QPushButtons
to the custom .ui, which we connect to our own slot functions. We also hide the existing button box (which is the workaround for disconnecting the slots from it's signals).
It takes a little more effort to set up the custom .ui file in QtDesigner. Instead of using the 'create dialog with buttons bottom' option, I created a dialog without buttons. I then added the two QPushButton
objects (with a horizontal box layout); the rest of the form dialog is much the same. You still need to make sure the line edit object names match your field names. Additionally, I renamed the two push buttons btn_OK
and btn_Cancel
as we will be accessing them in the Python logic later. To make things look nice, I used a form layout for the whole dialog, set a maximum width for the two push buttons of 75, and added a couple of spacers. My dialog in QtDesigner looks like this:
The object names are:
Segment ID line edit --> Segment_ID
Name line edit --> Name
OK push button --> btn_OK
Cancel push button --> btn_Cancel
The Python file to add the logic looks like this:
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
nameField = None
myDialog = None
def formOpen(dialog, layerid, featureid):
global myDialog
myDialog = dialog
global nameField
nameField = dialog.findChild(QLineEdit,'Name')
buttonOK = dialog.findChild(QPushButton, 'btn_OK')
buttonCancel = dialog.findChild(QPushButton, 'btn_Cancel')
myDialog.hideButtonBox()
buttonOK.clicked.connect(validate)
buttonCancel.clicked.connect(close_dialog)
def validate():
# Make sure that the name field isn't empty.
if not len(nameField.text()) > 0 or nameField.text() == 'NULL':
msgBox = QMessageBox()
msgBox.setText('Name field can not be null.')
msgBox.exec_()
myDialog.resetValues()
else:
# Return the form as accpeted to QGIS.
myDialog.save()
close_dialog()
def close_dialog():
if isinstance(myDialog.parent(), QDialog):
myDialog.parent().close()
After enabling macros in Settings-> Options-> General-> Project Files, I recorded the following two short screencasts showing the resulting behavior when adding and editing features.
Adding a new feature:
Editing an existing feature:
Best Answer
The following script generates a simple form. You can use it in your standalone PyQGIS application to display attributes for a given feature. You can also specify fields to be displayed (or not to be displayed).
Without
only
andexclude
:With
only
parameter: JustType
andArea
are displayedWith
exclude
parameter: All fields are displayed except ofType
andArea