hoffstadt / DearPyGui

Dear PyGui: A fast and powerful Graphical User Interface Toolkit for Python with minimal dependencies
https://dearpygui.readthedocs.io/en/latest/
MIT License
12.62k stars 669 forks source link

dpg.set_axis_limits just lock the axis min limit and let the max limit be free (+vice versa) #2016

Open pwilliam123 opened 1 year ago

pwilliam123 commented 1 year ago

Is your feature request related to a problem? Please describe. I want to have a function that can set limit of axis boundary but still allow right click for drag zoom to change the axis freely without pass the set boundary. Basically, I want to have a graph that can only be zoom OUT to certain axis, but they can move/zoom IN to any values when right click for drag zoom and pan and middle scroll zoom. So to accomplish that, I will need to have a function (similar to set_axis_limit) that can lock min OR max only and allow the other limit be free.

NOTE: in the built in right click function, we can lock ONE side of the axis and other side still be able to move freely, so I believe there might be a function call for that?

Describe the solution you'd like Allow axis limits to just lock the axis min limit and let the max limit be free (+vice versa) or possible to make dpg.set_axis_limits take in min or max only. In other words, I want to set the boundary of the plot axis, but still allow all the zoom/move feature as normal with out exceed the set boundary.

Describe alternatives you've considered In the built in right click function, we can lock ONE side of the axis and other side still be able to move freely. So it can still be done by manually lock one side. But that feature cannot be implemented in the code due to set_axis_limits require both min and max

Additional context Below is the current implementation for the features that was mentioned above, however, when it reaches the boundary axis (limit), the axis locked and cannot move freely anymore. For this example, I set the x axis boundary to [0, 200] and y boundary to [0, 1]. But it lock the zoon/move (Left click move) when it zoom/move to one/both side(s) of the axis boundary. If there a function to lock one side, then it will accomplish the task.

import dearpygui.dearpygui as dpg
from math import sin, cos

dpg.create_context()

sindatax = []
sindatay = []
for i in range(0, 200):
    sindatax.append(i )
    sindatay.append(0.5 + 0.5 * sin(50 * i / 1000))

Y_MAX = 1
Y_MIN = 0
X_MAX = 200
X_MIN = 0

with dpg.window(label="Tutorial", tag="win"):
    # create plot
    with dpg.plot(label="Line Series", height=400, width=400, tag='plot'):

        # REQUIRED: create x and y axes
        dpg.add_plot_axis(dpg.mvXAxis, label="x", tag = "x_axis")
        dpg.add_plot_axis(dpg.mvYAxis, label="y", tag="y_axis")

        # series belong to a y axis
        dpg.add_line_series(sindatax, sindatay, label="0.5 + 0.5 * sin(x)", parent="y_axis", tag="series_tag")
        dpg.set_axis_limits('x_axis', X_MIN, X_MAX)
        dpg.set_axis_limits('y_axis', Y_MIN, Y_MAX)

def right_click_drag_callback():
    dpg.set_axis_limits_auto('x_axis')
    dpg.set_axis_limits_auto('y_axis')

def right_click_release_callback():
    with dpg.mutex():
        xmin, xmax = dpg.get_axis_limits('x_axis')
        dpg.set_axis_limits('x_axis', xmin, xmax)

        ymin, ymax = dpg.get_axis_limits('y_axis')
        dpg.set_axis_limits('y_axis', ymin, ymax)

def wheel_callback(sender, data):

    if dpg.is_item_hovered('plot'):
        with dpg.mutex():
            if data < 0: # data < 0 means scroll down (i.e zoom out)
                dpg.set_axis_limits_auto('x_axis')
                xmin, xmax = dpg.get_axis_limits('x_axis')
                if xmax > X_MAX:
                    dpg.set_axis_limits('x_axis', xmin, X_MAX)
                elif xmin < X_MIN:
                    dpg.set_axis_limits('x_axis', X_MIN, xmax)

                dpg.set_axis_limits_auto('y_axis')
                ymin, ymax = dpg.get_axis_limits('y_axis')
                if ymax > Y_MAX:
                    dpg.set_axis_limits('y_axis', ymin, Y_MAX)
                elif ymin < Y_MIN:
                    dpg.set_axis_limits('y_axis', Y_MIN, ymax)

            else: # data >0 means scroll up (i.e zoom in)
                xmin, xmax = dpg.get_axis_limits('x_axis')
                if xmin >= X_MIN:
                    dpg.set_axis_limits_auto('x_axis')
                elif xmax <= X_MAX:
                    dpg.set_axis_limits_auto('x_axis')

                ymin, ymax = dpg.get_axis_limits('y_axis')
                if ymin >=Y_MIN:
                    dpg.set_axis_limits_auto('y_axis')
                elif ymax <= Y_MAX:
                    dpg.set_axis_limits_auto('y_axis')

def left_click_drag_callback(sender, data):
    dpg.set_axis_limits_auto('x_axis')
    xmin, xmax = dpg.get_axis_limits('x_axis')
    if xmax >= X_MAX:
        dpg.set_axis_limits('x_axis', xmin, X_MAX)
    elif xmin <= X_MIN:
        dpg.set_axis_limits('x_axis', X_MIN, xmax)

    dpg.set_axis_limits_auto('y_axis')
    ymin, ymax = dpg.get_axis_limits('y_axis')
    if ymax >= Y_MAX:
        dpg.set_axis_limits('y_axis', ymin, Y_MAX)
    elif ymin <= Y_MIN:
        dpg.set_axis_limits('y_axis', Y_MIN, ymax)

def double_click_callback(sender):
    dpg.fit_axis_data('x_axis')
    dpg.fit_axis_data('y_axis')

with dpg.handler_registry():
    dpg.add_mouse_drag_handler(button=dpg.mvMouseButton_Right, callback=right_click_drag_callback)
    dpg.add_mouse_drag_handler(button=dpg.mvMouseButton_Left, callback=left_click_drag_callback)
    dpg.add_mouse_double_click_handler(button=dpg.mvMouseButton_Left, callback =double_click_callback )
    dpg.add_mouse_release_handler(button=dpg.mvMouseButton_Right, callback=right_click_release_callback)
    dpg.add_mouse_wheel_handler(callback=wheel_callback)

dpg.create_viewport(title='Custom Title', width=800, height=600)
dpg.setup_dearpygui()
dpg.show_viewport()
dpg.start_dearpygui()
dpg.destroy_context()
v-ein commented 2 weeks ago

Trying to figure out a proper solution for this issue. Did I get it right that you need the following behavior?

  1. Be able to specify the range on an axis that would be the max possible area to be seen by the user. When the user zooms out, the plot never shows anything outside of that area. That is, if we say "lock X axis to [0, 10]", and the user starts zooming out, eventually the plot display 0 at the left side, 10 at the right side, and won't zoom out anymore. The user can then zoom in to view a different area within the locked range, but never go outside of the locked range.
  2. The locked range can be open on one end (like [0, +infinity]).