{"id":21127005,"url":"https://github.com/pyrustic/threadom","last_synced_at":"2026-02-16T02:39:24.619Z","repository":{"id":57475278,"uuid":"379410561","full_name":"pyrustic/threadom","owner":"pyrustic","description":"Tkinter-compatible multithreading","archived":false,"fork":false,"pushed_at":"2023-02-25T01:58:37.000Z","size":29,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-11-28T05:28:52.062Z","etag":null,"topics":["desktop","gui","library","lightweight","multithreading","performance","pyrustic","python","smooth","tkinter"],"latest_commit_sha":null,"homepage":"https://pyrustic.github.io","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pyrustic.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2021-06-22T21:58:42.000Z","updated_at":"2022-07-22T10:39:59.000Z","dependencies_parsed_at":"2022-09-10T02:23:47.582Z","dependency_job_id":"7d5e8dd6-2900-4bf7-8228-dfc865f36b64","html_url":"https://github.com/pyrustic/threadom","commit_stats":{"total_commits":10,"total_committers":1,"mean_commits":10.0,"dds":0.0,"last_synced_commit":"072eaf55f178a07230a55cdd6f3d43abf5771dd9"},"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/pyrustic/threadom","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyrustic%2Fthreadom","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyrustic%2Fthreadom/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyrustic%2Fthreadom/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyrustic%2Fthreadom/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pyrustic","download_url":"https://codeload.github.com/pyrustic/threadom/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyrustic%2Fthreadom/sbom","scorecard":{"id":751940,"data":{"date":"2025-08-11","repo":{"name":"github.com/pyrustic/threadom","commit":"072eaf55f178a07230a55cdd6f3d43abf5771dd9"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.7,"checks":[{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Code-Review","score":0,"reason":"Found 0/10 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact v0.0.3 not signed: https://api.github.com/repos/pyrustic/threadom/releases/48809433","Warn: release artifact v0.0.3 does not have provenance: https://api.github.com/repos/pyrustic/threadom/releases/48809433"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-22T20:37:35.531Z","repository_id":57475278,"created_at":"2025-08-22T20:37:35.531Z","updated_at":"2025-08-22T20:37:35.531Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29498775,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-16T02:07:14.481Z","status":"online","status_checked_at":"2026-02-16T02:03:22.852Z","response_time":115,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["desktop","gui","library","lightweight","multithreading","performance","pyrustic","python","smooth","tkinter"],"created_at":"2024-11-20T04:46:14.296Z","updated_at":"2026-02-16T02:39:24.596Z","avatar_url":"https://github.com/pyrustic.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- Image --\u003e\n\u003cdiv align=\"center\"\u003e\n    \u003cimg src=\"https://raw.githubusercontent.com/pyrustic/misc/master/media/jupitest.gif\" alt=\"Figure\" width=\"650\"\u003e\n    \u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/pyrustic/jupitest\"\u003eJupitest\u003c/a\u003e uses Threadom to perform smooth real-time test reports\n    \u003c/p\u003e\n\u003c/div\u003e\n\n# Threadom\n`Threadom` is a `Python` library to perform Tkinter-compatible multithreading. It's part of the [Pyrustic Open Ecosystem](https://pyrustic.github.io).\n\n[Installation](#installation) | [Reference](https://github.com/pyrustic/threadom/tree/master/docs/reference#readme) | [Website](https://pyrustic.github.io)\n\n## Overview\nIt is well known how difficult it is to implement `multithreading` in a `Tkinter` application.\n\nLet's simulate a background task that lasts 3 seconds in a Tkinter app.\n\n```python\nimport tkinter as tk\nimport time\nfrom threadom import Threadom\n\n\ndef task():\n    \"\"\"This task lasts 3 seconds\"\"\"\n    time.sleep(3)\n    print(\"Task completed\")\n\n\ndef on_start(root):\n    \"\"\"Function called when the button is clicked\"\"\"\n    threadom = Threadom(root)\n    threadom.run(task)\n\n\n# root\nroot = tk.Tk()\n\n# label\nlabel = tk.Label(root, text=\"Multithreading Demo\")\nlabel.pack()\n\n# button\ncommand = lambda root=root: on_start(root)\nbutton = tk.Button(root, text=\"Start\", command=command)\nbutton.pack()\n\n# mainloop !\nroot.mainloop()\n\n```\n\nYou can click the button `Start` 5 times quickly:\n- the user interface doesn't freeze at all;\n- you don't even notice that tasks are running in background;\n- you have the proof that tasks have been run when you read the output \"Task completed\".\n\nNow, let's send some data to the task function. This time, we care about the output of the task function and also we want it to raise an exception when the input data is an odd number.\n\n```python\nimport tkinter as tk\nimport time\nimport random\nfrom threadom import Threadom\n\n\ndef task(data):\n    \"\"\"\n    This task lasts 3 seconds and returns data*2 if all right.\n    It raises an exception when data is an odd number.\n    \"\"\"\n    time.sleep(3)\n    if (data % 2) != 0: # oops, odd number !\n        raise Exception\n    return data*2\n\n\ndef on_start(root):\n    \"\"\"Function called when the button is clicked\"\"\"\n    # Random integer\n    random_int = random.choice(range(10))\n    # Threadom instance\n    threadom = Threadom(root)\n    # Consumer callback, called when the task returns\n    consumer = lambda result: print(\"Task result: \", result)\n    # Upstream exception handler, called when an exception is raised while running the task\n    exception_handler = lambda error: print(\"Exception caught\")\n    # Run the task\n    threadom.run(task, target_args=(random_int,), consumer=consumer,\n                 upstream_exception_handler=exception_handler)\n    # Note: as you can guess, if the upstream_exception_handler parameter exists,\n    # a downstream_exception_handler parameter should exists too.\n    # The downstream_exception_handler is called when an exception is raised\n    # while running the consumer handler.\n\n\n# root\nroot = tk.Tk()\n\n# label\nlabel = tk.Label(root, text=\"Multithreading Demo\")\nlabel.pack()\n\n# button\ncommand = lambda root=root: on_start(root)\nbutton = tk.Button(root, text=\"Start\", command=command)\nbutton.pack()\n\n# mainloop !\nroot.mainloop()\n```\n\nThis seems nice, but what if we have a long-running task that produces some data that should be immediately consumed by the GUI ? You know, a task we can't wait it returns. \n```python\nimport tkinter as tk\nimport time\nfrom threadom import Threadom, QueueTail\n\n\ndef task(queue):\n    \"\"\"\n    This task puts a number in the queue every second.\n    \"\"\"\n    for x in range(10):\n        time.sleep(1)\n        queue.put(x)\n    # use QueueTail to indicate the tail of the queue\n    queue.put(QueueTail)\n\n\ndef display_data(data, strvar):\n    if data is QueueTail:\n        data = \"Task completed\"\n    strvar.set(data)\n\n\ndef on_start(root, strvar):\n    \"\"\"Function called when the button is clicked\"\"\"\n    # Threadom instance\n    threadom = Threadom(root)\n    # Consumer callback, called when the task returns\n    consumer = lambda data: print(\"Task completed\")\n    # Get a queue\n    queue = threadom.q()\n    # Run the task\n    threadom.run(task, target_args=(queue,), consumer=consumer)\n    # Consume the queue\n    consumer = lambda data, strvar=strvar: display_data(data, strvar)\n    threadom.consume(queue, consumer=consumer)\n\n\n# root\nroot = tk.Tk()\n\n# label\nstrvar = tk.StringVar(value=\"Multithreading Demo\")\nlabel = tk.Label(root, textvariable=strvar)\nlabel.pack()\n\n# button\ncommand = lambda root=root, strvar=strvar: on_start(root, strvar)\nbutton = tk.Button(root, text=\"Start\", command=command)\nbutton.pack()\n\n# mainloop !\nroot.mainloop()\n\n```\n\nYou can pause the queue consuming process whenever you want, resume it, or stop it. The `Threadom.consume` method returns a `qid` (queue ID). So you can do `threadom.pause(qid)`. Read the [reference](https://github.com/pyrustic/threadom/tree/master/docs/reference#readme) to get more details. I recommend you to use the class `threadom.QueueTail` to indicate the tail of a queue.\n\nThis library is part of the [Pyrustic Open Ecosystem](https://pyrustic.github.io). This is a work in progress. If you like it, adopt it, spread the words ;)\n\n\n## Installation\n[Pyrustic Framework](https://github.com/pyrustic/pyrustic#readme) and [Dresscode](https://github.com/pyrustic/dresscode#readme) come with `Threadom`, so you don't need to worry about the individual installation of `Threadom` if you use one of these frameworks.\n\n### First time\nInstall for the first time:\n\n```bash\n$ pip install threadom\n```\n\n### Upgrade\nTo upgrade `Threadom`:\n\n```bash\n$ pip install threadom --upgrade --upgrade-strategy eager\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpyrustic%2Fthreadom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpyrustic%2Fthreadom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpyrustic%2Fthreadom/lists"}