What is the best method to tell a function in a tkinter GUI to wait until a system call made by shutils resolves before proceeding? - python-3.x

I have a tkinter GUI that at some point prompts the user for a folder, checks if it exists, and then, if it does and the user consents, deletes and recreates it. I am doing this via the following code:
try:
self.status_string.set('Cleaning up output directory to be overwritten')
shutil.rmtree(output_folder)
while os.path.exists(output_folder):
time.sleep(1)
os.makedirs(output_folder + '/events')
except OSError:
self.status_string.set('Failed to create directory {0}, verify that you have permission to do so'.format(output_folder + '/events'))
I am currently calling time.sleep in order to force it to wait until the directory has been completely removed before trying to recreate it, because the contents can be large and it may take a while and I want to avoid the race condition. But it seems wrong to me to be using sleep during the mainloop of tkinter, and I am not certain that checking for existence after calling rmtree is valid. It seems to work in testing, but that could be luck. What is the proper way to wait for the system call to resolve before proceeding?

Related

Is there a better/more pythonic way to load an arbitrary set of functions from modules in another folder?

I'm just basically asking:
if it's considered OK to use exec() in this context
if there's a better/more pythonic solution
for any input or comments on how my code could be improved
First, some context. I have main.py which basically takes input and checks to see if I've written a command. Let's say I type '/help'. The slash just tells it my input was supposed to be a command, so then it checks if a function called 'help' exists, and if so, that function will be run.
To keep things tidy in main.py, and to allow myself to add more commands easily, I have a 'commands' directory, with individual command files in it, such as help.py. help.py would look like this for example:
def help():
print("You've been helped")
So then of course, I need to import help() from help.py, which was trivial.
As I added more commands, I decided to add an init.py file where I'd keep all the command import lines of code, and then just do 'from init import *' in main.py. At first, each time I added a command, I'd add another line in init.py to import it. But that wasn't as flexible as I wanted, so I thought, there's got to be a way to just loop through all the .py files in my commands directory and import them. I struggled with this for a while but came up with a solution that works.
In the init.py snippet below, I loop through the commands directory (and a couple others, but they're irrelevant to the question), and you'll see I use the dreaded exec() function to actually import the commands.
loaded, failed = '', ''
for directory in command_directories:
command_list = os.listdir(directory)
command_list.sort()
for command_file in command_list:
if command_file.endswith(".py"):
command_name = command_file.split(".")[0]
try:
# Evil exec() hack to use variable-name directories/modules
# Haven't found a more... pythonic... way to do this
exec(f"from {directory}.{command_name} import {command_name}")
loaded = loaded + f" - Loaded: {command_name}\n"
except:
failed = failed + f" - Failed to load: {command_name}\n"
if debug == True:
for init_debug in [loaded, failed]: print(init_debug)
I use exec() because I don't know a better way to make a variable with the name of the function being loaded, so I use {command_name} in my exec string to arbitrarily evaluate the variable name that will store the function I'm importing. And... well, it works. The functions work perfectly when called from main.py, so I believe they are being imported in the correct namespace.
Obviously, exec() can be dangerous, but I'm not taking any user input into it, just file names. Filenames that I only I am creating. This program isn't being distributed, but if it was, then I believe using exec() would be bad since there's potential someone could exploit it.
If I'm wrong about something, I'd love to hear about it and get suggestions for a better implementation. Python has been very easy to pick up for me, but I'm probably missing some of the fundamentals.
I should note, I'm running python 3.10 on replit (until I move this project to another host).

Running one Autokey script from another Autokey script?

Right now I've got two Autokey scripts (for modularity), one that opens a file, and one that puts text in it.
The one that opens the file has hotkey F1 (and we'll call this script 1 for simplicity), and the one that puts text in it has hotkey F2. I want a new Autokey script, that when I hit F3, it runs both the 1 script and the 2 script.
I've tried making the 3 script just send the F1 and F2 keys, but the timing is all off. It would be better if I could just call 1 and 2 from 3. Is this possible?
Thanks!
https://github.com/autokey/autokey/blob/fc7c6b90f3f28a57ad256829ef2b69e3be5148d2/lib/autokey/scripting.py#L1242
engine.run_script("<description>")
ought to do the trick
"Description" in this context is generally the name of the script in the side bar in the AutoKey interface. If you open up the .json file for the script you can see it for sure, but it will be the name displayed in the side bar unless you have duplicate names for scripts in the same folder or some other edge scenario
AutoKey is not recursive. It does not inspect the output of an AutoKey phrase or script to look for hotkeys or trigger abbreviations which would invoke further actions. That's why your initial solution does not work.
It depends on what you're actually trying to do.
If you have multiple independently useful scripts, the best approach is the one #Icallitvera offers.
If you just want to modularize shared functionality, you can create Python modules of functions and place them in the AutoKey Modules directory. Then, you can import them into any AutoKey script which needs them.
You can find/set the Modules directory from the AutoKey Main Menu via Settings->Configure AutoKey->Script Engine.
At the moment, this approach is limited because scripts invoked this way do not (easily) have access to the AutoKey API, so they can't include any API calls. We plan to fix this in the next major release, AutoKey 0.96. If you really need to do that now, ask on our support list or on Gitter.
I ran into that same problem and the only way I found to work around that restriction is by using the exec() function. Since the scripts with functions to share were already in the AutoKey user folder I used that. So in order to load shared functions of my user module "mygame" into the autokey script I used:
exec(open(engine.configManager.userCodeDir + "/" + "mygame.py").read())
To avoid name collisions when "importing" more than one script and a more module-like feeling I put the functions in classes and instantiate with a variable that is named like the module.
So in the end it looks like this:
mygame.py:
import time
class MyGame:
def GameReload(self):
self.GameExitNoSave()
time.sleep(0.3)
self.GameLoadCurrent()
def GameExitNoSave(self):
keyboard.send_key('d')
time.sleep(0.1)
keyboard.send_key('<up>')
time.sleep(0.05)
keyboard.send_key('<enter>')
def GameLoadCurrent(self):
keyboard.send_key('<down>')
time.sleep(0.1)
keyboard.send_key('<down>')
time.sleep(0.1)
keyboard.send_key('<enter>')
time.sleep(0.5)
keyboard.send_key('<enter>')
mygame = MyGame()
User script in AutoKey:
exec(open(engine.configManager.userCodeDir + "/" + "mygame.py").read())
mygame.GameReload():

How to, in Python, ignore certain/all keypresses while function executing in program on Macintosh

I would like to know, how would I, in Python, be able to ignore certain/all keypresses by the user while a certain function in the program is executing? Cross platform solutions are preferred, but if that is not possible, then I need to know how to do this on Macintosh. Any help is greatly appreciated! :)
EDIT: Right now, I am processing keypresses through the turtle module's onkey() function since I did create a program using turtle. I hope this helps avoid any confusion, and again, any help is greatly appreciated! :)
You might want to modify your question to show how you're currently processing key-presses. For example is this a command-line program and you're using curses?
You could use os.uname to return the os information or sys.platform, if that isn't available. The Python documentation for sys.platform indicates that 'darwin' is returned for OSX apple machines.
If the platform is darwin then you could, in your code, ignore whatever key-presses you want to.
Edit (Update due to changed question):
If you want to ignore key-presses when a certain function is being called you could either use a lock to stop the key-press function call and your particular function being executed together.
Here is an example of using a lock, this may not run, but it should give you a rough idea of what's required.
import _thread
a_lock = _thread.allocate_lock()
def certainFunction():
with a_lock:
print("Here's the code that you don't want to execute at the same time as onSpecificKeyCall()")
def onSpecificKeyCall():
with a_lock:
print("Here's the code that you don't want to execute at the same time as certainFunction()")
Or, depending on the circumstances, when the function which you don't want interrupting with a key press is called, you could call onkey() again, with the specific key to ignore, to call to a function that doesn't do anything. When your particular function has finished, you could call onkey() again to bind the key press to the function that processes the input.
I found similar problems, maybe it will help you with your problem:
globally capture, ignore and send keyevents with python xlib, recognize fake input
How do I 'lock the keyboard' to prevent any more keypresses being sent on X11/Linux/Gnome?
https://askubuntu.com/questions/160522/python-gtk-event-ignore

wxpython threading textctrl disappears until files are processed

Appropriate code provided below. I had this working once but have since messed up due to having to change some of the other code. Can't figure out what I've done. The encrypt function creates a thread and makes a call to function EncryptProc to process one or more files. Once each file is completed it should print the name to a textctrl. In the codes current state it waits until all threads/files are processed before printing. It then prints evrything in one go. During processing the textctrl also completely disappears. Any help would be much appreciated as its starting to drive me nuts, lol.
---EDIT---
CODE REMOVED
There should be no GUI access from within threads - in your case EncryptProc is writing to the text control directly - you need to either:
Use CallAfter in the thread to update the text control after the thread exits or
Raise custom event an event in the thread that carries the file name information
and have a hander in the main thread that updates the text control
on receiving the event.

running a .py file, giving it arguments and waiting for its return values

Just like the title says...
Example: I have a file called copy.py. That file wants a path to a file/folder which it will move to another directory and will then return "done" if it successfully moved the file. For some reason I have to run my copy.py file from another python program (it's not given that both files are in the same directory) and wait for copy.py to finish its actions. When it is finished, it should tell me "done" or, let's say, "error", so I know if it actually was successful or not.
Please answer in a way a python beginner can understand...
Often, you can just import the module and call its functionality, but if it's a stand-alone program that expects command-line arguments, etc., then you may want to separate the command-line handling from the functional part of the code, so that you can import and call it as I suggested at the beginning.
Failing that, just treat it like another program:
with os.popen('python copy.py {0} {1}'.format(src, dst)) as copy:
output = copy.readlines()
if 'error' in output:
# Oops...

Resources