Force canvas to predefined scales : help needed to improve a script

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Force canvas to predefined scales : help needed to improve a script

kimaidou
Hi QGIS !

I would like to be able in QGIS to force the canvas scales to some predefined ones. Since we now have project scales (and QGIS pre-defined scales) in the scale selector, we could use them, and add a simple checkbox near the combobox "Stick to predefined scales".

The aim is to force the canvas to render only at these scales. For example, if I use the rectangle zoom tool, and I should go toe 1/56003, I would instead land on 1/50000 , the closest pre-defined scale.

I made a little python script as a proof of concept to illustrate it :
http://paste.debian.net/167247/

In this script, I hard coded the scales, but oviously we should get them from the project properties.

I have a question regarding my script. Since I use the QgsMapCanvas::scaleChanged signal, I assume the rendering is done twice ? Once the "normal" way, and once again after I use the QgsMapCanvas::zoomScale method after calculating the new target scale.

Any idea for improving it ? I assume I would have to disconnect one (not found yet) signal/slot, then set the scale and reconnect this signal/slot afterwards ?

Regards

Michaël


_______________________________________________
Qgis-developer mailing list
[hidden email]
http://lists.osgeo.org/mailman/listinfo/qgis-developer
Reply | Threaded
Open this post in threaded view
|

Re: Force canvas to predefined scales : help needed to improve a script

Hugo Mercier
Hi,

Le 17/04/2015 10:46, kimaidou a écrit :

> Hi QGIS !
>
> I would like to be able in QGIS to force the canvas scales to some
> predefined ones. Since we now have project scales (and QGIS pre-defined
> scales) in the scale selector, we could use them, and add a simple
> checkbox near the combobox "Stick to predefined scales".
>
> The aim is to force the canvas to render only at these scales. For
> example, if I use the rectangle zoom tool, and I should go toe 1/56003,
> I would instead land on 1/50000 , the closest pre-defined scale.

That would be nice to have it in the core. I already had to do something
similar with a plugin.

>
> I made a little python script as a proof of concept to illustrate it :
> http://paste.debian.net/167247/
>
> In this script, I hard coded the scales, but oviously we should get them
> from the project properties.
>
> I have a question regarding my script. Since I use the
> QgsMapCanvas::scaleChanged signal, I assume the rendering is done twice
> ? Once the "normal" way, and once again after I use the
> QgsMapCanvas::zoomScale method after calculating the new target scale.
>
> Any idea for improving it ? I assume I would have to disconnect one (not
> found yet) signal/slot, then set the scale and reconnect this
> signal/slot afterwards ?

QObject::blockSignals might be of help here. But in this case, you don't
want to block all signals, only yours.

I guess your solution would work.
You can also use a flag in your callback, something like :

    def setScale( self, scale ):
        if self.reentrant:
                return

        targetScale = min(
            self.predefinedScales,
            key=lambda x:abs(x-scale)
        )
        self.reentrant = True
        self.mc.zoomScale( targetScale )
        self.reentrant = False

_______________________________________________
Qgis-developer mailing list
[hidden email]
http://lists.osgeo.org/mailman/listinfo/qgis-developer
Reply | Threaded
Open this post in threaded view
|

Re: Force canvas to predefined scales : help needed to improve a script

kimaidou
Hi Hugo,

Thanks for the anwser. I used some "print scale" and "print targetScale" in my script to check the behavious, and I did not have any "infinite loop" or strange behaviour with it. It seems the related QGIS code is armed against this risks.

This python script was only a prototype, and I will surely have a look at the cpp classes and propose a PR to add this option in QGIS core.

Michaël

2015-04-17 11:26 GMT+02:00 Hugo Mercier <[hidden email]>:
Hi,

Le 17/04/2015 10:46, kimaidou a écrit :
> Hi QGIS !
>
> I would like to be able in QGIS to force the canvas scales to some
> predefined ones. Since we now have project scales (and QGIS pre-defined
> scales) in the scale selector, we could use them, and add a simple
> checkbox near the combobox "Stick to predefined scales".
>
> The aim is to force the canvas to render only at these scales. For
> example, if I use the rectangle zoom tool, and I should go toe 1/56003,
> I would instead land on 1/50000 , the closest pre-defined scale.

That would be nice to have it in the core. I already had to do something
similar with a plugin.

>
> I made a little python script as a proof of concept to illustrate it :
> http://paste.debian.net/167247/
>
> In this script, I hard coded the scales, but oviously we should get them
> from the project properties.
>
> I have a question regarding my script. Since I use the
> QgsMapCanvas::scaleChanged signal, I assume the rendering is done twice
> ? Once the "normal" way, and once again after I use the
> QgsMapCanvas::zoomScale method after calculating the new target scale.
>
> Any idea for improving it ? I assume I would have to disconnect one (not
> found yet) signal/slot, then set the scale and reconnect this
> signal/slot afterwards ?

QObject::blockSignals might be of help here. But in this case, you don't
want to block all signals, only yours.

I guess your solution would work.
You can also use a flag in your callback, something like :

    def setScale( self, scale ):
        if self.reentrant:
                return

        targetScale = min(
            self.predefinedScales,
            key=lambda x:abs(x-scale)
        )
        self.reentrant = True
        self.mc.zoomScale( targetScale )
        self.reentrant = False

_______________________________________________
Qgis-developer mailing list
[hidden email]
http://lists.osgeo.org/mailman/listinfo/qgis-developer


_______________________________________________
Qgis-developer mailing list
[hidden email]
http://lists.osgeo.org/mailman/listinfo/qgis-developer
Reply | Threaded
Open this post in threaded view
|

Re: Force canvas to predefined scales : help needed to improve a script

kimaidou

* "replayed" variable to avoid probable infinite loop, thanks to Hugo's proposal

* you can now initialize the method with a python list of scales you want to force canvas to: 

fs =  fs = forcedScale( [25000, 50000, 100000 ] )

Cheers,
Michaël

2015-04-17 12:27 GMT+02:00 kimaidou <[hidden email]>:
Hi Hugo,

Thanks for the anwser. I used some "print scale" and "print targetScale" in my script to check the behavious, and I did not have any "infinite loop" or strange behaviour with it. It seems the related QGIS code is armed against this risks.

This python script was only a prototype, and I will surely have a look at the cpp classes and propose a PR to add this option in QGIS core.

Michaël

2015-04-17 11:26 GMT+02:00 Hugo Mercier <[hidden email]>:
Hi,

Le 17/04/2015 10:46, kimaidou a écrit :
> Hi QGIS !
>
> I would like to be able in QGIS to force the canvas scales to some
> predefined ones. Since we now have project scales (and QGIS pre-defined
> scales) in the scale selector, we could use them, and add a simple
> checkbox near the combobox "Stick to predefined scales".
>
> The aim is to force the canvas to render only at these scales. For
> example, if I use the rectangle zoom tool, and I should go toe 1/56003,
> I would instead land on 1/50000 , the closest pre-defined scale.

That would be nice to have it in the core. I already had to do something
similar with a plugin.

>
> I made a little python script as a proof of concept to illustrate it :
> http://paste.debian.net/167247/
>
> In this script, I hard coded the scales, but oviously we should get them
> from the project properties.
>
> I have a question regarding my script. Since I use the
> QgsMapCanvas::scaleChanged signal, I assume the rendering is done twice
> ? Once the "normal" way, and once again after I use the
> QgsMapCanvas::zoomScale method after calculating the new target scale.
>
> Any idea for improving it ? I assume I would have to disconnect one (not
> found yet) signal/slot, then set the scale and reconnect this
> signal/slot afterwards ?

QObject::blockSignals might be of help here. But in this case, you don't
want to block all signals, only yours.

I guess your solution would work.
You can also use a flag in your callback, something like :

    def setScale( self, scale ):
        if self.reentrant:
                return

        targetScale = min(
            self.predefinedScales,
            key=lambda x:abs(x-scale)
        )
        self.reentrant = True
        self.mc.zoomScale( targetScale )
        self.reentrant = False

_______________________________________________
Qgis-developer mailing list
[hidden email]
http://lists.osgeo.org/mailman/listinfo/qgis-developer



_______________________________________________
Qgis-developer mailing list
[hidden email]
http://lists.osgeo.org/mailman/listinfo/qgis-developer
Reply | Threaded
Open this post in threaded view
|

Re: Force canvas to predefined scales : help needed to improve a script

Nathan Woodrow
def __init__( self, *predefinedScales ):
and you can do this

forcedScale(25000, 50000, 100000)

On Fri, 17 Apr 2015 at 22:30 kimaidou <[hidden email]> wrote:

* "replayed" variable to avoid probable infinite loop, thanks to Hugo's proposal

* you can now initialize the method with a python list of scales you want to force canvas to: 

fs =  fs = forcedScale( [25000, 50000, 100000 ] )

Cheers,
Michaël

2015-04-17 12:27 GMT+02:00 kimaidou <[hidden email]>:
Hi Hugo,

Thanks for the anwser. I used some "print scale" and "print targetScale" in my script to check the behavious, and I did not have any "infinite loop" or strange behaviour with it. It seems the related QGIS code is armed against this risks.

This python script was only a prototype, and I will surely have a look at the cpp classes and propose a PR to add this option in QGIS core.

Michaël

2015-04-17 11:26 GMT+02:00 Hugo Mercier <[hidden email]>:
Hi,

Le 17/04/2015 10:46, kimaidou a écrit :
> Hi QGIS !
>
> I would like to be able in QGIS to force the canvas scales to some
> predefined ones. Since we now have project scales (and QGIS pre-defined
> scales) in the scale selector, we could use them, and add a simple
> checkbox near the combobox "Stick to predefined scales".
>
> The aim is to force the canvas to render only at these scales. For
> example, if I use the rectangle zoom tool, and I should go toe 1/56003,
> I would instead land on 1/50000 , the closest pre-defined scale.

That would be nice to have it in the core. I already had to do something
similar with a plugin.

>
> I made a little python script as a proof of concept to illustrate it :
> http://paste.debian.net/167247/
>
> In this script, I hard coded the scales, but oviously we should get them
> from the project properties.
>
> I have a question regarding my script. Since I use the
> QgsMapCanvas::scaleChanged signal, I assume the rendering is done twice
> ? Once the "normal" way, and once again after I use the
> QgsMapCanvas::zoomScale method after calculating the new target scale.
>
> Any idea for improving it ? I assume I would have to disconnect one (not
> found yet) signal/slot, then set the scale and reconnect this
> signal/slot afterwards ?

QObject::blockSignals might be of help here. But in this case, you don't
want to block all signals, only yours.

I guess your solution would work.
You can also use a flag in your callback, something like :

    def setScale( self, scale ):
        if self.reentrant:
                return

        targetScale = min(
            self.predefinedScales,
            key=lambda x:abs(x-scale)
        )
        self.reentrant = True
        self.mc.zoomScale( targetScale )
        self.reentrant = False

_______________________________________________
Qgis-developer mailing list
[hidden email]
http://lists.osgeo.org/mailman/listinfo/qgis-developer


_______________________________________________
Qgis-developer mailing list
[hidden email]
http://lists.osgeo.org/mailman/listinfo/qgis-developer

_______________________________________________
Qgis-developer mailing list
[hidden email]
http://lists.osgeo.org/mailman/listinfo/qgis-developer
Reply | Threaded
Open this post in threaded view
|

Re: Force canvas to predefined scales : help needed to improve a script

kimaidou
More pythonesque Nathan, thanks !

2015-04-17 14:32 GMT+02:00 Nathan Woodrow <[hidden email]>:
def __init__( self, *predefinedScales ):
and you can do this

forcedScale(25000, 50000, 100000)

On Fri, 17 Apr 2015 at 22:30 kimaidou <[hidden email]> wrote:

* "replayed" variable to avoid probable infinite loop, thanks to Hugo's proposal

* you can now initialize the method with a python list of scales you want to force canvas to: 

fs =  fs = forcedScale( [25000, 50000, 100000 ] )

Cheers,
Michaël

2015-04-17 12:27 GMT+02:00 kimaidou <[hidden email]>:
Hi Hugo,

Thanks for the anwser. I used some "print scale" and "print targetScale" in my script to check the behavious, and I did not have any "infinite loop" or strange behaviour with it. It seems the related QGIS code is armed against this risks.

This python script was only a prototype, and I will surely have a look at the cpp classes and propose a PR to add this option in QGIS core.

Michaël

2015-04-17 11:26 GMT+02:00 Hugo Mercier <[hidden email]>:
Hi,

Le 17/04/2015 10:46, kimaidou a écrit :
> Hi QGIS !
>
> I would like to be able in QGIS to force the canvas scales to some
> predefined ones. Since we now have project scales (and QGIS pre-defined
> scales) in the scale selector, we could use them, and add a simple
> checkbox near the combobox "Stick to predefined scales".
>
> The aim is to force the canvas to render only at these scales. For
> example, if I use the rectangle zoom tool, and I should go toe 1/56003,
> I would instead land on 1/50000 , the closest pre-defined scale.

That would be nice to have it in the core. I already had to do something
similar with a plugin.

>
> I made a little python script as a proof of concept to illustrate it :
> http://paste.debian.net/167247/
>
> In this script, I hard coded the scales, but oviously we should get them
> from the project properties.
>
> I have a question regarding my script. Since I use the
> QgsMapCanvas::scaleChanged signal, I assume the rendering is done twice
> ? Once the "normal" way, and once again after I use the
> QgsMapCanvas::zoomScale method after calculating the new target scale.
>
> Any idea for improving it ? I assume I would have to disconnect one (not
> found yet) signal/slot, then set the scale and reconnect this
> signal/slot afterwards ?

QObject::blockSignals might be of help here. But in this case, you don't
want to block all signals, only yours.

I guess your solution would work.
You can also use a flag in your callback, something like :

    def setScale( self, scale ):
        if self.reentrant:
                return

        targetScale = min(
            self.predefinedScales,
            key=lambda x:abs(x-scale)
        )
        self.reentrant = True
        self.mc.zoomScale( targetScale )
        self.reentrant = False

_______________________________________________
Qgis-developer mailing list
[hidden email]
http://lists.osgeo.org/mailman/listinfo/qgis-developer


_______________________________________________
Qgis-developer mailing list
[hidden email]
http://lists.osgeo.org/mailman/listinfo/qgis-developer


_______________________________________________
Qgis-developer mailing list
[hidden email]
http://lists.osgeo.org/mailman/listinfo/qgis-developer
Reply | Threaded
Open this post in threaded view
|

Re: Force canvas to predefined scales : help needed to improve a script

Nathan Woodrow
And here is a different way using just functions:

(https://gist.github.com/mdouchin/72a09b629a5557c1dc1c#comment-1435388)

def forcedScale(*scales):
    replayed = False
    
    def setScale(scale ):
        iface.mapCanvas().scaleChanged.disconnect(setScale)
        
        print "initial scale: %s" % scale
        
        targetScale = min(
            scales, 
            key=lambda x:abs(x-scale)
        )
        if targetScale == scale:
            return
        
        print "zoom to %s" % targetScale
        iface.mapCanvas().zoomScale( targetScale )
        iface.mapCanvas().scaleChanged.connect(setScale)
        
    # pre-defined scales
    predefinedScales = [
        5000,
        10000,
        25000,
        50000,
        100000,
        250000,
        500000
    ]
    
    # avoid loop

    iface.mapCanvas().scaleChanged.connect(setScale)
    
    if not scales:
        scales = predefinedScales

On Fri, 17 Apr 2015 at 22:34 kimaidou <[hidden email]> wrote:
More pythonesque Nathan, thanks !

2015-04-17 14:32 GMT+02:00 Nathan Woodrow <[hidden email]>:
def __init__( self, *predefinedScales ):
and you can do this

forcedScale(25000, 50000, 100000)

On Fri, 17 Apr 2015 at 22:30 kimaidou <[hidden email]> wrote:

* "replayed" variable to avoid probable infinite loop, thanks to Hugo's proposal

* you can now initialize the method with a python list of scales you want to force canvas to: 

fs =  fs = forcedScale( [25000, 50000, 100000 ] )

Cheers,
Michaël

2015-04-17 12:27 GMT+02:00 kimaidou <[hidden email]>:
Hi Hugo,

Thanks for the anwser. I used some "print scale" and "print targetScale" in my script to check the behavious, and I did not have any "infinite loop" or strange behaviour with it. It seems the related QGIS code is armed against this risks.

This python script was only a prototype, and I will surely have a look at the cpp classes and propose a PR to add this option in QGIS core.

Michaël

2015-04-17 11:26 GMT+02:00 Hugo Mercier <[hidden email]>:
Hi,

Le 17/04/2015 10:46, kimaidou a écrit :
> Hi QGIS !
>
> I would like to be able in QGIS to force the canvas scales to some
> predefined ones. Since we now have project scales (and QGIS pre-defined
> scales) in the scale selector, we could use them, and add a simple
> checkbox near the combobox "Stick to predefined scales".
>
> The aim is to force the canvas to render only at these scales. For
> example, if I use the rectangle zoom tool, and I should go toe 1/56003,
> I would instead land on 1/50000 , the closest pre-defined scale.

That would be nice to have it in the core. I already had to do something
similar with a plugin.

>
> I made a little python script as a proof of concept to illustrate it :
> http://paste.debian.net/167247/
>
> In this script, I hard coded the scales, but oviously we should get them
> from the project properties.
>
> I have a question regarding my script. Since I use the
> QgsMapCanvas::scaleChanged signal, I assume the rendering is done twice
> ? Once the "normal" way, and once again after I use the
> QgsMapCanvas::zoomScale method after calculating the new target scale.
>
> Any idea for improving it ? I assume I would have to disconnect one (not
> found yet) signal/slot, then set the scale and reconnect this
> signal/slot afterwards ?

QObject::blockSignals might be of help here. But in this case, you don't
want to block all signals, only yours.

I guess your solution would work.
You can also use a flag in your callback, something like :

    def setScale( self, scale ):
        if self.reentrant:
                return

        targetScale = min(
            self.predefinedScales,
            key=lambda x:abs(x-scale)
        )
        self.reentrant = True
        self.mc.zoomScale( targetScale )
        self.reentrant = False

_______________________________________________
Qgis-developer mailing list
[hidden email]
http://lists.osgeo.org/mailman/listinfo/qgis-developer


_______________________________________________
Qgis-developer mailing list
[hidden email]
http://lists.osgeo.org/mailman/listinfo/qgis-developer


_______________________________________________
Qgis-developer mailing list
[hidden email]
http://lists.osgeo.org/mailman/listinfo/qgis-developer
Reply | Threaded
Open this post in threaded view
|

Re: Force canvas to predefined scales : help needed to improve a script

kimaidou
Thanks a lot Nathan !

2015-04-17 14:40 GMT+02:00 Nathan Woodrow <[hidden email]>:
And here is a different way using just functions:

(https://gist.github.com/mdouchin/72a09b629a5557c1dc1c#comment-1435388)

def forcedScale(*scales):
    replayed = False
    
    def setScale(scale ):
        iface.mapCanvas().scaleChanged.disconnect(setScale)
        
        print "initial scale: %s" % scale
        
        targetScale = min(
            scales, 
            key=lambda x:abs(x-scale)
        )
        if targetScale == scale:
            return
        
        print "zoom to %s" % targetScale
        iface.mapCanvas().zoomScale( targetScale )
        iface.mapCanvas().scaleChanged.connect(setScale)
        
    # pre-defined scales
    predefinedScales = [
        5000,
        10000,
        25000,
        50000,
        100000,
        250000,
        500000
    ]
    
    # avoid loop

    iface.mapCanvas().scaleChanged.connect(setScale)
    
    if not scales:
        scales = predefinedScales

On Fri, 17 Apr 2015 at 22:34 kimaidou <[hidden email]> wrote:
More pythonesque Nathan, thanks !

2015-04-17 14:32 GMT+02:00 Nathan Woodrow <[hidden email]>:
def __init__( self, *predefinedScales ):
and you can do this

forcedScale(25000, 50000, 100000)

On Fri, 17 Apr 2015 at 22:30 kimaidou <[hidden email]> wrote:

* "replayed" variable to avoid probable infinite loop, thanks to Hugo's proposal

* you can now initialize the method with a python list of scales you want to force canvas to: 

fs =  fs = forcedScale( [25000, 50000, 100000 ] )

Cheers,
Michaël

2015-04-17 12:27 GMT+02:00 kimaidou <[hidden email]>:
Hi Hugo,

Thanks for the anwser. I used some "print scale" and "print targetScale" in my script to check the behavious, and I did not have any "infinite loop" or strange behaviour with it. It seems the related QGIS code is armed against this risks.

This python script was only a prototype, and I will surely have a look at the cpp classes and propose a PR to add this option in QGIS core.

Michaël

2015-04-17 11:26 GMT+02:00 Hugo Mercier <[hidden email]>:
Hi,

Le 17/04/2015 10:46, kimaidou a écrit :
> Hi QGIS !
>
> I would like to be able in QGIS to force the canvas scales to some
> predefined ones. Since we now have project scales (and QGIS pre-defined
> scales) in the scale selector, we could use them, and add a simple
> checkbox near the combobox "Stick to predefined scales".
>
> The aim is to force the canvas to render only at these scales. For
> example, if I use the rectangle zoom tool, and I should go toe 1/56003,
> I would instead land on 1/50000 , the closest pre-defined scale.

That would be nice to have it in the core. I already had to do something
similar with a plugin.

>
> I made a little python script as a proof of concept to illustrate it :
> http://paste.debian.net/167247/
>
> In this script, I hard coded the scales, but oviously we should get them
> from the project properties.
>
> I have a question regarding my script. Since I use the
> QgsMapCanvas::scaleChanged signal, I assume the rendering is done twice
> ? Once the "normal" way, and once again after I use the
> QgsMapCanvas::zoomScale method after calculating the new target scale.
>
> Any idea for improving it ? I assume I would have to disconnect one (not
> found yet) signal/slot, then set the scale and reconnect this
> signal/slot afterwards ?

QObject::blockSignals might be of help here. But in this case, you don't
want to block all signals, only yours.

I guess your solution would work.
You can also use a flag in your callback, something like :

    def setScale( self, scale ):
        if self.reentrant:
                return

        targetScale = min(
            self.predefinedScales,
            key=lambda x:abs(x-scale)
        )
        self.reentrant = True
        self.mc.zoomScale( targetScale )
        self.reentrant = False

_______________________________________________
Qgis-developer mailing list
[hidden email]
http://lists.osgeo.org/mailman/listinfo/qgis-developer


_______________________________________________
Qgis-developer mailing list
[hidden email]
http://lists.osgeo.org/mailman/listinfo/qgis-developer



_______________________________________________
Qgis-developer mailing list
[hidden email]
http://lists.osgeo.org/mailman/listinfo/qgis-developer