Any .sleep() or .wait() is bad in events. Events that don't have a dedicated thread should always run in a few milliseconds.
Delegating to an asynchronous thread and waiting there is reasonable for occasional cases. Perspective is tolerant of small sleeps in its events, as it adds threads dynamically. (But can explode into thousands of threads if misused.)
If you must perform many timed operations, you should:
Create state machines for each, and run them all from one timer event, or
Create your own persistent ThreadPoolExecutor, with a suitable number of threads, and use its various .schedule*() methods.
That's terrible. A robust, sleep-less approach would be two tags. One incremented once per second by WinCC, the other incremented once per second by Ignition (or echoing the WinCC value). With some rollover limit. Could even be just the clock second. Writing tags from two directions in handshakes is horrible and buggy.
(Or once per x seconds, whatever.)
Needing to use .sleep() is a "code smell" that suggests that your algorithm is flawed.
Here is an second example using the sleep function on a Button:
I have made a momentary button in perspective. Bu when operator clicks the button shortly the event is missed in the PLC. Therefore I have extend the mouse button click by using a sleep timer. When an operator clicks on the button the tag will be at least 2 seconds TRUE
def runAction(self, event):
import time
# This function writes an 1 for 2 second to tag
tag = self.view.params.TagPath
bool_value='true'
system.tag.writeBlocking([tag],bool_value)
time.sleep(2)
bool_value='False'
system.tag.writeAsync([tag],bool_value)
Don't do this. It cannot be made reliable. For what you describe, simply set the boolean TRUE and let the PLC reset it when it sees it. (Fire and forget is one of the few cases where writing a tag from two directions makes sense.)