Apr-06-2023, 08:36 PM
What have you tried to solve the problem?
tkinter help please
|
Apr-06-2023, 08:36 PM
What have you tried to solve the problem?
Apr-10-2023, 03:42 PM
(This post was last modified: Apr-10-2023, 03:42 PM by deanhystad.)
I was thinking about the test runner and realized the runner should not contain any test scripts. The test scripts should be independent of the runner, only using the runner's print() and request() methods.
I removed the test scripts from the runner code and made a directory structure like this: top folder When you write a new test script, give it a name that starts with "test_" and save it i the tests folder. Test scripts need to follow some guidelines. Each test script must:1. Assign a name, wihich will appear in a list of tests in the test runner window. 2. Assign a description, a list of strings that are printed in the test results window when the test is selected. 3. Contain a function named "script" that is executed to run the test. This is a simple test that does a countdown then displays a message in a tkinter message box. from tkinter import messagebox name = "Countdown" description = ["Test example.\n", "Performs a countdown.\n"] def script(x): """Counts down from 10 then displays message.""" try: for i in range(10, 0, -1): x.print(i, end=" ") x.wait(0.5) x.request(messagebox.showinfo, title="Countdown", message="Blastoff!") except SystemExit: pass x.print(*description, clear=True)The test runner program imports all the test modules and passes them to the test runner window. The test names get displayed in an option menu. To run a test you select the test in the option menu and press the "Run Test" menu. A Stop Test menu stops a running test. test_folder = Path(__file__).parent / "tests" sys.path.append(str(test_folder)) tests = [import_module(file.stem) for file in test_folder.glob("test_*.py")]All the code: import sys import time import threading import tkinter as tk from tkinter.scrolledtext import ScrolledText from functools import partial from pathlib import Path from importlib import import_module class MainWindow(tk.Tk): """A test runner""" def __init__(self, tests): """Initialize test runner. tests: list of test modules. """ super().__init__() self.title("Test Runner") test_names = sorted([test.name for test in tests]) self.tests = dict(zip(test_names, tests)) self.test = tk.StringVar(self, "") self.test.trace("w", self.select_test) frame = tk.Frame(self) frame.pack(side=tk.TOP, fill=tk.X) self.test_menu = tk.OptionMenu(frame, self.test, *test_names) self.test_menu.pack(side=tk.LEFT, padx=5, pady=5, expand=True, fill=tk.X) self.run_button = tk.Button( frame, text="Run Test", width=20, command=self.run_test ) self.run_button.pack(side=tk.LEFT) self.stop_button = tk.Button( frame, text="Stop Test", width=20, command=self.stop_test, state=tk.DISABLED ) self.stop_button.pack(side=tk.LEFT, padx=5, pady=4) self.text = ScrolledText(self, wrap=tk.WORD, width=80, height=20) self.text.pack(side=tk.TOP, padx=5, pady=(0, 5), expand=True, fill=tk.BOTH) self.request_action = None self.request_reply = None self.exit_test = False self.test.set(test_names[0]) def select_test(self, *args): """Display description of selected test""" module = self.tests[self.test.get()] self.print(*module.description, clear=True) def run_test(self): """Run selected test""" module = self.tests[self.test.get()] self.thread = threading.Thread(target=partial(module.script, self)) self.thread.start() self.test_menu["state"] = tk.DISABLED self.run_button["state"] = tk.DISABLED self.stop_button["state"] = tk.NORMAL self.monitor_test() def stop_test(self): """Exit test next time it calls print, wait or request""" self.exit_test = True def monitor_test(self): """Monitor test execution. Executes requests from the test.""" if self.thread and self.thread.is_alive(): # Test is still running if self.request_action is not None: # execute the request self.request_reply = self.request_action() self.request_action = None self.after(100, self.monitor_test) else: # Test is complete self.thread = None self.test_menu["state"] = tk.NORMAL self.run_button["state"] = tk.NORMAL self.stop_button["state"] = tk.DISABLED def print(self, *lines, end="\n", clear=False): """Print lines in scrolled text area. lines: Lines to print. end: Printed at the end of each line. Defaults to "\n". clear: Clear textbox before printing """ if self.exit_test: self.exit_test = False exit() if clear: self.text.delete(1.0, tk.END) for line in lines: self.text.insert(tk.END, f"{line}{end}") self.text.see(tk.END) def request(self, func, *args, **kwargs): """Test script uses request to execute a command in the GUI context. Use to draw a message box or dialog. Could even make a new window. func : function to execute. *args, **kwargs : arguments passed to action. """ if self.exit_test: self.exit_test = False exit() self.request_action = partial(func, *args, **kwargs) while self.request_action is not None: time.sleep(0.1) return self.request_reply def wait(self, seconds=0): count = max(int(seconds * 10), 1) for _ in range(count): if self.exit_test: self.exit_test = False exit() time.sleep(0.1) # Test scripts are python files in ./tests folder and start with "test_". # Collect tests in a dictionary and pass to the test runner __init__. test_folder = Path(__file__).parent / "tests" sys.path.append(str(test_folder)) tests = [import_module(file.stem) for file in test_folder.glob("test_*.py")] if tests: window = MainWindow(tests) window.mainloop() else: print("There are no tests in the test folder.")Recently added a way to stop a running test. Added a "Stop" button to the test runner. Pressing the button sets an "exit_test" flag. If the exit_test flag is set when test script calls the runner (print, request, wait), a SystemExit exception is raised, ending the test script. |
|