A JSON based markup language which translates JSON files into Flet UI
Project description
FJML
FJML is a JSON based markup language which translates JSON files into Flet UI for web, mobile and desktop applications.
Example:
{
"Header":{
"program_name":"Example",
"action_import":{
"import":"Actions",
"from":".path.to.func.file"
},
},
"Controls":[
{
"var_name":"msg",
"control_type":"Text",
"settings":{
"value":"Hello World",
"size":20,
"color":{"code_refs":"colors", "attr":"green"},
"weight":{
"control_type":"FontWeight",
"attr":"W_700"
}
}
},
{
"var_name":"msg_display",
"control_type":"Container",
"settings":{
"content":{"refs":"msg"},
"alignment":{"control_type":"alignment.center"},
"border_radius":10,
"padding":{
"control_type":"padding.symmetric",
"settings":{"horizontal":10, "vertical":8}
},
"width":200,
"ink":true,
"ink_color":{"code_refs":"colors", "attr":"grey"}
}
}
],
"UI":[
{
"route":"/",
"settings":{
"controls":[
{"refs":"msg_display"}
],
"horizontal_alignment":{"code_refs":"cross_align"},
"vertical_alignment":{"code_refs":"main_align"}
}
}
]
}
from fjml import data_types as dt
import flet as ft
class Colors:
green: str = ft.colors.GREEN_600
grey: str = ft.colors.GREY_200
class Actions(dt.EventContainer):
def _page_setup(self):
...
def _imports(self):
self.colors: Colors = Colors()
self.cross_align: str = ft.CrossAxisAlignment.CENTER
self.main_align: str = ft.MainAxisAlignment.CENTER
from fjml import load_program, Compiler, data_types as dt
from path.to.program import Actions
import flet as ft
class Paths:
PROGRAM: str = "path\\to\\program_folder"
COMPILED: str = "path\\to\\compiled_program\\compiled.fjml"
class App:
def __init__(self, compile_run: bool = False) -> None:
if compile_run:
compiler: Compiler = Compiler(Paths.PROGRAM, Paths.COMPILED)
compiler.compile()
async def run(self, page: ft.Page):
page = load_program(Paths.COMPILED, Actions)
page.go("/")
if __name__ == "__main__":
app: App = App(compile_run=True)
ft.app(target=app.run)
CLI Tooling:
FJML comes with 2 CLI commands and each have specific parameters:
-
registry:The registry command is a categorical command which automatically modifies the registry file which contains of all Flet controls of your current Flet installation.
Choice Value Type Action delete strdeletes registry file reset strresets registry file Example:
fjml registry update
The reset command should be used after installation to ensure proper functioning of fjml
-
make:The make command generates an FJML folder containing all the needed files for running your project. make has one mandatory parameter,
--name, and one optional parameter,--path.--namegenerates the name of your project while--pathdirects where that project is generated. If path is not entered it will use the current directory.Example:
fjml make --name Hello World
Python Integration
FJML allows the use of python code to perform actions such as API calls, function calls, etc. via the EventContainer Abstract Base Class.
- The main format of the Actions class which inherits from the
EventContainerlooks like this:from fjml.data_types import EventContainer class Actions(EventContainer): def _page_setup(self): ''' a custom page setup function to initialize your page object with data ''' def _imports(self): ''' an import function used to run operations outside of the page before rendering the UI ''' #you can then add custom functions to be used throughout the FJML code
EventContainer also includes multiple built-in helper classes and functions to help create programs
-
EventContainermethods and classes-
client_storage:
See Flet Page Docs
-
session:
See Flet Page Docs
-
page:
See Flet Page Docs.
-
dict_to_control:
Name Attributes Return Description dict_to_control control: dt.ControlDictdt.ControlTypeAllows creating controls using FJML syntax inside the EventContainer.
-
group_assign:
Name Attributes Return Description group_assign obj: Any', 'attribute_map: Mapping[str, Any]Noneallows assigning multiple attributes to an object at once
-
eval_locals:
This class's main use is to add or delete locals from the evil statement's locals parameter.
Methods Attributes Return Description add name: str,obj: AnyNoneadds an object to the eval statement's locals delete name: strNonedeletes an object from the eval statement's locals mass_add data: Mapping[str, Any]Noneadds multiple objects to the eval statement's locals mass_delete data: Sequence[str]Nonedeletes multiple objects from the eval statement's locals data NoneMapping[str, Any]returns a copy of all preset locals in the eval statement's locals
-
object_bucket:
Methods Attributes Return Description set_object name: str,obj: AnyCallableNoneadds any callable object to the bucket, so it can be called inside the UI code call_object name: str,kwargs: dict[str, Any]Anycalls the object with the necessary key word arguments. (used when object is called within the UI code) delete_object name: strNonedeletes the object from the bucket This class's main use is to register objects for use in FJML code via the "call" designator:
- ⠀
{ "call":"get_text", "settings":{ "index":1 } }
If an object is not registered it can not be called but can only be referenced using a code_refs or the func designators.
- ⠀
-
property_bucket:
Methods Attributes Return Description add name: str,obj: AnyNoneadds a property to be used as a code_refsinside the UI codecontains name: strboolused to check if a name is registered as property call name: str,operation: str,set_val: AnyNoneUses the property operations (set, get, del) to either set an object using the set_val parameter, get by just giving the name or deletion using the del operation. This class's main use is to register python functions as properties to be used as code_refs
-
setup_functions:
This class's main use is to register functions to be called to set up what ever API, environment, etc. when the UI starts up.
Methods Attributes Return Description add_func func: Callable,parameters: Sequence[Any]Noneadds a function to the class mass_add_func items: Sequence[tuple[Callable, Sequence[Any]]]Noneadds multiple functions to the class call_functions NoneNonecalls all functions added to the class
-
style_sheet:
this class is used primarily for retrieving styles set inside the style sheet. This is mainly used in the "_unpack" designator via the
{"styling":"xyz"}mapping value.Methods Attributes Return Description get_style path: strdt.JsonDictgets the style from a style sheet by name
-
view_operations:
This class is used to generate and register Flet views. Its main use is in the Flet's
Page.on_route_changeevent.Methods Attributes Return Description set_view route_name: str,view_settings: dt.ControlSettingsNoneadds a UIViewsto the compiled_model UI mapping attributeadd_view view: ft.ViewNoneadds a Flet view control to the page views make_view view_model: UIViewsft.Viewgenerates a Flet view control from a UIViewstype
-
UI Format
Main UI File:
{
"Header":{
//Used to declare certain values
},
"Imports":[
// Used to import controls from other files
],
"Controls":[
// Used to assign controls to variables
],
"UI":[
// Used to define route views
]
}
This format separates the header data, imports, controls and display UI.
-
Header:
Headers is primarily map based and thus requires one to use keys and values unlike the list based forms like the rest are. The keys in this block consists of:
-
import_folder:
Value Type Example str"extra"stores the name of the folder which contains the FJML imports
-
program_name:
Value Type Example str"Hello World"stores the name of the program
-
style_sheet_name:
Value Type Example str"style_sheet"Stores the name of the FJML style sheet.
-
action_import:
Value Type Example dt.JsonDict{"from":"...", "import":"..."}This dictionary imports the action class using the format:
Key Value Type Example from str".import_path.func"import str"Action"full example:
{ "action_import":{ "from":".import_path.func", "import":"Action" } }
The
action_importkey's value is equivalent to:from .import_path.func import Action
The import statement is run as if it was run in your main file.
-
extensions:
Value Type Example Sequence[dt.JsonDict][{"from":"...", "import":"...", "using":"..."}, ...]The value of this key consists of using a sequence of dictionaries which help import multiple controls at once:
Key Value Type Example from str.custom_controlsimport Union[str, Sequence[str]]"CustomBtn"or["CustomBtn", "CustomTxt"]using Optional[str]"CC"full example:
{ "extensions":[ { "from":".custom_controls", "import":["CustomBtn", "CustomTxt"], "using":"CC" } ] }
Using extension imports like this is equivalent to:
-
-
Imports:
Imports are called using the file name of the UI file defined inside the import folder defined by the key
"source". If the import folder includes different folders the use of the key"folder"can be used to indicate the specific folder inside the main import folder where you want to import from. e.g: -{"source:"container_ui"}-{source:["user_ui", "admin_ui"], "folder":"person_ui_folder"} -
Controls:
controls can be named using the
"var_name"key and then can be called and used in the python"Action"class using"self.name_text"or in another control using a dictionary using the"refs"key and the control name as its value. e.g:{"refs":"name_text"}Format example:{ "var_name":"foo", "control_type":"imported/registered control name", "settings":{ ... } }
-
UI:
The UI section is used to define the route views used by you program. These views use the format of:
{ "route":"/Home", "settings":{ //any view settings needed. P.S. the route parameter will always be ignored if set in the settings block. } }
and are contained in a Sequence
Imported UI File:
With imports the JSON structure is similar except that it only has the "Controls" container. Using controls from other files is still possible once all dependencies are also imported into the main file.
Imported file format:
{
"Controls":[]
}
Style Sheet File:
With style sheets you are able to create styles for use later in your program. You are able to section off your styles by names and sub-names and thus call them using the format {name}.{sub_name}.{sub_sub_name}.... This format can go on forever if needed but can increase rendering time of your program if you go too deep.
Style sheet Format:
{
"name_1":{
"sub_name":{
"height":200,
"width":500
}
},
"name_2":{
"sub_name":{
"height":200,
"width":500
}
}
}
styles can be used by then adding the "_unpack" attribute inside the control's "settings" dictionary with the dictionary formats:
{"styles":"{name}.{sub-name}"}- or
{"styles":"{name}.{sub-name} {name1}.{sub_name}"}if multiple styles are needed
Other FJML UI features include
-
Calling functions and objects:
class Action(EventContainer): def _imports(self) -> None: #register callable object self.object_bucket.set_object("calc_width", self.calc_width) def calc_width(self, height: int) -> int: return height*2
// call object { "control_type":"Container", "settings":{ "width":{ "call":"calc_width", "settings":{ "height":200 } } } }
-
UI loops:
{ "control_type":"Column", "settings":{ "controls":{ "control_type":"loop", "depth":1, "iterator":[1,2,3,4,5], "control":{ "control_type":"Text", "settings":{ "value":{ "control_type":"loop_index", "idx":[0] } } } } } }
-
Adding control to variables:
{ "var_name":"name", //<- Here "control_type":"Text", "settings":{ "value":"John Doe", "size":18 } }
this can be accessed inside the Actions class using
self.nameand if control is defined asself.nameinside Actions it can be called in FJML using{"refs":"name"} -
Using variables:
Allows the use of FJML variables to be referenced in the same file or else where without the need for constant importing
control variables:
{ "var_name":"name", //<- Here "control_type":"Text", "settings":{ "value":"John Doe", "size":18 } } { "var_name":"text_container", "control_type":"Container", "settings":{ "content":{"refs":"name"}, "padding":6 } }
code variables:
Allows the variables defined in python code to be accessed and used inside FJML code
class Actions(EventContainer): def _imports(self) -> None: self.text_size: int = 16
{ "var_name":"name", //<- Here "control_type":"Text", "settings":{ "value":"John Doe", "size":{"code_refs":"text_size"} } }
Attribute and index calling
The
idxkey works for both dictionaries and index based sequences.{ "var_name":"name", "control_type":"Text", "settings":{ "value":"John Doe", "size":18 } } { "var_name":"get_display_name", "control_type":"Text", "settings":{ "value":{ "refs":"name", "attr":"value" }, "size":18 } }
-
Group chains:
class TextSizes: data: list[Union[dict[str, int], int]] = [18, {"name":16}] class Actions(EventContainer): def _imports(self) -> None: self.text_sizes: TextSizes = TextSizes()
{ "var_name":"name", "control_type":"Text", "settings":{ "value":"John Doe", "size":{ "code_refs":"text_sizes", "group":[ {"attr":"data"}, {"idx":1}, {"idx":"name"} ] } } }
-
-
Action Class:
In order to link your action class to FJML code you must import the in the
Headercontainer using the key,action_import.-
Example:
{ "Header":{ ..., "action_import":{ "import":"Actions", "from":".ui_test_program.func" } }, "Imports":[...], "Controls":[...], "UI":[...] }
All action imports must exist in an importable path and be written as if it was run in the
main.pyfile. -
-
Custom Controls:
FJML allows you multiple ways to define and add custom controls to your project. This is done by using the
"extensions"key inside the"Header":{ "Header":{ ..., "extensions":[ { "using":"fm", "import":["Buttons", "Switches"], "from":"flet_material" } ] }, "Imports":[...], "Controls":[ { "var_name":"switch", "control_type":"fm.Switches", "settings":{} } ], "UI":[...] }
All imports must already be installed or exist in an importable path.
Running the app
from fjml import load_program, Compiler, data_types as dt
import flet as ft
class Paths:
PROGRAM: str = "path\\to\\program_folder"
COMPILED: str = "path\\to\\compiled_program\\compiled.fjml"
class App:
def __init__(self, compile_run: bool = False) -> None:
if not compile_run:
return
compiler: Compiler = Compiler(Paths.PROGRAM, Paths.COMPILED)
compiler.compile()
async def run(self, page: ft.Page):
page = load_program(Paths.COMPILED, page)
page.go("/")
if __name__ == "__main__":
app: App = App(compile_run=True)
ft.app(target=app.run)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fjml-0.0.5.tar.gz.
File metadata
- Download URL: fjml-0.0.5.tar.gz
- Upload date:
- Size: 43.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.9.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6e8124131281589241c08039b32e7aaf68ea7a9098c064cc435378fda5aa1cbb
|
|
| MD5 |
976e68d8b93e4f1a26354b15dfbae1a1
|
|
| BLAKE2b-256 |
db1c7e44dc09249aeaffe7ba9b5648a3a6a77b9eb5e2e5ccc18e46f85bf0fa49
|
File details
Details for the file fjml-0.0.5-py3-none-any.whl.
File metadata
- Download URL: fjml-0.0.5-py3-none-any.whl
- Upload date:
- Size: 44.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.9.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2faebb6b5f345c6fb8a668b8b51f156557c8b07dae38aa4d127f85481496ea60
|
|
| MD5 |
5b51c6380b9f2a2586272c8c42cd83a2
|
|
| BLAKE2b-256 |
9452613d5f4a494f51002064bcd47000296ea311480874bf7d7d35d29a57800e
|