Multiskript Werkzeug

Du hast eine Idee für ein Projekt?
Antworten
melatroid
User
Beiträge: 7
Registriert: Samstag 31. Mai 2025, 11:43

Hallo Leute!
Bin neu hier und wollte euch ein Tool vorstellen, das ich am entwickeln bin.
Das Tool wurde entwickelt, da ich unterschiedliche Einstellungen bei Skripten habe und es leid bin
alles händig zu machen. AutoPyToExe war anfänglich hilfreich aber lästig :twisted: .

Das Programm ist fast selbsterklärend, für den der mit Pyinstaller oder AutoPytoExe bisher gearbeitet hat.
Ihr braucht eine Python 3 Installation und Pyinstaller:
- pip install pyinstaller

Version 1.00 Blog
https://autopyplusplus.wordpress.com/

:: Download Version 1.00 :: Ger/Eng
https://drive.google.com/drive/folders/ ... drive_link

Hoffe damit kann jemand, außer meiner Wenigkeit was anfangen =)

Bild
Bild

Grüße Melatroid
Benutzeravatar
sparrow
User
Beiträge: 4525
Registriert: Freitag 17. April 2009, 10:28

Was genau soll der Vorteil gegenüber auto-py-to-exe sein, das etabliert ist und dessen Quelltext verfügbar ist?
melatroid
User
Beiträge: 7
Registriert: Samstag 31. Mai 2025, 11:43

1.) Du kannst in auto-py-to-exe nur ein Script bauen, mit diesem Tool vermutlich hunderte.

2.) Habe vor es im richtigen Moment auf Github zu laden, der ist noch nicht gekommen.

Für die ungläubigen:

Bild
Benutzeravatar
sparrow
User
Beiträge: 4525
Registriert: Freitag 17. April 2009, 10:28

Also ist der Vorteil, dass du für die GUI eine Vewaltungs-GUI gebaut hast?
Das ist eine ernstgemeinte Frage: Was genau ist der Vorteil?

Das mit den Ungläubigen verstehe ich nicht.
melatroid
User
Beiträge: 7
Registriert: Samstag 31. Mai 2025, 11:43

Es basiert ja auf pyinstaller, das ist keine gui, auto-py-to-exe basiert selbst darauf.
Du kannst bei besagtem Programm kein Templates erstellen, die irgendwelche hinterlegten Einstellungen speichern.
Der Vorteil zeigt sich folgendermaßen.:
Du benötigst für jede zu kompilierende .py ein separates Gui Fenster, das unübersichtlich zu bedienen ist wenn du mehrere davon offen hast.
Dazu kommen viel mehr Klicks pro kompiliertem Programm.
Weiterer Nachteil ist das du nach jedem öffnen von -apte- alle Einstellungen von Hand setzen musst ohne diese speichern oder wiederherstellen zu können.

Wichtigster Punkt.:
Ich schreibe Interfaces die andere Module importieren und deren Methoden nutzen, das bedeutet:
Es ist nötig eine einmal geänderte py. File, auf allen Modulen neu zu kompilieren.
Dafür ist -apte- nicht gedacht, aber weiterhin notwendig, wenn es um einzelne Exporte geht, ohne ein neuen Build eines System zu stellen.

zu den ungläubigen:
Wenn ich vor hätte dir eine Malware unterzujubeln, würde ich dir ein Katzenbild zeigen ;-)
Ich hingegen suche Tester.
Benutzeravatar
sparrow
User
Beiträge: 4525
Registriert: Freitag 17. April 2009, 10:28

Vielleicht gibt es den Anwendungsfall, aber ich glaube die meisten Menschen bauen für ihr Projekt ein .spec File und verfüttern das dann ab pyinstaller.

Das mit dem Quellcode hat nichts mit Malware zu tun. Es ist nur sehr ungewöhnlich Programme in einem windigen Wordpress-Blog + Download von googledrive zu verteilen.
Und klar gibt es harte Abzüge in der A-Note wenn der Quelltext nicht verfügbar ist, wenn es vergleichbare Lösungen gibt, bei denen das nicht so ist.
melatroid
User
Beiträge: 7
Registriert: Samstag 31. Mai 2025, 11:43

Ich habe gar nicht vor, dich um dein .spec zu bringen.

Allerdings wirst du mir recht geben müssen, das eine ungetestete Software auf Git nicht gut kommt.
Kann dir den Sourcecode per pn schicken
Benutzeravatar
__blackjack__
User
Beiträge: 13995
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Warum sollte „ungetestete Software auf Git nicht gut kommen“? Release early, release often, also nicht nur ungetestet, sondern auch unfertig.

Gerade das Du so vehement abstreitest, das Du da was komisches im Schilde führst, macht das ganze ja noch merkwürdiger und verdächtiger. Merkator? Bist Du das? 😈
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
melatroid
User
Beiträge: 7
Registriert: Samstag 31. Mai 2025, 11:43

Ich kenne ihn nicht.

aber zur Verschwörung =D Stimmt... wenn ich fertig bin werdet ihr es nutzen...

Aber ich merke gerade das jeder Hansgrämer eigene Pakete ablegen kann: https://pypi.org/search/
von daher ist mein Gdrive nicht unbedingt seriöser als pip =D
nezzcarth
User
Beiträge: 1748
Registriert: Samstag 16. April 2011, 12:47

Eben darum installiert man auch nicht einfach per pip irgendwelche unbekannten Pakete, ohne sich vorher darüber zu erkundigen und auf Tippfehler zu achten, genauso, wie man unter Windows eben keine Exe-Dateien aus unklaren Quellen annimmt oder gar ausführt. Simple Grundlagen der IT-Security.
Benutzeravatar
sparrow
User
Beiträge: 4525
Registriert: Freitag 17. April 2009, 10:28

melatroid hat geschrieben: Samstag 31. Mai 2025, 16:52Ich habe gar nicht vor, dich um dein .spec zu bringen.
Du missverstehst - ich nutze keine Tools um .exe-Dateien zu erstellen.
Und ich dachte, das zeigen deiner Idee dient dazu, Kommentare zu sammeln?

Der übliche Weg Python-Programme zu verteilen ist das nicht. Aber hier tauchen immer mal wieder User auf, die denken, dass die Scripte durch pyinstaller kompiliert werden und das im Bezug auf Sicherheit oder Laufzeit Vorteile bringt. Diese Annahme ist leider falsch und entstammt dem Gedanken, dass nur .exe-Dateien richtige Programme sind.

Welches Problem möchtest du denn mit dem Tool tatsächlich lösen?
Dass man dort jedes seiner Projekte einträgt und bei Bedarf mit in das Archiv packt? Warum liegen diese Informationen nicht _im_ Projekt? Denn dort sollten sie ja liegen um im Zweifelsfall ins Repository zu wandern.

Die Eigenschaften, die du da für jedes Projekt pflegst, ist nun einmal das, was man normalerweise in das spec-File schreibt. Dafür ist es da. Abgeleitet aus dem Screenshots vermute ich, dass du da einfach Parameter für den Aufruf von pyinstaller zusammenstückelst. Und das mit absoluten Pfaden, die dem Projekt gar nicht helfen weil sie nicht auf eine andere Umgebung übertragbar sind.
In meinen Augen macht es sehr viel mehr Sinn, relative Pfade und das .spec-File zu verwenden. Damit würde das Tool zumindest das Toolchaining verwenden, das etabliert ist, und eine Art Editor für die Specs sein.

Aber ich kann mir noch immer nicht vorstellen, warum ich alle meine Projekte auf einmal in Archive packen soll.
Kannst du mir erklären, wann das vorkommt? Also welches Problem durch dieses zentrale Feature gelöst werden soll?

Und persönlich macht mir etwas Angst, dass deine Scripte offensichtlich in einer xampp Unterverzeichnis wohnen.
melatroid hat geschrieben: Samstag 31. Mai 2025, 16:52Allerdings wirst du mir recht geben müssen, das eine ungetestete Software auf Git nicht gut kommt.
Sicher nicht.
__blackjack__ hat geschrieben: Samstag 31. Mai 2025, 18:43 Merkator? Bist Du das? 😈
Ich musste schmunzeln :)
Benutzeravatar
DeaD_EyE
User
Beiträge: 1217
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Hier ist dein Quellcode:

Code: Alles auswählen

# Source Generated with Decompyle++
# File: autoPy++_NEO.pyc (Python 3.10)

import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
from concurrent.futures import ThreadPoolExecutor, as_completed
import subprocess
import os
import json
import ast
import uuid
DATEI = 'myProject.apyscript'
CONFIG_DATEI = 'config.json'

class Projekt:
    
    def __init__(self, skript, name, icon, add_data, hidden_imports, version, output, onefile, console, upx, debug, options, compile_selected, clean, key, strip, noupx, runtime_hook, splash, spec_file = ('', '', '', '', '', '', True, True, False, False, '', True, False, '', False, False, '', '', '')):
        self.skript = skript
        if not name:
            pass
        self.name = os.path.splitext(os.path.basename(skript))[0]
        self.icon = icon
        self.add_data = add_data
        self.hidden_imports = hidden_imports
        self.version = version
        self.output = output
        self.onefile = onefile
        self.console = console
        self.upx = upx
        self.debug = debug
        self.options = options
        self.compile_selected = compile_selected
        self.clean = clean
        self.key = key
        self.strip = strip
        self.noupx = noupx
        self.runtime_hook = runtime_hook
        self.splash = splash
        self.spec_file = spec_file

    
    def to_dict(self):
        return self.__dict__

    
    def from_dict(data):
        pass
    # WARNING: Decompyle incomplete

    from_dict = staticmethod(from_dict)


class CreateToolTip:
    
    def __init__(self, widget, text):
        self.widget = widget
        self.text = text
        self.tooltip_window = None
        self.show_id = None
        self.widget.bind('<Enter>', self.schedule_tooltip)
        self.widget.bind('<Leave>', self.hide_tooltip)

    
    def schedule_tooltip(self, event = (None,)):
        if self.show_id is None:
            self.show_id = self.widget.after(500, self.show_tooltip)
            return None

    
    def show_tooltip(self):
        if self.show_id is None:
            return None
        x = None.widget.winfo_rootx() + 20
        y = self.widget.winfo_rooty() + 20
        self.tooltip_window = tw = tk.Toplevel(self.widget)
        tw.wm_overrideredirect(True)
        tw.wm_geometry(f'''+{x}+{y}''')
        label = tk.Label(tw, self.text, '#ffffe0', 'solid', 1, ('Segoe UI', 10), **('text', 'background', 'relief', 'borderwidth', 'font'))
        label.pack()
        self.show_id = None

    
    def hide_tooltip(self, event = (None,)):
        if self.show_id:
            self.widget.after_cancel(self.show_id)
            self.show_id = None
        if self.tooltip_window:
            self.tooltip_window.destroy()
            self.tooltip_window = None
            return None



class AutoPyToExeGUI:
    
    def __init__(self, master):
        self.master = master
        master.title('AutoPy++')
        master.geometry('800x600')
        master.resizable(True, True)
        master.iconbitmap('autoPy++.ico')
    # WARNING: Decompyle incomplete

    
    def load_config(self):
        pass
    # WARNING: Decompyle incomplete

    
    def save_config(self):
        self.config['language'] = self.current_language
        self.config['dark_mode'] = self.is_dark_mode
    # WARNING: Decompyle incomplete

    
    def toggle_design(self):
        self.master.config('wait', **('cursor',))
        self.master.update_idletasks()
        if self.is_dark_mode:
            self.set_light_mode()
            self.is_dark_mode = False
        else:
            self.set_dark_mode()
            self.is_dark_mode = True
        self.save_config()
        self.master.config('', **('cursor',))
        self.master.update_idletasks()

    
    def set_dark_mode(self):
        self.style.theme_use('clam')
        self.style.configure('TFrame', '#2e2e2e', **('background',))
        self.style.configure('TLabel', 'black', 'green2', **('background', 'foreground'))
        self.style.configure('TButton', 'black', 'green2', **('background', 'foreground'))
        self.style.configure('Treeview', 'black', 'green2', '#383838', **('background', 'foreground', 'fieldbackground'))
        self.style.configure('Treeview.Heading', 'black', 'green2', **('background', 'foreground'))
        self.master.configure('#2e2e2e', **('background',))

    
    def set_light_mode(self):
        self.style.theme_use('clam')
        self.style.configure('TFrame', '#f0f0f0', **('background',))
        self.style.configure('TLabel', '#f0f0f0', '#000000', **('background', 'foreground'))
        self.style.configure('TButton', '#e0e0e0', '#000000', **('background', 'foreground'))
        self.style.configure('Treeview', '#ffffff', '#000000', '#ffffff', **('background', 'foreground', 'fieldbackground'))
        self.style.configure('Treeview.Heading', '#e0e0e0', '#000000', **('background', 'foreground'))
        self.master.configure('#f0f0f0', **('background',))
        self.style.configure('Accent.TButton', '#0078D7', 'white', ('Segoe UI', 10, 'bold'), **('background', 'foreground', 'font'))

    
    def change_language(self, event):
        self.current_language = self.language_var.get()
        self.texts = self.sprachen[self.current_language]
        self.save_config()
        self.master.config('wait', **('cursor',))
        self.master.update_idletasks()
        self.master.title(self.texts['title'])
        self.tree.heading('Kompilieren', self.texts['compile_col'], **('text',))
        self.tree.heading('Name', self.texts['name_col'], **('text',))
        self.tree.heading('Skript', self.texts['script_col'], **('text',))
        button_mapping = {
            self.texts['compile_all_btn']: 'compile_all_btn',
            self.texts['load_btn']: 'load_btn',
            self.texts['save_btn']: 'save_btn',
            self.texts['delete_btn']: 'delete_btn',
            self.texts['edit_btn']: 'edit_btn',
            self.texts['add_btn']: 'add_btn' }
        for widget in self.main_frame.winfo_children():
            if isinstance(widget, ttk.Frame):
                for btn in widget.winfo_children():
                    if isinstance(btn, ttk.Button):
                        current_text = btn.cget('text')
                        if current_text in self.sprachen['de'].values() or current_text in self.sprachen['en'].values():
                            for key, value in button_mapping.items():
                                if current_text == self.sprachen['de'][value] or current_text == self.sprachen['en'][value]:
                                    btn.configure(self.texts[value], **('text',))
                                    if hasattr(btn, 'tooltip'):
                                        btn.tooltip.text = self.texts[f'''tooltip_{value}''']
                            continue
                        if current_text == 'Design' and hasattr(btn, 'tooltip'):
                            btn.tooltip.text = self.texts['tooltip_toggle_design']
                        continue
                    if isinstance(btn, ttk.Label) and btn.cget('text') in (self.sprachen['de']['language_label'], self.sprachen['en']['language_label']):
                        btn.configure(self.texts['language_label'], **('text',))
        self.status_var.set(self.texts['status_ready'])
        self.aktualisiere_treeview()
        self.master.config('', **('cursor',))
        self.master.update_idletasks()

    
    def handle_tree_click(self, event):
        col = self.tree.identify_column(event.x)
        if col == '#1':
            item = self.tree.identify_row(event.y)
            if item:
                idx = self.tree.index(item)
                projekt = self.projekte[idx]
                projekt.compile_selected = not (projekt.compile_selected)
                self.aktualisiere_treeview()
                if projekt.compile_selected:
                    pass
                elif self.current_language == 'de':
                    pass
                elif projekt.compile_selected:
                    pass
                
                state = 'disabled'
                self.status_var.set(self.texts['status_toggle_compile'].format(projekt.name, state, **('name', 'state')))
                return None
            return None

    
    def projekt_hinzufuegen(self):
        skript = filedialog.askopenfilename([
            ('Python-Dateien', '*.py'),
            ('Spec-Dateien', '*.spec')], **('filetypes',))
        if not skript:
            return None
        if None.endswith('.spec'):
            projekt = self.parse_spec_file(skript)
            if projekt:
                self.projekte.append(projekt)
                self.aktualisiere_treeview()
                self.status_var.set(self.texts['status_project_added'].format(projekt.name, **('name',)))
                return None
            return None
        p = None(skript, True, **('compile_selected',))
        if self.bearbeite_projekt(p):
            self.projekte.append(p)
            self.aktualisiere_treeview()
            self.status_var.set(self.texts['status_project_added'].format(p.name, **('name',)))
            return None

    
    def projekt_bearbeiten(self):
        selected = self.tree.selection()
        if not selected:
            messagebox.showwarning('Fehler', self.texts['error_no_project'])
            return None
        idx = None.tree.index(selected[0])
        p = self.projekte[idx]
        if self.bearbeite_projekt(p):
            self.aktualisiere_treeview()
            self.status_var.set(self.texts['status_project_edited'].format(p.name, **('name',)))
            return None

    
    def projekt_loeschen(self):
        selected = self.tree.selection()
        if not selected:
            messagebox.showwarning('Fehler', self.texts['error_no_project'])
            return None
        idx = None.tree.index(selected[0])
        projekt_name = self.projekte[idx].name
        self.projekte.pop(idx)
        self.aktualisiere_treeview()
        self.status_var.set(self.texts['status_project_deleted'].format(projekt_name, **('name',)))

    
    def parse_spec_file(self, spec_file):
        '''Parst eine .spec-Datei und erstellt ein Projekt-Objekt.'''
        pass
    # WARNING: Decompyle incomplete

    
    def generate_spec_file(self, projekt):
        '''Generiert eine .spec-Datei basierend auf einem Projekt.'''
        spec_content = []["\n        # -*- mode: python ; coding: utf-8 -*-\n        from PyInstaller.utils.hooks import collect_data_files\n\n        block_cipher = None\n\n        a = Analysis(['"][f'''{projekt.skript}''']["'],\n                     pathex=[],\n                     binaries=[],\n                     datas="][f'''{(lambda .0: [ (d.split(':')[0], d.split(':')[1]) for d in .0 ])(projekt.add_data.split(';')) if projekt.add_data else []}'''][',\n                     hiddenimports='][f'''{projekt.hidden_imports.split(',') if projekt.hidden_imports else []}'''][',\n                     hookspath=[],\n                     runtime_hooks='][f'''{[
            '{projekt.runtime_hook}'] if projekt.runtime_hook else []}'''][',\n                     excludes=[],\n                     win_no_prefer_redirects=False,\n                     win_private_assemblies=False,\n                     cipher=block_cipher,\n                     noarchive=False)\n\n        pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)\n\n        exe = EXE(pyz,\n                  a.scripts,\n                  '][f'''{'[]' if projekt.onefile else 'a.binaries + a.zip + a.datas'}'''][',\n                  exclude_binaries='][f'''{not (projekt.onefile)}'''][",\n                  name='"][f'''{projekt.name}''']["',\n                  debug="][f'''{projekt.debug}'''][',\n                  bootloader_ignore_signals=False,\n                  strip='][f'''{projekt.strip}'''][',\n                  upx='][f'''{projekt.upx}'''][',\n                  console='][f'''{projekt.console}'''][",\n                  disable_windowed_traceback=False,\n                  target_arch=None,\n                  codesign_identity=None,\n                  entitlements_file=None,\n                  icon='"][f'''{projekt.icon}''']["'"][f'''{f''', key="{projekt.key}"''' if projekt.key else ''}'''][f'''{f''', splash="{projekt.splash}"''' if projekt.splash else ''}'''][')\n\n        coll = COLLECT(exe,\n                       a.binaries,\n                       a.zipfiles,\n                       a.datas,\n                       strip='][f'''{projekt.strip}'''][',\n                       upx='][f'''{projekt.upx}'''][",\n                       upx_exclude=[],\n                       name='"][f'''{projekt.name}''']["'"][f'''{f''', distpath="{projekt.output}"''' if projekt.output else ''}''']([]["\n        # -*- mode: python ; coding: utf-8 -*-\n        from PyInstaller.utils.hooks import collect_data_files\n\n        block_cipher = None\n\n        a = Analysis(['"][f'''{projekt.skript}''']["'],\n                     pathex=[],\n                     binaries=[],\n                     datas="][f'''{(lambda .0: [ (d.split(':')[0], d.split(':')[1]) for d in .0 ])(projekt.add_data.split(';')) if projekt.add_data else []}'''][',\n                     hiddenimports='][f'''{projekt.hidden_imports.split(',') if projekt.hidden_imports else []}'''][',\n                     hookspath=[],\n                     runtime_hooks='][f'''{[
            '{projekt.runtime_hook}'] if projekt.runtime_hook else []}'''][',\n                     excludes=[],\n                     win_no_prefer_redirects=False,\n                     win_private_assemblies=False,\n                     cipher=block_cipher,\n                     noarchive=False)\n\n        pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)\n\n        exe = EXE(pyz,\n                  a.scripts,\n                  '][f'''{'[]' if projekt.onefile else 'a.binaries + a.zip + a.datas'}'''][',\n                  exclude_binaries='][f'''{not (projekt.onefile)}'''][",\n                  name='"][f'''{projekt.name}''']["',\n                  debug="][f'''{projekt.debug}'''][',\n                  bootloader_ignore_signals=False,\n                  strip='][f'''{projekt.strip}'''][',\n                  upx='][f'''{projekt.upx}'''][',\n                  console='][f'''{projekt.console}'''][",\n                  disable_windowed_traceback=False,\n                  target_arch=None,\n                  codesign_identity=None,\n                  entitlements_file=None,\n                  icon='"][f'''{projekt.icon}''']["'"][f'''{f''', key="{projekt.key}"''' if projekt.key else ''}'''][f'''{f''', splash="{projekt.splash}"''' if projekt.splash else ''}'''][')\n\n        coll = COLLECT(exe,\n                       a.binaries,\n                       a.zipfiles,\n                       a.datas,\n                       strip='][f'''{projekt.strip}'''][',\n                       upx='][f'''{projekt.upx}'''][",\n                       upx_exclude=[],\n                       name='"][f'''{projekt.name}''']["'"][f'''{f''', distpath="{projekt.output}"''' if projekt.output else ''}'''][')\n        '])
        return spec_content

    
    def bearbeite_projekt(self, projekt):
        win = tk.Toplevel(self.master)
        win.title(self.texts['title'])
        win.geometry('600x670')
        win.iconbitmap('autoPy++.ico')
        win.transient(self.master)
        win.grab_set()
        form_frame = ttk.Frame(win, '10', **('padding',))
        form_frame.pack('both', True, **('fill', 'expand'))
        
        def erstelle_eingabezeile(text = None, zeile = None, default = None, file_button = None, directory = None):
            ttk.Label(form_frame, text, **('text',)).grid(zeile, 0, 'e', 5, 2, **('row', 'column', 'sticky', 'padx', 'pady'))
            frame = ttk.Frame(form_frame)
            frame.grid(zeile, 1, 2, 'ew', **('row', 'column', 'pady', 'sticky'))
            e = ttk.Entry(frame, 60, **('width',))
            e.grid(0, 0, 'ew', **('row', 'column', 'sticky'))
            e.insert(0, default)
            if file_button or directory:
                filetypes = [
                    ('Alle Dateien', '*.*')] if not directory else []
                if text == self.texts['spec_file_label']:
                    filetypes = [
                        ('Spec-Dateien', '*.spec')]
                elif text == self.texts['runtime_hook_label']:
                    filetypes = [
                        ('Python-Dateien', '*.py')]
                elif text == self.texts['splash_label']:
                    filetypes = [
                        ('Bilddateien', '*.png *.jpg *.jpeg')]
                None(None, None, (lambda : self._wähle_datei(e, directory, filetypes)), **('text', 'command')).grid(0, 1, 5, **('row', 'column', 'padx'))
            form_frame.grid_columnconfigure(1, 1, **('weight',))
            return e

        e_name = erstelle_eingabezeile(self.texts['name_label'], 0, projekt.name)
        e_icon = erstelle_eingabezeile(self.texts['icon_label'], 1, projekt.icon, True, **('file_button',))
        e_add = erstelle_eingabezeile(self.texts['add_data_label'], 2, projekt.add_data)
        e_hidden = erstelle_eingabezeile(self.texts['hidden_imports_label'], 3, projekt.hidden_imports)
        e_version = erstelle_eingabezeile(self.texts['version_label'], 4, projekt.version, True, **('file_button',))
        e_output = erstelle_eingabezeile(self.texts['output_label'], 5, projekt.output, True, **('directory',))
        e_key = erstelle_eingabezeile(self.texts['key_label'], 6, projekt.key)
        e_runtime_hook = erstelle_eingabezeile(self.texts['runtime_hook_label'], 7, projekt.runtime_hook, True, **('file_button',))
        e_splash = erstelle_eingabezeile(self.texts['splash_label'], 8, projekt.splash, True, **('file_button',))
        e_spec_file = erstelle_eingabezeile(self.texts['spec_file_label'], 9, projekt.spec_file, True, **('file_button',))
        check_frame = ttk.Frame(form_frame)
        check_frame.grid(10, 0, 2, 5, 'w', **('row', 'column', 'columnspan', 'pady', 'sticky'))
        var_onefile = tk.BooleanVar(projekt.onefile, **('value',))
        ttk.Checkbutton(check_frame, self.texts['onefile_label'], var_onefile, **('text', 'variable')).grid(0, 0, 5, 2, 'w', **('row', 'column', 'padx', 'pady', 'sticky'))
        var_console_mode = tk.BooleanVar(projekt.console, **('value',))
        ttk.Radiobutton(check_frame, self.texts['console_label'], var_console_mode, True, **('text', 'variable', 'value')).grid(0, 1, 5, 2, 'w', **('row', 'column', 'padx', 'pady', 'sticky'))
        ttk.Radiobutton(check_frame, self.texts['windowed_label'], var_console_mode, False, **('text', 'variable', 'value')).grid(0, 2, 5, 2, 'w', **('row', 'column', 'padx', 'pady', 'sticky'))
        var_upx = tk.BooleanVar(projekt.upx, **('value',))
        ttk.Checkbutton(check_frame, self.texts['upx_label'], var_upx, **('text', 'variable')).grid(0, 3, 5, 2, 'w', **('row', 'column', 'padx', 'pady', 'sticky'))
        var_debug = tk.BooleanVar(projekt.debug, **('value',))
        ttk.Checkbutton(check_frame, self.texts['debug_label'], var_debug, **('text', 'variable')).grid(0, 4, 5, 2, 'w', **('row', 'column', 'padx', 'pady', 'sticky'))
        var_clean = tk.BooleanVar(projekt.clean, **('value',))
        ttk.Checkbutton(check_frame, self.texts['clean_label'], var_clean, **('text', 'variable')).grid(0, 5, 5, 2, 'w', **('row', 'column', 'padx', 'pady', 'sticky'))
        var_strip = tk.BooleanVar(projekt.strip, **('value',))
        ttk.Checkbutton(check_frame, self.texts['strip_label'], var_strip, **('text', 'variable')).grid(0, 6, 5, 2, 'w', **('row', 'column', 'padx', 'pady', 'sticky'))
        var_noupx = tk.BooleanVar(projekt.noupx, **('value',))
        ttk.Checkbutton(check_frame, self.texts['noupx_label'], var_noupx, **('text', 'variable')).grid(0, 7, 5, 2, 'w', **('row', 'column', 'padx', 'pady', 'sticky'))
        ttk.Label(form_frame, self.texts['options_label'], **('text',)).grid(11, 0, 'ne', 5, **('row', 'column', 'sticky', 'pady'))
        txt_options = scrolledtext.ScrolledText(form_frame, 50, 4, ('Segoe UI', 10), **('width', 'height', 'font'))
        txt_options.grid(11, 1, 5, 'ew', **('row', 'column', 'pady', 'sticky'))
        txt_options.insert(tk.END, projekt.options)
        form_frame.grid_columnconfigure(1, 1, **('weight',))
        hinweise_text = 'Beispielhafte Optionen:\n  --onefile             (Erstellt eine einzelne ausführbare Datei)\n  --windowed            (Keine Konsole anzeigen)\n  --add-data "daten\\*;daten" (Dateien/Ordner einbinden)\n  --hidden-import=modul (Versteckte Module importieren)\n  --icon=mein_icon.ico  (Setzt das Programm-Icon)\n  --runtime-hook=hook.py (Benutzerdefinierte Hooks)\n  --noupx               (UPX-Kompression deaktivieren)\n'
        hinweise_label = ttk.Label(form_frame, hinweise_text, 'gray', 'left', **('text', 'foreground', 'justify'))
        hinweise_label.grid(12, 1, 'w', 5, **('row', 'column', 'sticky', 'padx'))
        button_frame = ttk.Frame(form_frame)
        button_frame.grid(13, 0, 2, 10, **('row', 'column', 'columnspan', 'pady'))
        saved = [
            False]
        
        def speichern():
            if not e_name.get():
                pass
            projekt.name = os.path.splitext(os.path.basename(projekt.skript))[0]
            projekt.icon = e_icon.get()
            projekt.add_data = e_add.get()
            projekt.hidden_imports = e_hidden.get()
            projekt.version = e_version.get()
            projekt.output = e_output.get()
            projekt.onefile = var_onefile.get()
            projekt.console = var_console_mode.get()
            projekt.upx = var_upx.get()
            projekt.debug = var_debug.get()
            projekt.clean = var_clean.get()
            projekt.key = e_key.get()
            projekt.strip = var_strip.get()
            projekt.noupx = var_noupx.get()
            projekt.runtime_hook = e_runtime_hook.get()
            projekt.splash = e_splash.get()
            projekt.spec_file = e_spec_file.get()
            projekt.options = txt_options.get('1.0', tk.END).strip()
            saved[0] = True
            win.destroy()

        save_project_btn = ttk.Button(button_frame, self.texts['tooltip_save_project'].split('.')[0], speichern, 'Accent.TButton', **('text', 'command', 'style'))
        save_project_btn.grid(0, 1, 5, 5, **('row', 'column', 'padx', 'pady'))
        CreateToolTip(save_project_btn, self.texts['tooltip_save_project'])
        cancel_project_btn = ttk.Button(button_frame, self.texts['tooltip_cancel_project'].split('.')[0], win.destroy, **('text', 'command'))
        cancel_project_btn.grid(0, 0, 5, 5, **('row', 'column', 'padx', 'pady'))
        CreateToolTip(cancel_project_btn, self.texts['tooltip_cancel_project'])
        self.master.wait_window(win)
        return saved[0]

    
    def _wähle_datei(self, entry, directory, filetypes = (False, None)):
        if directory:
            pfad = filedialog.askdirectory()
        elif not filetypes:
            pass
        pfad = filetypes([
            ('Alle Dateien', '*.*')], **('filetypes',))
        if pfad:
            entry.delete(0, tk.END)
            entry.insert(0, pfad)
            return None
        return filedialog.askopenfilename

    
    def aktualisiere_treeview(self):
        pass
    # WARNING: Decompyle incomplete

    
    def speichern(self):
        if not self.projekte:
            messagebox.showwarning('Fehler', self.texts['error_no_project'])
            return None
        datei = None.asksaveasfilename('.apyscript', [
            ('Textdateien', '*.apyscript'),
            ('Spec-Dateien', '*.spec'),
            ('Alle Dateien', '*.*')], DATEI, **('defaultextension', 'filetypes', 'initialfile'))
    # WARNING: Decompyle incomplete

    
    def laden(self):
        datei = filedialog.askopenfilename([
            ('apyscript', '*.apyscript'),
            ('Spec-Dateien', '*.spec'),
            ('Alle Dateien', '*.*')], DATEI, **('filetypes', 'initialfile'))
    # WARNING: Decompyle incomplete

    
    def lade_datei(self):
        pass
    # WARNING: Decompyle incomplete

    
    def kompilieren(self):
        selected_projects = (lambda .0: [ p for p in .0 if p.compile_selected ])(self.projekte)
        if not selected_projects:
            messagebox.showwarning('Fehler', self.texts['status_error_no_project'])
            self.status_var.set(self.texts['status_error_no_project'])
            return None
        anzahl_threads = None.thread_count_var.get()
        total_projects = len(selected_projects)
        self.progress_var = tk.DoubleVar()
        self.progress_var.set(0)
        self.status_var.set('Kompilierung läuft...' if self.current_language == 'de' else 'Compilation running...')
        progress_bar = ttk.Progressbar(self.main_frame, self.progress_var, 100, **('variable', 'maximum'))
        progress_bar.pack('x', 5, **('fill', 'pady'))
        
        def compile_project(p):
            pass
        # WARNING: Decompyle incomplete

        with (lambda .0 = None: pass# WARNING: Decompyle incomplete
)(selected_projects) as futures:
            executor = ThreadPoolExecutor(anzahl_threads, **('max_workers',))
            for i, future in enumerate(as_completed(futures), 1):
                result = future.result()
                self.status_var.set(result)
                self.progress_var.set((i / total_projects) * 100)
                self.master.update_idletasks()
            None(None, None, None)
    # WARNING: Decompyle incomplete


if __name__ == '__main__':
    root = tk.Tk()
    app = AutoPyToExeGUI(root)
    root.mainloop()
    return None

Ist nicht komplett, da nicht der komplette Bytecode in Quellcode übersetzt werden konnte. Die ungenutzten Imports könnten vom Quellcode stammen, aber ast wird z.B. ein Artefakt des Dekompilieren gewesen sein.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
melatroid
User
Beiträge: 7
Registriert: Samstag 31. Mai 2025, 11:43

Ja danke für die Hinweise, den Sourcecode hier mal zur Verfügung gestellt.:

https://drive.google.com/drive/u/0/fold ... W2gMuuKy-r

Ps. Ihr dürfte ja gerne Meckern =D
Benutzeravatar
__blackjack__
User
Beiträge: 13995
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@melatroid: Du hast gefragt! 😝

`sys` und `uuid` werden importiert, aber nirgends verwendet.

`Projekt` ist eine Datenklasse, in die durchaus ein bisschen Verhalten rein könnte und die `AutoPyToExeGUI` dagegen ist eine Gottklasse in der viel zu viel drin ist in ca. 20 Methoden in ca. 800 Zeilen. Wenn man in einer Methode Fenster und lokale Funktionen erstellt, ist das ein Zeichen, dass da zu viel passiert. Ausserdem ist das nicht nur die GUI sondern auch die komplette Programmlogik in der Klassen — das lässt sich also beispielsweise ohne GUI gar nicht testen.

Es gibt dort zwei Attribute als Wahrheitswerte: `upx` und `noupx` die beide mit False initialisiert werden, das ist mindestens mal erklärungsbedürftig. Beim erstellen einer `spec`-Datei wird anscheinend auch nur `upx` berücksichtigt.

Dann braucht man auch nicht so ekelige Hacks wie ``saved = [False]`` und in einer verschachtelten Funktion dann ``saved[0] = True`` um das an anderer Stelle sichtbar zu machen. Wenn man das ordentlich macht ist das ein Attribut.

Es werden sowohl `os.path`-Funktionen als auch `pathlib.Path` verwendet.

Der Code für `name` vom `Projekt` um den aus `skript` zu generieren wenn er nicht gesetzt ist, existiert zwei mal. Einmal in der `Projekt.__init__()` und einmal in der GUI-Klasse, ziemlich weit weg im Quelltext. Das sollte nur *einmal* im Code existieren. Am besten wenn das Attribut abgefragt wird, statt beim ”setzen”. Das ist ein `property()`. Ups, in `baue_befehl()` steht der Code auch noch mal. Genau so etwas passiert dann: man übersieht Kopien wenn man mal etwas daran ändern will oder muss, und dann entwickelt sich der Code auseinander und man bekommt Inkonsistenzen und Fehler.

`os.path.exists()` macht man nur wenn man wirklich nur wissen will ob eine Datei existiert. Wenn man das testet bevor man die Datei öffnet, ist der Test ja schon im öffnen enthalten. Ausserdem werden in `load_config()` dann sowieso alle Ausnahmen behandelt, was diesen Test so richtig überflüssig macht, denn man kann den einfach entfernen ohne das sich das Programmverhalten ändert. Und mit `os.path.exists()` zu testen ob es eine Datei gibt, und falls nicht einen `FileNotFoundError` auszulösen bevor man die Datei öffnet, ist auch nicht so wirklich sinnvoll, weil `open()` ja genau das schon selbst mach: testen ob es die Datei gibt, und falls nicht, einen `FileNotFoundError` auslösen.

Ein `Path`-Objekt mit einer Zeichenkette erstellen, dann gleich danach wieder in eine Zeichenkette umwandeln, die dann wieder mit `os.path`-Funktionen verarbeitet wird, macht keinen Sinn.

`load_config()` ist keine Methode. Entweder sollte das eine werden, statt eine Funktion zu sein, die in einer Klasse steckt, oder man sollte auch `save_config()` zu einer Funktion machen und beides aus der Klasse heraus nehmen.

Für mehrsprachige Anwendungen gibt es in der Standardbibliothek das `gettext`-Modul. Das sollte man sich nicht selber basteln.

Sooo, das reicht erst mal. 🤓
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Benutzeravatar
sparrow
User
Beiträge: 4525
Registriert: Freitag 17. April 2009, 10:28

Und auch wenn es eine mehrsprachige Anwendung ist, sollte man sich bei den Namen im Code auf eine Sprache festlegen. Im Idealfall Englisch.
melatroid
User
Beiträge: 7
Registriert: Samstag 31. Mai 2025, 11:43

@__blackjack___ Erstmal Danke für eure Verbesserungen, hab das mit OS.path etc.. bereits geändert.

@sparrow Quellcode ist jetzt auf Englisch.

Review 1.21
https://drive.google.com/file/d/1a4fPFD ... drive_link
Benutzeravatar
Kebap
User
Beiträge: 772
Registriert: Dienstag 15. November 2011, 14:20
Wohnort: Dortmund

Wenn du jetzt git benutzen würdest, könnte man sogar nachvollziehen, welche Änderungen zwischen den Versionen stattgefunden haben.
MorgenGrauen: 1 Welt, 8 Rassen, 13 Gilden, >250 Abenteuer, >5000 Waffen & Rüstungen,
>7000 NPC, >16000 Räume, >200 freiwillige Programmierer, nur Text, viel Spaß, seit 1992.
Antworten