I would like to execute a python script sequence.py by clicking a button on usercontrol.py, all the necessary code I want to initiate is located in sequence.py main()
function sequentially.
The script sequence.py works fine by itself, and if I try to call the script from another script simply like this test.py:
import sequence as sq
sq.main()
it will also run fine.
But if I try to run it by clicking a button in usercontrol.py which contains variuos Tkinter GUI elements:
import sequence as sq
import tkinter as tk
import customtkinter
class App(customtkinter.CTk):
def __init__(self):
super().__init__()
.... CODE ....
self.start_button_1 = customtkinter.CTkButton(master=self.frame_1, text="Start", command=sq.main)
.... CODE ....
if __name__ == "__main__":
app = App()
app.mainloop()
It will crash at the crucial first steps of sequence.py.
While I think this has more to do with Tkinter, I am trying to automate a legacy software using pywinauto, the huge stack of code in sequence.py works entirely fine, except the main thing where It should open the process for automation, the window is not opened, I get a back telling me that there is no process to move windows, then after that the windows appears. sequence.py code:
import time, pywinauto
from pywinauto import application
from pywinauto.keyboard import send_keys
def main():
# Step 1: Connects to the Application, creates directories and calibrates the main window to user monitor settings
main_processName = "EFrame2"
try:
.... CODE (works fine) ....
except Exception:
user_name = input('Name: ')
user_password = input('Password: ')
send_keys('^%e') # CTRL + ALT + E shortcut to open the application
time.sleep(5)
main_pid = application.process_from_module(module = main_processName)
main_process = application.Application().connect(process = main_pid, timeout=10)
main_dlg = main_process.window(class_name="TFormMDI")
monitor_set_position(main_dlg) # it crashes at this function because main_dlg it not shown
connect_sequence(main_process, main_pid, user_name, user_password)
pass
if __name__ == "__main__":
main()
The most frustrating thing, is that all variables are returning the values as it should on debug mode.. It still finds the process and connects to it. Then I get the Traceback, and instantly the Windows that should be shown prior - appears.
To summarize, using a tkinter button to initiate the script with pywinauto somehow doesn't work when launching a new process, maybe this could be a processing/thread problem? Any pointers to solving this issue are welcome.
EDIT 1 (adding Traceback if needed):
Traceback (most recent call last):
File "C:\Users\linas\AppData\Local\Programs\Python\Python311-32\Lib\site-packages\pywinauto\application.py", line 250, in __resolve_control
ctrl = wait_until_passes(
^^^^^^^^^^^^^^^^^^
File "C:\Users\linas\AppData\Local\Programs\Python\Python311-32\Lib\site-packages\pywinauto\timings.py", line 458, in wait_until_passes
raise err
pywinauto.timings.TimeoutError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\linas\AppData\Local\Programs\Python\Python311-32\Lib\tkinter\__init__.py", line 1948, in __call__
return self.func(*args)
^^^^^^^^^^^^^^^^
File "C:\Users\linas\AppData\Local\Programs\Python\Python311-32\Lib\site-packages\customtkinter\windows\widgets\ctk_button.py", line 553, in _clicked
self._command()
File "c:\Dokumentai\auto\module\sequence.py", line 567, in main
monitor_set_position(main_dlg)
File "c:\Dokumentai\auto\module\sequence.py", line 95, in monitor_set_position
main_dlg.move_window(x=0, y=0, width=800, height=800, repaint=True)
^^^^^^^^^^^^^^^^^^^^
File "C:\Users\linas\AppData\Local\Programs\Python\Python311-32\Lib\site-packages\pywinauto\application.py", line 396, in __getattribute__
ctrls = self.__resolve_control(self.criteria)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\linas\AppData\Local\Programs\Python\Python311-32\Lib\site-packages\pywinauto\application.py", line 261, in __resolve_control
raise e.original_exception
File "C:\Users\linas\AppData\Local\Programs\Python\Python311-32\Lib\site-packages\pywinauto\timings.py", line 436, in wait_until_passes
func_val = func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\linas\AppData\Local\Programs\Python\Python311-32\Lib\site-packages\pywinauto\application.py", line 203, in __get_ctrl
dialog = self.backend.generic_wrapper_class(findwindows.find_element(**criteria[0]))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\linas\AppData\Local\Programs\Python\Python311-32\Lib\site-packages\pywinauto\findwindows.py", line 87, in find_element
raise ElementNotFoundError(kwargs)
pywinauto.findwindows.ElementNotFoundError: {'class_name': 'TFormMDI', 'backend': 'win32', 'process': 10812}
Answering my own question, if someone would have a similar issue in the future with Tkinter.
The problem was Threading, in my sequence.py, I was using timing functions like time.sleep(5)
, and it was causing problems, lagging ant etc. when using on a single thread together with GUI. I don't understand the details about threading, I am beginner myself, but you can probably count on this youtube video to help you out.
The ultimate change that I have done that fixed everything for me is in usercontrol.py, I have changed the command of the button and put the start script into a function:
import sequence as sq
import tkinter as tk
import customtkinter
import threading
class App(customtkinter.CTk):
def __init__(self):
super().__init__()
.... CODE ....
self.start_button_1 = customtkinter.CTkButton(master=self.frame_1, text="Start", command=lambda: threading.Thread(target=self.initiate_weekly_report_module).start())
.... CODE ....
def initiate_weekly_report_module(self):
sq.main()
if __name__ == "__main__":
app = App()
app.mainloop()