I would suggest getting rid of the default "Ok/Cancel" buttons and creating your own push buttons/event listeners (button may be created in Qt Designer):
code e.g:
def doSomething(self):
# process data
def closeForm(self):
self.dlg.close()
def initGui(self):
# more stuff here not shown
self.dlg.pushButton1.clicked.connect(self.doSomething)
self.dlg.pushButton2.clicked.connect(self.closeForm)
self.dlg.pushButton1.setShortcut("A")
When the user clicks the pushButton1 the data will be processed in the doSomething function and the form will stay open. When the user clicks the pushButton2 the form will close.
Update, added statement above when you click the "A" key it will call clicked event of pushButton1 which will call the doSomething function.
One method (which you are currently using) is the standard QComboBox. First we need to import some more classes so add this before importing PyQt4:
from qgis.core import QgsMapLayerRegistry, QgsField, QgsExpression
from PyQt4.QtCore import QVariant, QSettings, QTranslator, qVersion, QCoreApplication
Then in your run(self)
function, we can use the following (I've added some comments which hopefully will help you understand what's going on):
def run(self):
# here I populate a combo box in a form
layers = self.iface.legendInterface().layers()
layer_list = []
for layer in layers:
layer_list.append(layer.name())
self.dlg.Cmb_Capa.addItems(layer_list)
self.dlg.show()
result = self.dlg.exec_()
if result:
# Read current text from combo box
combo_layer = self.dlg.Cmb_Capa.currentText()
# Identify layer from table of contents by matching name with the combo box text
selected_layer = QgsMapLayerRegistry.instance().mapLayersByName(combo_layer)[0]
# Add a "real" field type with the name "Area"
selected_layer.dataProvider().addAttributes( [ QgsField("Area", QVariant.Double) ] )
# Update the attribute table
selected_layer.updateFields()
# Allow editing of the selected layer
selected_layer.startEditing()
# Find index of the newly-created "Area" field
idx = selected_layer.fieldNameIndex( "Area" )
# Use standard QgsExpression to calculate area for each feature
e = QgsExpression( """ $area """ )
e.prepare( selected_layer.pendingFields() )
for f in selected_layer.getFeatures():
f[idx] = e.evaluate(f)
selected_layer.updateFeature(f)
# Save the selected layer
selected_layer.commitChanges()
Alternatively, you could use QGIS custom widget: QgsMapLayerComboBox. This is automatically updated when any changes are made to the layer list (i.e. layers added/removed, names changed etc) without you having to refresh the plugin. To use this, you will need to import an additional class:
from qgis.gui import QgsMapLayerProxyModel
Now your run(self)
function can look like this:
def run(self):
# Here, QgsMapLayerComboBox automatically reads the relevant layers
self.dlg.Cmb_Capa.setFilters( QgsMapLayerProxyModel.PolygonLayer )
self.dlg.show()
result = self.dlg.exec_()
if result:
combo_layer = self.dlg.Cmb_Capa.currentText()
selected_layer = QgsMapLayerRegistry.instance().mapLayersByName(combo_layer)[0]
selected_layer.dataProvider().addAttributes( [ QgsField("Area", QVariant.Double) ] )
selected_layer.updateFields()
selected_layer.startEditing()
idx = selected_layer.fieldNameIndex( "Area" )
e = QgsExpression( """ $area """ )
e.prepare( selected_layer.pendingFields() )
for f in selected_layer.getFeatures():
f[idx] = e.evaluate(f)
selected_layer.updateFeature(f)
selected_layer.commitChanges()
Note: When using QGIS custom widgets, you may need to edit your "plugin_name_dialog_base.ui" file, find the QgsMapLayerComboBox class
and replace the <header>
to qgis.gui.h.
For example, it might look like this:
<customwidget>
<class>QgsMapLayerComboBox</class>
<extends>QComboBox</extends>
<header>qgsmaplayercombobox.h</header>
</customwidget>
And it needs to look like this:
<customwidget>
<class>QgsMapLayerComboBox</class>
<extends>QComboBox</extends>
<header>qgis.gui.h</header>
</customwidget>
Best Answer
I would use a Line Edit widget.
Inside your
run()
function put something like:Then define your
printSelectedArea
function:Note that this will print the selected area of your currently selected layer. You would need to adjust the code if you want to add areas across layers, which I didn't see from your question.
When a user chooses a layer in your
comboBox
you should disconnect theprintSelectedArea
slot from the previous selected layer and connect it to the new one. You could also disconnect theprintSelectedArea
slot from anySIGNAL
when your plugin is closed or unload. Otherwise, you would end up messingprintSelectedArea
calls up.Additionally, you would need to add conditionals to make sure there is a
selectedLayer
before callingselectedFeatures()
.