178 lines
5.8 KiB
TypeScript
178 lines
5.8 KiB
TypeScript
import { DesignData, WidgetData } from './types';
|
|
import {
|
|
getVariableName,
|
|
generateVariableNames,
|
|
getWidgetTypeForGeneration,
|
|
indentText,
|
|
} from './utils';
|
|
import {
|
|
getWidgetParameters,
|
|
getPlaceParameters,
|
|
generateWidgetContent,
|
|
} from './widgetHelpers';
|
|
import { generateEventHandlers, getWidgetEventBindings } from './eventHelpers';
|
|
|
|
export class CodeGenerator {
|
|
private indentLevel = 0;
|
|
private designData: DesignData | null = null;
|
|
|
|
public generateTkinterCode(designData: DesignData): string {
|
|
this.designData = designData;
|
|
const className = designData.form.className || 'Application';
|
|
console.log(
|
|
'[Generator] Start, widgets:',
|
|
designData.widgets.length,
|
|
'events:',
|
|
designData.events?.length || 0
|
|
);
|
|
const lines: string[] = [];
|
|
const nameMap = generateVariableNames(designData.widgets);
|
|
|
|
lines.push('import tkinter as tk');
|
|
lines.push('from tkinter import ttk');
|
|
lines.push('');
|
|
|
|
lines.push(`class ${className}:`);
|
|
this.indentLevel = 1;
|
|
|
|
lines.push(this.indent('def __init__(self):'));
|
|
this.indentLevel = 2;
|
|
lines.push(this.indent('self.root = tk.Tk()'));
|
|
lines.push(this.indent(`self.root.title("${designData.form.title}")`));
|
|
lines.push(
|
|
this.indent(
|
|
`self.root.geometry("${designData.form.size.width}x${designData.form.size.height}")`
|
|
)
|
|
);
|
|
lines.push(this.indent('self.create_widgets()'));
|
|
lines.push('');
|
|
|
|
this.indentLevel = 1;
|
|
lines.push(this.indent('def create_widgets(self):'));
|
|
this.indentLevel = 2;
|
|
|
|
designData.widgets.forEach((widget) => {
|
|
console.log('[Generator] Widget:', widget.id, widget.type);
|
|
lines.push(...this.generateWidgetCode(widget, nameMap));
|
|
});
|
|
|
|
this.indentLevel = 1;
|
|
lines.push(this.indent('def run(self):'));
|
|
this.indentLevel = 2;
|
|
lines.push(this.indent('self.root.mainloop()'));
|
|
lines.push('');
|
|
|
|
const hasEvents = designData.events && designData.events.length > 0;
|
|
|
|
if (hasEvents) {
|
|
console.log('[Generator] Generating event handlers');
|
|
lines.push(
|
|
...generateEventHandlers(
|
|
designData,
|
|
(text) => indentText(1, text),
|
|
(text) => indentText(2, text)
|
|
)
|
|
);
|
|
}
|
|
|
|
this.indentLevel = 0;
|
|
lines.push('if __name__ == "__main__":');
|
|
this.indentLevel = 1;
|
|
lines.push(this.indent('try:'));
|
|
this.indentLevel = 2;
|
|
lines.push(this.indent(`app = ${className}()`));
|
|
lines.push(this.indent('app.run()'));
|
|
this.indentLevel = 1;
|
|
lines.push(this.indent('except Exception as e:'));
|
|
this.indentLevel = 2;
|
|
lines.push(this.indent('import traceback'));
|
|
lines.push(this.indent('traceback.print_exc()'));
|
|
lines.push(this.indent('input("Press Enter to exit...")'));
|
|
|
|
return lines.join('\n');
|
|
}
|
|
|
|
public generateCreateWidgetsBody(designData: DesignData): string {
|
|
this.designData = designData;
|
|
const lines: string[] = [];
|
|
const nameMap = generateVariableNames(designData.widgets);
|
|
this.indentLevel = 2; // Inside class -> inside create_widgets
|
|
|
|
designData.widgets.forEach((widget) => {
|
|
lines.push(...this.generateWidgetCode(widget, nameMap));
|
|
});
|
|
|
|
return lines.join('\n');
|
|
}
|
|
|
|
public generateEventHandler(event: any): string {
|
|
// Simple helper to generate a single event handler method
|
|
// Used when injecting new methods
|
|
const lines: string[] = [];
|
|
// Use existing signature if available (to preserve user arguments)
|
|
// Note: signature does not include 'def ' prefix in our current extraction logic?
|
|
// Let's check base.py: sig_raw = self.source_code[def_start_idx : start_idx].strip()
|
|
// It includes 'def name(...)'.
|
|
// So we just use it directly.
|
|
|
|
if (event.signature) {
|
|
lines.push(` ${event.signature}:`);
|
|
} else {
|
|
lines.push(` def ${event.name}(self, event=None):`);
|
|
}
|
|
|
|
if (event.code) {
|
|
const codeContent =
|
|
typeof event.code === 'string'
|
|
? event.code
|
|
: String(event.code || '');
|
|
const body = codeContent
|
|
.split('\n')
|
|
.map((l: string) => ' ' + l)
|
|
.join('\n');
|
|
lines.push(body);
|
|
} else {
|
|
lines.push(' pass');
|
|
}
|
|
return lines.join('\n');
|
|
}
|
|
|
|
private generateWidgetCode(
|
|
widget: WidgetData,
|
|
nameMap: Map<string, string>
|
|
): string[] {
|
|
const lines: string[] = [];
|
|
const varName = getVariableName(widget, nameMap);
|
|
|
|
const widgetType = getWidgetTypeForGeneration(widget.type);
|
|
lines.push(
|
|
this.indent(
|
|
`self.${varName} = ${widgetType}(self.root${getWidgetParameters(widget)})`
|
|
)
|
|
);
|
|
|
|
const placeParams = getPlaceParameters(widget);
|
|
lines.push(this.indent(`self.${varName}.place(${placeParams})`));
|
|
|
|
const contentLines = generateWidgetContent(widget, varName);
|
|
if (contentLines.length > 0) {
|
|
contentLines.forEach((l) => lines.push(this.indent(l)));
|
|
}
|
|
|
|
lines.push(
|
|
...getWidgetEventBindings(
|
|
this.designData,
|
|
widget,
|
|
varName,
|
|
(text) => this.indent(text)
|
|
)
|
|
);
|
|
|
|
return lines;
|
|
}
|
|
|
|
private indent(text: string): string {
|
|
return indentText(this.indentLevel, text);
|
|
}
|
|
}
|