Discuss wxPython - Latest topics https://discuss.wxpython.org/latest Latest topics Sun, 15 Mar 2026 16:58:46 +0000 SplitterWindow sash not visible on Windows Newbie Help No matter what style(s) I enable, the sash is never visible. I’m using wxFormBuilder to create the UI. Everything works as expected, the only problem is the sash isn’t visible.

I can only embed one image, so I’ll add a screenshot in a reply.

Here’s the structure in wxFormBuilder:

And all mentions of the Splitter in the generated Frame code:

        self.m_splitter1 = wx.SplitterWindow( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.SP_3D|wx.SP_THIN_SASH )
        self.m_splitter1.SetSashGravity( 0.5 )
        self.m_splitter1.Bind( wx.EVT_IDLE, self.m_splitter1OnIdle )
        self.m_splitter1.SetMinimumPaneSize( 50 )
...
        self.m_panel1 = wx.Panel( self.m_splitter1, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
...
        self.m_panel2 = wx.Panel( self.m_splitter1, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
...
        self.m_splitter1.SplitHorizontally( self.m_panel1, self.m_panel2, 0 )
        bSizer7.Add( self.m_splitter1, 1, wx.EXPAND, 5 )
...
    def m_splitter1OnIdle( self, event ):
        self.m_splitter1.SetSashPosition( 0 )
        self.m_splitter1.Unbind( wx.EVT_IDLE )

Any suggestions on how to get the sash to be visible?

4 posts - 2 participants

Read full topic

]]>
https://discuss.wxpython.org/t/splitterwindow-sash-not-visible-on-windows/40426 Sun, 15 Mar 2026 16:58:46 +0000 No No No discuss.wxpython.org-topic-40426 SplitterWindow sash not visible on Windows
Frame.Raise() not working on Windows Newbie Help Here’s the code, which runs in a daemon thread to respond to notifications from second instances (to prevent multiple instances). All the IPC code is working fine, the only problem is that frm.Raise() doesn’t raise the window.

        if frm.IsIconized(): frm.Iconize(False)
        frm.Raise()
        frm.mToday.SetFocus()

If the window is iconized, then frm.Iconize(False) both restores the window and raises it. However, if the window is not iconized, but partially or completely hidden under another window, the taskbar icon flashes but the window is not raised.

I also tried using CallAfter(frm.Raise) but it had no effect.

What am I missing? Is the fact that this runs in a daemon thread the problem? Is this just a limitation of Windows 11?

6 posts - 3 participants

Read full topic

]]>
https://discuss.wxpython.org/t/frame-raise-not-working-on-windows/40425 Sat, 14 Mar 2026 19:36:42 +0000 No No No discuss.wxpython.org-topic-40425 Frame.Raise() not working on Windows
wxDatePickerCtrl firing change events on calendar navigation Newbie Help I have a wxDatePickerCtrl configured with wxDP_DROPDOWN to display the calendar. I was expecting to get a DateChanged event when the user selected a date, but I’m getting events when the user merely navigates forwards or backwards in the calendar.

I can see that what’s happening is that wxWidgets is “selecting” a date after changing the calendar, and I guess it’s working as designed.

My question: Is there a way to distinguish a real “user chose a date” event from “a date was selected due to navigating within the calendar”? The DateEvent object doesn’t seem to have anything that would help. Or, maybe, a way to turn off those “spurious” events?

1 post - 1 participant

Read full topic

]]>
https://discuss.wxpython.org/t/wxdatepickerctrl-firing-change-events-on-calendar-navigation/40423 Mon, 09 Mar 2026 21:52:03 +0000 No No No discuss.wxpython.org-topic-40423 wxDatePickerCtrl firing change events on calendar navigation
DataViewListCtrl last column fixed size/non-resizable still takes all remaining space Newbie Help wxPython on Windows

I want the last column of a DataViewLIstCtrl to have a fixed size, with remaining space spread out over all the other columns. Google results suggest this is accomplished by

  • Setting the last column width to a fixed amount (i.e. 100px) and removing the wxDATAVIEW_COL_RESIZABLE flag
  • Making all the other columns resizable

I did this but the last column still takes up all remaining space. Is this just a limitation of the Windows implementation?

3 posts - 3 participants

Read full topic

]]>
https://discuss.wxpython.org/t/dataviewlistctrl-last-column-fixed-size-non-resizable-still-takes-all-remaining-space/40422 Sun, 08 Mar 2026 22:48:55 +0000 No No No discuss.wxpython.org-topic-40422 DataViewListCtrl last column fixed size/non-resizable still takes all remaining space
Sorting a ListCtrl column wxPython Users I have used wxpython for years (decades?), but have only just started using wx.ListCtrl. I have a working example.
Now I want to be able to sort by a column when the user clicks on a column header (or column), and I am finding the attempt incredibly frustrating. I don’t understand what the class documentation says about sorting (what is a “sort indicator”? - this term is never defined, nor what you are supposed to do with it). I have tried several examples on the web (most/all use a mixin) but they are all incomplete or broken.
Please, please, please, could someone give me a minimal (but complete) working program that creates a ListCtrl (2 rows and 2 columns is fine) and demonstrates how to sort by one column or another (I don’t care how the sorting is triggered, whatever is easiest).
I am using wxpython 4.2.3 and python 3.13.3 on Windows 11.
Thanks in advance,
Rob Cliffe

10 posts - 5 participants

Read full topic

]]>
https://discuss.wxpython.org/t/sorting-a-listctrl-column/40420 Thu, 05 Mar 2026 19:16:59 +0000 No No No discuss.wxpython.org-topic-40420 Sorting a ListCtrl column
GLCanvas: SystemError: <built-in function IsDisplaySupported> returned a result with an exception set wxPython Users Hi!

I’m having issues with wx.glcanvas.GLCanvas. I originally wanted to create a wxVTKRenderWindowInteractor which failed due to problems that boil down to wxpython’s GLCanvas.

Most notably the GLCanvas.__init__ method, but even glcanvas.GLCanvas.IsDisplaySupported([glcanvas.WX_GL_RGBA]) fail with an error along the lines of SystemError: <built-in function IsDisplaySupported> returned a result with an exception set.

Full stack trace is

NotImplementedError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/user/developer/3d-medical-imaging/wx_test.py", line 4, in <module>
    glcanvas.GLCanvas.IsDisplaySupported([glcanvas.WX_GL_RGBA])
SystemError: <built-in function IsDisplaySupported> returned a result with an exception set

My specs are

Linux 6.14.0-37-generic #37~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC x86_64 GNU/Linux
wxPython 4.2.5 from pypi
Python 3.11.14

I am not able to get any more meaningful debug messages. I tried

XDG_SESSION_TYPE=x11
WXTRACE=glcanvas
WXTRACE=all
WXDEBUG=1
G_MESSAGES_DEBUG=all
GDK_DEBUG=gl
GDK_BACKEND=x11

Maybe the problems are also called by other libraries. But I am unsure about how to approach that.

Thanks for any advice.

2 posts - 2 participants

Read full topic

]]>
https://discuss.wxpython.org/t/glcanvas-systemerror-built-in-function-isdisplaysupported-returned-a-result-with-an-exception-set/40419 Thu, 05 Mar 2026 09:26:24 +0000 No No No discuss.wxpython.org-topic-40419 GLCanvas: SystemError: <built-in function IsDisplaySupported> returned a result with an exception set
wxDataViewListCtrl sizing questions Newbie Help I’m having trouble understanding how a DataViewListCtrl is sized.

I have created a Frame in wxFormBuilder with the following nested BoxSizer structure:

  • bSizer1 (Vertical)
    • bSizer2 (Horizontal, proportion 1)
    • bSizer3 (Vertical, proportion 1)
    • bSizer4 (Horizontal, proportion 3)
      • DataViewListCtrl (name dvRegList)
        • DataViewListColumn
        • DataViewListColumn
        • DataViewListColumn

If bSizer4 is Vertical orientation, dvRegList does not expand horizontally to fill the sizer. It’s just a tiny border on the far left of the sizer, the height of the header row and just a few pixels wide. This is surprising because a button and static text in a vertical sizer display normally.

If bSizer4 is Horizontal, dvRegList expands horizontally but displays only the header, with none of the rows, even if I resize the window so that there’s obviously room for the rows.

The only way I can get it to display rows is to set a minimum height, and when I do it now behaves as expected… it fills its sizer both vertically and horizontally, and resizes with the window.

So I have 2 questions:

  • Why does the DataViewListCtrl not fill the sizer when the sizer’s orientation is Vertical
  • Why (when the sizer is Horizontal) does it need to have an explicit minimum vertical height set.

I would have expected the control to fill its sizer by default, and I can’t find anything in the docs that specifies the observed behavior.

I’m sure I’m missing something obvious, but would like to know where this is documented.

Thanks

8 posts - 3 participants

Read full topic

]]>
https://discuss.wxpython.org/t/wxdataviewlistctrl-sizing-questions/40416 Fri, 20 Feb 2026 19:28:21 +0000 No No No discuss.wxpython.org-topic-40416 wxDataViewListCtrl sizing questions
Detected Dark / Light / System mode wxPython Dev “wx.SystemSettings.GetAppearance()” no longer seems to work with “wxpython-4.3.0a16030 and wxWidgets 3.3.0”.

I know this isn’t a final version, but is this normal?
01__wx.SystemAppearance.py (1.8 KB)
02__dark_mode.py (4.2 KB)

If anyone would like to test these scripts on their system and let me know if they work, I would be very grateful (please provide me with the following information: wxPython, wxWidgets, operating system version, etc.).

Also, like many other users, I would like to see an example of using dark mode as soon as possible to begin various tests.

Thanks

14 posts - 5 participants

Read full topic

]]>
https://discuss.wxpython.org/t/detected-dark-light-system-mode/40410 Sun, 08 Feb 2026 14:02:18 +0000 No No No discuss.wxpython.org-topic-40410 Detected Dark / Light / System mode
TypeError: copy_file() takes from 2 to 7 positional arguments but 8 were given wxPython Users Hi there,
I face error at the end of the “.whl” generation file with both wxpython 4.2.2 and 4.2.4.
I use Fedora 43 / python 3.14.

I launch the command :
pip wheel -v wxPython-4.2.2.tar.gz 2>&1 | tee build.log

And it fails with this message :
TypeError: copy_file() takes from 2 to 7 positional arguments but 8 were given

  ------------ BUILD FINISHED ------------
  To use wxPython from the build folder (without installing):
   - Set your PYTHONPATH variable to /tmp/pip-req-build-nzpvslec.
   - You may also need to set your (DY)LD_LIBRARY_PATH to /tmp/pip-req-build-nzpvslec/build/wxbld/gtk3/lib,
     or wherever the wxWidgets libs have been installed.

  Finished command: build_py (17m33.230s)
  Finished command: build (25m34.929s)
  Done!
  WARNING: Building this way assumes that all generated files have been
  generated already.  If that is not the case then use build.py directly
  to generate the source and perform the build stage.  You can use
  --skip-build with the bdist_* or install commands to avoid this
  message and the wxWidgets and Phoenix build steps in the future.

  "/home/kredge/Development/GestConsoPhoenix4.2.2--1.4.4/env4.2.4-py3.14/bin/python" -u build.py build
  running build_py
  creating build/lib.linux-x86_64-cpython-314/wx
  Traceback (most recent call last):
    File "/home/kredge/Development/GestConsoPhoenix4.2.2--1.4.4/env4.2.4-py3.14/lib64/python3.14/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py", line 389, in <module>
      main()
      ~~~~^^
    File "/home/kredge/Development/GestConsoPhoenix4.2.2--1.4.4/env4.2.4-py3.14/lib64/python3.14/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py", line 373, in main
      json_out["return_val"] = hook(**hook_input["kwargs"])
                               ~~~~^^^^^^^^^^^^^^^^^^^^^^^^
    File "/home/kredge/Development/GestConsoPhoenix4.2.2--1.4.4/env4.2.4-py3.14/lib64/python3.14/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py", line 280, in build_wheel
      return _build_backend().build_wheel(
             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
          wheel_directory, config_settings, metadata_directory
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      )
      ^
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/build_meta.py", line 441, in build_wheel
      return _build(['bdist_wheel', '--dist-info-dir', str(metadata_directory)])
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/build_meta.py", line 429, in _build
      return self._build_with_temp_dir(
             ~~~~~~~~~~~~~~~~~~~~~~~~~^
          cmd,
          ^^^^
      ...<3 lines>...
          self._arbitrary_args(config_settings),
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      )
      ^
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/build_meta.py", line 410, in _build_with_temp_dir
      self.run_setup()
      ~~~~~~~~~~~~~~^^
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/build_meta.py", line 520, in run_setup
      super().run_setup(setup_script=setup_script)
      ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/build_meta.py", line 317, in run_setup
      exec(code, locals())
      ~~~~^^^^^^^^^^^^^^^^
    File "<string>", line 364, in <module>
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/__init__.py", line 117, in setup
      return distutils.core.setup(**attrs)  # type: ignore[return-value]
             ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/_distutils/core.py", line 186, in setup
      return run_commands(dist)
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/_distutils/core.py", line 202, in run_commands
      dist.run_commands()
      ~~~~~~~~~~~~~~~~~^^
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/_distutils/dist.py", line 1000, in run_commands
      self.run_command(cmd)
      ~~~~~~~~~~~~~~~~^^^^^
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/dist.py", line 1107, in run_command
      super().run_command(command)
      ~~~~~~~~~~~~~~~~~~~^^^^^^^^^
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/_distutils/dist.py", line 1019, in run_command
      cmd_obj.run()
      ~~~~~~~~~~~^^
    File "<string>", line 230, in run
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/_distutils/cmd.py", line 341, in run_command
      self.distribution.run_command(command)
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/dist.py", line 1107, in run_command
      super().run_command(command)
      ~~~~~~~~~~~~~~~~~~~^^^^^^^^^
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/_distutils/dist.py", line 1019, in run_command
      cmd_obj.run()
      ~~~~~~~~~~~^^
    File "<string>", line 144, in run
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/_distutils/command/build.py", line 135, in run
      self.run_command(cmd_name)
      ~~~~~~~~~~~~~~~~^^^^^^^^^^
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/_distutils/cmd.py", line 341, in run_command
      self.distribution.run_command(command)
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/dist.py", line 1107, in run_command
      super().run_command(command)
      ~~~~~~~~~~~~~~~~~~~^^^^^^^^^
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/_distutils/dist.py", line 1019, in run_command
      cmd_obj.run()
      ~~~~~~~~~~~^^
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/command/build_py.py", line 79, in run
      self.build_packages()
      ~~~~~~~~~~~~~~~~~~~^^
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/_distutils/command/build_py.py", line 380, in build_packages
      self.build_module(module, module_file, package)
      ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/_distutils/command/build_py.py", line 351, in build_module
      return self.copy_file(module_file, outfile, preserve_mode=False)
             ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/command/build_py.py", line 66, in copy_file
      return super().copy_file(  # pyright: ignore[reportReturnType] # pypa/distutils#309
             ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
          infile, outfile, preserve_mode, preserve_times, link, level
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      )
      ^
    File "/tmp/pip-build-env-9qiygpxx/overlay/lib/python3.14/site-packages/setuptools/_distutils/cmd.py", line 405, in copy_file
      return file_util.copy_file(
             ~~~~~~~~~~~~~~~~~~~^
          infile,
          ^^^^^^^
      ...<4 lines>...
          link,
          ^^^^^
      )
      ^
    File "<string>", line 287, in wx_copy_file
  TypeError: copy_file() takes from 2 to 7 positional arguments but 8 were given
  error: subprocess-exited-with-error
  
  × Building wheel for wxPython (pyproject.toml) did not run successfully.
  │ exit code: 1
  ╰─> No available output.
  
  note: This error originates from a subprocess, and is likely not a problem with pip.
  full command: /home/kredge/Development/GestConsoPhoenix4.2.2--1.4.4/env4.2.4-py3.14/bin/python /home/kredge/Development/GestConsoPhoenix4.2.2--1.4.4/env4.2.4-py3.14/lib64/python3.14/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py build_wheel /tmp/tmp22rqx7zk
  cwd: /tmp/pip-req-build-nzpvslec
  Building wheel for wxPython (pyproject.toml): finished with status 'error'
  ERROR: Failed building wheel for wxPython
Failed to build wxPython
ERROR: Failed to build one or more wheels 

Any idea to this issue ?

Thanks a lot.

11 posts - 3 participants

Read full topic

]]>
https://discuss.wxpython.org/t/typeerror-copy-file-takes-from-2-to-7-positional-arguments-but-8-were-given/40408 Sat, 07 Feb 2026 13:59:06 +0000 No No No discuss.wxpython.org-topic-40408 TypeError: copy_file() takes from 2 to 7 positional arguments but 8 were given
Strange problem with wx.FileDialog on Windows wxPython Users I’m having a strange result show up for a user of my software in Greece. The code in question is simply this:

        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', pth, self.newGPXfile,
            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)

This code works for thousands of people.

The FileDialog is failing with an error where it is reporting that the file (to be created) is not found. The directory has been selected from a Windows file browser, so I know that exists. The file, of course, should not yet exist. I have had the user select shorter versions of the path (for example, directly into the Documents directory, avoiding the directory name with spaces) and that does not help. Saving into a directory that is not mapped to OneDrive (the desktop) does work OK.

I have seen another strange thing for this user where UTF-8 is not their default, so that might be related, but I’m not a Windows user and don’t know what Windows settings would be related to that.

I’m stumped. Any suggestions would be welcome.

FWIW This is wx 4.2.3 in Python 3.13.7 on Windows 11.

2 posts - 2 participants

Read full topic

]]>
https://discuss.wxpython.org/t/strange-problem-with-wx-filedialog-on-windows/40404 Wed, 04 Feb 2026 18:00:46 +0000 No No No discuss.wxpython.org-topic-40404 Strange problem with wx.FileDialog on Windows
wxPython master branch now tracking wxWidgets 3.3 Announcements Hi all,

The master branch has recently been moved to track wxWidgets 3.3. Do note this is the unstable API branch where the API may change over time until wxWidgets 3.4 is released. Eventually, this will lead to a wxPython 4.3.0 release.

I’ve also created the wxPy-4.2.x branch which will continue to track wxWidgets 3.2. We may make another 4.2.x release or two if there is demand.

Please report any bugs, or better yet, make pull requests with fixes. :slight_smile:

Scott

7 posts - 4 participants

Read full topic

]]>
https://discuss.wxpython.org/t/wxpython-master-branch-now-tracking-wxwidgets-3-3/40403 Tue, 03 Feb 2026 01:07:34 +0000 No No No discuss.wxpython.org-topic-40403 wxPython master branch now tracking wxWidgets 3.3
ScrollWindow doesn't display until after scroll Newbie Help I have an application where I load a file and display its contents in hexadecimal. I display the bytes in a ScrolledWindow. I’ve gotten it to load everything up. The only issue is that the contents of the window are not visible until a scroll event. I’ve created (what I think is) a minimal example.
Anybody have any ideas why this is happening? How to get around it?

ScrollTest.py (3.6 KB)

5 posts - 3 participants

Read full topic

]]>
https://discuss.wxpython.org/t/scrollwindow-doesnt-display-until-after-scroll/40402 Fri, 30 Jan 2026 20:56:08 +0000 No No No discuss.wxpython.org-topic-40402 ScrollWindow doesn't display until after scroll
Restoring windows positions under Wayland Uncategorized tl;dr How do I create a wxPython application/window so its position is kept between the launches under Wayland?

I have a few wxPython programs I wrote for myself that need to remember their window’s position and size when closed, and restore it when launched again. On Windows and on Linux under X11 I used a simple combination of GetRect()/SetSize(). On Linux under Wayland the “size” part of those functions still works, but an attempt to get the position, through GetRect(), GetPosition() or whatever, always returns (0, 0), and an attempt to set the position yields no result (the window is just centered on the default display or something, looks particularly hilarious for an application that creates multiple windows and they all appear in the center). As I understand after some reading and googling, that is by design - Wayland doesn’t allow applications to manipulate their windows’ position. However, I see some applications still remembering and restoring their windows’ positions between the launches - by themselves, or by somehow delegating that to the desktop environment, I do not know. Sadly, none of them that I have are written in wxPython to look at the sources. How do I create a wxPython application/window so its position is kept between the launches under Wayland?

Here’s my code example for the reference (the positioning part doesn’t work under Wayland - what do I need to change?):

#!/usr/bin/env python3
import wx

class MainWindow(wx.Frame):
    def __init__(self, parent, window_id, title):		
        wx.Frame.__init__(self, parent, window_id, title)
        self.Bind(wx.EVT_CLOSE, self.OnClose)		
        self.Show(True)

    def ReadSettings(self):
        rect = [0, 0, 100, 100]
        try:
            with open("settings.txt", "r") as f:
                rect = [int(s) for s in f.read().split(",")]
        except:
            pass
        self.SetSize(*rect)

    def WriteSettings(self):
        with open("settings.txt", "w") as f:
            f.write(",".join((str(s) for s in self.GetRect())))
    
    def OnClose(self, evt):
        self.WriteSettings()
        self.Destroy()

app = wx.App(0)
frame = MainWindow(None, -1, "Test")
frame.ReadSettings()
frame.Show(1)
app.MainLoop()

6 posts - 2 participants

Read full topic

]]>
https://discuss.wxpython.org/t/restoring-windows-positions-under-wayland/40399 Sat, 24 Jan 2026 01:24:45 +0000 No No No discuss.wxpython.org-topic-40399 Restoring windows positions under Wayland
Open source country flags quiz game wxPython Dev “Country flags quiz”, a question/answer game where you have to find
the names of the countries corresponding to the flags displayed.
Original source of this game was programmed in Python and wxPython by Francesc Zacarias.

It has been successfully tested on the following platforms:

  • Windows 10/11 | Python 3.11.9 |
    wxPython 4.2.3 | wxWidgets 3.2.6

  • Linux Mint 21 | Python 3.10.12 |
    wxPython 4.2.1 gtk3 | wxWidgets 3.2.2.1

  • MacOS Sequoia 15 | Python 3.12.4 |
    wxPython 4.2.2 | wxWidgets 3.2.6



Have fun!

1 post - 1 participant

Read full topic

]]>
https://discuss.wxpython.org/t/open-source-country-flags-quiz-game/40396 Sat, 10 Jan 2026 10:34:41 +0000 No No No discuss.wxpython.org-topic-40396 Open source country flags quiz game
Open source slide puzzle game wxPython Dev “Slide puzzle”, move the tiles adjacent to the empty space to swap them.
The goal is to put the tiles back in order.

slide_puzzle_game_v1.0.zip (20.2 KB)

It has been successfully tested on the following platforms:

  • Windows 10/11 | Python 3.11.9 |
    wxPython 4.2.3 | wxWidgets 3.2.6

  • Linux Mint 21 | Python 3.10.12 |
    wxPython 4.2.1 gtk3 | wxWidgets 3.2.2.1

  • MacOS Sequoia 15 | Python 3.12.4 |
    wxPython 4.2.2 | wxWidgets 3.2.6




Have fun!

1 post - 1 participant

Read full topic

]]>
https://discuss.wxpython.org/t/open-source-slide-puzzle-game/40395 Sat, 10 Jan 2026 09:37:50 +0000 No No No discuss.wxpython.org-topic-40395 Open source slide puzzle game
Open source snake game wxPython Dev “Snake” is a classic arcade game where the player controls a growing line,
called the “snake,” which moves around the screen to eat food items.
Each time the snake eats, it becomes longer, making the game more challenging.
The objective is to avoid colliding with the walls or the snake’s own body while
trying to consume as much food as possible to achieve a high score.
The game continues until the snake crashes, and the player’s goal is to achieve
the highest score possible before losing.

snake_game_v1.0.zip (345.4 KB)

It has been successfully tested on the following platforms:

  • Windows 10/11 | Python 3.11.9 |
    wxPython 4.2.3 | wxWidgets 3.2.6

  • Linux Mint 21 | Python 3.10.12 |
    wxPython 4.2.1 gtk3 | wxWidgets 3.2.2.1

  • MacOS Sequoia 15 | Python 3.12.4 |
    wxPython 4.2.2 | wxWidgets 3.2.6




Have fun!

1 post - 1 participant

Read full topic

]]>
https://discuss.wxpython.org/t/open-source-snake-game/40394 Sat, 10 Jan 2026 09:25:42 +0000 No No No discuss.wxpython.org-topic-40394 Open source snake game
Open source meteor game wxPython Dev “Meteor” is an arcade game where the player controls a ship in an endless meteor storm.
As time passes, the number of obstacles and the speed increase.
The goal is to survive as long as possible by avoiding falling meteors.

meteor_game_v1.0.zip (528.4 KB)

It has been successfully tested on the following platforms:

  • Windows 10/11 | Python 3.11.9 |
    wxPython 4.2.3 | wxWidgets 3.2.6

  • Linux Mint 21 | Python 3.10.12 |
    wxPython 4.2.1 gtk3 | wxWidgets 3.2.2.1

  • MacOS Sequoia 15 | Python 3.12.4 |
    wxPython 4.2.2 | wxWidgets 3.2.6




Have fun!

1 post - 1 participant

Read full topic

]]>
https://discuss.wxpython.org/t/open-source-meteor-game/40393 Sat, 10 Jan 2026 09:10:24 +0000 No No No discuss.wxpython.org-topic-40393 Open source meteor game
HamClock Launcher - a new app + Building universal binary apps via py2app wxPython Users Hello,

I’ve made a new wxPython app that is a wrapper around HamClock for Macs. HamClock is an amateur radio clock tool with all kinds of nifty things for hams. However, it normally requires XQuartz to build and can run as an X app or a web server. HamClock itself is C++ code. There is a macports version (have a PR request in with them to add in the web server variants).

My goal in this was to bundle everything in the app so no dev tools, Xcode, XQuartz, “python stuff” etc were needed to run it. The hardware requirements are not much for HamClock – many folks run it on a Raspberry Pi. However, there are folks like me that have a number of old macs around not doing anything, and a subset of hams that use macs for their amateur radio work.

I did end up compiling the hamclock binaries separately – used a 10.15 box to build the Intel versions, and built the Apple Silicon binaries on a macOS 15.7 box. I did have trouble installing a universal binary of wxPython, but ended up just using arch -x86_64 to build an Intel version on my M1 MacBook Pro - and that seems to work just fine.

Anyway, thought I would share it here
https://huberthickman.github.io/HamClockLauncher/

I used this as a test bed for Claude - had to fix some bits of Claude’s code but it did pretty well. The signing script is my own - based upon some previous work I did embedding a SML/NJ interpreter inside of a wxPython app - there were some very tricky dependency-graph type issues with the order of signing of things with the SML/NJ interpreter - so ended up just signing things in a loop three times in a gordian knot approach.

Of note note I use the Chirp software (another wxPython app) to help program radios… I feel like I among old friends, software wise…

Hubert Hickman

1 post - 1 participant

Read full topic

]]>
https://discuss.wxpython.org/t/hamclock-launcher-a-new-app-building-universal-binary-apps-via-py2app/40389 Sun, 04 Jan 2026 04:14:32 +0000 No No No discuss.wxpython.org-topic-40389 HamClock Launcher - a new app + Building universal binary apps via py2app
Language-independent hotkeys on Linux Uncategorized How do I set hotkeys (keyboard shortcuts) for a specific widget, that would work regardless of the active keyboard input language? Preferably, in a platform-independent way, but, at the very least, under Linux and wxGTK? Should be something simple, all reasonable software behave like that, but I couldn’t find how to do it.

I’ve tried with EVT_KEY_DOWN, but that required separate treatment of every individual letter attached to the hardware key. Perhaps the GetRawKeyFlags() could help, but how do I get the value it would return not for the event handler, but for a character (say, I have ‘Ctrl+Q’ written in some text config, or even hardcoded, how do I get the scan code of the hardware key the letter Q is assigned to)?

The menu shortcuts, actually, work regardless of the current input language (in the example below, ‘Ctrl+W’ triggers even when it’s in Cyrillic, so it’s kinda ‘Ctrl+Ц’). But, as I understand, you can only have one global menu attached to the main frame. What if I don’t want my frame to have a menu? What if I need the same hotkeys used by different widgets for different purposes?

I’ve also tried with accelerator tables. Despite reading somewhere on this forum that you can have only one global accelerator table, sort of like the menu, I was able to assign separate accelerators to separate widgets and they worked “correctly”, depending on the active focus. But, unlike with the menu, there I ran into the same problem again: in the example below the ‘Ctrl+E’ only triggers if the input is in Latin.

Windows doesn’t have any of those problems, ‘Ctrl+Q’, ‘Ctrl+W’ and ‘Ctrl+E’, respectively, get triggered regardless of the active input language.

import wx
    
class MainWindow(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, wx.ID_ANY, title, size=(500, 500))

        #1 - Key event processed in OnKeyPressed
        self.panel = wx.Panel(self)
        self.panel.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed)
        self.hotkey_lat          = 'Q' # on the QWERTY layout
        self.hotkey_cyr          = 'й' # same key in Cyrillic layout (has to be the lower case letter, too)
        self.hotkey_raw_key_flag = 24  # value of GetRawKeyFlags() under GTK

        #2 - Menu event processed in OnMenu1
        self.menu_bar = wx.MenuBar()
        menu = wx.Menu()
        menuitemid = wx.NewIdRef()
        menu.Append(menuitemid, f"Menu {menuitemid}\tCtrl+W", "Do the Ctrl-W")
        self.menu_bar.Append(menu, "Menu")
        self.SetMenuBar(self.menu_bar)
        self.Bind(wx.EVT_MENU, self.OnMenu1, id=menuitemid)

        #3 - Accelerator processed in AccelOnE
        accelitemid = wx.NewIdRef()
        accel_entry_E = wx.AcceleratorEntry(wx.ACCEL_CTRL, ord('E'), accelitemid)
        accel_table = wx.AcceleratorTable([accel_entry_E])
        self.panel.SetAcceleratorTable(accel_table)
        self.Bind(wx.EVT_MENU, self.AccelOnE, id=accelitemid)
        
        
    def OnKeyPressed(self, evt):
        is_caught = False
        if evt.ControlDown():

            # triggers when the keyboard is in Latin
            if evt.GetUnicodeKey() == ord(self.hotkey_lat):
                is_caught = True
                print(f"Ctrl-{self.hotkey_lat} triggered!")

            # triggers when the keyboard is in Cyrillic
            if evt.GetUnicodeKey() == ord(self.hotkey_cyr):
                is_caught = True
                print(f"Ctrl-{self.hotkey_cyr} triggered!")

            # triggers in both of the above cases
            if evt.GetRawKeyFlags() == self.hotkey_raw_key_flag:
                is_caught = True
                print(f"Ctrl-{self.hotkey_raw_key_flag} triggered!")
        evt.Skip(not is_caught)

    def OnMenu1(self, evt):
        print("Ctrl-W menu item triggered!")

    def AccelOnE(self, evt):
        print("Ctrl-E accelerator entry triggered!")

app = wx.App()
frame = MainWindow(None, -1, "Window")
frame.Show(1)
app.MainLoop()

5 posts - 2 participants

Read full topic

]]>
https://discuss.wxpython.org/t/language-independent-hotkeys-on-linux/40383 Tue, 09 Dec 2025 21:05:00 +0000 No No No discuss.wxpython.org-topic-40383 Language-independent hotkeys on Linux
Anyone built a wxPython UI to visualize live sensor data from ESP32 or Raspberry Pi? Uncategorized Hi all, has anyone here used wxPython to build a desktop GUI that displays live data from sensors (e.g. from ESP32 or Raspberry Pi)? I recently read a project about using ESP32 + MQTT to stream sensor values to a web service https://www.theengineeringprojects.com/2021/11/esp32-mqtt.html and it got me thinking a wxPython app might be a good middle‑man for local monitoring.

I’ve also seen Arduino forum threads and Raspberry Pi community projects where people push sensor data to dashboards or loggers but fewer examples coupling that data with a native GUI. If you’ve done similar, how did you handle real‑time updates and threading (for sensor polling vs UI responsiveness)? Any pointers or best practices you’d recommend?

4 posts - 4 participants

Read full topic

]]>
https://discuss.wxpython.org/t/anyone-built-a-wxpython-ui-to-visualize-live-sensor-data-from-esp32-or-raspberry-pi/40380 Sat, 06 Dec 2025 21:30:20 +0000 No No No discuss.wxpython.org-topic-40380 Anyone built a wxPython UI to visualize live sensor data from ESP32 or Raspberry Pi?
Windows11 App Icon wxPython Users In windows 10, i was displaying my wxPython app icon using:

self.SetIcon(wx.Icon(r"_data\Images\WinIco.ico"))

My machine has now been updated to windows 11 & the app icon is no longer visible & replaced by a generic windows icon.
The window icon is still displayed for the app.
image

Does anyone know any tricks/tips with windows11 to bring back the app icon functionality?

Thanks in advance,

GabboCH

9 posts - 4 participants

Read full topic

]]>
https://discuss.wxpython.org/t/windows11-app-icon/40369 Wed, 19 Nov 2025 08:27:39 +0000 No No No discuss.wxpython.org-topic-40369 Windows11 App Icon
Issue with wxdemo with wxpython 4.2.4 Uncategorized executing ‘wxdemo’ from cmd on win 11, the demo archive is searched for in https://extras.wxpython.org/wxPython4/extras/4.2.4/wxPython-demo-4.2.4.tar.gz but it doesn’t exist (404 not found)
Can I download the demo archive from another place?

thanks, Marco

8 posts - 5 participants

Read full topic

]]>
https://discuss.wxpython.org/t/issue-with-wxdemo-with-wxpython-4-2-4/40365 Sat, 01 Nov 2025 09:31:07 +0000 No No No discuss.wxpython.org-topic-40365 Issue with wxdemo with wxpython 4.2.4
Type Hint Confusion: wx.GetApp() returns AppConsole but should be App? Newbie Help Hi everyone,

I’m relatively new to wxPython and encountered what I believe might be a type hint inconsistency in the Python interface (pyi) files.

In the pyi files, wx.GetApp() is annotated as returning an AppConsole type:

def GetApp() -> AppConsole:
    """
    GetApp() -> AppConsole
    
    Returns the current application object.
    """

However, when I try to use wx.GetApp().GetTopWindow(), my IDE (using the type hints) warns me that AppConsole has no method GetTopWindow. This seems misleading because in practice, wx.GetApp() does return an object that has the GetTopWindow method.

I tested this with a simple example:

import wx

app = wx.App()
print(app)           # Output: <wx.core.App object at 0x...>
print(wx.GetApp())   # Output: <wx.core.App object at 0x...>

Both app and wx.GetApp() point to the same object, which is an instance of wx.App (not AppConsole), and wx.App does have the GetTopWindow method.

This suggests that the return type hint for wx.GetApp() should probably be App rather than AppConsole.

My Question:

I’m currently working around this by manually modifying the type hint to return App instead, which resolves the IDE warnings and works correctly in practice. I’m posting this question to confirm whether this is indeed a labeling error in the type hints, or if there are other considerations behind using AppConsole . If this is indeed an error that can be fixed, that would be great for future users!

Thank you for reading this far.

7 posts - 4 participants

Read full topic

]]>
https://discuss.wxpython.org/t/type-hint-confusion-wx-getapp-returns-appconsole-but-should-be-app/40356 Tue, 14 Oct 2025 04:21:01 +0000 No No No discuss.wxpython.org-topic-40356 Type Hint Confusion: wx.GetApp() returns AppConsole but should be App?
Problem using CaptureMouse wxPython Dev I have written a wx application to draw a rectangle around a section of the screen and subsequently OCR that section an put the resulting text on the clipboard. The section is defined by clicking the mouse down at the start of the desired rectangle and releasing it once the mouse has been moved to the opposite corner. This works. But, for example, if I do this over the developer studio’s window, I find I am selecting text in the developer studio as well as drawing the rectangle. So what is CaptureMouse doing? Note that I can draw the rectangle and grab the section without using CaptureMouse. I only use it because I thought it would stop mouse events going to other windows. But it doesn’t seem to do so. Here’s the code:

import wx
from enum import Enum, auto

class State(Enum):
    WAITING = auto()
    INITIAL = auto()
    MOUSE_DOWN = auto()
    CLOSE = auto()


class ScreenGrabber(wx.Frame):
    def __init__(self):
        super().__init__(None)
        self.state = State.WAITING
        self.origin = wx.Point()
        self.current = wx.Point()
        self.Bind(wx.EVT_MOUSE_CAPTURE_CHANGED,self.OnMouseCaptureChangedEvent)
        self.Bind(wx.EVT_MOUSE_CAPTURE_LOST,self.OnMouseCaptureLostEvent)
        self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouseEvent)
        self.wantCapture = False
        self.log = []

    def Start(self):
        self.state = State.INITIAL

    def Stop(self):
        self.state = State.CLOSE

    def Log(self, text):
        self.log.append(text)

    def OnTimer(self):
        if self.wantCapture and not self.HasCapture():
            self.Log('CaptureMouse re-called')
            self.CaptureMouse()
        mouseState : wx.MouseState = wx.GetMouseState()
        if self.state == State.WAITING:
            wx.CallLater(500, self.OnTimer)
        elif self.state == State.INITIAL:
            if mouseState.LeftIsDown():
                self.Log('CaptureMouse called')
                self.CaptureMouse()
                self.wantCapture = True
                self.origin = self.current = mouseState.GetPosition()
                self.state = State.MOUSE_DOWN
            wx.CallLater(20, self.OnTimer)
        elif self.state == State.MOUSE_DOWN:
            self.current = mouseState.GetPosition()
            self.DrawSection()
            if mouseState.LeftIsDown():
                wx.CallLater(20, self.OnTimer)
            else:
                self.GrabScreenSection()
                self.state = State.WAITING
                self.origin = wx.Point()
                self.current = wx.Point()
                self.ReleaseMouse()
                self.Log('ReleaseMouse called')
                self.wantCapture = False
                wx.CallLater(20, self.OnTimer)
        elif self.state == State.CLOSE:
            self.Close()
        else:
            wx.CallLater(20, self.OnTimer)

    def OnMouseCaptureChangedEvent(self, event):
        self.Log('OnMouseCaptureChangedEvent occurred')
        pass

    def OnMouseCaptureLostEvent(self, event):
        self.ReleaseMouse()
        self.Log('OnMouseCaptureLostEvent called: mouse released')
        pass

    def OnMouseEvent(self, event):
        if self.wantCapture:
            self.Log('OnMouseEvent called')
        pass

    def DrawSection(self):
        dc = wx.ScreenDC()
        dc.SetBrush(wx.Brush('white', wx.BRUSHSTYLE_TRANSPARENT))
        dc.StartDrawingOnTop()
        dc.DrawRectangle(x=self.origin[0], y=self.origin[1], width=abs(self.origin[0] - self.current[0]),
                         height=abs(self.origin[1] - self.current[1]))
        dc.EndDrawingOnTop()
        pass

    def  GrabScreenSection(self):
        print('Screen section grabbed')
        pass


if __name__ == "__main__":
    app = wx.App(None)
    grabber = ScreenGrabber()
    grabber.Show()
    grabber.Start()
    grabber.OnTimer()
    app.MainLoop()
    pass

type or paste code here

3 posts - 2 participants

Read full topic

]]>
https://discuss.wxpython.org/t/problem-using-capturemouse/40352 Mon, 06 Oct 2025 11:42:14 +0000 No No No discuss.wxpython.org-topic-40352 Problem using CaptureMouse
Help for an interface Uncategorized Hello.
I’m a new user of wxpython but not a beginner with python.

I want to create an interface with the spécification given in the image :slight_smile:

I don’t know how to chose the good architecture for my program !
Is it possible to have some help please ?

7 posts - 6 participants

Read full topic

]]>
https://discuss.wxpython.org/t/help-for-an-interface/40345 Wed, 24 Sep 2025 15:37:32 +0000 No No No discuss.wxpython.org-topic-40345 Help for an interface
Errors placing wx commands into threads via CallAfter wxPython Users I am working on a prototype of GUI testing/demo code and I am struggling to execute actions that will be done on modal windows. I do understand a few things: to communicate with a modal window, the code that does that must be in a separate thread, so it starts concurrently with the modal dialog. Also, wx commands should not be executed directly in that separate thread; they should be placed into a CallAfter() so that they get called by the main event loop.

Clearly from how my code is failing there is more I need to know. I’d really love some help from anyone who better understands how to properly write multi-threaded code that properly interacts with wx.

My test is for code to “manage” a stock dialog from my application (here isolated in file dlg.py) by clicking on the “Set All” button and to then close that dialog via the “OK” button. The dialog and the concurrent tread are started in response to clicking on the ‘Open modal window’ button in the main frame.

I have tried three approaches to doing this and each is failing in a different way.

  1. In test1.py, the active part of the code is
    threading.Thread(target=doInWindow,
        args=('Select atoms for action',['Set All','OK'])).start()
    OpenDialog(choices)
    print('selected=',dlgResults['selected'])

where doInWindow calls invokeButton(), which uses this to invoke each of the two buttons:

    buttonevt = wx.PyCommandEvent(wx.EVT_BUTTON.typeId, btn.GetId())
    frame.ProcessEvent(buttonevt)

This test script runs without errors but fails to work properly. The problem in test1.py is that while the “OK” button gets activated by the above, the previous call that is supposed to activate the “Set All” button does not. FWIW, all buttons in the dialog are owned by the main wx.Dialog frame (no panels, etc.) and I’m pretty sure btn.GetId() is giving me the right Id for the “Set All” button.

  1. In test2.py, the only change is that instead of using PyCommandEvent/ProcessEvent to generate an event, I use code like this to use the mouse to invoke the button:
    pos = frame.ClientToScreen(btn.GetPosition() + btn.GetSize()/2)
    sim = wx.UIActionSimulator()
    sim.MouseMove(pos.x,pos.y)
    time.sleep(0.1)
    sim.MouseClick(wx.MOUSE_BTN_LEFT)
    time.sleep(0.1)

This does seem to properly invoke both buttons, but fails at the .ShowModal() with a “wx._core.wxAssertionError: C++ assertion “handled == 1” failed…” error characteristic of a wx command being called from a thread – but as far as I am aware, I’m not doing that. There are effectively two upper level routines in openSelect (which is called by an event handler and thus is part of the event loop). OpenDialog is called directly by openSelect, so that is not in a thread and doInWindow, is called in a thread, but that uses only CallAfter calls:

def doInWindow(winname,bnlList):
    time.sleep(0.1) # wait for window to open
    wx.CallAfter(getButtons,winname,bnlList)
    count = waitForDone(asyncResults)
    buttons,frame = asyncResults['buttons'],asyncResults['frame']
    for btxt,b in zip(bnlList,buttons):
        wx.CallAfter(invokeButton2,frame,b)
        count = waitForDone(asyncResults)
        print('invoke done',btxt,b,count)

so I don’t see what I’m doing wrong. (waitForDone is plain Python that has some time.sleep() calls, but does not interact with wx.)

  1. My understanding is that since the routine to be run by my callback (openSelect) will take some time to complete, it really should not be run directly by the event loop, but rather I should invoke that in a thread. So in test3.py I place openSelect into a thread and revised openSelect so that it uses CallAfter for all routines that use wx:
    threading.Thread(target=doInWindow,
        args=('Select atoms for action',['Set All','OK'])).start()
    wx.CallAfter(OpenDialog,choices)
    time.sleep(0.1)
    while len(dlgResults) == 0: time.sleep(0.1)

This also fails with the same wx._core.wxAssertionError error as before.

FWIW, I’m developing this on a Mac running Python 3.13 and wx 4.2.3

dlg.py (12.2 KB)
test1.py (4.1 KB)
test2.py (4.2 KB)
test3.py (4.6 KB)

2 posts - 1 participant

Read full topic

]]>
https://discuss.wxpython.org/t/errors-placing-wx-commands-into-threads-via-callafter/40343 Mon, 22 Sep 2025 17:10:41 +0000 No No No discuss.wxpython.org-topic-40343 Errors placing wx commands into threads via CallAfter
PlotCanvas and ScrollLeft wxPython Dev Hello,
‘PlotCanvas’ object has no attribute ‘ScrollLeft’.
Could you add a function like ScrollLeft’ near ScrollRight’

def ScrollLeft(self, units):
    """Move view right number of axis units."""
    self.last_PointLabel = None  # reset pointLabel
    if self.last_draw is not None:
        graphics, xAxis, yAxis = self.last_draw
        xAxis = (xAxis[0] - units, xAxis[1] - units)
        self._Draw(graphics, xAxis, yAxis)

Thanks a lot

some wheels are here
https://sourceforge.net/projects/pcpu/files/Python3%20ubuntu%20amd64/noble/

3 posts - 2 participants

Read full topic

]]>
https://discuss.wxpython.org/t/plotcanvas-and-scrollleft/40342 Sat, 20 Sep 2025 08:10:56 +0000 No No No discuss.wxpython.org-topic-40342 PlotCanvas and ScrollLeft
Update GUI from another thread with pubsub Uncategorized #!/usr/bin/env python3 import wx import threading import time from pubsub import pub # an attempt to combine the message handler and the wx.CallAfter call with a decorator def pubsubdecorator(fn): # check the arguments of the function passed print(f"fn: {fn.__code__.co_varnames}") # wrapper #1 # doesn't work - gives some error about unknown optional argumenr "percent" #def wrapper(*args, **kwargs): # wx.CallAfter(fn, *args, **kwargs) # wrapper #2 # works, but completely impractical def wrapper(self, percent): wx.CallAfter(fn, self, percent) # check the arguments of the function returned print(f"wrapper: {wrapper.__code__.co_varnames}") return wrapper class MainWindow(wx.Frame): def __init__(self, title): wx.Frame.__init__(self, None, wx.ID_ANY, title) self.label = wx.StaticText(self) self.button = wx.Button(self, wx.ID_ANY, "Start") self.windowSizer = wx.BoxSizer(wx.VERTICAL) self.windowSizer.Add(self.label) self.windowSizer.Add(self.button) self.button.Bind(wx.EVT_BUTTON, self.OnBtnStart) #pub.subscribe(self.OnProgressNaive, "progress") # gives a runtime error sooner or later pub.subscribe(self.OnProgressTwoSteps, "progress") # OK, but takes a two step approach with explicit wx.CallAfter self.SetSizer(self.windowSizer) def OnBtnStart(self, evt): # the thread function - sends a message # with a number in an infinite loop def thread_fn(): pc = 0 while True: pc = (pc + 1) % 100 pub.sendMessage("progress", percent = pc) time.sleep(0.1) t = threading.Thread(target = thread_fn) t.start() #@pubsubdecorator def OnProgressNaive(self, percent): self.label.SetLabel(f"{percent:03d}% thread: {threading.get_ident()}") def OnProgressTwoSteps(self, percent): wx.CallAfter(self.OnProgressTwoSteps_, percent) def OnProgressTwoSteps_(self, percent): self.label.SetLabel(f"{percent:03d}% thread: {threading.get_ident()}") app = wx.App() frame = MainWindow(f"Thread {threading.get_ident()}") frame.Show(1) app.MainLoop()

I use pubsub to update GUI from another thread. As I remember, on Windows directly manipulating GUI elements from within a pubsub message handler (see the OnProgressNaive function above) always worked, causing no visible problems - although that may still be wrong, working only by accident; on Linux, however, sooner or later it causes some Pango assertions, followed by the program termination due to an address boundary error. As I understand, that is because the message handler is still executed outside of the main GUI thread (if you run the code example above, and the thread id that appears inside the window after the “Start” button is pressed is different from the thread id in the window title, it will eventually crash; if they are same, it will not). A solution I found some long time ago is to handle the message in two steps (see the OnProgressTwoSteps functions above) - one responds to the message and calls the other through wx.CallAfter in the GUI thread. That works, but is rather cumbersome and error-prone.

Is there a way to somehow automate that wx.CallAfter call? I was thinking about a decorator, but pubsub seems to rely on the argument names, not kept by the standard *args, **kwargs approach (see the wrapper #1 fragment); it only works if I explicitly use the same exact parameter names as in the message handler (see the wrapper #2 fragment), but that “solution” is rather useless.

Or is there a better way to update the GUI from another thread, that is different altogether?

1 post - 1 participant

Read full topic

]]>
https://discuss.wxpython.org/t/update-gui-from-another-thread-with-pubsub/40340 Fri, 19 Sep 2025 23:04:03 +0000 No No No discuss.wxpython.org-topic-40340 Update GUI from another thread with pubsub
Configuring RichTextCtrl to handle URLs wxPython Users I have been trying to set up styles for URLs in a RichTextCtrl so that they appear as blue, underlined text and when you left-click on them they will trigger an EVT_TEXT_URL event.

That part is working. However, if I click on one of the URLs and then load new text (or even the same text) into the RTC, all the text is marked as URLs!

I have only tested this on wxPython 4.2.3 gtk3 (phoenix) wxWidgets 3.2.7 + Python 3.12.3 + Linux Mint 22.2, so I don’t know if it happens on other platforms.

Below is a simplified example. To trigger the behaviour, left-click on one of the URLs and then click on the “Reload” button.

Any ideas for how to stop this happening?

import re
import wx
import wx.richtext as rt

TEXT = """\
Here are some URLs to test.

Left-click on a URL, then click on "Reload" 
and *all* the text will be marked as URLs????

 https://wxpython.org/

 https://www.bbc.co.uk/news

 https://www.theregister.com/Week/

"""

URL_REGEX = re.compile(r'(http|https)://([\w\-_]+(?:\.[\w\-_]+)+)([\w\-.,@?^=%&:/~+#]*[\w\-@?^=%&/~+#])?')

class MyFrame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent)
        self.SetSize(wx.Size(600, 350))
        self.SetTitle("Test RichTextCtrl")
        self.panel = wx.Panel(self, wx.ID_ANY)
        main_sizer = wx.BoxSizer(wx.VERTICAL)
        self.rtc = rt.RichTextCtrl(self.panel, wx.ID_ANY)
        main_sizer.Add(self.rtc, 1, wx.EXPAND, 0)
        bottom_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.reload_button = wx.Button(self.panel, wx.ID_ANY, "Reload")
        bottom_sizer.Add(self.reload_button, 0, wx.RIGHT, 16)
        self.close_button = wx.Button(self.panel, wx.ID_ANY, "Close")
        bottom_sizer.Add(self.close_button, 0, 0, 0)
        main_sizer.Add(bottom_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.TOP | wx.BOTTOM, 8)
        self.panel.SetSizer(main_sizer)
        self.Layout()

        self.displayText()

        self.rtc.Bind(wx.EVT_TEXT_URL, self.OnURL)
        self.reload_button.Bind(wx.EVT_BUTTON, self.OnReload)
        self.close_button.Bind(wx.EVT_BUTTON, self.OnClose)

    def displayText(self):
        print("Display Text:")
        self.rtc.SetValue(TEXT)
        self.markAllURLs(TEXT)

    def markURL(self, start, end, url_text):
        """Mark a URL in the RichTextCtrl. """

        url_style = rt.RichTextAttr()
        url_style.SetTextColour(wx.BLUE)
        url_style.SetFontUnderlined(True)
        url_style.SetURL(url_text)
        self.rtc.SetStyle(start, end, url_style)

    def markAllURLs(self, text):
        """Mark all the URLs in the RichTextCtrl. """

        for m in re.finditer(URL_REGEX, text):
            print(f" {m.start(0)} {m.end(0)} {m.group(0)}")
            self.markURL(m.start(0), m.end(0), m.group(0))

    def OnClose(self, _evt):
        self.Destroy()

    def OnReload(self, _evt):
        self.displayText()

    def OnURL(self, evt):
        print("OnURL()", evt.GetString())

if __name__ == "__main__":
    app = wx.App()
    frame = MyFrame(None)
    frame.Show()
    app.MainLoop()

2 posts - 1 participant

Read full topic

]]>
https://discuss.wxpython.org/t/configuring-richtextctrl-to-handle-urls/40339 Sun, 14 Sep 2025 13:44:49 +0000 No No No discuss.wxpython.org-topic-40339 Configuring RichTextCtrl to handle URLs
Drawing a line object when clicking a button Newbie Help My main purpose is to draw a line on ShapeCanvas when when clicking the Add Edge button. The code I wrote is below:

import wx
import wx.lib.ogl as ogl


class MyPen:
    def __init__(self, color, width, style):
        self.my_pen = wx.Pen()
        wx.Pen.SetColour(self.my_pen, color)
        wx.Pen.SetWidth(self.my_pen, width)
        wx.Pen.SetStyle(self.my_pen, style)


class EdgeShape(ogl.LineShape):
    def __init__(self, arrow, x1, y1, x2, y2, selected, canvas):
        super().__init__()
        if arrow:
            self.AddArrow(ogl.ARROW_ARROW)

        self.MakeLineControlPoints(2)
        self.SetEnds(x1, y1, x2, y2)
        self.pen = MyPen(wx.Colour(0, 0, 0), 2, wx.PENSTYLE_SOLID).my_pen
        self.brush = wx.BLACK_BRUSH
        self.SetPen(self.pen)
        self.SetBrush(self.brush)
        self.SetCanvas(canvas)
        canvas.diagram.AddShape(self)
        self.Show(True)


class GraphCanvas(ogl.ShapeCanvas):
    def __init__(self, parent):
        # def __init__(self, parent, shape):
        super().__init__(parent)
        maxWidth = 1000
        maxHeight = 1000
        self.SetScrollbars(20, 20, maxWidth // 20, maxHeight // 20)
        self.SetBackgroundColour("LIGHT BLUE")
        # Create a diagram and assign the diagram to the ShapeCanvas and assign the ShapeCanvas to the diagram
        self.diagram = ogl.Diagram()
        self.SetDiagram(self.diagram)
        self.diagram.SetCanvas(self)


class MyFrame(wx.Frame):
    def __init__(self):
        super().__init__(None, title="wxPython OGL Clickable Shapes")

        add_edge_btn = wx.Button(self, label="Add Edge")
        add_node_btn = wx.Button(self, label="Add Node")

        # Setup and display the ShapeCanvas
        self.graph_canvas = GraphCanvas(self)

        main_sizer = wx.BoxSizer(wx.HORIZONTAL)
        button_sizer = wx.BoxSizer(wx.VERTICAL)
        button_sizer.Add(add_edge_btn, flag=wx.CENTER)
        button_sizer.Add(add_node_btn, flag=wx.CENTER)
        main_sizer.Add(button_sizer, proportion=1, flag=wx.EXPAND)
        main_sizer.Add(self.graph_canvas, proportion=3, flag=wx.EXPAND)
        self.SetSizer(main_sizer)
        add_edge_btn.Bind(wx.EVT_BUTTON, self.add_edge)
        add_node_btn.Bind(wx.EVT_BUTTON, self.add_node)
        # edge_shape = EdgeShape(False, 10, 10, 100, 100, False, self.graph_canvas)
        self.Show()

    def add_edge(self, event):
        edge_shape = EdgeShape(False, 10, 10, 100, 100, False, self.graph_canvas)

    def add_node(self, event):
        pass


if __name__ == '__main__':
    app = wx.App(False)
    ogl.OGLInitialize()
    frame = MyFrame()
    app.MainLoop()

The code above does not initially shows the line. However, the line is shown only after I use the scroll bars. The intent is to have the line shown immediately after pressing the button.

I tried moving this line

edge_shape = EdgeShape(False, 10, 10, 100, 100, False, self.graph_canvas)

to be under MyFrame class instead of under the add_edge() event and the line shows up immediately.

Can someone please help me to know whey the line does not display when it is created under the even function? Thanks

2 posts - 2 participants

Read full topic

]]>
https://discuss.wxpython.org/t/drawing-a-line-object-when-clicking-a-button/40335 Sat, 13 Sep 2025 16:20:38 +0000 No No No discuss.wxpython.org-topic-40335 Drawing a line object when clicking a button