How to hide/show a window in one method?

Why doesn’t this work (App.Method)?

Window.Hide()

// Do something

Window.Show()

It doesn’t matter how long the code is running, the Window does not hide. If I call an AppleScript between Hide/Show everything works as expected. When I remove the Show command at the end the Window will hide but only when the Method ends.

In modern computer systems, the UI is updated when the event stack has completed. Hide and Show aren’t being drawn until the exact same time: when your long-running code has finished.

You need to put long running code in a Thread so that it doesn’t lock up the UI drawing.

Thanks for the explanation. The code is running for only a second and the Interface shouldn’t be used in that time.

But I just have found a simple workaround:

Window.Hide()
DoEvents()

// Do something

Window.Show()

I do not recommend throwing DoEvents around whenever you can’t figure something out. Best of luck to you.

DoEvents should trigger an analyzer warning. Something like “This method may cause your app to misbehave.”

People use DoEvents out of laziness, but I think just telling people not to, or even why not to, is not really an effective solution. We need easier tools to solve these kinds of problems.

If Xojo has closures and promises, the language would catch up to the last ten years of common practices. You could then do something like

Window.Hide().Then(SomeMethod()).Then(Window.Show)

Though I admit, the real syntax would have to be something… different. Regardless, giving us some way to do the desired task without teaching the user about threads, addhandler, interface update, etc. would go a long way to helping users develop better apps.

6 Likes

simple said a method call will interupt the main event loop in xojo.
that is why your call with doevents solve your problem somehow.
instead of
hide do show (all in one)
try
hide
do
show

for longer methods over few seconds you need a thread.

xojo could change this behavior but they will not do.

You could also use a timer to accomplish the task.

Main method:
Hide window
Start timer

Timer:
Do task
Show window

The main point is you have to give time back to the system so it can update the screen. DoEvents is not a good solution.

5 Likes

Thanks for providing a solution to avoid DoEvents.

To save some typing, you can use Timer.CallLater() - see
https://documentation.xojo.com/api/language/timer.html#timer-calllater

1 Like

Thanks to all for helping. I realized that the Timer does not speed up things as I thougt. So I think threading will be better. Is it possible to dynamically create multiple threads, so if thread 1 has not finished its work, then create a new one? The Desktop Example shows 4 threads tied to a window. My threads should work without a window being called from a Method App.Action().

Yes. If you subclass Thread, you can create as many instances of your own thread class as you need.

Screen Shot 2022-04-11 at 12.12.51

Screen Shot 2022-04-11 at 12.13.05

var oThread as new MyThread

// maroMyThreads is an array property stored somewhere it won't go out of scope
maroMyThreads.Add(oThread)

oThread.Start

Thanks for the hint. It seems not to work when the Thread calls a shell command. That seem to block the whole App until the command has finished. In this example I play a longer sound. In my real code, I call several shell commands depending on the results.

Var sh As New Shell
sh.Execute("/usr/bin/afplay /System/Library/Sounds/Submarine.aiff")
// Process sh.Result …

It seems not to work when the Thread calls a shell command.
That seem to block the whole App until the command has finished.

see ExecuteModes Asynchronous maybe it helps
https://documentation.xojo.com/api/os/shell.html#shell

Not every action is thread friendly in Xojo. FolderItem actions you’ll find behave similarly. I whipped up a test project and can confirm what you’ve found about afplay + synchronous shell + thread.

You may need to refactor your design using asynchronous shells instead to free up the UI.

Thanks again. Didn’t imagine that it will get so complicated so fast. :wink:

How can I store the returned result so that the calling thread which is waiting for it gets the right one? Or do I have to split my code with multiple Shell commands into a lot of Completed() methods? But how do I transfer the values from one to next?

Is it right that async Shells can’t be created with code, but only by an item in the left sidebar? This seems not to work:

Var sh As New Shell
sh.ExecuteMode = Shell.ExecuteModes.Asynchronous
sh.Execute("mycommand")

In the DataAvailable event move the data to a property with ReadAll. The design of your classes/subclasses will determine the plan for this, so we’ll keep moving for now.

This would be best, really. We can design a class/subclass architecture that keeps things clean in your sidebar while still doing what we want.

I might recommend a design where you have a class that acts as the “controller”. You could optionally use Thread as a superclass for this, but if it’s mostly just waiting for shells I don’t see it as necessary. The controller class would retain the results and move them from one shell command to the next.

You can create them in code, but you still need to retain the reference while it runs. You can store it in a property of a window, class, module, or whatever. Yes it still shows up in the navigator, but it would be a property. If you really really don’t need to subclass Shell for this it can be done, but that doesn’t always mean that you should.

I have made an example class design for three chained shells run by a controller class:
Chained_Shells.xojo_xml_project

Hi Tim,

late thanks for your investments and the example. I haven’t transferred it to my project yet. Before I start learning, one question: Would a Worker be easier to implement, or will non-async shell commands in a Worker also block the whole App?

I found the answer myself in the forum. A worker seems to be no improvement, so I’ve to go with an async shell chain.

1 Like

Just be aware (in case you aren’t) that when you run a project in the IDE, threading is used to simulate the function of the Worker. The true performance can only be gauged in a built app.

1 Like

Thanks for your additional hints. After some investigations, I came to the conclusion that chained shell calls are impractical in my case. I use a lot of shell command likes conditional mkdir with -m parameter, sips and screencapture. It seems it’s better to transfer some parts my App into a shell script so that I only have one shell.execute in my Xojo Project.

I also could transfer all shell command to native Xojo code. Creating folders with permissions shouldn’t be hard, cropping/resizing images seems also to be possible, but what about “screencapture”? Is it possible without using MBS?