Use File and CRS Selection Widgets in PyQGIS

Almost every plugin dialog asks for the same few things: an input file, an output folder, a coordinate reference system, an extent, a field. Plain Qt widgets can do each — a line edit plus a browse button plus a QFileDialog — but QGIS ships specialised widgets that already behave the way QGIS users expect: CRS selection with search and recently used systems, file pickers that remember folders and filter by GIS formats, extent boxes that take the canvas or a layer's extent. Using them makes dialogs smaller, more consistent and less buggy.

This recipe belongs to Qt Designer for GIS Interfaces. It sets up file and folder pickers, CRS selection, extent input and field selection, wires them together, uses them in Qt Designer, and reads their values safely.

QGIS input widgets for common questionsFour widgets answer the usual dialog questions. QgsFileWidget picks a file or folder with filters and a remembered last location. QgsProjectionSelectionWidget picks a CRS with search, project CRS, layer CRS and recent choices. QgsExtentGroupBox sets an extent from the canvas, a layer or by drawing. QgsFieldComboBox picks a field from a layer, optionally filtered by type.Which file, which CRS, where, which fieldQgsFileWidgetfile or folderfiltersremembers folderQgsProjection-SelectionWidgetsearch, recentsproject/layer CRSQgsExtentGroupBoxcanvas, layer,draw on mapwith CRSQgsFieldComboBoxfields of a layertype filterlinked to layer

Prerequisites

Pick files and folders with QgsFileWidget

QgsFileWidget combines a line edit, a browse button and drag-and-drop. Its storage mode decides whether it picks an existing file, a file to save, or a folder.

from qgis.gui import QgsFileWidget
from qgis.PyQt.QtWidgets import QDialog, QFormLayout

dlg = QDialog()
form = QFormLayout(dlg)

input_file = QgsFileWidget()
input_file.setStorageMode(QgsFileWidget.GetFile)
input_file.setFilter("GeoPackage (*.gpkg);;Shapefile (*.shp);;All files (*)")
input_file.setDialogTitle("Choose the input layer")
input_file.setDefaultRoot("/data/inbox")

output_dir = QgsFileWidget()
output_dir.setStorageMode(QgsFileWidget.GetDirectory)

report = QgsFileWidget()
report.setStorageMode(QgsFileWidget.SaveFile)
report.setFilter("PDF (*.pdf)")
report.setConfirmOverwrite(True)

form.addRow("Input", input_file)
form.addRow("Output folder", output_dir)
form.addRow("Report", report)
input_file.fileChanged.connect(lambda path: print("input:", path))

Breakdown: GetFile, GetMultipleFiles, GetDirectory and SaveFile cover the cases; for saving, setConfirmOverwrite asks before replacing an existing file. Filters use the familiar Qt syntax and appear in the browse dialog; QGIS can also generate a filter of every supported vector format with QgsProviderRegistry.instance().fileVectorFilters(). setDefaultRoot sets the starting folder; the widget also remembers the last folder used. fileChanged fires whether the user browses, types or drops a file, so one handler covers all input methods.

Pick a CRS with QgsProjectionSelectionWidget

CRS choice is where hand-built dialogs are weakest. The projection selection widget offers the project CRS, layer CRS, recently used systems and a full searchable dialog.

Options in the CRS selectorThe CRS selector's drop-down lists the project CRS, the CRS of a chosen layer, a default, recently used systems and an option to open the full CRS dialog with search by name or code. Options can be hidden or added with setOptionVisible. The current selection is read with crs() and changes are signalled by crsChanged.Common choices first, search when neededquick choicesproject CRSlayer CRSrecently usedfull dialogsearch name/codeany CRSread itcrs()crsChanged

from qgis.gui import QgsProjectionSelectionWidget
from qgis.core import QgsCoordinateReferenceSystem, QgsProject

crs_widget = QgsProjectionSelectionWidget()
crs_widget.setCrs(QgsProject.instance().crs())
crs_widget.setOptionVisible(QgsProjectionSelectionWidget.ProjectCrs, True)
crs_widget.setOptionVisible(QgsProjectionSelectionWidget.CrsNotSet, False)
crs_widget.setLayerCrs(QgsCoordinateReferenceSystem("EPSG:25832"))
crs_widget.crsChanged.connect(lambda crs: print("target CRS:", crs.authid()))
form.addRow("Target CRS", crs_widget)

Breakdown: Starting with the project CRS matches what users usually want. Hiding "not set" prevents an invalid choice when a CRS is mandatory; keep it visible when "no change" is a valid answer. setLayerCrs adds the input layer's CRS as a quick option — update it when the input changes. crs() returns a QgsCoordinateReferenceSystem, so no parsing of strings is needed. For geographic versus projected checks before analysis, see choosing a projected CRS for analysis.

Pick an extent with QgsExtentGroupBox

Clip, export and download dialogs need an area. The extent group box lets users take the current canvas extent, a layer's extent, draw on the map, or type coordinates — and reports the extent with its CRS.

from qgis.gui import QgsExtentGroupBox
from qgis.utils import iface

extent_box = QgsExtentGroupBox()
extent_box.setMapCanvas(iface.mapCanvas())
extent_box.setOriginalExtent(iface.mapCanvas().extent(), iface.mapCanvas().mapSettings().destinationCrs())
extent_box.setCurrentExtent(iface.mapCanvas().extent(), iface.mapCanvas().mapSettings().destinationCrs())
extent_box.setOutputCrs(crs_widget.crs())
extent_box.setCheckable(True)
extent_box.setChecked(False)
form.addRow(extent_box)
crs_widget.crsChanged.connect(extent_box.setOutputCrs)

Breakdown: Giving the box the map canvas enables "draw on canvas" and "map canvas extent" buttons. The output CRS is the CRS in which outputExtent() returns coordinates — linking it to the CRS widget keeps the two consistent when the user changes the target CRS. A checkable group box makes the extent optional: unchecked means "whole layer". Read extent_box.isChecked() before using the extent.

Dialogs are easier to use when widgets react to each other: choosing a layer fills the field list, choosing an input file sets a sensible output name.

Widgets that update each otherA map layer combo box drives a field combo box, which lists only that layer's numeric fields. The layer's CRS is offered in the CRS selector. Choosing an input file suggests an output file name in the output folder. Each link is one signal connection.One signal per linklayer combolayerChangedfield combosetLayer()CRS selectorsetLayerCrs()file widgetsuggests outputname

from qgis.gui import QgsMapLayerComboBox, QgsFieldComboBox
from qgis.core import QgsMapLayerProxyModel, QgsFieldProxyModel
from pathlib import Path

layer_combo = QgsMapLayerComboBox()
layer_combo.setFilters(QgsMapLayerProxyModel.PolygonLayer)
field_combo = QgsFieldComboBox()
field_combo.setFilters(QgsFieldProxyModel.Numeric)
field_combo.setAllowEmptyFieldName(False)

def on_layer(layer):
    field_combo.setLayer(layer)
    if layer is not None:
        crs_widget.setLayerCrs(layer.crs())

layer_combo.layerChanged.connect(on_layer)
on_layer(layer_combo.currentLayer())

def suggest_output(path):
    if path and not report.filePath():
        report.setFilePath(str(Path(path).with_name(Path(path).stem + "_report.pdf")))
input_file.fileChanged.connect(suggest_output)
form.insertRow(0, "Layer", layer_combo)
form.insertRow(1, "Value field", field_combo)

Breakdown: Filtering the layer combo to polygon layers and the field combo to numeric fields means users can only choose valid inputs, which removes a whole class of validation code. Calling on_layer once at setup initialises the field list for the preselected layer. Suggesting an output name from the input saves typing but never overwrites a name the user already entered. Using a QgsMapLayerComboBox in a plugin dialog covers layer combo options in more depth.

Other QGIS widgets worth knowing

The same family includes widgets for most other values a GIS dialog asks for. Each replaces a plain Qt widget with one that understands QGIS conventions.

from qgis.gui import (QgsDoubleSpinBox, QgsColorButton, QgsScaleWidget,
                      QgsFieldExpressionWidget, QgsDateTimeEdit)
from qgis.PyQt.QtGui import QColor

distance = QgsDoubleSpinBox()
distance.setRange(0, 10000)
distance.setSuffix(" m")
distance.setClearValue(100)            # reset button returns to 100

colour = QgsColorButton()
colour.setColor(QColor("#2563eb"))
colour.setAllowOpacity(True)

scale = QgsScaleWidget()
scale.setMapCanvas(iface.mapCanvas())  # "current scale" button

expr = QgsFieldExpressionWidget()
expr.setLayer(layer_combo.currentLayer())
layer_combo.layerChanged.connect(expr.setLayer)

for label, w in (("Buffer distance", distance), ("Highlight colour", colour),
                 ("Export scale", scale), ("Filter", expr)):
    form.addRow(label, w)

Breakdown: QgsDoubleSpinBox adds a clear button that resets to a defined value — handy for "back to default". QgsColorButton opens QGIS's colour picker with palettes, recent colours and opacity. QgsScaleWidget accepts scales in the familiar 1:x form and can take the current canvas scale. QgsFieldExpressionWidget lets users pick a field or write an expression with the full expression builder, linked to the chosen layer — far more flexible than a field combo where users might want "area / 10000" instead of a column. All of them can be placed in Qt Designer like the widgets above.

Use the widgets in Qt Designer

All these widgets are available in Qt Designer's QGIS widget palette when Designer is started with QGIS's plugin path; in the .ui file they appear with their QGIS class names and are created automatically when the form loads.

from qgis.PyQt import uic
import os

FORM, _ = uic.loadUiType(os.path.join(os.path.dirname(__file__), "export_dialog.ui"))

class ExportDialog(QDialog, FORM):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)
        self.mOutputFolder.setStorageMode(QgsFileWidget.GetDirectory)
        self.mLayer.layerChanged.connect(self.mField.setLayer)
        self.mField.setLayer(self.mLayer.currentLayer())

    def values(self):
        return {"layer": self.mLayer.currentLayer(), "field": self.mField.currentField(),
                "crs": self.mCrs.crs(), "folder": self.mOutputFolder.filePath()}

Breakdown: Widgets placed in Designer are configured in code after setupUi for anything Designer does not expose, such as storage modes and signal links. A values() method gathering everything into a dictionary keeps the rest of the plugin independent of widget names. Validate the dictionary before running — empty paths, invalid CRSs — as in validating plugin dialog input. Setting up Designer with QGIS widgets is covered in using QGIS custom widgets in Qt Designer.

QGIS version compatibility

QgsFileWidget, QgsProjectionSelectionWidget, QgsExtentGroupBox, QgsMapLayerComboBox and QgsFieldComboBox work on QGIS 3.34 LTR, 3.40 LTR and QGIS 4. On QGIS 4, filter and mode enums are scoped — QgsFileWidget.StorageMode.GetFile, Qgis.LayerFilter.PolygonLayer, QgsFieldProxyModel.Filter.Numeric — and QgsMapLayerProxyModel filters moved to Qgis.LayerFilter in 3.34.

Troubleshooting

  • The field list stays empty. The field combo was never given a layer; connect layerChanged and initialise once.
  • The extent is in the wrong CRS. The extent box's output CRS was not set or not updated with the CRS widget.
  • The file widget returns an empty string. The user typed nothing; validate before running.
  • QGIS widgets are missing in Designer. Designer was not started with QGIS's widget plugin path.

Conclusion

Use QgsFileWidget with the right storage mode and filters, QgsProjectionSelectionWidget with sensible quick options, QgsExtentGroupBox linked to the canvas and output CRS, and filtered layer and field combo boxes linked by signals; configure Designer-placed widgets after setupUi, gather values in one method and validate them before running.

Frequently Asked Questions

Can QgsFileWidget pick multiple files? Yes — GetMultipleFiles mode, read with splitFilePaths(filePath()).

Can a file widget accept URLs? Yes — users can paste a URL into the line edit, and filePath() returns it; check whether your code accepts remote sources before allowing it.

How do I remember the last values between sessions? Store them with QgsSettings on accept and restore them on open, as in remembering the last used folder.

Do these widgets respect QGIS's theme and language? Yes. They use QGIS's own icons, styling and translations, which is one reason dialogs built from them feel native in every language QGIS supports.

Can I disable typing so users must browse? Set the file widget's line edit read-only with lineEdit().setReadOnly(True); browsing and drag-and-drop still work.

Is there a widget for picking a Processing output? Processing parameter widgets exist, but for plugins the widgets here are simpler.

Can the CRS widget show only projected systems? Validate the choice instead; the widget lists all CRSs, and a check on isGeographic() with a message is clearer.