1

I'm working on a Plugin PyQGIS. I've wrote a method that calculate a buffer, with this buffer I get the id of a line, with this id I flash the line for a few seconds.

enter image description here

I used flashFeatureIds:

self.canvas.flashFeatureIds(layer, id_of_the_line , endColor = QColor(255,0,0,255), duration = 50000)

The method has a parameter duration, but I'm looking for this duration to depend of another action, like a click on a button linked to another method.

==> In place of a number of seconds, the line should be flashed until the user press another action button.

Is it possible to insert an action in a pre programmed parameter ?

1

It sounds as though using the QgsRubberBand class would work better for your use case. It is a class specifically for drawing transient features on the map canvas e.g. while digitizing new features etc. but can also be used to temporarily highlight existing features using the setToGeometry() method. Once you have created your rubber band object, you can show it using the show() method, then when the user clicks a button, call the reset() method.

This example may be more elaborate than you need, but below is a commented script which you can run in the Python console, play around with and inspect the code to see an example of creating a rubber band and setting it to the geometry of an existing line feature, after obtaining the feature id, then resetting/ hiding the rubber band on a button click.

The real rubber band functionality is in the show_rb() method. Note the QgsCoordinateTransform() to transform the feature geometry from the layer crs to the project crs (in case they are different).

You should be able to adapt this fairly easily into your plugin. Just remember that in a plugin you will need to import any classes you want to use.

class Dlg(QDialog):

    def __init__(self, iface):
        QDialog.__init__(self)
        # declare instance attributes
        self.iface = iface
        self.project = QgsProject().instance()
        self.canvas = self.iface.mapCanvas()
        # create rubber band object
        self.rb = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry)
        # create and set up gui widgets
        self.layout = QGridLayout(self)
        self.lbl_lyr = QLabel('Select layer:', self)
        self.lyr_cb = QgsMapLayerComboBox()
        self.lyr_cb.setFilters(QgsMapLayerProxyModel.LineLayer)
        self.lbl_fid = QLabel('Select feature ID:', self)
        self.fid_spin_box = QSpinBox(self)
        self.btn_show_rb = QPushButton('Show Rubber Band', self)
        self.btn_hide_rb = QPushButton('Hide Rubber Band', self)
        self.layout.addWidget(self.lbl_lyr, 0, 0)
        self.layout.addWidget(self.lyr_cb, 0, 1)
        self.layout.addWidget(self.lbl_fid, 0, 2)
        self.layout.addWidget(self.fid_spin_box, 0, 3)
        self.layout.addWidget(self.btn_show_rb, 1, 0, 1, 2)
        self.layout.addWidget(self.btn_hide_rb, 1, 2, 1, 2)
        # connect signals and slots
        self.lyr_cb.layerChanged.connect(self.layer_changed)
        self.btn_show_rb.clicked.connect(self.show_rb)
        # for one-line functions, use lambda functions as slots
        self.btn_hide_rb.clicked.connect(lambda: self.rb.reset()) # reset rubber band on button click
        self.rejected.connect(lambda: self.rb.reset()) # reset rubber band when dialog closed
        # call method to set feature id spin box range to max & min fids of current layer
        self.layer_changed(self.lyr_cb.currentLayer())

    def layer_changed(self, layer):
        min_fid = min([f.id() for f in layer.getFeatures()])
        max_fid = max([f.id() for f in layer.getFeatures()])
        self.fid_spin_box.setRange(min_fid, max_fid)

    def show_rb(self):
        # QgsVectorLayer object
        layer = self.lyr_cb.currentLayer()
        # QgsFeature object
        feat = layer.getFeature(self.fid_spin_box.value())
        # QgsGeometry object
        geom = feat.geometry()
        # in case layer crs does not match project crs, carry out transformation
        x_form = QgsCoordinateTransform(layer.crs(), self.project.crs(), self.project)
        geom.transform(x_form)
        # set rubber band to feature geometry
        self.rb.setToGeometry(geom)
        # set rubber band color and width
        self.rb.setStrokeColor(Qt.red)
        self.rb.setWidth(2)
        # show rubber band
        self.rb.show()


W = Dlg(iface)
W.show()

Result:

enter image description here

  • I didn't think of a rubberband, great trick tip ! I'll adapt my code, instead of the id of the line, if I get min and max id that would work. Thanks ! – Svhooren Dec 17 '19 at 8:28
  • You're welcome! Glad it helped. – Ben W Dec 17 '19 at 8:32
0

I'm not sure I understood your request correctly but I give you an example by passing in the parameters of your function the duration and adding a pushButton.

def flashFeature(self, number_seconds):
    self.canvas.flashFeatureIds(layer, id_of_the_line , endColor = QColor(255,0,0,255), 
    duration = number_seconds)

self.pushButton.pressed.connect(self.flashFeature(50000))

By spending your time in a function, it can vary very easily and you can imagine recovering the time from any other function.

  • Sorry, I thought I was clear because I know what I want, I'm editing my question : In place of a number of seconds, the line should be flashed until the user press another action button. – Svhooren Dec 16 '19 at 15:34
  • I confirm I misunderstood! From my post, if you set a duration of 0 instead of 50,000, does that solve your problem? It's not very elegant, I agree. – Vincent Bré Dec 16 '19 at 15:41
  • No, nothing is flashed :/ Maybe I would need to use a pyqt signals from the function to say "set the flash off" but I don't see how to do it, flashFeatureIds doesn't have a on/off parameter – Svhooren Dec 16 '19 at 15:49
  • If the duration is equal to 0, there is no flash, it's your goal no ? – Vincent Bré Dec 16 '19 at 15:54
  • No, the user click on the map, it makes a buffer, with the buffer i get the ID of a line, this line need to flashed until the user click on the insert button I've made, so if he takes 20 minutes it must be flashed 20 minutes, but if it takes 2 seconds it needs to be 2 second. Am I clearer ? if yes I will edit my question – Svhooren Dec 16 '19 at 16:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.