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

Updating x-axis minimum and maximum value with time data #2157

Open skjelanger opened 10 months ago

skjelanger commented 10 months ago

Hello,

As far as I can tell from the documentation, it is not possible to fix the maximum and minimum values of an x-axis for an line_series plot, if the x-axis has time=True. The closest I get is dpg.set_axis_limits(axis, ymin, ymax), however it does not work properly on x-axis objects, when it is formatted as a time series.

I would like to be able to do this.

Minimum reproducible code

import dearpygui.dearpygui as dpg
import time
from math import sin

dpg.create_context()
dpg.create_viewport(title='CSV Reader', width=1020, height=580)

time_now = time.time()
tmin = time_now-20
tmax = time_now+20

def sindata():
    sindatax = []
    sindatay = []
    for i in range(0, 100):
        sindatax.append(time_now+i)
        sindatay.append(0.5 + 0.5 * sin(50 * i / 100))
    return sindatax, sindatay

sindatax, sindatay = sindata()

with dpg.window(label="Main Window", width=1080, height=500):
    with dpg.plot(label="Plot",height=490, width=900):                    
        dpg.add_plot_axis(dpg.mvXAxis, label="x", tag='xaxis', time=True)
        ax_y1 = dpg.add_plot_axis(dpg.mvYAxis, tag='yaxis')
        dpg.add_line_series(sindatax, sindatay, label="sin", tag='datay', parent=dpg.last_item())
        dpg.set_axis_limits('xaxis', tmin,tmax)

dpg.setup_dearpygui()
dpg.show_viewport(maximized=True)
dpg.start_dearpygui()
dpg.destroy_context()
v-ein commented 10 months ago

Can you format your code sample as "code" so that we can read and copy it? (the <> button on the formatting panel).

skjelanger commented 10 months ago

Apoliges @v-ein ,now it should be formatted as code.

v-ein commented 10 months ago

It looks to me like a precision issue, somewhat similar to #386 and #2067. Axis limits are float fields in C++. The number of seconds returned by time.time() is around 1.6e9. I've played with numbers a little bit, trying to display a sin() graph in the range (x, x+20), and found that up to x == 1e7 it all looks the same, while with x == 1e8 and above it starts drifting or completely disappearing.

As always with precision errors, it depends on your numbers, and you can get completely different behavior for close numbers. That is, when you use time.time(), it might be that some runs will show something and others will show a blank plot.

BTW #386 and #2067 deal with timestamps, too, that is, the numbers there are of the same scale as what you get from time.time() here. Same kind of issues.

v-ein commented 10 months ago

The bad news are: there's no workaround until somebody fixes these precision issues.

Also, set_axis_limits is mentioned in #1847: same issue there.

skjelanger commented 10 months ago

But it is also a 'problem' that you can not manually set the limits of the x-axis?

v-ein commented 10 months ago

You can. Try to use dates around May or June 1970 - it will work just fine.

v-ein commented 10 months ago

Technically, a date is just a number -- the number of seconds since Jan 1, 1970. As long as it's smaller than 1e7, you'll be just fine, whether you use the time format for x axis or regular numeric format. But, 1e7 seconds is only about 115 days, or 4 months. So even June 1970 is probably risky (though it works for me).

skjelanger commented 10 months ago

Thank you for the help. What I am trying to do is to plot the last 60 seconds of data, in a sliding window. I can therefore make a work-around by subtracting '1692000000' from all of my timestamps and setting no_tick_labels=True :)

nkraemer2 commented 1 month ago

Is this precision issue scheduled to be fixed at some point? I can probably work around it by setting the day to be Jan 1st 1970 as suggested, but I'd rather not have to if I don't need to. Ideally I'd like to be able to work with nanosecond level precision for current dates. An underlying uint64 datatype representing ns from epoch would work.

v-ein commented 1 month ago

Unlikely to be fixed before #2275 (just because #2275 is huge and everything else is being held up). You can also fix it on your own and push a PR. (Note: I'm not one of DPG developers and my comment here does not reflect their position).

Talking about nanoseconds, double doesn't seem to be sufficient for that. It only provides ~16 digit precision (per Wikipedia), whereas for current dates and nanosecond precision you'd need 18 digits.

nkraemer2 commented 1 month ago

Thanks for quick input. Agreed, just moving to double would be insufficient for ns precision. I'm suggesting using a 64 bit signed or unsigned integer with units of 1 = 1ns as the basis for time instead of floating point values. I'm don't think I'll have time to fix it myself, but if I do I'll should be able to share the results.

v-ein commented 1 month ago

Changing it to int64 for X values and double for Y values won't be trivial, if it's doable at all. DPG relies on ImPlot to render plots. You can probably create your own ImPlotGetter as well as your own heterogeneous point type (with x being int64 and y being double). Then, fingers crossed, ImPlot::PlotLineG might be able to render that. So it's more than just changing type name here and there.

BTW I'm talking about the modern version of ImPlot, which #2275 brings to DPG.