PoC convert wxPython top-level widgets to wxGlade design files
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
-
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)
-
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.
-
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>
-
Open the new generated design with wxGlade
$ wxglade example.wxg
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.