I’ve created a custom ProgressBar using the Canvas from some examples floating around. It’s pretty cool of Xojo to be able to do this, and I’ve even got a countdown clock (on separate timer) inside the ProgressBar always moving along at the tip of where the bar is. Here’s my issue. The timing of the bar using this method is off. For instance, I have to double the period time of the seconds to get the desired effect. For instance, setting the Period to 240 in order to get 2 minutes. And while that seems ok on Windows, It’s off a few seconds in the macOS and Linux. My countdown clock is accurate, it’s just the graphics ProgressBar that can be off. Is there a way to lock Canvas ProgressBar to the countdown clock timer I have, and fill proportionally to whatever the size of the Progress Canvas is? If possible, I’d like these to perfectly synced. Thanks!
A timer is not guaranteed to fire at exactly the period you set. Typically, you set the timer to fire more frequently and then check the elapsed time using Ticks or Microseconds. I often have the timer reset its Period based on the remaining time, so it will check more frequently the closer it is to needing to act.
I understand it’s not perfect, but I do think it’s odd that the custom progressbar with canvas and paint is exactly twice the actual seconds. I’d be happy to know why that is, and how to adjust to make it match (roughly) actual seconds.
Just out of curiosity. Obviously, I can just double my intended time and it works.
So… just to verify some math…
Timer period is defined in milliseconds, so 1000 = 1 second. You’re using 240… so your progress bar is supposed to increment 500 times in those two minutes? Is it actually 500px across?
I only ask since 240 seconds is four minutes, not two.
Apologies, I might have stated it in reverse. If I want two minutes, I set the period for 240, not 120.
OK, here’s some more information. On shorter times (30 seconds), the Canvas based Progressbar and the countdown clock timer seem to finish together. The longer you go, or between different machines and operating systems, it gets out of whack, with one or the other finishing first.
Here’s some code. This is my Countdown Timer.
Me.Period = 1000 dim mins, secs as integer dim countdowntime as Integer countdowntime = max(0, countdowntime - 1) Label3.text = str(countdowntime, "0") self.countdowntime = self.countdowntime - 1 mins = self.countdowntime \ 60 secs = self.countdowntime mod 60 Label3.text = str(mins, "00") + ":" + str(secs, "00") if self.Countdowntime = 0 then me.mode = 0 end
Here is my Progressbar timer for my custom Progressbar.
me.Period = 240 if BarPosition = 487 then me.mode=0 BarPosition=0 OKQUIT=True quit else BarPosition=BarPosition+1 ClockPosition=ClockPosition+1 end if PROGRESS_CANVAS.Invalidate
I also have this in the Window open event. It only seems to work here.
self.Countdowntime = 120
The ProgressBar Canvas is 487 increments long. Making it slightly longer helps some…but the longer you make the time period, it gets out of whack again.
What I’d like to do is have my clock hit 00:00 the exact time that the ProgressBar reaches the end.
Is this possible? I appreciate any help…this part of XOJO is very new to me, but extremely cool as well. I just want to get it right! Thanks!
Right. The timer execution is approximate, not exact. You need to register the time in ticks or microseconds when the timer starts and then use that to calculate BarPosition and ClockPosition every time the timer action event fires. There will be times when BarPosition will increment by 2, not 1.
Seems like they would be inaccurate together. Like maybe it truly isn’t 2 minutes, but they should complete at the same time.
Anyway, any tips on what you’re proposing? I’m new to using timers.
OK, so I’ve ben trying out the System.Ticks and not having any luck. I’ve moved the Progress Bar to an even 500, and wanted to start checking the real time with System Ticks in sections of 5 and recalibrating. The problem is, the CountDown integer never gets the new adjustments time. I’ve been pulling my hair out with this the last few days. Why isn’t this working?
Me.Period = 1000 dim mins, secs as integer dim countdowntime as Integer countdowntime = max(0, countdowntime - 1) countdowntime = self.countdowntime - 1 mins = self.countdowntime \ 60 secs = self.countdowntime mod 60 PrePaintClock= str(mins, "00") + ":" + str(secs, "00") if Countdowntime = 0 then me.mode = 0 ClockDone = "Yes" elseif Countdowntime = MessageTime \2 -1 then seconds=System.Ticks /60 elseif self.countdowntime = MessageTime *4/5 \2 then seconds2=System.Ticks /60 seconds2=seconds2 - seconds countdowntime=MessageTime *4/5 \2 - Seconds2 BarPosition=100 if TargetMacOS then ClockPosition = 100 -50 else ClockPosition = 100 -45 end else end PROGRESS_CANVAS.Refresh
This is likely a big part of your problem. You create a local variable that is destroyed at the end of the Action event. You’re not updating self.countdowntime at all. Get rid of the local variable and only use the property directly.
This makes no sense at all. If you already have a timer to do the count down, WHY another timer?? You have no way of synchronizing it with the first one so it will never be guaranteed to end at the same time.
You should use only one timer and set the countdown, labels AND the Custom Progressbar value from there.
memory the time as lasttime and calc the difference each tick by currenttime-lasttime
the difference is the time past each step.
add the time past to a variable to have a complete time and use it for progress bar value and clock display.
the (next) timer event only occur/continue if your app is not inside of a (main thread) method.
I had removed that variable line before when I moved that to the a window property. However, with the line removed, the progress bar starts counting down too fast (every other second) and everything gets out of whack.
As I mentioned earlier in this thread, I’d love to use only one timer. However, for the countdown clock to work properly, you have set its timer period to 1000. For the Progress Bar, the period has to match twice that of the time you desire, I.E. Period=240 for two minutes. If you can demonstrate how to combine these two into one timer, i’m all ears. I’d much rather have that.
Can you give a small example of that? I’d love to have just one timer, but as I mentioned to Ivan, these timers have to be set to different periods to work. I can correct a 500 point progress bar in the clock timer, but I see no way to make it smoothly scroll when just using the CountdownClock timer set to a period of 1000.
You know, instead of using a timer to conform to this arbitrary time, you could probably get the progress bar and the countdown to reflect the actual process.
If your process can give regular updates on how far it’s gotten, it’s easy to translate into %.
(Steps completed / steps total)
Figuring out the time remaining can be done like this:
- Record the start time before the process (in seconds) using Ticks (this is verbose for clarity)
Var start as double = ticks
- On every step, calculate the time elapsed and divide by % complete.
Var pct as double = stepsdone / stepstotal Var deltaticks as double = ticks - start Var totalticks as double = deltaticks / pct Var ticksremaining as double = totalticks - deltaticks Var secondsRemaining as double = ticksRemaining / 60
pct * 100to update the progress bar
The nice thing about this is that it will compensate for unforeseen delays and for other high cpu processes (like backups) taking time away from your app, all while keeping the user up to date on the current estimate. It’s usually wildly inaccurate at the beginning, but if you don’t show the time until you get to 5%, that’ll usually compensate enough.
There’s no process. This is inherently just a progress bar app based on a time.
Am I misunderstanding what you are trying to do or is it what i’ve implemented in this project?
I have not seen that before, but it is similar. I’m painting the countdown clock inside the progress bar, just inside the furthest end, and moving it along as the bar grows.
Your project certainly gives me some things to work around with internally on mine. I’ll try to convert mine to your internal logic, and see how it goes. I’ll check back,