Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Controlling a Public Timer 1

Status
Not open for further replies.

stanlyn

Programmer
Sep 3, 2003
945
US
Hi,
I need to know how to access a timer that my main.prg creates by running the "AppBaseTimer.prg" Here is the top 16 lines of the "AppBaseTimer" code.

I need to control it from various forms and code. I have tried _vfp and _screen and otimeobj.interval and several other with no success.

Thanks, Stanley


Code:
Public otimerobj
otimerobj = Createobject('MyTimer')

Define Class MyTimer As Container
	Add Object otimerobj As timerclock
Enddefine

Define Class timerclock As Timer
	Interval = 8000
	**
	Procedure Timer
		If gcmodulename="DRI FullServer"
		Else
			If m.gnshutseconds=0
 
Use intellisense.
The root object is the public variable otimerob defined by line 1. It's a 'mytimer' class instance by line 2.
But that is not a timer itself, it's a container control. That's the first questionable thing, but it is, what it is. Just look at the class definition lines 4-6, there's your container that has an object inside it. And that object also is called otimerobj. But that doesn't mean Add object adds itself to itself. It just means that the public variable otimerobj in a container that has a Timer inside as otimerobj.otimerobj.

So your access to the code timer is otimerobj.otimerobj and the interval property is otimerobj.otimerobj.interval.

When you don't see that, when you have a hard time understanding that code, you still should spot by just reading lines 1 and 2, that the code creates a variable otinmerobj. And this variable is not added to _VFP or _SCREEN. Ir exists independent of anything. And intellisense will guide you, if you make a testrun in the command window:
appbasetimer_hkvnju.png


There's a lot of further properties and methods in the way to this subobject, but you see it's there.

As that PRG seems to be a part of a framework you use and is makes AppBaseTimer.prg I don't think you're supposed to make use of it. The framework makes use of it. And as it's based on a public variable, you can only have one tuner like that, which I guess the application object of that framework already will use itself. Calling that PRG more than once will just override the public object with itself.

If you want a timer, than write your own timer class and create that where you want it. What makes no sense is to create the base class "Timner" as that has no timer code and you can't add that code at runerime, So unlike other classes you can build up from the scratch like a base form. So please, just really learn how to define a class either as here in PRG code or also as a class within a class library, because tinmers actually need to be defined in a project before you create them at runtime. You don't get around with oRimer = Createobject("timer") and then setting timer procedure code.

You don't have to learn much, as that PRG already gives you an example starting from line 8 - Define Class timerclock As Timer - and going forward until an ENDDEINE. Just ensure your copy of that code in your own PRG uses another class name than timerclock, because same names for several class definitions will make it a casino game what CREATEONCJET("timerclock") will create. Class names have to be unique within a project to not get in trouble. And if you have that and give it your own Timer procedure code, don't make use of the construct this framework code uses of a public variable. Add this timer where you need it. As single timer in _screen or as timer per form or per container of something.

In fact this latter thing - adding the timer to a form or container - is much easier, if you create a classllb (VCX) and a timer class of it. Then - at design time of a form, you can drag a timer class from that library onto your form where it will become thisform.timer1 or in general a timer1 object of the part you drag it to. Amd if you don't know what that means, learn to use the visual designer, the form and class designer.

Bye, Olaf.

Olaf Doschke Software Engineering
 
I'm not clear as to why you have placed the timer inside a container. It would be simpler to define MyTimer as a timer rather than a container. You could then use oTimerObj to directly address the timer. For example, to change the interval, you could simply do [tt]oTimerObj.Interval = 2000[/tt] (or whatever) from anywhere within your application.

In any case, the code you have posted is quite wrong. You seem to be creating an object named oTimerObj, based on a class MyTimer. But MyTimer itself contains oTimerOjb. I can't see how that could work.

So you want something like this:

Code:
Public otimerobj
otimerobj = Createobject('MyTimer')

Define Class MyTimer As Timer
	Interval = 8000
	**
	Procedure Timer
		If gcmodulename="DRI FullServer"
		Else
			If m.gnshutseconds=0


Also, _VFP and _SCREEN are not relevant here.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Mike Lewis said:
But MyTimer itself contains oTimerOjb. I can't see how that could work.

It's confusing, but properties can have same names as variables, or as parent objects, with multiple containers or pages of pageframes, you can also have many Text1 textboxes, as property names or subobject names only have to be unique to other object or property names of the same nesting level.

Otherwise, I second you, the container class is a complete waste, when the timer is created into a public variable. Even adding a timer to a form you can directly add a timer object to a form, it doesn't need to be in a container first. That's the reason, I also recommended to create an own timer class by copying the definition starting from line 8.

Bye, Olaf.

Olaf Doschke Software Engineering
 
Another tip for you. If you actually wanted to subclass the timer class and reuse its code the way to subclass a class is to specify the classname in the AS clause of the definition:

Instead of..
Code:
Define Class MyTimer As Container
	Add Object otimerobj As timerclock
Enddefine
...you instead subclass the timerclock class as MyTimer class by defining it this way:
Code:
Define Class MyTimer As timerclock
Enddefine

This way it would be the same class just inherited 1:1 with a new name. The core definition of a timer is it's Timer() event that is the code happening after each Interval milliseconds.
So a way to make sensible use of that subclassing would be defining it this way:

Code:
Define Class MyTimer As timerclock

Procedure Timer()
   * Your code, part 1
   DODEFAULT() && execute the inherited code
   * Your code, part 2
Endproc 
Enddefine

Now it will depend whether it makes sense to first or last execute the inherited code, to only have new code before or after, you may even call the inherited code twice, if it would make sense. For example if the inherited code writes into a log file that is specified in a property, you could set that property to file1, call DODEFAULT(), then change to file2 and call DODEFAULT() again to have some data in two log files.

What makes sense is subject to change, but one thing is for sure specifically about the timer class: Not writing own code in the Timer() event you just inherit what the original timer class does, you don't need a new inheritance level for that, subclassing the timer only makes sense, when you want to do something additional in the Timer() event. And because the inherited code would not run at all, if you would not call DODEFAULT in any place of your new Timer() event code, in that case, the only things you inherit is the interval value and some other minor settings, You would also not change the behavior of a framework, an application object that uses the timerclock class will not change to use your class definition, inheritance is one way, it doesn't inject your new behavior into the inherited class. So, in that case, it would only make sense when you could define the timer class the application uses.

And in the simple case you want to execute your own timer event code without executing inherited code, then simply write your own class based on the Timer base class. There can be a reason to inherit timer code - see my remark from earlier - but not in that case.

Bye, Olaf.

Olaf Doschke Software Engineering
 
Hi Olaf and Mike,
I wrote this code way back in 1998 and it has worked flawlessly in many installs since then. I have never had to revisit this code in all these years.

I do understand what you are saying and will revise the code to reflect. Its also confusing to me now, hence the question.

Way back when I wrote it, I had been using timers attached to forms and other objects, but never to "nothing" as in this case. I was not knowingly adding or using an application object, which would make more sense. Hind sight is 20/20. At that time, the question became, how can a timer be added to the main.prg (non-object). I eventually got it working.

Yesterday, I was troubleshooting some new unrelated code and the timer was kicking in and changing values in the debugger (common in timers). So, I set out to temporarily disable the timer in the code so I could work through the other unrelated work, and was unable to quickly see how to talk to it. It was also the end of a 26 hour shift, and before quitting I thought about posting a quick question here, which is what I did. Thanks to you guys, I have something to work with now.

This timer was created so that when the app starts up it starts this timer that looks for a file every 8 seconds and if found, it triggers the apps shutdown process. The file it looks for is added by the server version of the app by an admin ticking a Block Client Access checkbox for maintenance concerns. Once the maintenance has finished the admin un-ticks the checkbox and the client is allowed access again.

Anyway, thanks again,
Stanley
 
Stanley,

I take your point about the timer kicking in during a debugger session. Although this won't solve that particular problem, in general the timer should always temporarily disable itself as soon as its Timer method is called, and re-enable itself when the method has finished. This is easy as putting [tt]THIS.Enabled = .F.[/tt] at the start of the Timer event, and [tt]THIS.Enabled = .T.[/tt] at the end.

Adding a timer to a form should be no problem. Just drop it on the form in the usual way, just as you would with a textbox or label. Give it a name, then adjust its properties and add your code.

That said, you are right that adding a timer to a form wouldn't be appropriate in this case, given that you want it to be active the whole time. For what it's worth, the way I handle this situation is to have a Shutdown class, which I instantiate at the start of the run. The class enables the timer, sets its interval and checks for the semaphore file. It also handles the many other chores involved in forcing the application to close, including warning the user to save their work (which itself is subject to a timeout in case the user is no longer present), programmatically reverting any unsaved edits and rolling back any transactions in progress, and finally closing the system down.

No doubt you are doing something similar. But putting it all in a single class means that you can hide all those details from the main part of the application. It also means that the shutdown procedure is completely reusable, so you could easily incorporate it into other applications.

Mike


__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Hi Mike,
I just look, and it looks like I've doing what you do since day one, as the shutdown process does all the cleanup work. As the app starts (main.prg), it calls "AppBaseTimer" and fires every 8 seconds looking for the file, then goes away for 8 seconds. If found the shutdow.prg is called that does the cleanup... Interesting, here is the approx iteration count per install... ((((60*60)*24)*365)*22)/8 = 86,724,000 and never an issue.

Thanks, Stanley


Code:
Public goTimer
goTimer = Createobject('MyTimer')

Define Class MyTimer As Timer
	Interval = 8000
	**
	Procedure Timer
		If gcModuleName="DRI FullServer"
		Else
			If m.gnShutSeconds=0
				If Type("gcSharedDataPath")="U"
					Public gcshareddatapath, gcshutfile, glforcelogoff
				Endif

				Local llClosePreference
				llClosePreference = .F.

				If  .Not. Used("Preference")
					Use .\Data\Preference In 0
					llClosePreference = .T.
				Endif

				Select "Preference"
				Goto 1
				gcSharedDataPath = Upper(Alltrim(Preference.shared_datapath))
				gcShutFile = Strtran(gcSharedDataPath, "\DATA\", "\")+"temp\DoLogOff.txt"

				If llClosePreference=.T.
					Use In Select("Preference")
				Endif

				If File(gcShutFile)
					m.gnShutSeconds = Val(Filetostr(gcShutFile))
					m.gtShutTime = Datetime()+Val(Filetostr(gcShutFile))
				Endif
			Endif

			If m.gnshutseconds>0
				If File(gcshutfile)
					Wait Window Noclear Nowait "A Forced Maintenance Log Off is being issued by the " + ;
						"System Administrator...  Please save your data and close your application before " + ;
						"this forced log off procedure begins!"+Chr(13)+Chr(13)+ ;
						"Log Off will automatically commence in " + Alltrim(Str(m.gtshuttime-Datetime()))+" seconds..."

					If Datetime() >= m.gtShutTime
						Do 'Shutdown'
						Wait Clear
					Endif
				Else
					m.gnShutSeconds = 0
					m.gtShutTime = {}
				Endif
			Endif
		Endif
	Endproc
Enddefine
 
Mike,
Now days, I use an application object to do what I believe you do in your class. It may or not be similar. At the start of the app, I call a procedure in my "MasterProcs.prg" named "AppObject" which is nothing more that a public object that I can add additional temporary pems to anytime. If I need it long term, I edit the procedure and add it in. I guess that in a way its similar to your class.

Thanks, Stanley

 
Stanley, I think you're right. What you are doing does look similar to mine.

When I first developed the class, I was surprised at how much detail I had to think about, that I hadn't anticipated. Things like: What happens if another user tries to log in after you have issued the shutdown message but before the shutdown takes place? Or tries to login after the shutdown has taken place and while the maintenance work (whatever it was that required the shutdown) is in progress? What if the administrator decides to cancel the shutdown? And so on.

None of these is difficult, but it does show that it's not quite as easy as some people think.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Okay, this both contradicts and confirms that I interpreted the program name 'AppBaseTimer.prg' to belong to some kind of application framework. It it implements an application feature to be able to react to an outside signal with a shutdown.

Nobody said there's anything hindering a timer to work in a container, it's just not necessary, neither as standalone object variable needs to be a visual control not to be able to add an instance of this composed class to a form. A timer is non-visual in the sense it only has a visual representation in the form of the little button with the clock icon in a designer and is invisible at runtime. The same goes for the custom base class. I don't know if that was the reason you built it that way back in 1998, I don't know if that's why you defined it in a PRG and not in a VCX, there only is the session class that can't be picked as base class of a VCX class and since later also some member classes.

If it would be a piece of a bigger plan, putting it into a container just enables you to add more to it. A timer itself has no addobject while a container has and can host and nest a composition of class instances as is normal for a complex application object, it also can be nested. So it's composition friendly in both directions as host/master object or client/child/slave object.

The downside is a more complex name than necessary if you really just need a goTimer as you named it now, then just create the timer class as goTimer and addressing it is simpler.

And last not least the only thing more elegant would be, if it just needs to be instantiated and works completely independent from addressing it from the outside, ie just instantiate it when the application is ready to be controlled by such a shutdown timer, let it be part of a composed application object where you already drag anything into the class design at design time. The only line of code addressing such a timer may be a line in application init enabling the timer once it's ready to start going into READ EVENTS thus both ready to work or to be shut down. When the timer is part of the application object, you can do one step more elegant: You can make the routine checking whether the app should shut down part of the root application object, so it can run this check already at the start before it pulls up all kind of other child objects and the timer would also just call into goApp.checkforshutdown() or This.parent.checkforshutdown(), as it's part of goApp.

Bye, Olaf.

Olaf Doschke Software Engineering
 
Hi Mike,

>> I was surprised at how much detail I had to think about, that I hadn't anticipated.
So true, and vfp is baby stuff compared to a modern dynamic, responsive data driven web site, which I find so remarkable that it can even run...

Handling of new logins coming after the admin starts their maintenance session is automatically because when the admin for the server version starts the session, it places the lock flag file on the server and not the client. And the timer within the client looks for that file every 8 seconds and if found it starts the shutdown process. Simple and clean...

Stanley
 
Hi all,

For years now, I have been programming without classing and subclassing. In my early days I started each application subclassing all the base classes. I would eventually build special features into that app.

Then maybe years later in a different appp, I would need some of that old functionality (ie. an object) and either I had to copy/paste all the code from it into the new apps subclasses or deal with installing and maintaining the old apps class libraries. This quickly became a nightmare to manage, especially when changing dev machine and class locations, so I quit them all together.

So now, all of my apps are build from the base classes where I can modify the objects anyway I want and not have any issues moving them into whatever app I need them in at any time.

Most of my apps are very different in design, so at least for me, all this subclassing is a waste of time. If on the other hand my apps were very similar (ie. cookie cutter), then it would make sense.

So, I understand the pros and cons of classing, and it has never worked for me productively. I find it more productive to build everything from the base classes and anytime I need that functionality, I grab the code or object and modify it for the current app with no dependencies.

I know many will argue this and the subject is quite controversial, and I do not wish to start a war here. I'm just stating my experience...

Thanks, Stanley
 
Don't you need the same functionality in multiple places in a single application? To me, that's the big power of subclassing.

For example, I'm working on an application that all over the places uses what we're calling a look-up control. It's a label, a button, and a couple of textboxes arranged inside a container and set up so to look up data in a table, you can click the button or type a partial match into the textbox. That pops up a picklist (based on another class). I've put a ton of code in the class, and then I have several dozen subclasses, each configured for lookup of a different kind of data. If I had to build this functionality each time I needed it, even by cut-and-paste, there's no way we could have these all over the application. And, more importantly, each time we found a bug or wanted to enhance the way it works, I'd have to touch dozens of places.

As for sharing across applications, I find there is some functionality I always want in certain controls and so I have a standard set of base classes. But I don't share them between projects. For each new project, I start with a copy and customize for that client.

Tamar
 
The last time I had to introduce the subclassed level od controls you mention are always useless to you was for a resizing capability of a system. And it bit me doubly that this wasn't foreseen.

You can't add code aad methods to the textbox base class, unless you do to the individual textbox put on an individual code, that's always the warning that goes with that and it's ironic if it hits you even though the project istn't your own creation. I inherited that, haha.

It's not all about that, but it disappoints when you always recommend and evangelize the idea, see it not taken into consideration, and become the victim of it.

The way you do your PRGs with the usage of the class built into the head section - if you always do it that way - makes it easy to use the class by a DO your.PRG call, but you only will ever have one instance with a predefined variable name. You're hindering yourself to make better use of classes.

Nye, Olaf.

Olaf Doschke Software Engineering
 
>> You're hindering yourself to make better use of classes.
Maybe and maybe not... Not sure what qualifies. I still use some of the old classes I created 20 years ago in what little I do in VFP these days. When I create an app now, I create a form by selecting a new form from VFP's control list and it shows as being the base class. I then add text boxes and etc and they all say they are base classes. Occasionally, I need to grab one of the old custom controls, and if trivial, I copy the code I need into the current base class code. So whatever that means, is what I'm doing.

Also it may be worth noting that if I need all the text boxes to have specific properties, I set them all at runtime with the setall command.

As earlier said, I've wasted more time with custom classes than not, therefore I avoid them. Now, if you live in VFP everyday, then totally know how to use every nook and cranny of VFP would be a huge plus. I do little in VFP these days, but in spurts, like heavy use for 2-3 months and nothing for 2-3 years, therefore I find myself relearning. That is just the way it is...

Thanks, Stanley
 
I agree with Tamar's point about re-usability applying within an application as well as across different applications.

But there is another important reason for using classes: encapsulation. In other words, hiding the details of a process or function in order to make it easier to use and to change.

For example, I have a Settings class which is responsible for maintaining the various bits of low-volume information that most applications need: user preferences, size and positions of windows, company-wide information, and so on. The class provides the mechanism for storing and retrieving this information. The information itself is currently held in a DBF, but the class is the only component that knows that. If I ever decide to change the storage mechanism - to use the Registry for example, or maybe an XML file - I can make that change entirely within the class, without having to worry about side effects elsewhere in the application.

This also applies to the Shutdown class that we discussed earlier. For me, that is one of the great advantages of object orientation.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
SetAll unfortunately is no solution to all things you need to set in general. You can specify a class as third parameter. But it then only works for that specific class. If you mix base textboxes and a specific datetextbox or amount textbos you'll begin needing multiple setall where a single base class could do what you want. Even just by setting its properties like fontname and size that inherits.

You could go for unspecific setall, but then encounter problems again by addressing too many controls. Simply see how you would do setall("width",this.width*factor) - this isn't possible. Resizer libraries like wmresize therefore iterate the Object() collection. That's fine, that's what I also embedded in some form base class, which at least existed. But think about forms that change themselves at runtime by removing and adding containers. In that moment the top/left position is known from the old container, that's no big deal, but the new container is designed at a specific size that's not valid resized and needs to be changed without affecting the already resized part of the form. That all is a bit more complex. Invluding the fact the application already has some base resizing behavior, but that mainly was anchoring form parts without any zooming and that makes touch buttons too small in the long run eventually.

No, exactly that was used in that application and also became a problem.

In code inheritance, you can also override and extend at some subclasses and create branches of families,

Bye, Olaf.

Olaf Doschke Software Engineering
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top