apkpy 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,68 @@
1
+ Metadata-Version: 2.4
2
+ Name: apkpy
3
+ Version: 0.1.0
4
+ Summary: A simple framework to build Android apps using Python and CSS-like styling
5
+ Home-page: https://github.com/teu-usuario/apkpy
6
+ Author: Teu Nome
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.6
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: click
13
+ Dynamic: author
14
+ Dynamic: classifier
15
+ Dynamic: description
16
+ Dynamic: description-content-type
17
+ Dynamic: home-page
18
+ Dynamic: requires-dist
19
+ Dynamic: requires-python
20
+ Dynamic: summary
21
+
22
+ # APKPy 🚀
23
+
24
+ A lightweight Python framework to build Android apps with a simple syntax and CSS-like styling.
25
+
26
+ APKPy allows you to create mobile interfaces using Python functions and style them with a CSS-inspired syntax, providing a real-time preview on Windows before compiling to Android.
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ pip install apkpy
32
+ Quick Start
33
+ Create a file named writehere.py:
34
+
35
+ Python
36
+ from apkpy_lib.ui import button, input_field, run
37
+
38
+ # Define your styles
39
+ style = """
40
+ main_button {
41
+ background-color: blue;
42
+ color: white;
43
+ }
44
+ """
45
+
46
+ # Build your UI
47
+ input_field(placeholder="Enter your name")
48
+ button("Click Me", id="main_button")
49
+
50
+ # Run the Windows preview
51
+ run()
52
+ How to Run
53
+ To see your app preview on Windows, simply run:
54
+
55
+ Bash
56
+ python writehere.py
57
+ To build your Android project (APK structure):
58
+
59
+ Bash
60
+ apkpy build
61
+ Features
62
+ Python-based UI: No need to learn complex XML.
63
+
64
+ CSS Styling: Easy styling with a familiar syntax.
65
+
66
+ Instant Preview: See how your app looks on Windows immediately.
67
+
68
+ Android Ready: Transpiles your Python code into native Java code.
@@ -0,0 +1,13 @@
1
+ apkpy_lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ apkpy_lib/cli.py,sha256=d-RgUZ9r10TA3yV18ofpeosLwiVkxTI23fnvGRJdQ0k,2543
3
+ apkpy_lib/ui.py,sha256=Qle_6GPrNvUPDW0EfCEB1rDTMFhVlqXqcSq9RbLFxVc,2443
4
+ apkpy_lib/core/compiler.py,sha256=X2WwhiRt0wbcSP0A42xAqicPNKNxFZlTJTpFMBhHFyw,2707
5
+ apkpy_lib/core/style_parser.py,sha256=ElQJcP0LDBqAqLcPQM43RugAXVNgJze7KPeYpKWL-LY,480
6
+ apkpy_lib/core/__pycache__/compiler.cpython-313.pyc,sha256=A6MHfx0lCcn4ALWsxWC0haXS3h5XMXT6GdeuLMmRNhE,1929
7
+ apkpy_lib/templates/android_project/app/src/main/AndroidManifest.xml,sha256=0_fVGYg_9_VMcbnwDS46-l_7Cqxnp5v-HBvsjrXg7aA,663
8
+ apkpy_lib/templates/android_project/app/src/main/java/com/exemplo/MainActivity.java,sha256=QQ1u3BQpYKYjpIn4chG5cHSH9nbGSXey3JTmTL98HIM,663
9
+ apkpy-0.1.0.dist-info/METADATA,sha256=7ndFK9Vp0uxOZJ7EYA_pFs9PT8h9kV3_Cmofrm7IOMQ,1737
10
+ apkpy-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
11
+ apkpy-0.1.0.dist-info/entry_points.txt,sha256=UgHrx1XVZPGs7VZeiafagRDTL1Z5z175mt9WjMf_7uo,45
12
+ apkpy-0.1.0.dist-info/top_level.txt,sha256=Z4vuhNYR6Ybxm2qoF-387wz_wqmtPS-uJ2a78p33Ae0,10
13
+ apkpy-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ apkpy = apkpy_lib.cli:main
@@ -0,0 +1 @@
1
+ apkpy_lib
apkpy_lib/__init__.py ADDED
File without changes
apkpy_lib/cli.py ADDED
@@ -0,0 +1,74 @@
1
+ import click
2
+ import os
3
+ import zipfile
4
+ import shutil
5
+
6
+ # Adicione esta linha aqui para o editor saber de onde vem a função!
7
+ from .core.compiler import translate_python_to_java
8
+
9
+ @click.group()
10
+ def main():
11
+ """APKPy: Transforme Python em Projetos Android (APK)"""
12
+ pass
13
+
14
+ # ... resto do código (start e build) ...
15
+
16
+ @main.command()
17
+ @click.argument('name')
18
+ def start(name):
19
+ """Cria um novo projeto: apkpy start meu_projeto"""
20
+ click.echo(f"Criando o projeto: {name}...")
21
+
22
+ # Define os caminhos
23
+ current_dir = os.getcwd()
24
+ project_path = os.path.join(current_dir, name)
25
+
26
+ if not os.path.exists(project_path):
27
+ os.makedirs(project_path)
28
+ # Cria o arquivo de entrada do usuário
29
+ with open(os.path.join(project_path, "writehere.py"), "w") as f:
30
+ f.write("# Escreva seu app aqui\nbutton('Clique aqui')")
31
+ click.echo(f"Sucesso! Entre na pasta {name} e edite o writehere.py")
32
+ else:
33
+ click.echo("Erro: Essa pasta já existe.")
34
+
35
+ @main.command()
36
+ def build():
37
+ """Compila o código e gera o ZIP pronto para o Android Studio"""
38
+ file_path = "writehere.py"
39
+ template_path = os.path.join(os.path.dirname(__file__), "templates/android_project")
40
+ output_zip = "projeto_final.zip"
41
+
42
+ if not os.path.exists(file_path):
43
+ click.echo("Erro: writehere.py não encontrado.")
44
+ return
45
+
46
+ # 1. Gerar o código Java
47
+ with open(file_path, "r", encoding="utf-8") as f:
48
+ python_code = f.read()
49
+
50
+ java_code = translate_python_to_java(python_code)
51
+
52
+ # 2. Criar uma pasta temporária para montar o projeto
53
+ build_dir = "build_temp"
54
+ if os.path.exists(build_dir): shutil.rmtree(build_dir)
55
+ shutil.copytree(template_path, build_dir)
56
+
57
+ # 3. Injetar o código na MainActivity.java
58
+ main_activity_path = os.path.join(build_dir, "app/src/main/java/com/exemplo/MainActivity.java")
59
+
60
+ with open(main_activity_path, "r") as f:
61
+ content = f.read()
62
+
63
+ # Substitui o marcador pelo código gerado pelo seu compiler.py
64
+ new_content = content.replace("// INJECT_CODE_HERE", java_code)
65
+
66
+ with open(main_activity_path, "w") as f:
67
+ f.write(new_content)
68
+
69
+ # 4. Transformar em ZIP
70
+ shutil.make_archive("meu_app_android", 'zip', build_dir)
71
+ shutil.rmtree(build_dir) # Limpa a pasta temporária
72
+
73
+ click.echo("🚀 Sucesso! Arquivo 'meu_app_android.zip' gerado.")
74
+ click.echo("Agora é só extrair e abrir no Android Studio.")
@@ -0,0 +1,68 @@
1
+ import ast
2
+ # No topo do compiler.py adicione:
3
+ from .style_parser import parse_css
4
+
5
+ class JavaTranspiler(ast.NodeVisitor):
6
+ def __init__(self, styles=None):
7
+ self.java_lines = []
8
+ self.counter = 0
9
+ self.styles = styles or {} # Aqui guardamos os estilos
10
+
11
+ def visit_Call(self, node):
12
+ if isinstance(node.func, ast.Name) and node.func.id == 'button':
13
+ self.counter += 1
14
+
15
+ # Tenta pegar o ID do botão (ex: id="botão1")
16
+ button_id = None
17
+ for kw in node.keywords:
18
+ if kw.arg == 'id':
19
+ button_id = kw.value.value
20
+
21
+ label = node.args[0].value
22
+ var_name = f"btn{self.counter}"
23
+
24
+ java_code = f'\n Button {var_name} = new Button(this);'
25
+ java_code += f'\n {var_name}.setText("{label}");'
26
+
27
+ # APLICA O CSS NO JAVA
28
+ if button_id in self.styles:
29
+ s = self.styles[button_id]
30
+ if 'background-color' in s:
31
+ color = s['background-color'].replace('blue', 'Color.BLUE').replace('red', 'Color.RED')
32
+ java_code += f'\n {var_name}.setBackgroundColor({color});'
33
+ if 'color' in s:
34
+ java_code += f'\n {var_name}.setTextColor(Color.WHITE);'
35
+
36
+ java_code += f'\n layout.addView({var_name});'
37
+ self.java_lines.append(java_code)
38
+
39
+ def translate_python_to_java(python_code):
40
+ try:
41
+ # 1. Extrair o estilo (CSS) se ele existir dentro de triplas aspas
42
+ # Procuramos por algo entre style = """ ... """
43
+ import re
44
+ style_match = re.search(r'style\s*=\s*"""(.*?)"""', python_code, re.DOTALL)
45
+
46
+ styles = {}
47
+ clean_python_code = python_code
48
+
49
+ if style_match:
50
+ css_content = style_match.group(1)
51
+ styles = parse_css(css_content) # Usa o teu style_parser!
52
+ # Removemos o bloco de estilo para o AST não se confundir
53
+ clean_python_code = python_code.replace(style_match.group(0), "")
54
+
55
+ # 2. Transforma o código Python limpo em árvore (AST)
56
+ tree = ast.parse(clean_python_code)
57
+
58
+ # 3. Passamos os estilos encontrados para o Transpiler
59
+ transpiler = JavaTranspiler(styles=styles)
60
+ transpiler.visit(tree)
61
+
62
+ if not transpiler.java_lines:
63
+ return " // Nenhum componente detectado"
64
+
65
+ return "\n".join(transpiler.java_lines)
66
+
67
+ except Exception as e:
68
+ return f" // Erro na tradução: {str(e)}"
@@ -0,0 +1,15 @@
1
+ import re
2
+
3
+ def parse_css(css_text):
4
+ styles = {}
5
+ # Procura blocos tipo: nome { propriedades }
6
+ blocks = re.findall(r'(\w+)\s*{(.*?)}', css_text, re.DOTALL)
7
+
8
+ for name, content in blocks:
9
+ props = {}
10
+ # Procura cada linha tipo: color: red;
11
+ lines = re.findall(r'([\w-]+)\s*:\s*(.*?);', content)
12
+ for key, value in lines:
13
+ props[key.strip()] = value.strip()
14
+ styles[name.strip()] = props
15
+ return styles
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <manifest xmlns:android="http://schemas.microsoft.com/apk/res/android">
3
+ <application
4
+ android:allowBackup="true"
5
+ android:icon="@mipmap/ic_launcher"
6
+ android:label="Meu App ApkPy"
7
+ android:theme="@style/Theme.AppCompat.Light">
8
+ <activity
9
+ android:name=".MainActivity"
10
+ android:exported="true">
11
+ <intent-filter>
12
+ <action android:name="android.intent.action.MAIN" />
13
+ <category android:name="android.intent.category.LAUNCHER" />
14
+ </intent-filter>
15
+ </activity>
16
+ </application>
17
+ </manifest>
@@ -0,0 +1,22 @@
1
+ package com.exemplo;
2
+
3
+ import android.os.Bundle;
4
+ import android.widget.LinearLayout;
5
+ import android.widget.Button;
6
+ import androidx.appcompat.app.AppCompatActivity;
7
+
8
+ public class MainActivity extends AppCompatActivity {
9
+ @Override
10
+ protected void onCreate(Bundle savedInstanceState) {
11
+ super.setTheme(androidx.appcompat.R.style.Theme_AppCompat_Light);
12
+ super.onCreate(savedInstanceState);
13
+
14
+ LinearLayout layout = new LinearLayout(this);
15
+ layout.setOrientation(LinearLayout.VERTICAL);
16
+
17
+ // O Python vai colocar os botões aqui:
18
+ // INJECT_CODE_HERE
19
+
20
+ setContentView(layout);
21
+ }
22
+ }
apkpy_lib/ui.py ADDED
@@ -0,0 +1,78 @@
1
+ import tkinter as tk
2
+ import re
3
+ import __main__
4
+
5
+ # Dicionário global para guardar os estilos processados
6
+ global_styles = {}
7
+
8
+ def parse_css_internal(css_text):
9
+ """Lógica para transformar o texto CSS num dicionário Python"""
10
+ styles = {}
11
+ # Procura blocos: nome { ... }
12
+ blocks = re.findall(r'(\w+)\s*{(.*?)}', css_text, re.DOTALL)
13
+ for name, content in blocks:
14
+ props = {}
15
+ # Procura propriedades: color: red;
16
+ lines = re.findall(r'([\w-]+)\s*:\s*(.*?);', content)
17
+ for key, value in lines:
18
+ props[key.strip()] = value.strip()
19
+ styles[name.strip()] = props
20
+ return styles
21
+
22
+ def load_styles_now():
23
+ """Procura a variável 'style' no ficheiro writehere.py e carrega-a"""
24
+ global global_styles
25
+ if hasattr(__main__, 'style'):
26
+ global_styles = parse_css_internal(__main__.style)
27
+
28
+ class AppPreview:
29
+ def __init__(self):
30
+ self.root = tk.Tk()
31
+ self.root.title("APKPy Preview - Windows")
32
+ self.root.geometry("360x640")
33
+
34
+ # Frame principal que simula o ecrã do telemóvel
35
+ self.layout = tk.Frame(self.root)
36
+ self.layout.pack(fill="both", expand=True, padx=20, pady=20)
37
+
38
+ def add_button(self, text, command=None, bg=None, fg=None):
39
+ # Se não houver cor definida no CSS, usa o padrão do sistema
40
+ tk_bg = bg if bg else "SystemButtonFace"
41
+ tk_fg = fg if fg else "black"
42
+
43
+ btn = tk.Button(
44
+ self.layout,
45
+ text=text,
46
+ font=("Arial", 12),
47
+ command=command,
48
+ bg=tk_bg,
49
+ fg=tk_fg,
50
+ relief="raised",
51
+ pady=5
52
+ )
53
+ btn.pack(pady=10, fill="x")
54
+
55
+ def run(self):
56
+ self.root.mainloop()
57
+
58
+ # Instância única da interface
59
+ _app = AppPreview()
60
+
61
+ def button(text, id=None, command=None):
62
+ """Função chamada no writehere.py"""
63
+ # Força o carregamento do CSS se ele ainda não foi lido
64
+ if not global_styles:
65
+ load_styles_now()
66
+
67
+ # Busca o estilo pelo ID (ex: botao_azul)
68
+ style = global_styles.get(id, {})
69
+ bg = style.get('background-color')
70
+ fg = style.get('color')
71
+
72
+ _app.add_button(text, command, bg, fg)
73
+
74
+ def run():
75
+ """Inicia a janela do Windows"""
76
+ # Garante uma última verificação dos estilos antes de abrir
77
+ load_styles_now()
78
+ _app.run()