Skip to content

Commit 87261a1

Browse files
ysfchnhammerhaishreyashsaitwalStormiFire
authored
Version - v2.1.0 (#30)
Check #30 for release notes. Co-authored-by: Nathan <43486313+StormiFire@users.noreply.github.com> Co-authored-by: Shreyash Saitwal <40118018+ShreyashSaitwal@users.noreply.github.com> Co-authored-by: StormiFire <7520363-StormiFire@users.noreply.gitlab.com>
1 parent 26d0eda commit 87261a1

17 files changed

Lines changed: 930 additions & 908 deletions

File tree

.github/FUNDING.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
patreon: ysfchn

README.md

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1-
# 🧱 DynamicComponents-AI2 `Extension`
1+
![Icon](assets/icon.png)
22

3-
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/c9fee4822c864505a2ade6d19731caa5)](https://app.codacy.com/manual/ysfchn/DynamicComponents-AI2?utm_source=github.com&utm_medium=referral&utm_content=ysfchn/DynamicComponents-AI2&utm_campaign=Badge_Grade_Dashboard)
4-
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fysfchn%2FDynamicComponents-AI2.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fysfchn%2FDynamicComponents-AI2?ref=badge_shield)
3+
# DynamicComponents-AI2 `Extension`
4+
5+
[![Maintainability](https://api.codeclimate.com/v1/badges/31e4cd31de1bd0e186c8/maintainability)](https://codeclimate.com/github/ysfchn/DynamicComponents-AI2/maintainability)
56

67
Fully supported Dynamic Components extension for MIT App Inventor 2. It is based on Java's reflection feature, so it creates the components by searching for a class by just typing its name. So it doesn't have a limited support for specific components, because it supports every component which is ever added to your App Inventor distribution!
78

89
So if you use Kodular, you will able to create all Kodular components, if you use App Inventor, you will able to create all App Inventor components and so on. Extension components are supported too!
910

11+
> ⚠ The `beta` branch will be reset after every release. So stay on the `main` branch if you don't know what you do.
12+
13+
[![forthebadge](https://forthebadge.com/images/badges/its-not-a-lie-if-you-believe-it.svg)](https://forthebadge.com)
14+
1015
## 🧩 Blocks
1116

1217
<table style="width:100%">
@@ -41,15 +46,15 @@ So if you use Kodular, you will able to create all Kodular components, if you us
4146
<br><br>
4247
<table>
4348
<tr>
44-
<td><img src="assets/blocks/text.png"></td>
49+
<td><img src="assets/other/text.png"></td>
4550
<td><b>Name of the component.</b><br>✅ Doesn't require to add existing component.<br> ❌ Only components can be created.</td>
4651
</tr>
4752
<tr>
48-
<td><img src="assets/blocks/component_block.png"></td>
53+
<td><img src="assets/other/component_block.png"></td>
4954
<td><b>Block of existing component to create new one from it.</b><br>❌ Requires a existing component.<br>✅ Extensions can be created also.</td>
5055
</tr>
5156
<tr>
52-
<td><img src="assets/other/class_text.png" href="assets/blocks/class_text_full.png"></td></td>
57+
<td><img src="assets/other/class_text.png" href="assets/other/class_text_full.png"></td></td>
5358
<td><b>Full class name of the component.</b><br>✅ Doesn't require to add existing component.<br>✅ Extensions can be created also.<br><br>To learn the class name of the component use <code>GetName</code> block.</td>
5459
</tr>
5560
</table>
@@ -142,8 +147,7 @@ So if you use Kodular, you will able to create all Kodular components, if you us
142147
<td>
143148
Set a property of a component by typing its property name. Can be known as a Setter property block.<br>
144149
It can be also used to set properties that only exists in Designer.
145-
Supported values are; "string", "boolean", "integer" and "float". For other values, you should use
146-
Any Component blocks.
150+
It works for common types. For other values, you should use Any Component blocks.
147151
</td>
148152
</tr>
149153
<!-- SET PROPERTIES -->
@@ -337,7 +341,7 @@ So if you use Kodular, you will able to create all Kodular components, if you us
337341
<img src="assets/blocks/method_lastusedid.png">
338342
</td>
339343
<td>
340-
Returns the last created component's ID by Create block.
344+
Returns the last component's ID.
341345
</td>
342346
</tr>
343347
<!-- USED IDS -->
@@ -394,6 +398,16 @@ So if you use Kodular, you will able to create all Kodular components, if you us
394398
Returns the version name of the extension.
395399
</td>
396400
</tr>
401+
<!-- ASYNC -->
402+
<tr>
403+
<td align="right">
404+
<img src="assets/blocks/setget_async.png"><br>
405+
<img src="assets/blocks/setget_async_2.png">
406+
</td>
407+
<td>
408+
Sets whether component creation should work asynchronously or synchronously.
409+
</td>
410+
</tr>
397411
<!-- SCHEMA CREATED -->
398412
<tr>
399413
<td align="right">
@@ -403,6 +417,15 @@ So if you use Kodular, you will able to create all Kodular components, if you us
403417
Raises after Schema has been created with Schema block.
404418
</td>
405419
</tr>
420+
<!-- COMPONENT CREATED -->
421+
<tr>
422+
<td align="right">
423+
<img src="assets/blocks/event_componentcreated.png">
424+
</td>
425+
<td>
426+
Raises after a component has been created using the Create block. It also will be raised for components that created with Schema.
427+
</td>
428+
</tr>
406429
</table>
407430

408431
## 🔨 Building

TemplateCreator/README.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# ⚡ Template Creator <small>for Dynamic Components AI2</small>
22
This sub-project allows you to create templates from App Inventor Project files automatically! So you don't need to write templates manually anymore!
33

4-
It includes two scripts. `TemplateCreate.py` does the main job which is generating the template, and `cli.py` is made for you to access the `TemplateCreate.py` easily.
4+
It includes two scripts. `TemplateCreate.py` does the main job which is generating the template, and `menu.py` is made for you to access the `TemplateCreate.py` easily.
55

66
> This script requires Python that needs to be installed, but if you have a solution that will work on everyone's computer without installing something, you can always create a Pull Request and a new tool for that :)
77
@@ -15,13 +15,12 @@ Then install the external modules by executing `pip install -r requirements.txt`
1515

1616
## 📦 Usage
1717

18-
* Insert your .aia file in this directory. And remember its name for later step.
18+
* Execute the `main.py` file.
19+
You can do that in your terminal by entering this folder and executing `python main.py`.
1920

20-
* Let's suppose your .aia file name is "HelloWorld.aia", then execute this command:<br>
21-
`python cli.py "HelloWorld.aia"`
21+
* A file dialog will open, just select the AIA/AIS file that you want to convert, then click "Open" button.
2222

23-
You can also type the screen name that you want to get template of it.<br>
24-
`python cli.py "HelloWorld.aia" --screen=Screen1`
23+
* If the project contains more than one screen, you will be asked to select which screen will be used with selection dialog.
2524

2625
If everything goes well, you will see the generated JSON file in this directory.
2726

TemplateCreator/TemplateCreate.py

Lines changed: 94 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,129 @@
1+
# --------------------------------------------
12
# TemplateCreator
2-
# by Yusuf Cihan
3+
#
4+
# Generates DynamicComponents-AI2 schemas by
5+
# parsing App Inventor project file automatically.
6+
#
7+
# - Yusuf Cihan
8+
#
9+
# MIT license.
10+
# --------------------------------------------
311

412
import ast
5-
from flatten_json import flatten, unflatten_list
613
import re
7-
8-
# Color converter
9-
def BuildColor(R : int, G : int, B : int, A : int):
10-
if A == 0:
11-
return 255
12-
else:
13-
return (B + (G + (R + (256 * A)) * 256) * 256) - 4294967296
14-
15-
def GenerateTemplate(SCM : dict, extensions : dict, legacy : bool = False):
14+
import json
15+
16+
EXTENSIONS = {}
17+
KEYS = []
18+
19+
# Color converter for AI2
20+
def BuildColor(code : str):
21+
A = 255
22+
val = str(code)[2:]
23+
if len(val) != 6:
24+
A = int(str(val)[0:2], 16)
25+
R = int(str(val)[2:4], 16)
26+
G = int(str(val)[4:6], 16)
27+
B = int(str(val)[6:], 16)
28+
return A << 24 | R << 16 | G << 8 | B
29+
30+
31+
def Rearrange(obj : dict):
32+
global EXTENSIONS
33+
global KEYS
34+
for key, value in obj.copy().items():
35+
if key in obj:
36+
# If key ends with Uuid or Version, ignore it.
37+
# Because DynamicComponents-AI2 extension's JSON templates doesn't need it.
38+
if key == "Uuid" or key == "$Version":
39+
del obj[key]
40+
# Rename the $Name according to the template structure.
41+
elif key == "$Name":
42+
obj["id"] = value
43+
del obj[key]
44+
# Rename the $Type according to the template structure.
45+
elif key == "$Type":
46+
# Use the extension's full class name if it is defined in the extensions dictionary.
47+
if value in EXTENSIONS:
48+
obj["type"] = EXTENSIONS[value]
49+
del obj[key]
50+
else:
51+
obj["type"] = value
52+
del obj[key]
53+
# Rename the $Components according to the template structure.
54+
elif key == "$Components":
55+
obj["components"] = value
56+
del obj[key]
57+
# Move the properties inside a "properties" object.
58+
# components/Button/Text --> components/Button/properties/Text
59+
else:
60+
# Copy of the value for editing purposes.
61+
v = value
62+
# Create a empty dictionary named properties if doesn't exists.
63+
if "properties" not in obj:
64+
obj["properties"] = {}
65+
# Convert value to color if it presents a color code.
66+
# Colors are started with &H.
67+
if str(value).startswith("&H") and (len(str(value)) == 8 or len(str(value)) == 10):
68+
v = BuildColor(v)
69+
# Parse the values to their type automatically.
70+
# If any error raised, use the value without changing the type.
71+
try:
72+
x = ast.literal_eval(v)
73+
if key == "Text":
74+
obj["properties"][key] = str(v)
75+
else:
76+
obj["properties"][key] = x
77+
except:
78+
obj["properties"][key] = v
79+
del obj[key]
80+
# If value contains template parameters, add them to the KEYS list.
81+
if value is str:
82+
for parameter in re.findall(r'(?<=(?<!\{)\{)[^{}]*(?=\}(?!\}))', value):
83+
if parameter not in KEYS:
84+
KEYS.append(parameter)
85+
return obj
86+
87+
88+
def GenerateTemplate(SCM : dict, extensions : dict):
1689
# Template that will be modified later.
1790
template = {
1891
# Use app name as template name.
1992
"name": SCM["Properties"]["AppName"],
20-
21-
# Current metadata version.
93+
# Current metadata version.
2294
# Needs to be 1, until a new type of metadata releases.
2395
"metadata-version": 1,
24-
2596
# Extension version that this template generated for.
2697
"extension_version": 5,
27-
2898
# Template author name.
2999
"author": "<your name>",
30-
31100
# List of AI2 distributions that will template work on.
32101
"platforms": SCM["authURL"],
33-
34102
# Contains used extensions in this template along with their class names.
35103
# Example:
36104
# {
37105
# "HelloWorld": "io.foo.HelloWorld"
38106
# }
39107
"extensions": extensions,
40-
41108
# Template parameters.
42109
# Will be generated automatically from SCM.
43110
"keys": [],
44-
45111
# Components that will be created.
46112
# Will be generated automatically from SCM.
47113
"components": []
48114
}
49-
50-
# Create a variable to store modified flatted JSON.
51-
flatten_json = {}
52-
53-
# Edit the flatten JSON.
54-
for key, value in flatten(SCM["Properties"], "/").items():
55-
k = str(key)
56-
val = value
57-
# If key ends with Uuid or Version, ignore it.
58-
# Because DynamicComponents-AI2 extension's JSON templates doesn't need it.
59-
if k.endswith("/Uuid") or k.endswith("/$Version"):
60-
continue
61-
# Else;
62-
else:
63-
# Replace the "$Components" with "components" according to the template structure.
64-
# $Components --> components
65-
k = k.replace("/$Components/", "/components/")
66-
67-
# Rename the $Name and $Type according to the template structure.
68-
# $Name --> id
69-
# $Type --> type
70-
if k[-5:] in ["$Name", "$Type"]:
71-
k = k.replace("/$Name", "/id").replace("/$Type", "/type")
72-
# Move the properties inside a "properties" object.
73-
# components/Button/Text --> components/Button/properties/Text
74-
else:
75-
path = k.split("/")
76-
path.insert(-1, "properties")
77-
k = "/".join(path)
78-
79-
# Check if value contains template parameter(s).
80-
# Parameters are defined with curly brackets.
81-
# {text}, {age}, {color}
82-
for parameter in re.findall(r'(?<=(?<!\{)\{)[^{}]*(?=\}(?!\}))', str(value) + " " + k):
83-
if parameter not in template["keys"]:
84-
template["keys"].append(parameter)
85-
86-
# Try to convert the value automatically.
87-
# So if value is "True" or "False", then it will be converted to the bool and so on.
88-
try:
89-
val = ast.literal_eval(value)
90-
except:
91-
pass
92-
93-
# An exception for the color converting.
94-
if str(val).startswith("&H"):
95-
val = str(val)[2:]
96-
if len(val) == 6:
97-
alpha = int("FF", 16)
98-
else:
99-
alpha = int(str(val)[0:2], 16)
100-
val = BuildColor(int(str(val)[2:4], 16), int(str(val)[4:6], 16), int(str(val)[6:], 16), alpha)
101-
102-
# If the component name is in the extensions list,
103-
# then use its full internal name as it is an external package that
104-
# doesn't exists in the App Inventor sources.
105-
if k.endswith("/type") and (val in extensions):
106-
val = extensions[val]
107-
108-
# Add the value and key to the modified flatten dictionary.
109-
flatten_json[k] = val
110-
111-
# Now, unflat the modified flatten dictionary.
112-
# Save the output to the template.
113-
template["components"] = unflatten_list(flatten_json, "/")["$Components"]
114-
115-
# Remove DynamicComponent instances from template, because it is not needed.
115+
global EXTENSIONS
116+
global KEYS
117+
# Save extensions in the list.
118+
EXTENSIONS = extensions
119+
# Convert SCM data to Dynamic Components schema data.
120+
template["components"] = json.loads(json.dumps(SCM), object_hook = Rearrange)["properties"]["Properties"]["components"]
121+
# Save found keys in the template.
122+
template["keys"] = KEYS
123+
# Remove DynamicComponent instances from template, because they are not needed.
116124
for component in template["components"].copy():
117125
if component["type"] == "DynamicComponents":
118126
if component in template["components"]:
119127
template["components"].remove(component)
120128

121-
# Return the template.
122129
return template

TemplateCreator/cli.py

Lines changed: 0 additions & 48 deletions
This file was deleted.

0 commit comments

Comments
 (0)