-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Can't use tkinter with new venv set up with uv #7036
Comments
This is a python-build-standalone quirk: https://gregoryszorc.com/docs/python-build-standalone/main/quirks.html#tcl-tk-support-files We might need to set the variable as described there. 🤔 |
For the record, rye did this astral-sh/rye#233 |
I'm not opposed to doing a similar thing here. |
to maybe point others to a workaround, all you'd need to do is to export an env variable called TCL_LIBRARY pointing to the correct tcl directory. ATTENTION: this assumes that your .venv is based on the default python available to uv and also that tcl is at version 8.6, so keep that in mind #!/bin/bash
# Get the Python path
PYTHON_PATH=$(cd ~ && uv run which python)
# Extract the base path
BASE_PATH=$(dirname "$(dirname "$PYTHON_PATH")")
# Construct the TCL_LIBRARY path
TCL_LIBRARY="$BASE_PATH/lib/tcl8.6"
# Set the environment variable
export TCL_LIBRARY
# Print the set variable (for verification)
echo "TCL_LIBRARY has been set to: $TCL_LIBRARY" for comfort I've then just put the var in my .env file which gets automatically loaded by .envrc (direnv) - which btw would totally be an appealing feature for uv to replicate :P |
The quirks page says
However,
What am I doing wrong? |
Those are in python installation directory not in virtualenv, I'm not next to linux box but under windows: (venvflon) PS C:\Users\mplichta\Projects\venvflon> uv python list
cpython-3.13.0-windows-x86_64-none C:\Users\mplichta\AppData\Roaming\uv\python\cpython-3.13.0-windows-x86_64-none\python.exe
cpython-3.12.7-windows-x86_64-none <download available> So, path would be: As workaroud you can use tkinker like: from os import environ
from pathlib import Path
from sys import base_prefix
environ["TCL_LIBRARY"] = str(Path(base_prefix) / "tcl" / "tcl8.6")
environ["TK_LIBRARY"] = str(Path(base_prefix) / "tcl" / "tk8.6")
import tkinter as tk |
Oh I understand now. On macOS they're under |
This is an important issue for me and my employers. I'm currently using the workaround suggeted above. |
It'd be a stretch to say it's a good first issue, there's a lot of nuance to the possible solutions and I would have to do quite a bit of research to even understand what the trade-offs are. I think the first best step is to understand Rye's approach and if there are any alternatives. If it's the most compelling approach still, we should be able to port it over without too much difficulty. |
Since most people expect to use Tkinter that comes with the python, I guess including I tried @emcek method on Mac and the result is successful from os import environ
from pathlib import Path
from sys import base_prefix
environ["TCL_LIBRARY"] = str(Path(base_prefix) / "lib" / "tcl8.6")
environ["TK_LIBRARY"] = str(Path(base_prefix) / "lib" / "tk8.6")
print(environ["TCL_LIBRARY"])
print(environ["TK_LIBRARY"])
from tkinter import *
from tkinter import ttk
root = Tk()
frm = ttk.Frame(root, padding=10)
frm.grid()
ttk.Label(frm, text="Hello World!").grid(column=0, row=0)
ttk.Button(frm, text="Quit", command=root.destroy).grid(column=1, row=0)
root.mainloop() However I have changed the path of Maybe uv can populate the two environmental variable by default? |
So... expanding on the workaround so it works on all platforms, doesn't clobber existing env vars, might work with tk 8.7+, and doesn't bother doing anything if tk is actually working already (given that just because you used uv to write your library there's no guarantee it will be run using the standalone Python builds that uv uses)... we have something like this. import tkinter
from os import environ
from pathlib import Path
from sys import base_prefix
import platform
if not ("TCL_LIBRARY" in environ and "TK_LIBRARY" in environ):
try:
tkinter.Tk()
except tkinter.TclError:
tk_dir = "tcl" if platform.system() == "Windows" else "lib"
tk_path = Path(base_prefix) / tk_dir
environ["TCL_LIBRARY"] = str(next(tk_path.glob("tcl8.*")))
environ["TK_LIBRARY"] = str(next(tk_path.glob("tk8.*")))
def main():
tk = tkinter.Tk()
tk.mainloop()
if __name__ == "__main__":
main() |
Hmmm... my workaround above fixes tk, but when I try to use the tkagg backend for matplotlib things get broken again. I'll do some more investigation.
|
This issue is mentioned in #6893 Seems like Tkinter is just unusable for now. |
Broadly, we have two options:
--- a/Lib/tkinter/__init__.py
+++ b/Lib/tkinter/__init__.py
@@ -54,6 +54,14 @@
_magic_re = re.compile(r'([\\{}])')
_space_re = re.compile(r'([\s])', re.ASCII)
+# Facilitate discovery of the Tcl/Tk libraries.
+import os
+
+if 'TCL_LIBRARY' not in os.environ:
+ os.environ['TCL_LIBRARY'] = sys.base_prefix + '/tcl/tcl' + _tkinter.TCL_VERSION
+if 'TK_LIBRARY' not in os.environ:
+ os.environ['TK_LIBRARY'] = sys.base_prefix + '/tcl/tk' + _tkinter.TK_VERSION
+
def _join(value):
"""Internal function.""" The advantage of (1) is that it's slightly easier, doesn't require us to maintain a patch, and could be disabled in The advantage of (2) is that we'll only set those variables when |
I'd say having a .pth file is the "better" approach of the two in terms of best practices and style etc. so that's what I'd gravitate to... but, thinking of ease of use especially for beginners and the "uv just works" experience there's probably no way around patching. Is it possible to get a list of similar or identical use case? Is this a tkinter speciality because of the UI nature of it or would this affect lots of other builtins, too? |
I have a minor preference for the upstream patch, I think? There are consumers other than uv. Perhaps a dumb question, but why do we need to set an environment variable? How does this work in the standard distributions? |
I'm not totally certain why this works in other Pythons (e.g., Framework Python distributions). |
So for the Framework Python at least, the paths appear to get compiled in to the binary:
|
(I'll see if we can do something similar.) |
I think another thing we could do is symlink those files into |
So if a fresh venv gets created the tcl dependencies are "copied" (symlinked) from somewhere else to the |
Ok, I have a patch that is working well here: astral-sh/python-build-standalone#421. It avoids all the disadvantages, at the cost of requiring us to maintain a CPython C patch. |
Thank you for looking into this an resolving it! As a user of uv I wonder when this patch will be available? Will I need to wait for new Python patch releases like 3.10.17? or can force uv to use an archive from a specific standalone release, marked by its date? |
It'll be available following a We don't support using arbitrary archives yet. |
I just released this in https://github.com/astral-sh/uv/releases/tag/0.5.11 |
For the record, this won't work out of the box with Python versions that were already downloaded.
But it does work with newly installed versions!
|
You can |
This gives me the error message:
I can successfully run
python -c "from tkinter import Tk; window = Tk()"
in a freshconda
environment, but when I install python on conda it also installstk
, so maybe that's why it works? Maybe this isn't an issue withuv
?, but if anyone knows how to get this working I'd be very gateful!uv 0.4.4 (Homebrew 2024-09-04)
macOS
The text was updated successfully, but these errors were encountered: