Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PanZoom Toolbar #316

Open
deisi opened this issue Sep 12, 2016 · 11 comments
Open

PanZoom Toolbar #316

deisi opened this issue Sep 12, 2016 · 11 comments
Labels

Comments

@deisi
Copy link

deisi commented Sep 12, 2016

Hello

I'm trying to create a custom toolbar for my needs, that allows me to Zoom in x and y axis direction independently. I'm aware of https://github.com/bloomberg/bqplot/issues/121, but I absolutely don't understand how to use PanZoom and how to incorporate it into a custom Toolbar. As it is suggested there. Could somebody clarify this for me.

Thanks a lot.

@dmadeka
Copy link
Contributor

dmadeka commented Sep 12, 2016

Well, you can do this entirely on the python side using ipywidgets ToggleButtons. You can create a PanZoom everytime the value is changed, with only one direction as per #121 ..

Does that make any sense?

@dmadeka
Copy link
Contributor

dmadeka commented Sep 12, 2016

Basically, the concept of a Scale is a mapping between the data and pixels. The PanZoom object takes a scale, and pans or zooms along that scale. If you pass it the x scale, it will panzoom only along x, or if you pass it the y, it will panzoom only along the y.

@deisi
Copy link
Author

deisi commented Sep 13, 2016

Okay I think I got it. This is a working example. Maybe not pretty but does the job. Any suggestions?

import numpy as np
from IPython.display import display
from bqplot import LinearScale, Axis, Lines, Figure, Toolbar, PanZoom
from ipywidgets import ToggleButton

x_label = ""
y_label = ""
title = ""

x_sc = LinearScale()
y_sc = LinearScale()

line = Lines(scales={'x':x_sc, 'y':y_sc})

ax_x = Axis(scale=x_sc,
            label=x_label)
ax_y = Axis(scale=y_sc, 
            label=y_label, orientation='vertical')

# Zoom only y-scale
py = PanZoom(scales={'y': [y_sc]})
def py_update(new):
    if tb.value:
        fig.interaction = py
    else:
        fig.interaction = None


tb = ToggleButton(description="py")
tb.observe(py_update, "value")

fig = Figure(marks=[line], axes=[ax_x, ax_y], 
             title=title)

tb0 = Toolbar(figure=fig)

fig.marks[0].x = np.arange(10)
fig.marks[0].y = np.random.random(10)


display(fig, tb0, tb)

Now the issue is, that the reste button wont work any more... so I have to add a new one, or?

@dmadeka
Copy link
Contributor

dmadeka commented Sep 14, 2016

In ToggleButtons, you can use options and create your own reset button for now. But @SylvainCorlay for this purpose, shouldn't Toolbar take instances of the PanZoom?

@dmadeka
Copy link
Contributor

dmadeka commented Sep 14, 2016

I had:

import numpy as np
from IPython.display import display
from bqplot import LinearScale, Axis, Lines, Figure, Toolbar, PanZoom
from ipywidgets import ToggleButtons

x_label = ""
y_label = ""
title = ""

x_sc = LinearScale()
y_sc = LinearScale()

line = Lines(scales={'x':x_sc, 'y':y_sc})

ax_x = Axis(scale=x_sc,
            label=x_label)
ax_y = Axis(scale=y_sc, 
            label=y_label, orientation='vertical')

def py_update(new):
    if tb.value == 'y':
        tb0._panzoom.scales={'y': [y_sc]}
    elif tb.value == 'x':
        tb0._panzoom.scales={'x': [x_sc]}
    else:
        tb0._panzoom.scales={'x': [x_sc], 'y': [y_sc]}

tb = ToggleButtons(options=['y', 'x', 'both'], value='both')
tb.observe(py_update, "value")

fig = Figure(marks=[line], axes=[ax_x, ax_y], 
             title=title)

tb0 = Toolbar(figure=fig)

fig.marks[0].x = np.arange(10)
fig.marks[0].y = np.random.random(10)

display(fig, tb0, tb)

@deisi
Copy link
Author

deisi commented Sep 15, 2016

Ah that looks good. I give it a try. Thanks a lot.

@deisi
Copy link
Author

deisi commented Sep 15, 2016

So I added a guard in the py_update but its still note completely right, because when you first zoom in say y and then x and afterwards click the reset button it only resets the (last) x-zoom.

def py_update(new):
    if not tb0._panning:
        return
    if tb.value == 'y':
        tb0._panzoom.scales={'y': [y_sc]}
    elif tb.value == 'x':
        tb0._panzoom.scales={'x': [x_sc]}
    else:
        tb0._panzoom.scales={'x': [x_sc], 'y': [y_sc]}

@dmadeka
Copy link
Contributor

dmadeka commented Sep 15, 2016

When you switch from x to y the new reset frame becomes the previous zoom. If you want to globally reset, perhaps you can just have a reset button and manually go back to normal.

@SylvainCorlay ?

@deisi
Copy link
Author

deisi commented Sep 16, 2016

perhaps you can just have a reset button and manually go back to normal

I try this, but I don't know how. I don't find any property in the fig or the scale or the axes, that I can use to reset the scale to normal.
The scales have a min and a max property, but in case of x_sc and y_sc they are only the same as what I see until I click the Reset button once. Afterwards, they keep there values and become kind of arbitrary.

@dmadeka
Copy link
Contributor

dmadeka commented Oct 3, 2016

@SylvainCorlay Any answers?

@DougRzz
Copy link

DougRzz commented Jul 28, 2017

All, first of all, I think BQPlot and Ipywidgets are excellent.

Here is my solution for x+y zoom. @deisi - it includes the reset of zoom.

bqplot_xyzoom2

Latest version of ipwidgets 7.0b2 required to set the width of the togglebuttons

pip install --pre bqplot
jupyter nbextension enable --py --sys-prefix bqplot
jupyter nbextension enable --py --user bqplot

from bqplot.interacts import PanZoom
import ipywidgets as widgets
import bqplot as bq
from traitlets import link
from collections import OrderedDict

import numpy as np

buttonWidth = '50px'

x_sc = bq.LinearScale()
y_sc = bq.LinearScale()

x_data = np.arange(500)
y_data = np.random.randn(3, 500)

line_chart = bq.Lines(x=x_data, y=y_data, scales= {'x': x_sc, 'y': y_sc}, 
                       display_legend=True, labels=["line 1", "line 2", "line 3"] )

ax_x = bq.Axis(scale=x_sc)
ax_y = bq.Axis(scale=y_sc, orientation='vertical', tick_format='0.2f')

fig = bq.Figure(marks=[line_chart], axes=[ax_x, ax_y])
fig.layout.width = '95%'

pz = PanZoom(scales={'x': [x_sc], 'y': [y_sc]})
pzx = PanZoom(scales={'x': [x_sc]})
pzy = PanZoom(scales={'y': [y_sc], })

#
zoom_interacts = widgets.ToggleButtons(
                                        options=OrderedDict([
                                            ('xy ', pz), 
                                            ('x ', pzx), 
                                            ('y ', pzy),   
                                            (' ', None)]),
                                            icons = ["arrows", "arrows-h", "arrows-v", "stop"],
                                            tooltips = ["zoom/pan in x & y", "zoom/pan in x only", "zoom/pan in y only", "cancel zoom/pan"]
                                        )
zoom_interacts.style.button_width = buttonWidth

ResetZoomButton = widgets.Button(
    description='',
    disabled=False,
    button_style='', # 'success', 'info', 'warning', 'danger' or ''
    tooltip='Reset zoom',
    icon='arrows-alt'
)

def resetZoom(new):
    # Reset the x and y axes on the figure
    fig.axes[0].scale.min = None
    fig.axes[1].scale.min = None
    fig.axes[0].scale.max = None
    fig.axes[1].scale.max = None  
    
ResetZoomButton.on_click(resetZoom)
ResetZoomButton.layout.width = buttonWidth

link((zoom_interacts, 'value'), (fig, 'interaction'))
widgets.VBox([fig, widgets.HBox([zoom_interacts,ResetZoomButton])], align_self='stretch')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants