Timer Issue

TWELVE

Active Member
Licensed User
Hello,

I'm facing an issue with a timer control.I am using several timers in my program and added a new one to update a progressbar.The code looks similar to this:


B4X:
App_start:

             ProgressBar.New1
   AddTimer("Timer8") 
   Timer8.Interval = 50
   Timer8.Enabled = False

...

Sub Sub1
Timer8.Enabled = true
do something
Timer8.Enabled = false

End Sub

...

Sub Timer8_Tick

Do something with the progressbar

ProgressBar.Value = some value

End Sub

In fact the subs are nested two levels more, means the sub1 is called by another sub, which is called by a button event.

The timer never gets fired ! I put in some other code into Timer8_Tick, but it is never called.If i do this:

B4X:
AddTimer("Timer8") 
   Timer8.Interval = 50
   Timer8.Enabled = True

, the timer is running as expected and executes the code in the Timer8_Tick Sub.I'm confused because i use different timers and never had this problem.

Has anyone an explanation and/or a solution to this...?


regards,

TWELVE
 

agraham

Expert
Licensed User
Longtime User
Timer events are raised by sending messages to the application message loop which then calls the timer event code in the same way that button, mouse and other events are handled. The message loop is run when there is no further Basic4ppc call to run. i.e when the code returns from App_Start or any event Sub. Hogging the main thread will stop timer events just as it stops GUI events being processed and makes the GUI freeze.

B4X:
Timer8.Enabled = true
do something
Timer8.Enabled = false
This code which, is running on the main thread doesn't return to the message loop so no timer messages can be processed so no timer events are called.
 

TWELVE

Active Member
Licensed User
I'm trying to understand what this means for the code design.As mentioned i use several timers this way and all do work fine.The timer is enabled in a sub before i really start to block the main thread by resizing an image using jpeg.loadthumbnail.

I use the timers often as additional threads and it is working fine so far.Is the code within the timers stopped if something like jpeg.loadthumbnail is executed in a sub...?

Currently i have no clue, because i call timerx.enabled quite often in subs and don't care about what's running somewhere else.If i follow your explanation, i would never be able to enable a timer somewhere outside of the app_start.

I was thinking of adding a threading object, but tend to keep it as simple as possible hence avoiding this if other options are there.


regards,

TWELVE
 

agraham

Expert
Licensed User
Longtime User
If i follow your explanation, i would never be able to enable a timer somewhere outside of the app_start.
No that's not true. In that example the timer is disabled on entry to the Sub. You enable it and then disable it before leaving the Sub. As it is still disabled when your application gets back to the message loop no timer ticks are generated. If you left it enabled when leaving the Sub all would be fine.
 

TWELVE

Active Member
Licensed User
As it is still disabled when your application gets back to the message loop no timer ticks are generated. If you left it enabled when leaving the Sub all would be fine.

Now it is getting even more complicated.That's not really true, because the code outlined above is not that much exactly.

The sub doing the timer enabled / disable in fact gets a param, that decides if the timer is enabled or disabled.But neither works, the timer never gets enabled and if i enable it from app_start, it get never disabled.

I'm very curious about this because i use that a lot and it always worked fine.

I call timerX.enabled from any sub, so what's wrong with it..?


regards,

TWELVE
 

TWELVE

Active Member
Licensed User
I just started to understand what you're trying to explain.The difference with my other timers appears to be that they are enabled in subs that are called by menus or buttons, the subs end as almost as immediatly, while the sub where i have the issue currently performs something longer operation, and if i understan you right then the timer event will not be handled until the sub returns ( to the main loop...? ).The evidence for this is, that i present a messagebox from within that sub and this is the moment when the timer starts.I never saw that before because i disabled the timer before the messagebox.

I tried a DoEvents, but this does not help either.Why..?

I will think a bit about it now...


regards,

TWELVE
 

TWELVE

Active Member
Licensed User
I placed some debugging code and - surprisingly - it's not the timer.In opposition to what you explained above, my timer starts and stops fine ! It is the progressbar, that does not update.

I bring up a panel, that has two text labels and a progressbar on it.Currently it looks these elements are not updated, even though the timer_tick doing this update is running.


regards,

TWELVE
 

agraham

Expert
Licensed User
Longtime User
If you are using message boxes in your debugging code then you will not see what I am trying to explain. Message boxes while they are displayed are pumping the message queue so timer events will get executed while a message box is shown. DoEvents pumps the message queue as well allowing events to run.

The difference with my other timers appears to be that they are enabled in subs that are called by menus or buttons, the subs end as almost as immediatly, while the sub where i have the issue currently performs something longer operation, and if i understan you right then the timer event will not be handled until the sub returns ( to the main loop...? ).
That is exactly correct and what I was trying to explain.
 

TWELVE

Active Member
Licensed User
If you are using message boxes in your debugging code then you will not see what I am trying to explain. Message boxes while they are displayed are pumping the message queue so timer events will get executed while a message box is shown. DoEvents pumps the message queue as well allowing events to run.

I'm aware that the messagebox forces the events to get executed.The issue ( at least my view to it) changed a bit, because i found that the timer gets enabled ( i log the timer state into a logfile when the sub that enables and disables the timer is run) while i was thinking that it would not.

So the progressbar does not update while the timer is enabled.Can i assume then that the timer event does not fire even though the timer is enabled...?

I tried to place several DoEvents, but no improvement here.How can i get out of this situation...?

I use another timer to update some gui elements, that's working fine because the programm is then in the main loop.

I'm afraid i have to setup a thread for that update task.

How would the behavior change, if the whole program runs under a thread...?

B4X:
App_Start
...
Call Thread(MainThread)

End Sub


Sub MainThread
...
My current program code
...
End Sub

From my current understanding, this should remove the limitations implied by the B4P message handling..?

regards,

TWELVE
 

agraham

Expert
Licensed User
Longtime User
How would the behavior change, if the whole program runs under a thread...?
I don't know as I haven't seen the structure of your program and so don't really understand the problem you are having. As I said before, if you post some code so I can see it happening, or not!, I can try to explain how to do what you want.

The timer is enabled in a sub before i really start to block the main thread by resizing an image using jpeg.loadthumbnail ....Is the code within the timers stopped if something like jpeg.loadthumbnail is executed in a sub...?
I have been trying to explain that if you block the main thread with a long running operation then nothing happens as all events run on the main thread. If LoadThumbnail is taking a long time on the main thread you would need to run it on a thread to allow the main thread to handle your timer events and use a Thread event or a flag or some other mechanism to signal the end of the opration.

From my current understanding, this should remove the limitations implied by the B4P message handling..?
It's not B4P message handling, this is how Windows, and most other GUI based environments works. For simplicity they only allow the main thread to update the GUI otherwise the thread locking for access to the GUI gets horribly complicated.
 

TWELVE

Active Member
Licensed User
This here is the piece of code where i'm having that timer issue:

B4X:
Sub App_Start
...
AddObject("Jpeg", "Jpeg")
AddImage ("Form1", "Image1", 80,80,100,100)

AddPanel("form14","Panel17",25,70,190,100) 
AddLabel("Panel17","Label70",40,5,130,20,"")
AddLabel("Panel17","Label72",25,30,150,30,"")
Panel17.Color = 166,167,217
Label70.FontSize = 13
Label72.FontSize = 9
panel17.Visible = False
label70.Visible = False
label72.Visible = False
Label70.FontColor = 0,0,0
Label70.Color = 166,167,217
Label72.Color = 166,167,217

AddTimer("Timer8") 
   Timer8.Interval = 50
   Timer8.Enabled = False

Jpeg.New1

ProgressBar.New1("Panel17",20,70,150,15)
ProgressBar.Minimum = 0
ProgressBar.Maximum = 100

...
End Sub

Sub AddEvents

AddEvent ("Button15", "Click", "EventManager") '

End Sub

Sub Eventmanager
..
Case "button15" : MySub
..
End Sub

Sub MySub
...
some other code
...
WaitingBox("Form14,"Resize","Please wait...",True)
...
JPEG.LoadThumbnail
WaitingBox("","","",False)
...
MsgBox
End Sub


Sub WaitingBox(Form,Title,Description,ShowHideWaitingBox)

Select ShowHideWaitingBox

Case True :
FormLib.ChangeParent("Panel17",Form)
Label70.Text = Title
Label72.Text = Description
Panel17.Visible = True 
Label70.Visible = True
Label72.Visible = True
Panel17.BringToFront 
Timer8.Enabled = True
DoEvents

Case False :
Timer8.Enabled = False
Panel17.Visible = False 
Label70.Visible = False
Label72.Visible = False
DoEvents
End Select
End Sub

Sub Timer8_Tick
'this will move the waitingbox progressbar

If ProgressBar.Value < 100 Then
ProgressBar.Value = ProgressBar.Value + 1
ProgressBar.Refresh
DoEvents
Else
ProgressBar.Value = 0
End If

End Sub

WaitingBox is just an indication to the user that the program is currently busy ( like waitcursor).My sub "MySub" ( where the jpeg.loadthumbnail is loaded) is called by a button ( button15) via EventManager.It calls then WaitingBox, which enables or disables the timer8.Timer8 is just manipulating the progressbar.Panel17 is the box which contains two textlabels and the progressbar.Panel17 is child of form14.The panel appears the way i expect, but the progressbar does not move - until the msgbox appears ( and as i noticed and you already explained, this forces the events to be executed).When the msgbox appeared, the progressbar is moving the way i expect.

The timer gets enabled, but never triggered.Because i see sometimes gui refresh issues, i place DoEvents where needed, which works ok.The DoEvents does not trigger the timer in my case, so i'm curious how i can force the timer without leaving my sub.

Obviously i can use a threading object to achieve my goal, but i would like to keep it simple when possible and also to understand, why DoEvents does not trigger the timer event.

I understood so far that my timer ( any event) would not be processed until my sub returns or something like a msgbox is brought up.

DoEvents appears to work, but only for GUI refresh.


regards,

TWELVE
 

agraham

Expert
Licensed User
Longtime User
B4X:
WaitingBox("Form14,"Resize","Please wait...",True)
[COLOR="DarkGreen"]' you enable the Timer here but call DoEvents straight away
' so the Timer won't have ticked yet so there is nothing for DoEvents to do[/COLOR]
...
JPEG.LoadThumbnail
[COLOR="DarkGreen"]' none of your code can run until this call returns
' the main thread is blocked and there is nothing you can about that
' except move the call to a separate thread[/COLOR]
There is no way you can display a progress bar while LoadThumbnail is executing. None of your code, including Timers or any other events, will execute until LoadThumbnail finishes.
 

TWELVE

Active Member
Licensed User
I setup two new threads, one that does the jpeg.loadthumbnail and the other moves the progressbar, but this doesn't work either.The bar is just not moving until the msgbox appears !!!.I start the thread that moves the progressbar and then wait 10 secs. until i execute jpeg.loadthumbnail.

So this doesn't seem to be the point, in other words if i just comment out the code for the jpeg load, this wouldn't change this behavior !

Is this because my thread calls another thread...?

I'm very frustrated that i cannot find a solution for such a quite simple function.


Furthermore the threading makes things more complicated because i cannot use formlib.changeparent in conjunction with threading..can i let appear a panel on top of any form without changing the parent of that panel to that form..?


regards,

TWELVE
 

agraham

Expert
Licensed User
Longtime User
Is this because my thread calls another thread...?
I can't comment without seeing the code, however a thread cannot "call" another thread so I suspect you are doing something wrong.
I'm very frustrated that i cannot find a solution for such a quite simple function.
Don't be frustrated. Showing a progress bar during a blocking operation is not in fact simple at all. In fact it is impposible to give an accurate progress indicator unless the operation taking a long time is designed to implement it - and LoadThumbail isn't, it just runs to completion. You might be better just showing the WaitCursor.
i cannot use formlib.changeparent in conjunction with threading..can i let appear a panel on top of any form without changing the parent of that panel to that form..?
I am afraid I don't understand what this has got to do with threading :confused:
 

TWELVE

Active Member
Licensed User
however a thread cannot "call" another thread so I suspect you are doing something wrong.

Actually that is even working, even though not the way i'd like to see, but this is not necessarily related to the thread-calls-thread method.

B4X:
Sub App_start
AddEvent ("Button16", "Click", "EventManager")
End Sub

Sub EventManager
Case "button15" : Thread2.Start("MyThread")
End Sub

Sub MyThread
...
WaitingBox
...
End Sub


Sub WaitingBox
...
Thread3.Start(MoveBar)
...
End Sub

Sub MoveBar
...
do something with the bar
...
End Sub


You might be better just showing the WaitCursor.

This were my thoughts as well...i cannot spend so much time in that relatively unimportant feature, but still difficult for me to accept i cannot solve it at this time.

and LoadThumbail isn't, it just runs to completion.


I know - my idea was to show a moving progress bar as long as the jpeg. is running to give the user a kind of response.I don't like if a gui freezes so i like to make this as asynchronous as possible.I wanted to use the progressbar for something like async http as well.

I'm not concerned about the fact that jpeg.loadthumbnail will freeze everything, i'm concerned about the fact, that i cannot make the bar moving even though jpeg. is not called at all.Only the msgbox will make that happen, no matter if jpeg.loadthumbnail is running or not.

So i'd like to point you more in that direction...just think there's no jpeg.whatever at all...if a msgbox makes happen what i want, how can i achieve that without the msgbox...?

Edit:

Quote:
i cannot use formlib.changeparent in conjunction with threading..can i let appear a panel on top of any form without changing the parent of that panel to that form..?

I am afraid I don't understand what this has got to do with threading

I get an error message, that a control owned by a thread cannot be re-owned by another thread.The panel is created under the app_start and i try to use the formlib.changeparent while i'm in a thread...

Edit2:

This error disappears if i do not start a thread from within a thread.I just start the MoveBar as thread and then the formlib.changeparent does work without error message.

regards,

TWELVE
 
Last edited:

TWELVE

Active Member
Licensed User
Thanks for your code piece...that's the way i set it up, but for some weird reason it is not working.In fact it makes no difference if i put the heavy loaded sub in a thread or if the progressbar update runs in a thread or both run in a thread.I tried any combination, but none worked.

I found that this here worked:

- the timer has to be enabled before the heavy loaded sub is called ( or started as thread)

- it only works if i call another sub from within the timer, which prints a text into my console windows ( a textbox on the main form) and into a log file.For some unknown reason this seems to force the timer event ( or the progress bar update).

The second is no option as it puts too much load on the program.

If i do so, the progressbar is moving, but even if i put the heavy loaded jpeg.loadthumbnail in a thread, the progressbar stops to move once the jpeg. i called.

...but...updating a textbox - as in your example - is working just fine..!!

So there's appears to be a difference between a textbox and a progressbar....

Another issue is that if i start my sub in a thread, the panel with the progressbar only appears the first time when the thread is started, on all subsequent thread starts the panel remains invisible.

So looks like there are some inconsistencies here...


Edit:

I found that this issue is related to the panel somehow.If i make the form instead of the panel the parent of the progressbar, everything works as expected..!


regards,

TWELVE
 
Last edited:

agraham

Expert
Licensed User
Longtime User
I really don't understand why you seem to be having so much trouble. Perhaps you should post some code do I can see why it doesn't work. There is no difficullty using a ProgressBar. Here is the same example I posted earlier using a ProgressBar instead of a Textbox.
 

Attachments

  • Progress2.sbp
    1.5 KB · Views: 193

TWELVE

Active Member
Licensed User
As i wrote, if the parent of the progressbar is a panel it's not gonna work.At least here.I still use 6.50, while you're using latest 6.90.

o every control that has a panel as parent does not update while my program is in the sub that calls the jpeg.loadthumbnail (even if the jpeg.loadthumbnail is not running)

o all controls that have a form as parent are updating as expected.Once i make a panel the parent, the controls show up on the panel, but do not update - as long as my sub is running or until a msgbox is presented.

o i add the panel, labels,timer and the progressbar during runtime using AddObject, AddPanel, AddTimer and AddLabel.

o no timer event if the timer is enabled from the sub.If i start the timer before i jump to the sub then the timer events will be raised as expected

o cannot use formlib.changeparent for the progressbar.This throws an expection.Basically saying that it cannot convert something into systems.windows.form.control

o cannot use formlib.changeparent for a panel to change the parent of a panel more than one time if called from within a thread.Throwing exception, basically saying it cannot change parent of a control that has been created in a different thread.So if the formlib.changeparent changed the parent of a panel to a form, it cannot be changed to another form on a subsequent call if called from somewhere else than the thread where it was called first.

o i use controlsEx 1.3 and a formlib of unknown version ( because it is not showing the version if no .cs is available).I also tried formlib 2.51, but when compiled with that version, i get exceptions on my PPC2003 device.But 2.51 does not change the behavior regarding the panel, so doesn't matter.

I personally think these are just bugs...

regards,

TWELVE
 
Top