diff --git a/.github/workflows/jit.yml b/.github/workflows/jit.yml index 66bff36fe5e90c..025ff7ecbeeaff 100644 --- a/.github/workflows/jit.yml +++ b/.github/workflows/jit.yml @@ -64,10 +64,10 @@ jobs: include: - target: i686-pc-windows-msvc/msvc architecture: Win32 - runner: windows-2025-vs2026 + runner: windows-2025 - target: x86_64-pc-windows-msvc/msvc architecture: x64 - runner: windows-2025-vs2026 + runner: windows-2025 - target: aarch64-pc-windows-msvc/msvc architecture: ARM64 runner: windows-11-arm diff --git a/.github/workflows/reusable-windows-msi.yml b/.github/workflows/reusable-windows-msi.yml index a74724323ec15f..d07b4f7f29e487 100644 --- a/.github/workflows/reusable-windows-msi.yml +++ b/.github/workflows/reusable-windows-msi.yml @@ -17,7 +17,7 @@ env: jobs: build: name: installer for ${{ inputs.arch }} - runs-on: ${{ inputs.arch == 'arm64' && 'windows-11-arm' || 'windows-2025-vs2026' }} + runs-on: ${{ inputs.arch == 'arm64' && 'windows-11-arm' || 'windows-2025' }} timeout-minutes: 60 env: ARCH: ${{ inputs.arch }} diff --git a/.github/workflows/reusable-windows.yml b/.github/workflows/reusable-windows.yml index c6e8128884e90c..dbb192fb8819a4 100644 --- a/.github/workflows/reusable-windows.yml +++ b/.github/workflows/reusable-windows.yml @@ -26,7 +26,7 @@ env: jobs: build: name: Build and test (${{ inputs.arch }}, ${{ inputs.interpreter }}) - runs-on: ${{ inputs.arch == 'arm64' && 'windows-11-arm' || 'windows-2025-vs2026' }} + runs-on: ${{ inputs.arch == 'arm64' && 'windows-11-arm' || 'windows-2025' }} timeout-minutes: 60 env: ARCH: ${{ inputs.arch }} diff --git a/Doc/extending/extending.rst b/Doc/extending/extending.rst index d33cbd2813d637..110dfea8cb98ab 100644 --- a/Doc/extending/extending.rst +++ b/Doc/extending/extending.rst @@ -231,10 +231,8 @@ calling the Python callback functions from a C callback. Other uses are also imaginable. Fortunately, the Python interpreter is easily called recursively, and there is a -standard interface to call a Python function. (I won't dwell on how to call the -Python parser with a particular string as input --- if you're interested, have a -look at the implementation of the :option:`-c` command line option in -:file:`Modules/main.c` from the Python source code.) +standard interface to call a Python function. (If you're interested in how to call the +Python parser with a particular string as input, see :ref:`veryhigh`.) Calling a Python function is easy. First, the Python program must somehow pass you the Python function object. You should provide a function (or some other @@ -641,7 +639,7 @@ and the object is freed. An alternative strategy is called :dfn:`automatic garbage collection`. (Sometimes, reference counting is also referred to as a garbage collection -strategy, hence my use of "automatic" to distinguish the two.) The big +strategy, hence the use of "automatic" to distinguish the two.) The big advantage of automatic garbage collection is that the user doesn't need to call :c:func:`free` explicitly. (Another claimed advantage is an improvement in speed or memory usage --- this is no hard fact however.) The disadvantage is that for diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst index 0257de63067003..02912189465659 100644 --- a/Doc/library/tkinter.rst +++ b/Doc/library/tkinter.rst @@ -1955,6 +1955,37 @@ Base and mixin classes color change when the mouse passes over a slider). Return the resulting setting. + .. method:: tk_appname(name=None) + + Query or set the name used to communicate with this application through + the ``send`` command. + With no argument, return the current name; otherwise change it to *name* + and return the actual name set, which may have a suffix appended to keep + it unique among the applications on the display. + + .. versionadded:: next + + .. method:: tk_useinputmethods(boolean=None, *, displayof=0) + + Query or set whether Tk uses the X Input Methods (XIM) for filtering + events, and return the resulting state. + This is significant only on X11; if XIM support is not available it + always returns ``False``. + + .. versionadded:: next + + .. method:: tk_caret(*, x=None, y=None, height=None) + + Set or query the caret location for the widget's display. + The caret is the per-display insertion position used for global focus + indication (for accessibility) and for placing the input method + (XIM or IME) window. + With no argument, return the current location as a dictionary with the + keys ``'x'``, ``'y'`` and ``'height'``; otherwise update the given + coordinates. + + .. versionadded:: next + .. method:: tk_scaling(number=None, *, displayof=0) Query or set the scaling factor used by Tk to convert between physical diff --git a/Doc/library/tkinter.ttk.rst b/Doc/library/tkinter.ttk.rst index fc79c7fa1845d5..b375b6908679de 100644 --- a/Doc/library/tkinter.ttk.rst +++ b/Doc/library/tkinter.ttk.rst @@ -2016,6 +2016,16 @@ If you don't know the class name of a widget, use the method Returns a tuple of all known themes. + .. method:: theme_styles(themename=None) + + Returns a tuple of all styles in *themename*. + If *themename* is not given, the current theme is used. + + .. versionadded:: next + + Availability: Tk 9.0. + + .. method:: theme_use(themename=None) If *themename* is not given, returns the theme in use. Otherwise, sets diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index b50a43f08b5dec..f394298da96a7c 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -173,6 +173,11 @@ tkinter synchronization of the displayed view with the underlying text. (Contributed by Serhiy Storchaka in :gh:`151675`.) +* Added the :meth:`ttk.Style.theme_styles + ` method which returns the list of styles + defined in a theme. + (Contributed by Serhiy Storchaka in :gh:`151920`.) + * Added new :class:`!tkinter.Canvas` methods :meth:`~tkinter.Canvas.rchars` which replaces the text or coordinates of canvas items, and :meth:`~tkinter.Canvas.rotate` which rotates the coordinates of canvas items. @@ -194,6 +199,12 @@ tkinter badge) and :meth:`~tkinter.Wm.wm_stackorder` (toplevel stacking order). (Contributed by Serhiy Storchaka in :gh:`151874`.) +* Added the :meth:`~tkinter.Misc.tk_appname`, + :meth:`~tkinter.Misc.tk_useinputmethods` and :meth:`~tkinter.Misc.tk_caret` + methods, exposing the application send name, the X Input Methods state and + the input method caret location. + (Contributed by Serhiy Storchaka in :gh:`151886`.) + * Added support for more options in :class:`!tkinter.PhotoImage` methods: the *format* parameter of :meth:`~tkinter.PhotoImage.put`, the *metadata* parameter of :meth:`~tkinter.PhotoImage.put`, :meth:`~tkinter.PhotoImage.read`, @@ -205,7 +216,6 @@ tkinter dithered image when its data was supplied in pieces. (Contributed by Serhiy Storchaka in :gh:`151888`.) - xml --- diff --git a/Lib/test/test_tkinter/test_misc.py b/Lib/test/test_tkinter/test_misc.py index 80dc163fc18de4..15239efd83904f 100644 --- a/Lib/test/test_tkinter/test_misc.py +++ b/Lib/test/test_tkinter/test_misc.py @@ -463,6 +463,29 @@ def test_tk_bisque(self): self.assertEqual(root['background'], '#ffe4c4') self.assertRaises(TypeError, root.tk_bisque, 'x') + def test_tk_appname(self): + old = self.root.tk_appname() + self.assertIsInstance(old, str) + self.addCleanup(self.root.tk_appname, old) + # Setting the name returns the actual name (possibly with a suffix + # appended to keep it unique). + new = self.root.tk_appname('PythonTkTest') + self.assertIsInstance(new, str) + self.assertEqual(self.root.tk_appname(), new) + + def test_tk_useinputmethods(self): + old = self.root.tk_useinputmethods() + self.assertIsInstance(old, bool) + self.addCleanup(self.root.tk_useinputmethods, old) + # Setting returns the resulting state. On systems without XIM support + # the state is always False, so only check the True->False direction. + self.assertIs(self.root.tk_useinputmethods(False), False) + + def test_tk_caret(self): + self.assertIsNone(self.root.tk_caret(x=5, y=10, height=20)) + caret = self.root.tk_caret() + self.assertEqual(caret, {'x': 5, 'y': 10, 'height': 20}) + def test_tk_scaling(self): old = self.root.tk_scaling() self.assertIsInstance(old, float) diff --git a/Lib/test/test_ttk/test_style.py b/Lib/test/test_ttk/test_style.py index f85f76eb499278..6fbcfbdc9b9e4f 100644 --- a/Lib/test/test_ttk/test_style.py +++ b/Lib/test/test_ttk/test_style.py @@ -6,7 +6,8 @@ from test import support from test.support import requires from test.test_tkinter.support import setUpModule # noqa: F401 -from test.test_tkinter.support import AbstractTkTest, get_tk_patchlevel +from test.test_tkinter.support import (AbstractTkTest, get_tk_patchlevel, + requires_tk) requires('gui') @@ -124,6 +125,25 @@ def test_theme_use(self): self.style.theme_use(curr_theme) + @requires_tk(9, 0) + def test_theme_styles(self): + # The 'default' theme is always available and defines the base styles. + default_styles = self.style.theme_styles('default') + self.assertIsInstance(default_styles, tuple) + self.assertIn('.', default_styles) + self.assertIn('TButton', default_styles) + + # Without an argument the current theme is used. + styles = self.style.theme_styles() + self.assertIsInstance(styles, tuple) + self.assertIn('.', styles) + + for theme in self.style.theme_names(): + self.assertIsInstance(self.style.theme_styles(theme), tuple) + + self.assertRaises(tkinter.TclError, + self.style.theme_styles, 'nonexistingname') + def test_theme_settings(self): style = self.style theme = style.theme_use() diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index dd7407176ddd8f..b1015d2d5d0c23 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -745,6 +745,47 @@ def tk_setPalette(self, *args, **kw): self.tk.call(('tk_setPalette',) + _flatten(args) + _flatten(list(kw.items()))) + def tk_appname(self, name=None): + """Query or set the name used to communicate with this application + through the send command. + + With no argument, return the current name; otherwise change it to NAME + and return the actual name set (which may have a suffix appended to + keep it unique).""" + if name is None: + return self.tk.call('tk', 'appname') + return self.tk.call('tk', 'appname', name) + + def tk_useinputmethods(self, boolean=None, *, displayof=0): + """Query or set whether Tk uses the X Input Methods (XIM) for filtering + events, and return the resulting state. + + This is significant only on X11; if XIM support is not available it + always returns False.""" + args = ('tk', 'useinputmethods') + self._displayof(displayof) + if boolean is not None: + args += (boolean,) + return self.tk.getboolean(self.tk.call(args)) + + def tk_caret(self, *, x=None, y=None, height=None): + """Set or query the caret location for this widget's display. + + The caret is the per-display insertion position used for global focus + indication (for accessibility) and for placing the input method + (XIM or IME) window. With no argument, return the current location as + a dictionary with keys 'x', 'y' and 'height'; otherwise update the + given coordinates.""" + args = ('tk', 'caret', self._w) + for option, value in (('-x', x), ('-y', y), ('-height', height)): + if value is not None: + args += (option, value) + if len(args) > 3: + self.tk.call(args) + else: + values = self.tk.splitlist(self.tk.call(args)) + return {values[i][1:]: self.tk.getint(values[i + 1]) + for i in range(0, len(values), 2)} + def tk_scaling(self, number=None, *, displayof=0): """Query or set the scaling factor used by Tk to convert between physical units and pixels. diff --git a/Lib/tkinter/ttk.py b/Lib/tkinter/ttk.py index c3a5ac160573a6..0cabc5c3140fd6 100644 --- a/Lib/tkinter/ttk.py +++ b/Lib/tkinter/ttk.py @@ -492,6 +492,20 @@ def theme_names(self): return self.tk.splitlist(self.tk.call(self._name, "theme", "names")) + def theme_styles(self, themename=None): + """Returns a list of all styles in themename. + + If themename is omitted, the current theme is used. + + Availability: Tk 9.0. + """ + if themename is None: + return self.tk.splitlist( + self.tk.call(self._name, "theme", "styles")) + return self.tk.splitlist( + self.tk.call(self._name, "theme", "styles", themename)) + + def theme_use(self, themename=None): """If themename is None, returns the theme in use, otherwise, set the current theme to themename, refreshes all widgets and emits diff --git a/Misc/NEWS.d/next/Library/2026-06-22-01-51-06.gh-issue-151886.MsLlNz.rst b/Misc/NEWS.d/next/Library/2026-06-22-01-51-06.gh-issue-151886.MsLlNz.rst new file mode 100644 index 00000000000000..7a454ab95f371f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-22-01-51-06.gh-issue-151886.MsLlNz.rst @@ -0,0 +1,4 @@ +Add the :meth:`!tkinter.Misc.tk_appname`, +:meth:`!tkinter.Misc.tk_useinputmethods` and :meth:`!tkinter.Misc.tk_caret` +methods, wrapping the ``tk appname``, ``tk useinputmethods`` and ``tk caret`` +Tk commands. diff --git a/Misc/NEWS.d/next/Library/2026-06-22-14-30-00.gh-issue-151920.Th3mSt.rst b/Misc/NEWS.d/next/Library/2026-06-22-14-30-00.gh-issue-151920.Th3mSt.rst new file mode 100644 index 00000000000000..2c4a069a794b10 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-22-14-30-00.gh-issue-151920.Th3mSt.rst @@ -0,0 +1,3 @@ +Add the :meth:`ttk.Style.theme_styles ` +method, wrapping the Tk ``ttk::style theme styles`` subcommand, which +returns the list of styles defined in a theme.