Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.12
22 changes: 22 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[project]
authors = [
{name = "Davide Rosa", email = "dddomodossola@gmail.com"},
]
dependencies = [
"lxml>=6.0.2",
"setuptools<81",
]
description = "Add your description here"
keywords = ["gui-library", "remi", "platform-independent", "ui", "gui"]
license = {file = "LICENSE"}
name = "remi"
readme = "README.md"
requires-python = ">=3.8,<3.13"
version = "2025.11.29"

[project.scripts]
xml2remi = "xml2remi.__main__:main"

[build-system]
build-backend = "setuptools.build_meta"
requires = ["setuptools", "wheel"]
9 changes: 5 additions & 4 deletions remi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@
)

from .server import App, Server, start
from pkg_resources import get_distribution, DistributionNotFound

try:
__version__ = get_distribution(__name__).version
except DistributionNotFound:
# package is not installed
from importlib.metadata import version

__version__ = version(__name__)
except ImportError:
# Fallback for older Python versions
pass
35 changes: 18 additions & 17 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,26 @@
long_description = fh.read()

params = {
'name':"remi",
'description':"Python REMote Interface library",
'use_scm_version':{'version_scheme': 'post-release'},
'long_description':long_description,
'long_description_content_type':"text/markdown",
'url':"https://github.com/rawpython/remi",
'download_url':"https://github.com/rawpython/remi/archive/master.zip",
'keywords':["gui-library", "remi", "platform-independent", "ui", "gui"],
'author':"Davide Rosa",
'author_email':"dddomodossola@gmail.com",
'license':"Apache",
'packages':setuptools.find_packages(),
'include_package_data':True,
'setup_requires':['setuptools_scm'],
"name": "remi",
"description": "Python REMote Interface library",
"use_scm_version": {"version_scheme": "post-release"},
"long_description": long_description,
"long_description_content_type": "text/markdown",
"url": "https://github.com/rawpython/remi",
"download_url": "https://github.com/rawpython/remi/archive/master.zip",
"keywords": ["gui-library", "remi", "platform-independent", "ui", "gui"],
"author": "Davide Rosa",
"author_email": "dddomodossola@gmail.com",
"license": "Apache",
"packages": setuptools.find_packages(),
"include_package_data": True,
"setup_requires": ["setuptools_scm"],
"entry_points": {"console_scripts": ["xml2remi = xml2remi"]},
}
try:
setup(**params)
except:
del params['setup_requires']
params['use_scm_version'] = False
params['version'] = '2022.7.27'
del params["setup_requires"]
params["use_scm_version"] = False
params["version"] = "2025.11.29"
setup(**params)
140 changes: 140 additions & 0 deletions uv.lock

Large diffs are not rendered by default.

Empty file added xml2remi/__init__.py
Empty file.
137 changes: 137 additions & 0 deletions xml2remi/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import xml.etree.ElementTree as ET
import sys, os
import remi.gui as widget_list

try:
import lxml.etree as lxml_ET

USE_LXML = True
except ImportError:
USE_LXML = False


class RemiXMLTranslator:
def __init__(self):
# No longer need widget_map - we'll use dynamic class resolution
pass

def translate_xml_to_code_from_root(self, root, file_path=None):
"""Translate XML root element to Remi Python code."""
code_lines = []
code_lines.append("from remi import server, gui")
code_lines.append("")

# Generate code for root widget
root_var = self._generate_widget_code(root, code_lines, "root", file_path)
code_lines.append("") # Add empty line after root initialization

# Add return statement
code_lines.append(f"return {root_var}")

return "\n".join(code_lines)

def _generate_widget_code(
self, element, code_lines: list[str], var_name: str, file_path: str = None
):
"""Recursively generate code for a widget and its children."""
tag = element.tag
if not hasattr(widget_list, tag):
line_info = ""
if USE_LXML and hasattr(element, "sourceline"):
line_info = (
f" at {os.path.abspath(file_path)}, line {element.sourceline}"
)
elif file_path:
line_info = f" in {os.path.abspath(file_path)}"
raise ValueError(f'Unknown widget type: "{tag}"{line_info}')
widget_class = tag

# Collect attributes
kwargs = {}
for attr, value in element.attrib.items():
match attr:
case "width" | "height":
kwargs[attr] = value
case _:
kwargs[attr] = f'"{value}"'

# Generate instantiation
args_str = ", ".join([f"{k}={v}" for k, v in kwargs.items()])
code_lines.append(f"{var_name} = gui.{widget_class}({args_str})")

# Generate code for children
child_vars = []
for i, child in enumerate(element):
child_var = f"{var_name}_{child.tag}_{i}"
child_vars.append(child_var)
self._generate_widget_code(child, code_lines, child_var, file_path)
key = child.attrib.get("key", "")
if key:
code_lines.append(f"{var_name}.append({child_var}, '{key}')")
else:
code_lines.append(f"{var_name}.append({child_var})")
# Add empty line after append/add_tab, except for the last child
if i < len(element) - 1:
code_lines.append(
""
) # Add empty line for readability after append/add_tab

return var_name

def translate_xml_file_to_code(self, xml_file_path: str):
"""Translate XML file to Remi Python code."""
if USE_LXML:
tree = lxml_ET.parse(xml_file_path)
root = tree.getroot()
else:
with open(xml_file_path, "r", encoding="utf-8") as f:
xml_string = f.read()
root = ET.fromstring(xml_string)
return self.translate_xml_to_code_from_root(root, xml_file_path)


# Command line usage
def main():
if len(sys.argv) != 2:
print("Usage: python translator.py <xml_file>")
sys.exit(1)

xml_file = sys.argv[1]
translator = RemiXMLTranslator()
code = translator.translate_xml_file_to_code(xml_file)

# Parse the generated code
lines = code.split("\n")
import_line = lines[0]
code_lines = lines[2:-1] # Remove import, empty line, and return statement
ui_code = "\n".join(
" " + line if line.strip() else "" for line in code_lines
)

base_name = os.path.splitext(xml_file)[0]
py_file = base_name + ".py"

full_code = f"""{import_line}

class MyApp(server.App):
def __init__(self, *args):
super(MyApp, self).__init__(*args)

def main(self):
{ui_code}
# add your code here

return root

if __name__ == "__main__":
server.start(MyApp)
"""

with open(py_file, "w", encoding="utf-8") as f:
f.write(full_code)

print(f"Generated {py_file}")


if __name__ == "__main__":
main()
5 changes: 5 additions & 0 deletions xml2remi/error_example.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<VBox>
<Label text="Hello World" />
<WrongWidget text="Error" />
<Button text="Click Me" />
</VBox>
35 changes: 35 additions & 0 deletions xml2remi/example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from remi import server, gui

class MyApp(server.App):
def __init__(self, *args):
super(MyApp, self).__init__(*args)

def main(self):
root = gui.VBox()
root_Label_0 = gui.Label(text="Hello World")
root.append(root_Label_0)

root_Button_1 = gui.Button(text="Click Me")
root.append(root_Button_1)

root_TabBox_2 = gui.TabBox()
root_TabBox_2_VBox_0 = gui.VBox()
root_TabBox_2_VBox_0_Label_0 = gui.Label(text="Content of Tab 1")
root_TabBox_2_VBox_0.append(root_TabBox_2_VBox_0_Label_0)
root_TabBox_2.append(root_TabBox_2_VBox_0)

root_TabBox_2_HBox_1 = gui.HBox()
root_TabBox_2_HBox_1_TextInput_0 = gui.TextInput()
root_TabBox_2_HBox_1.append(root_TabBox_2_HBox_1_TextInput_0)

root_TabBox_2_HBox_1_CheckBox_1 = gui.CheckBox()
root_TabBox_2_HBox_1.append(root_TabBox_2_HBox_1_CheckBox_1)
root_TabBox_2.append(root_TabBox_2_HBox_1)
root.append(root_TabBox_2)

# add your code here

return root

if __name__ == "__main__":
server.start(MyApp)
13 changes: 13 additions & 0 deletions xml2remi/example.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<VBox>
<Label text="Hello World" />
<Button text="Click Me" />
<TabBox>
<VBox>
<Label text="Content of Tab 1" />
</VBox>
<HBox>
<TextInput />
<CheckBox />
</HBox>
</TabBox>
</VBox>