epezent / implot

Immediate Mode Plotting
MIT License
4.71k stars 521 forks source link

How to independently use `ShowDatePicker` and `ShowTimePicker` to select date/time? [Almost solved] #521

Open sirlis opened 12 months ago

sirlis commented 12 months ago

@epezent is it necessary to wrap ShowDatePicker and ShowTimePicker to a single and independent function/widget to use? I think they are really good to use and beautiful, and did some work to wrap them together. I tried to initialize and use ShowDatePicker and ShowTimePicker but faced with some problems so I raised this issue. After coding for a while I managed to solve most of them.

Basically, I want to pick a date and a time using the popup widget, save them to a std::string like 2023-05-06 15:24:16, and show them by ImGui::Text(). Also I will save the string to a json file. Next time when I open the program, I will load the date and time from json, parse the string and show them. Also I can initialize the widget again by the loaded date and time, and change them again by the widget.

The desired function/widget may support the following features:

1) [Solved] Switch from 12-hour system to 24-hour system. Solved by setting

ImPlotStyle &style = ImPlot::GetStyle();
style.Use24HourClock = true;

2) [Partially solved] Initialize the widget by inputting a UTC string and a TimeZone string under different hour system. Currently 24-hour system. HAVEN'T TRIED to initialize under 12-hour system.

3) [Still workng on it] Support microseconds. I am trying to add this by not modifying any implot codes but it seems impossible.

I placed the code below in case someone may need.

//...in render loop
if (ImGui::Button("Change"))
    ImGui::OpenPopup("SelectDateTime##");
static std::string strDateTime = "2020-01-01 10:11:23";
static std::string strTimeZone = "UTC-3";
ImPlot_DateTimePicker(strDateTime, strTimeZone);
//...

std::string timestamp2string(std::time_t timestamp)
{
    std::tm *tm = std::gmtime(&timestamp);
    char buffer[80];
    std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tm);
    return buffer;
}

int timezone2int(const std::string tzstring)
{
    size_t found = tzstring.find("UTC");
    if (found != std::string::npos)
    {
        std::string number_str = tzstring.substr(found + 3);
        std::stringstream ss(number_str);
        int number;
        ss >> number;
        return number;
    }
    else
        return 0;
}

std::string ImPlot_DateTimePicker(std::string &UTCdatetime, std::string TimeZone = "UTC+0")
{
    std::tm tm = {};
    std::stringstream ss(UTCdatetime);
    ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S.%f");
    // timestamp0  = current timestamp of UTC+0
    static std::time_t timestamp0 = std::mktime(&tm) + timezone2int(TimeZone) * 3600;
    tm.tm_hour = 0;
    tm.tm_min = 0;
    tm.tm_sec = 0;
    // deltaDate = delta seconds from current midnight to current timestamp
    static std::time_t deltaDate = timestamp0 - (std::mktime(&tm) + timezone2int(TimeZone) * 3600);
    if (ImGui::BeginPopup("SelectDateTime##"))
    {
        static int PickerLevel = 0;
        static ImPlotTime time_selected = timestamp0;
        static ImPlotTime date_selected = timestamp0;
        ImPlotStyle &style = ImPlot::GetStyle();
        style.Use24HourClock = true;
        // `TimePicker` only changes hour, minute, second around initial timestamp `time_stamp0`
        ImPlot::ShowTimePicker("##time", &time_selected);
        std::time_t deltaTime = time_selected.S - timestamp0;
        ImGui::Separator();
        // `DatePicker` directly select and return timestamp at midnight
        ImPlot::ShowDatePicker("##date", &PickerLevel, &date_selected, &date_selected);
        std::time_t timestamp = date_selected.S + deltaDate + deltaTime; // current timestamp
        UTCdatetime = timestamp2string(timestamp) + ".000";// [TODO] modify to support ms microseconds
        ImGui::EndPopup();
    }

}

Screenshot. The time zone selection combobox is not included in the code

image