PoC convert wxPython top-level widgets to wxGlade design files

builds.sr.ht status

This proof of concept converts hand-crafted wxPython widgets e.g. dialogs or panels into XML based wxGlade design files.

This document as well as the source code uses three different naming convention for the same object, depending on the context used:

  • node - for any details returned by / related to the ast parser
  • control - refers to the intermediate representation
  • widget - refers to wxWidgets

The parsing of the source code would be the most difficult task. Fortunately the built-in ast module provides all necessary functionality to parse Python files. The parser returns all the information required in the further course of the program.

The program uses a subclass of ast.NodeVisitor to walk the Python statements and extract the useful details. The ast parser returns a node object with all details for each Python statement.

The subclass DesignAnalyzer implements the logic. It only uses the node types for class definitions (visit_ClassDef()), assignments (visit_Assign()) and expressions (visit_Expr()).

Node information returned by the ast parser is stored as an intermediate representation in the two dictionaries DesignAnalyzer.controls_wo_parent and DesignAnalyzer.controls_all.

controls_wo_parent is a temporary storage for controls not assigned to any sizer controls. It should be empty when everything has been processed completely.

controls_all provides fast and uniform access to all controls.

If the parser finds a function call of the form <sizer>.Add(<widget>, ...), then it appends the control to sizers children list (Control.children) and removes the control from the DesignAnalyzer.controls_wo_parent at the same time.

By calling <Top-Level-Widget>.SetSizer(<Top-Level-Sizer>) the last sizer without parent object is assigned to the toplevel element DesignAnalyzer.toplevel.

After that, all elements below the top-level element should be processed and controls_wo_parent should be empty. If not, the remaining widget is not in use or the source code is too complex for parsing.

The last step completes the tree with the intermediate representation. It can now be transferred to any representation.

In this PoC the program transforms the tree into an XML-based design file for wxGlade. The class XMLFormatterWXGlade implements this straight forward task and writes, starting with the top-level control, all children recursively into a XML file.

The program does not have consistency checks.

Feedback is very welcome.

Room for improvement

  • add XRC support
  • add support for class variables e.g. to use for styles

Limitations

  • one class per file
  • only one top-level widget (wx.Dialog/wx.Frame/wx.Panel) per class
  • variables will be ignored - inline it
  • ...

Your favourite IDE helps to prepare the Python source code.

Supported and tested Python versions

  • Python 2.7, tested with 2.7.18

Requirements

There are no additional requirements. All you need is Python.

Example: Convert UIReport dialog from example.py

  1. The source file example.py

     $ head -n 20 example.py 
     # -*- coding: LATIN-1 -*-
    
     import wx
    
    
     class UIReport(wx.Dialog):
         def __init__(self, *args, **kwds):
             wx.Dialog.__init__(self, *args,
                                style=wx.DEFAULT_DIALOG_STYLE | wx.MAXIMIZE_BOX | wx.MINIMIZE_BOX | wx.RESIZE_BORDER,
                                **kwds)
    
             self.SetSize((600, 300))
             self.SetTitle("Bericht ansehen")
             self.SetFocus()
    
             root_sizer = wx.BoxSizer(wx.VERTICAL)
    
             text_ctrl = wx.TextCtrl(self, wx.ID_ANY, "")
             root_sizer.Add(text_ctrl, 1, wx.ALL | wx.EXPAND, 5)
    
             static_line = wx.StaticLine(self, wx.ID_ANY)
    
  2. Convert it

     $ ./wxpy2wxg.py example.py 
     wxpy2wxg.py version 2020-05-21; Carsten Grohmann (c) 2020; Licence: MIT
     Starting conversion with reading Python source from example.py.
     WARNING:root:Ignore valid but probably useless assignment at line 17
     The conversion is done. The design is written into example.wxg.
    
  3. Show the design example.wxg

     $ head example.wxg 
     <?xml version="1.0" encoding="UTF-8"?>
     <!-- generated by wxpy2wxg.py version 2020-05-21 -->
     <application>
         <object class="UIReport" name="UIReport" base="EditDialog">
             <size>600, 300</size>
             <focused>1</focused>
             <title>Bericht ansehen</title>
             <style>wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxMINIMIZE_BOX|wxRESIZE_BORDER</style>
             <object class="wxBoxSizer" name="root_sizer" base="EditBoxSizer">
                 <orient>wxVERTICAL</orient>
    
  4. Open the new generated design with wxGlade

     $ wxglade example.wxg
    

Screenshot Converted design in wxGlade

Changelog

2020-06-21

  • Add a simple test

2020-06-02

  • Rename variables to better understand the context

2020-06-01

  • Extend documentation

2020-05-21

  • Initial version

Project page and feedback

The project page is at https://www.carstengrohmann.de/wxpython2wxglade.html.

The source code is at https://sr.ht/~carstengrohmann/wxPython2wxGlade.

Comments, suggestions and patches are welcome and appreciated. Please email me.

License

This software is covered by the MIT License.

Copyright (c) 2020-2021 Carsten Grohmann mail@carstengrohmann.de

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.