working on code generation

This commit is contained in:
paul
2024-09-26 23:16:43 +05:30
parent 7bba819c38
commit 5be078f846
11 changed files with 256 additions and 77 deletions

View File

@@ -767,7 +767,7 @@ class Canvas extends React.Component {
// Find the dragged widget object
let dragWidgetObj = this.findWidgetFromListById(dragElementID)
console.log("Drag widget obj: ", dragWidgetObj, dropWidgetObj)
// console.log("Drag widget obj: ", dragWidgetObj, dropWidgetObj)
if (dropWidgetObj && dragWidgetObj) {
const dragWidget = this.widgetRefs[dragWidgetObj.id]

View File

@@ -195,8 +195,6 @@ class Widget extends React.Component {
componentDidMount() {
// FIXME: initial layout is not set properly
if (this.state.attrs.layout){
this.setLayout(this.state.attrs.layout.value)
// console.log("prior layout: ", this.state.attrs.layout.value)
@@ -534,7 +532,7 @@ class Widget extends React.Component {
}
setWidgetName(name) {
console.log("named: ", name)
this.updateState({
widgetName: name.length > 0 ? name : this.state.widgetName
})
@@ -601,7 +599,7 @@ class Widget extends React.Component {
})
this.setAttrValue("layout", value)
this.props.onLayoutUpdate({parentId: this.__id, parentLayout: layout})// inform children about the layout update
this.props.onLayoutUpdate({parentId: this.__id, parentLayout: value})// inform children about the layout update
}

View File

@@ -2,6 +2,78 @@ import { createFilesAndDownload } from "../../../codeEngine/utils"
import MainWindow from "../widgets/mainWindow"
import { message } from "antd"
import TopLevel from "../widgets/toplevel"
// FIXME: if the toplevel comes first, before the MainWindow in widgetlist the root may become null
// Recursive function to generate the code list, imports, requirements, and track mainVariable
function generateTkinterCodeList(widgetList = [], widgetRefs = [], parentVariable = null, mainVariable = "") {
let variableNames = [] // {widgetId: "", name: "", count: 1}
let imports = new Set([])
let requirements = new Set([])
let code = []
for (let widget of widgetList) {
// console.log("Key: ", widget)
const widgetRef = widgetRefs[widget.id].current
let varName = widgetRef.getVariableName()
imports.add(widgetRef.getImports())
requirements.add(widgetRef.getRequirements())
// Set main variable if the widget is MainWindow
if (widget.widgetType === MainWindow) {
mainVariable = varName
}
if (!variableNames.find((val) => val.variable === varName)) {
variableNames.push({ widgetId: widgetRef.id, name: varName, count: 1 })
} else {
// Avoid duplicate names
const existingVariable = variableNames.find((val) => val.variable === varName)
const existingCount = existingVariable.count
existingVariable.count += 1
varName = `${existingVariable.name}${existingCount}`
variableNames.push({ widgetId: widgetRef.id, name: varName, count: 1 })
}
const currentParentVariable = variableNames.find(val => val.widgetId === widget.id)?.name || parentVariable
let widgetCode = widgetRef.generateCode(varName, currentParentVariable)
if (!(widgetCode instanceof Array)) {
throw new Error("Generate code function should return array, each new line should be a new item")
}
// Add \n after every line
widgetCode = widgetCode.flatMap((item, index) => index < widgetCode.length - 1 ? [item, "\n"] : [item])
code.push(...widgetCode)
code.push("\n\n")
// Recursively handle child widgets, which are full widget objects
if (widget.children && widget.children.length > 0) {
const childResult = generateTkinterCodeList(widget.children, widgetRefs, varName, mainVariable)
// Merge child imports, requirements, and code
imports = new Set([...imports, ...childResult.imports])
requirements = new Set([...requirements, ...childResult.requirements])
code.push(...childResult.code)
// Track the main variable returned by the child
mainVariable = childResult.mainVariable || mainVariable
}
}
return {
imports: Array.from(imports), // Convert Set to Array
code: code,
requirements: Array.from(requirements), // Convert Set to Array
mainVariable
}
}
async function generateTkinterCode(projectName, widgetList=[], widgetRefs=[]){
@@ -10,7 +82,10 @@ async function generateTkinterCode(projectName, widgetList=[], widgetRefs=[]){
let mainWindowCount = 0
for (let widget of widgetList){
// only MainWindow and/or the TopLevel should be on the canvas
const filteredWidgetList = widgetList.filter(widget => widget.widgetType === MainWindow || widget.widgetType === TopLevel)
for (let widget of filteredWidgetList){
if (widget.widgetType === MainWindow){
mainWindowCount += 1
}
@@ -27,64 +102,47 @@ async function generateTkinterCode(projectName, widgetList=[], widgetRefs=[]){
return
}
let variableNames = [] // {widgetId: "", name: "", count: 1}
// let code = [`# This code is generated by PyUIbuilder: https://github.com/paulledemon \n`]
// code.push("\n", "import tkinter as tk", "\n\n")
let imports = new Set([])
let requirements = new Set([])
// widget - {id, widgetType: widgetComponentType, children: [], parent: "", initialData: {}}
const generatedObject = generateTkinterCodeList(filteredWidgetList, widgetRefs, "", "")
let code = [`# This code is generated by PyUIbuilder: https://github.com/paulledemon \n`]
code.push("\n", "import tkinter as tk", "\n\n")
const {code: codeLines, imports, requirements, mainVariable} = generatedObject
const code = [
"# This code is generated by PyUIbuilder: https://github.com/paulledemon",
"\n\n",
...imports,
"\n\n",
...codeLines,
"\n",
`${mainVariable}.mainloop()`,
]
let mainVariable = "" // the main window variable
for (let widget of widgetList){
console.log("Key: ", widget)
const widgetRef = widgetRefs[widget.id].current
let varName = widgetRef.getVariableName()
imports.add(widgetRef.getImports())
requirements.add(widgetRef.getRequirements())
if (widget.widgetType === MainWindow){
mainVariable = varName
}
if (!variableNames.find((val) => val.variable === varName)){
variableNames.push({widgetId: widgetRef.id, name: varName, count: 1})
}else{
// Avoid duplicate names
const existingVariable = variableNames.find((val) => val.variable === varName)
const existingCount = existingVariable.count
existingVariable.count += 1
varName = `${existingVariable.name}${existingCount}`
variableNames.push({widgetId: widgetRef.id, name: varName, count: 1})
}
const parentVariable = variableNames.find(val => val.widgetId === widget.id)?.name || null
console.log("widget ref: ", widgetRef)
let widgetCode = widgetRef.generateCode(varName, parentVariable)
if (!widgetCode instanceof Array){
throw new Error("Generate code function should return array, each new line should be a new item")
}
// add \n after every line
widgetCode = widgetCode.flatMap((item, index) => index < code.length - 1 ? [item, "\n"] : [item])
code.push(...widgetCode)
code.push("\n")
}
code.push(`\n${mainVariable}.mainloop()`) // start the main loop for tkinter
// TODO: create requirements.txt file
console.log("Code: ", code.join(""))
message.info("starting zipping files, download will start in a few seconds")
// createFilesAndDownload([], projectName).then(() => {
const createFileList = [
{
fileData: code.join(""),
fileName: "main.py",
folder: ""
}
]
if (requirements.length > 0){
createFileList.push({
fileData: requirements.join("\n"),
fileName: "requirements.txt",
folder: ""
})
}
// createFilesAndDownload(createFileList, projectName).then(() => {
// message.success("Download complete")
// }).catch(() => {
// message.error("Error while downloading")

View File

@@ -0,0 +1,27 @@
import tkinter as tk
class EntryWithPlaceholder(tk.Entry):
def __init__(self, master=None, placeholder="placeholder", *args, **kwargs):
super().__init__(master, *args, **kwargs)
self.placeholder = placeholder
self.placeholder_color = color
self.default_fg_color = self['fg']
self.bind("<FocusIn>", self.foc_in)
self.bind("<FocusOut>", self.foc_out)
self.put_placeholder()
def put_placeholder(self):
self.insert(0, self.placeholder)
self['fg'] = self.placeholder_color
def foc_in(self, *args):
if self['fg'] == self.placeholder_color:
self.delete('0', 'end')
self['fg'] = self.default_fg_color
def foc_out(self, *args):
if not self.get():
self.put_placeholder()

View File

@@ -64,6 +64,26 @@ export class CheckBox extends TkinterBase{
this.setWidgetInnerStyle("backgroundColor", "#fff0")
}
generateCode(variableName, parent){
const labelText = this.getAttrValue("checkLabel")
const bg = this.getAttrValue("styling.backgroundColor")
const fg = this.getAttrValue("styling.foregroundColor")
const code = [
`${variableName} = tk.Checkbutton(master=${parent}, text="${labelText}")`,
`${variableName}.config(bg="${bg}", fg="${fg}")`,
]
if (this.getAttrValue("defaultChecked")){
code.push(`${variableName}.select()`)
}
code.push(`${variableName}.${this.getLayoutCode()}`)
return code
}
getToolbarAttrs(){
const toolBarAttrs = super.getToolbarAttrs()
@@ -109,7 +129,7 @@ export class CheckBox extends TkinterBase{
export class RadioButton extends Widget{
static widgetType = "radio_button"
// FIXME: the radio buttons are not visible because of the default height provided
constructor(props) {
super(props)
@@ -162,6 +182,28 @@ export class RadioButton extends Widget{
this.setWidgetInnerStyle("backgroundColor", "#fff0")
}
generateCode(variableName, parent){
const bg = this.getAttrValue("styling.backgroundColor")
const fg = this.getAttrValue("styling.foregroundColor")
const radios = this.getAttrValue("radios")
// TODO: from here
const code = [
`${variableName}_var = tk.IntVar()`,
`${variableName} = tk.Radiobutton(master=${parent}, text="")`,
`${variableName}.config(bg="${bg}", fg="${fg}")`,
]
if (this.getAttrValue("defaultChecked")){
code.push(`${variableName}.select()`)
}
code.push(`${variableName}.${this.getLayoutCode()}`)
return code
}
getToolbarAttrs(){
const toolBarAttrs = super.getToolbarAttrs()

View File

@@ -2,7 +2,8 @@ import { Layouts, PosType } from "../../../canvas/constants/layouts"
import Tools from "../../../canvas/constants/tools"
import Widget from "../../../canvas/widgets/base"
// TODO: add full width and full height in base widget
// TODO: the pack should configure width and height of widgets
class TkinterBase extends Widget {
@@ -20,7 +21,7 @@ class TkinterBase extends Widget {
let layoutManager = `pack()`
if (parentLayout === Layouts.FLEX){
layoutManager = `pack(${direction === "horizontal"? "tk.LEFT" : "tk.TOP"})`
layoutManager = `pack(side=${direction === "row" ? "tk.LEFT" : "tk.TOP"})`
}else if (parentLayout === Layouts.GRID){
const row = this.getAttrValue("gridManager.row")
const col = this.getAttrValue("gridManager.col")
@@ -34,7 +35,6 @@ class TkinterBase extends Widget {
}
setParentLayout(parentLayout){
console.log("parent layout: ", parentLayout)
const {layout, direction, gap} = parentLayout
@@ -195,7 +195,7 @@ class TkinterBase extends Widget {
}
this.setState(newData, () => {
let layoutAttrs = this.setParentLayout(parentLayout).attrs || {}
let layoutAttrs = this.setParentLayout(parentLayout) || {}
// UPdates attrs
let newAttrs = { ...this.state.attrs, ...layoutAttrs }

View File

@@ -43,12 +43,25 @@ class Button extends TkinterBase{
}
}
componentDidMount(){
super.componentDidMount()
this.setWidgetName("button")
this.setAttrValue("styling.backgroundColor", "#E4E2E2")
}
generateCode(variableName, parent){
const labelText = this.getAttrValue("buttonLabel")
const bg = this.getAttrValue("styling.backgroundColor")
const fg = this.getAttrValue("styling.foregroundColor")
return [
`${variableName} = tk.Button(master=${parent}, text="${labelText}")`,
`${variableName}.config(bg="${bg}", fg="${fg}")`,
`${variableName}.${this.getLayoutCode()}`
]
}
getToolbarAttrs(){
const toolBarAttrs = super.getToolbarAttrs()
@@ -69,7 +82,8 @@ class Button extends TkinterBase{
return (
<div className="tw-w-flex tw-flex-col tw-w-full tw-h-full tw-rounded-md
tw-border tw-border-solid tw-border-gray-400 tw-overflow-hidden">
<div className="tw-p-2 tw-w-full tw-flex tw-place-content-center tw-place-items-center tw-h-full tw-text-center" style={this.state.widgetInnerStyling}>
<div className="tw-p-2 tw-w-full tw-flex tw-place-content-center tw-place-items-center tw-h-full tw-text-center"
style={this.state.widgetInnerStyling}>
{/* {this.props.children} */}
<div className="tw-text-sm" style={{color: this.getAttrValue("styling.foregroundColor")}}>
{this.getAttrValue("buttonLabel")}

View File

@@ -12,11 +12,17 @@ class Frame extends TkinterBase{
this.droppableTags = {
exclude: ["image", "video", "media", "toplevel", "main_window"]
}
}
this.state = {
...this.state,
generateCode(variableName, parent){
}
const bg = this.getAttrValue("styling.backgroundColor")
return [
`${variableName} = tk.Frame(master=${parent})`,
`${variableName}.config(bg="${bg}")`,
`${variableName}.${this.getLayoutCode()}`
]
}
componentDidMount(){

View File

@@ -50,6 +50,18 @@ export class Input extends TkinterBase{
this.setWidgetName("Entry")
}
generateCode(variableName, parent){
const placeHolderText = this.getAttrValue("labelWidget")
const bg = this.getAttrValue("styling.backgroundColor")
const fg = this.getAttrValue("styling.foregroundColor")
return [
`${variableName} = tk.Entry(master=${parent}, text="${placeHolderText}")`,
`${variableName}.config(bg="${bg}", fg="${fg}")`,
`${variableName}.${this.getLayoutCode()}`
]
}
getToolbarAttrs(){
const toolBarAttrs = super.getToolbarAttrs()

View File

@@ -46,22 +46,26 @@ class Label extends TkinterBase{
}
}
generateCode(parent){
const label = this.getAttrValue("labelWidget")
return (`
${this.getWidgetName()} = tk.Label(master=${parent}, text="${label}")
${this.getWidgetName()}.${this.getLayoutCode()}
`)
}
componentDidMount(){
super.componentDidMount()
this.setAttrValue("styling.backgroundColor", "#fff0")
this.setWidgetInnerStyle("backgroundColor", "#fff0")
this.setAttrValue("styling.backgroundColor", "#E4E2E2")
this.setWidgetName("label")
}
generateCode(variableName, parent){
const labelText = this.getAttrValue("labelWidget")
const bg = this.getAttrValue("styling.backgroundColor")
const fg = this.getAttrValue("styling.foregroundColor")
return [
`${variableName} = tk.Label(master=${parent}, text="${labelText}")`,
`${variableName}.config(bg="${bg}", fg="${fg}")`,
`${variableName}.${this.getLayoutCode()}`
]
}
getToolbarAttrs(){
const toolBarAttrs = super.getToolbarAttrs()

View File

@@ -8,6 +8,7 @@ import TkinterBase from "./base"
class SpinBox extends TkinterBase{
static widgetType = "spin_box"
constructor(props) {
super(props)
@@ -75,6 +76,23 @@ class SpinBox extends TkinterBase{
this.setWidgetName("SpinBox")
}
generateCode(variableName, parent){
const min = this.getAttrValue("spinProps.min")
const max = this.getAttrValue("spinProps.max")
const step = this.getAttrValue("spinProps.step")
const defaultValue = this.getAttrValue("spinProps.defaultValue")
const bg = this.getAttrValue("styling.backgroundColor")
const fg = this.getAttrValue("styling.foregroundColor")
return [
`${variableName} = tk.Spinbox(master=${parent})`,
`${variableName}.config(bg="${bg}", fg="${fg}", from_=${min}, to=${max},
increment=${step})`,
`${variableName}.${this.getLayoutCode()}`
]
}
getToolbarAttrs(){
const toolBarAttrs = super.getToolbarAttrs()