Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QProgressBar breaks with large values



  • I am using Qt 5.15.0
    I have a program that takes a file and hashes its content and I want to display the progress with a ProgressBar. Everything works fine for mid-sized files.
    The problem comes if the file is > 2GB in size. Since QProgressBar::setRange() only takes signed 32-bit integers, if my file is 3GB large, the upper limit
    will overflow and become negative, causing the ProgressBar to reset and show no progress at all.
    Similar thing if the file is 5GB large: The upper limit overflows and gets set to 1GB, this time showing progress but incorrectly since the progress bar will be full when we have only hashed 1 out of 5 GBs.
    How could this be solved? I do the percentage calculation on my own using 64-bit values, so the percentage text displayed is correct, however the progress bar fills up too quickly or doesn't move at all, depending on how the value overflows.
    I could track the progress in KBs or use a value range from 0-100 but that would cost a lot of precision and doesn't really look like a good solution to me.
    I am not that familiar with Qt, this is my first project using it, so any tips are appreciated.
    Thanks for reading.

    In case it is needed, this is the code that updates the progress bar (signal emitted from another thread):

    void Controller::UpdateFileProgress(const size_t min, const size_t max, const size_t value)
    {
        ui->fileProgressBar->setRange((int)min, (int)max);
        ui->fileProgressBar->setValue((int)std::min(value, max));
    
        if (value >= max)
        {
            ui->fileProgressBar->setFormat("100%");
        }
        else
        {
            double percentage = static_cast<double>(value) / static_cast<double>(max);
            ui->fileProgressBar->setFormat(QString::number((percentage * 100), 'g', 3)+ "%");
        }
    }
    

  • Lifetime Qt Champion

    Then use scaled values for your progress bar.



  • @Christian-Ehrlicher That was also one of my thoughts but I was hoping there would be a better way.
    Is it possible to extends the QProgressBar class and override these methods and attributes?


  • Lifetime Qt Champion

    @Kekz said in QProgressBar breaks with large values:

    Is it possible to extends the QProgressBar class and override these methods and attributes?

    No and I don't see why it should be needed. If you need it derive from QProgressBar and do the scaling there but adding a /100 or similar before passing the values isn't worth the trouble imo.



  • @Christian-Ehrlicher Deriving was what I meant, sry. Then I will probably go that route and use 64-bit values, thanks for the help.
    On second thought, that probably won't work. I will need to resort to scaling...



  • @Kekz said in QProgressBar breaks with large values:

    Is it possible to extends the QProgressBar class and override these methods and attributes?

    Yes, it is.
    The question could be: is it worth? rather than scale the values as @Christian-Ehrlicher suggested...



  • @Kekz
    If it floats your boat, derive from QProgressBar and write your own setRange(), setValue() etc. new method overloads which accept a size_t or double or whatever. And that can do the division to do the "scaling" there, so your callers won't have to.



  • @JonB
    I was hoping to be able to also override the value, min and max attributes to force Qt to use 64-bit values, but that doesn't seem to work.
    I'll have to scale the values down, it's an ugly solution but the only one that seems possible.



  • @Kekz Internally

    ProgressBar will display the percentage of steps that have been 
    completed when you later give it the current step value. 
    
    The percentage is calculated by dividing the progress
     (value() - minimum()) / (maximum() - minimum())
    

    So you can set value min max as 0,SCALE_MAX always irrespective file size.
    ui->fileProgressBar->setRange(0,SCALE_MAX );

    void Controller::UpdateFileProgress( const size_t FileSize, const size_t value)
    

    double ratio= static_cast<double>(value) / static_cast<double>(FileSize);
    int scaledvalue =( ratio * SCALE_MAX );



  • @Kekz said in QProgressBar breaks with large values:

    I was hoping to be able to also override the value, min and max attributes to force Qt to use 64-bit values, but that doesn't seem to work.

    You cannot "override" variables, or the type of variables, in C++ (e.g. you cannot declare variables virtual). QProgressBar uses ints (32-bit) for these, and you cannot alter that.

    I'll have to scale the values down, it's an ugly solution but the only one that seems possible.

    I do not see this as even vaguely "ugly". QProgressBar works off a range of possible values something like -2,000,000,000 to +2,000,000,000, which is more than enough to cover the visible "steps" available on the bar, and that's what you must supply it with for min/max/value. Scaling the number from, say a float to an int seems fine to me, and you can add your own methods to your QProgressBar subclass to make this invisible to the outside world.



  • @JonB I guess you are right, my concerns about loosing precision were unfounded since 2 billion steps will always be more than enough.
    I will do as @nagesh (and many others) suggested and mark this as solved.



  • @Kekz
    A 32-bit has 4 billion range. Your horizontal screen resolution is, say, 1,920 pixels. So 2 million bits available per pixel the user can see. I don't think losing precision will be an issue :)


Log in to reply