diff --git a/docs/source/conf.py b/docs/source/conf.py index fd343df..af41ed7 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -30,12 +30,6 @@ autosummary_generate = True -html_theme = "sphinx_rtd_theme" +html_theme = "furo" html_static_path = ["_static"] - -import os -import sys - -sys.path.insert( - 0, os.path.abspath("../../mytk") -) # or your actual source folder +# mytk must be installed (pip install -e .) for autodoc to find the package. diff --git a/mytk/__init__.py b/mytk/__init__.py index 7d3703a..7d2fb33 100644 --- a/mytk/__init__.py +++ b/mytk/__init__.py @@ -1,18 +1,8 @@ +# Re-export tkinter so users can do `from mytk import *` and get a complete toolkit. from tkinter import * import tkinter.ttk as ttk import tkinter.font as tkFont -from functools import partial -import platform -import time -import signal -import subprocess -import sys -import weakref -import json -from enum import StrEnum -import importlib - from .modulesmanager import ModulesManager from .bindable import Bindable from .app import App @@ -31,7 +21,6 @@ CellEntry, NumericEntry, IntEntry, - CellEntry, LabelledEntry, ) from .controls import Slider @@ -44,4 +33,9 @@ from .figures import Figure, XYPlot, Histogram from .videoview import VideoView -__version__ = "0.9.12" +from importlib.metadata import version, PackageNotFoundError +try: + __version__ = version("mytk") +except PackageNotFoundError: + __version__ = "unknown" + diff --git a/mytk/__main__.py b/mytk/__main__.py index 51aaf82..fe732c3 100644 --- a/mytk/__main__.py +++ b/mytk/__main__.py @@ -2,70 +2,109 @@ import sys import argparse import subprocess -from mytk import * +from mytk import Bindable + def printClassHierarchy(aClass): def printAllChilds(aClass): for child in aClass.__subclasses__(): - print("\"{0}\" -> \"{1}\"".format(aClass.__name__, child.__name__)) + print('"{0}" -> "{1}"'.format(aClass.__name__, child.__name__)) printAllChilds(child) + print("# Paste this in the text field of http://www.webgraphviz.com/") print("digraph G {") - print(" rankdir=\"LR\";") + print(' rankdir="LR";') printAllChilds(aClass) print("}") -root = os.path.dirname(__file__) -examples_dir = os.path.join(root, "example_apps") -examples = [ f for f in sorted(os.listdir(examples_dir)) if f.endswith('.py') and not f.startswith('__') ] - -ap = argparse.ArgumentParser(prog='python -m mytk') -ap.add_argument("-e", "--examples", required=False, default='all', - help="Specific example numbers, separated by a comma") -ap.add_argument("-c", "--classes", required=False, action='store_const', - const=True, help="Print the class hierarchy in graphviz format") -ap.add_argument("-l", "--list", required=False, action='store_const', - const=True, help="List all the accessible examples") -ap.add_argument("-t", "--tests", required=False, action='store_const', - const=True, help="Run all Unit tests") - -args = vars(ap.parse_args()) -runExamples = args['examples'] -runTests = args['tests'] -printClasses = args['classes'] -listExamples = args['list'] - -if runExamples == 'all': - runExamples = range(1, len(examples)+1) -elif runExamples == '': - runExamples = [] -else: - runExamples = [int(y) for y in runExamples.split(',')] - -if printClasses: - printClassHierarchy(Bindable) - -elif runTests: - moduleDir = os.path.dirname(os.path.realpath(__file__)) - err = os.system('cd {0}/tests; {1} -m unittest'.format(moduleDir, sys.executable)) -elif listExamples: - for i, app in enumerate(sorted(examples)): - print(f"{i+1:2d}. {app}") -elif runExamples: - for i in runExamples: - entry = examples[i-1] - - filepath = os.path.join(examples_dir, entry) - title = f"# mytk example file: {filepath}" - - print(f"\n\n\n") - print("#"*len(title)) - print(title) - print("#"*len(title)) - print(f"\n") - - with open(filepath, "r") as file: - print(file.read()) - - subprocess.run([sys.executable, os.path.join(examples_dir, entry)]) +def main(): + root = os.path.dirname(__file__) + examples_dir = os.path.join(root, "example_apps") + examples = [ + f + for f in sorted(os.listdir(examples_dir)) + if f.endswith(".py") and not f.startswith("__") + ] + + ap = argparse.ArgumentParser(prog="python -m mytk") + ap.add_argument( + "-e", + "--examples", + required=False, + default="all", + help="Specific example numbers, separated by a comma", + ) + ap.add_argument( + "-c", + "--classes", + required=False, + action="store_const", + const=True, + help="Print the class hierarchy in graphviz format", + ) + ap.add_argument( + "-l", + "--list", + required=False, + action="store_const", + const=True, + help="List all the accessible examples", + ) + ap.add_argument( + "-t", + "--tests", + required=False, + action="store_const", + const=True, + help="Run all Unit tests", + ) + + args = vars(ap.parse_args()) + runExamples = args["examples"] + runTests = args["tests"] + printClasses = args["classes"] + listExamples = args["list"] + + if runExamples == "all": + runExamples = range(1, len(examples) + 1) + elif runExamples == "": + runExamples = [] + else: + runExamples = [int(y) for y in runExamples.split(",")] + + if printClasses: + printClassHierarchy(Bindable) + + elif runTests: + tests_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "tests") + result = subprocess.run( + [sys.executable, "-m", "unittest"], + cwd=tests_dir, + ) + sys.exit(result.returncode) + + elif listExamples: + for i, app in enumerate(sorted(examples)): + print(f"{i+1:2d}. {app}") + + elif runExamples: + for i in runExamples: + entry = examples[i - 1] + filepath = os.path.join(examples_dir, entry) + title = f"# mytk example file: {filepath}" + + print(f"\n\n\n") + print("#" * len(title)) + print(title) + print("#" * len(title)) + print(f"\n") + + with open(filepath, "r") as file: + print(file.read()) + + subprocess.run([sys.executable, os.path.join(examples_dir, entry)]) + + +if __name__ == "__main__": + main() diff --git a/mytk/entries.py b/mytk/entries.py index 28ffd99..8f1aadc 100644 --- a/mytk/entries.py +++ b/mytk/entries.py @@ -19,7 +19,7 @@ def __init__( self._widget_args["width"] = character_width self.value = value self.bind_properties("value", self, "value_variable") - self.value_variable = StringVar() + self.value_variable = StringVar(value=value) def create_widget(self, master): self.parent = master diff --git a/mytk/eventcapable.py b/mytk/eventcapable.py index 7f737bd..ed89ffc 100644 --- a/mytk/eventcapable.py +++ b/mytk/eventcapable.py @@ -119,7 +119,7 @@ def after_cancel_all(self): """ Cancels all currently scheduled tasks for this object. """ - self.after_cancel_many(self.scheduled_tasks) + self.after_cancel_many(list(self.scheduled_tasks)) def bind_event(self, event: str, callback: Callable): """ diff --git a/mytk/tests/envtest.py b/mytk/tests/envtest.py index 7b654e4..ddb76c9 100644 --- a/mytk/tests/envtest.py +++ b/mytk/tests/envtest.py @@ -1,14 +1,3 @@ -import sys -import os - -# append module root directory to sys.path -this_dir = os.path.dirname( - os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -) -sys.path.insert(0, this_dir) - -# print(this_dir) - import pathlib import unittest from mytk import App, View @@ -16,7 +5,6 @@ class MyTkTestCase(unittest.TestCase): def setUp(self): - timeout = 1000 self.app = App() testcase_id = self.id() self.app.window.widget.title(testcase_id) diff --git a/mytk/tests/testCustomDialogs.py b/mytk/tests/testCustomDialogs.py index 70c7dc8..845809d 100644 --- a/mytk/tests/testCustomDialogs.py +++ b/mytk/tests/testCustomDialogs.py @@ -68,7 +68,7 @@ def populate_widget_body(self): diag = MyDialog( title="Test Window", buttons_labels=[Dialog.Replies.Ok, Dialog.Replies.Cancel], - # auto_click=[Dialog.Replies.Ok, 1000], + auto_click=(Dialog.Replies.Ok, 100), ) self.assertIsNotNone(diag) reply = diag.run() diff --git a/mytk/tests/testImages.py b/mytk/tests/testImages.py index 6cafa21..9f7f255 100644 --- a/mytk/tests/testImages.py +++ b/mytk/tests/testImages.py @@ -17,10 +17,8 @@ def test_init_empty(self): self.assertIsNotNone(Image()) def test_resource_directory(self): - resource_directory = pathlib.Path(__file__).parent.parent / "resources" - self.assertEqual( - resource_directory, pathlib.Path("/Users/dccote/GitHub/myTk/mytk/resources") - ) + self.assertTrue(self.resource_directory.exists()) + self.assertTrue(self.resource_directory.is_dir()) def test_init_with_path(self): self.assertIsNotNone(Image(filepath=self.resource_directory / "error.png")) diff --git a/mytk/tests/testMyApp.py b/mytk/tests/testMyApp.py index b2ee869..d46833f 100644 --- a/mytk/tests/testMyApp.py +++ b/mytk/tests/testMyApp.py @@ -1,5 +1,6 @@ import envtest import unittest +from functools import partial from mytk import * diff --git a/mytk/videoview.py b/mytk/videoview.py index bcc872d..ffbe28c 100644 --- a/mytk/videoview.py +++ b/mytk/videoview.py @@ -7,7 +7,10 @@ from .popupmenu import PopupMenu import importlib import signal -import cv2 +try: + import cv2 +except ImportError: + cv2 = None class VideoView(Base): diff --git a/pyproject.toml b/pyproject.toml index af292b4..88caa11 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,15 +1,16 @@ [build-system] -requires = ["setuptools<69", "wheel"] +requires = ["setuptools>=70", "wheel"] build-backend = "setuptools.build_meta" [project] name = "mytk" -version="0.9.14" +version="0.9.15" description = "A wrapper for Tkinter for busy scientists" readme = "README.md" license = {file = "LICENSE"} +requires-python = ">=3.11" dynamic = ["dependencies"] authors = [ {name = "Daniel Côté", email = "dccote@cervo.ulaval.ca"} @@ -20,19 +21,44 @@ maintainers = [ classifiers = [ "Development Status :: 3 - Alpha", - - "Topic :: Software Development :: Build Tools", - - # Specify the Python versions you support here. + "Intended Audience :: Science/Research", + "Topic :: Scientific/Engineering", + "Topic :: Software Development :: Libraries :: Python Modules", + "Environment :: MacOS X", + "Environment :: Win32 (MS Windows)", + "Environment :: X11 Applications", + "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", ] -# py_modules=["mytk"] +[project.urls] +Homepage = "https://github.com/DCC-Lab/myTk" +Repository = "https://github.com/DCC-Lab/myTk" +"Bug Tracker" = "https://github.com/DCC-Lab/myTk/issues" + +[project.optional-dependencies] +video = ["opencv-python"] +optics = ["raytracing"] +docs = ["furo", "sphinx"] +all = ["mytk[video,optics]"] [tool.setuptools.package-data] "mytk.resources" = ["*.png"] [tool.setuptools.dynamic] dependencies = {file = ["requirements.txt"]} + +[tool.ruff] +target-version = "py311" +line-length = 100 + +[tool.ruff.lint] +select = ["E", "F", "W", "I"] +ignore = ["E501"] + +[tool.pytest.ini_options] +testpaths = ["mytk/tests"] diff --git a/requirements.txt b/requirements.txt index e895549..67ef9b1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,8 @@ -matplotlib==3.9.2 +matplotlib>=3.9 numpy packaging pyperclip -Requests +requests scipy pandas openpyxl -raytracing -opencv-python \ No newline at end of file