working on custom tk
BIN
src/frameworks/customtk/assets/widgets/main/Toplevel.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
src/frameworks/customtk/assets/widgets/main/button.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
src/frameworks/customtk/assets/widgets/main/check.png
Normal file
|
After Width: | Height: | Size: 8.1 KiB |
BIN
src/frameworks/customtk/assets/widgets/main/dropdown.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
src/frameworks/customtk/assets/widgets/main/frame.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
src/frameworks/customtk/assets/widgets/main/frame2.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
src/frameworks/customtk/assets/widgets/main/input.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/frameworks/customtk/assets/widgets/main/label.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
src/frameworks/customtk/assets/widgets/main/mainwindow.jpg
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
src/frameworks/customtk/assets/widgets/main/mainwindow.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
src/frameworks/customtk/assets/widgets/main/radio.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
src/frameworks/customtk/assets/widgets/main/slider.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
src/frameworks/customtk/assets/widgets/main/spinbox.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
BIN
src/frameworks/customtk/assets/widgets/main/textarea.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
src/frameworks/customtk/assets/widgets/main/widget.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
src/frameworks/customtk/assets/widgets/plugins/clock.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
src/frameworks/customtk/assets/widgets/plugins/map.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
src/frameworks/customtk/assets/widgets/plugins/tables.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
src/frameworks/customtk/assets/widgets/plugins/video.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
43
src/frameworks/customtk/constants/cursor.js
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
|
||||
export const Tkinter_TO_WEB_CURSOR_MAPPING = {
|
||||
|
||||
"arrow": "default",
|
||||
"circle": "wait",
|
||||
"clock": "wait",
|
||||
"cross": "crosshair",
|
||||
"dotbox": "not-allowed",
|
||||
"exchange": "alias",
|
||||
"fleur": "move",
|
||||
"heart": "default", // mapping doesn't exist
|
||||
"man": "default",
|
||||
"mouse": "default",
|
||||
"pirate": "default",
|
||||
"plus": "zoom-in",
|
||||
"shuttle": "default",
|
||||
"sizing": "se-resize",
|
||||
"spider": "default",
|
||||
"spraycan": "default",
|
||||
"star": "default",
|
||||
"target": "cell",
|
||||
"tcross": "crosshair",
|
||||
"trek": "default",
|
||||
"watch": "wait",
|
||||
"X_cursor": "not-allowed",
|
||||
"bottom_left_corner": "sw-resize",
|
||||
"bottom_right_corner": "se-resize",
|
||||
"bottom_side": "s-resize",
|
||||
"top_left_corner": "nw-resize",
|
||||
"top_right_corner": "ne-resize",
|
||||
"top_side": "n-resize",
|
||||
"left_side": "w-resize",
|
||||
"right_side": "e-resize",
|
||||
"hand1": "pointer",
|
||||
"hand2": "pointer",
|
||||
"sb_h_double_arrow": "ew-resize",
|
||||
"sb_v_double_arrow": "ns-resize",
|
||||
"center_ptr": "default",
|
||||
"question_arrow": "help",
|
||||
"umbrella": "default",
|
||||
"pencil": "copy",
|
||||
}
|
||||
20
src/frameworks/customtk/constants/fontFamily.js
Normal file
@@ -0,0 +1,20 @@
|
||||
|
||||
|
||||
|
||||
export const Tkinter_To_GFonts= {
|
||||
"Arial": "Roboto",
|
||||
"Courier": "Courier Prime",
|
||||
"Comic Sans MS": "Comic Neue",
|
||||
"Fixedsys": "Cousine",
|
||||
"Helvetica": "Roboto",
|
||||
"Times": "Merriweather",
|
||||
"System": "Noto Sans",
|
||||
"Verdana": "Open Sans",
|
||||
"Symbol": "Noto Sans Symbols",
|
||||
"TkDefaultFont": "Noto Sans",
|
||||
"TkFixedFont": "Source Code Pro",
|
||||
"TkMenuFont": "Roboto",
|
||||
"TkHeadingFont": "Montserrat",
|
||||
"TkTextFont": "Roboto",
|
||||
"TkTooltipFont": "Lato",
|
||||
}
|
||||
24
src/frameworks/customtk/constants/styling.js
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
export const RELIEF = [
|
||||
"FLAT",
|
||||
"RAISED",
|
||||
"SUNKEN",
|
||||
"GROOVE",
|
||||
"RIDGE"
|
||||
]
|
||||
|
||||
|
||||
export const JUSTIFY = [
|
||||
"LEFT",
|
||||
"CENTER",
|
||||
"RIGHT"
|
||||
]
|
||||
|
||||
|
||||
export const ANCHOR = [
|
||||
"n",
|
||||
"s",
|
||||
"e",
|
||||
"w",
|
||||
"center"
|
||||
]
|
||||
191
src/frameworks/customtk/engine/code.js
Normal file
@@ -0,0 +1,191 @@
|
||||
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 = "", usedVariableNames = new Set()) {
|
||||
let variableMapping = new Map() // Map widget to variable { widgetId: variableName }
|
||||
let imports = new Set([])
|
||||
let requirements = new Set([])
|
||||
let code = []
|
||||
|
||||
for (let widget of widgetList) {
|
||||
const widgetRef = widgetRefs[widget.id].current
|
||||
let varName = widgetRef.getVariableName()
|
||||
|
||||
// Add imports and requirements to sets
|
||||
widgetRef.getImports().forEach(importItem => imports.add(importItem))
|
||||
widgetRef.getRequirements().forEach(requirementItem => requirements.add(requirementItem))
|
||||
|
||||
// Set main variable if the widget is MainWindow
|
||||
if (widget.widgetType === MainWindow) {
|
||||
mainVariable = varName
|
||||
}
|
||||
|
||||
// Ensure unique variable names across recursion
|
||||
let originalVarName = varName
|
||||
let count = 1;
|
||||
|
||||
// Check for uniqueness and update varName
|
||||
while (usedVariableNames.has(varName)) {
|
||||
varName = `${originalVarName}${count}`
|
||||
count++
|
||||
}
|
||||
|
||||
usedVariableNames.add(varName)
|
||||
variableMapping.set(widget.id, varName) // Store the variable name by widget ID
|
||||
|
||||
// Determine the current parent variable from variableNames or fallback to parentVariable
|
||||
let currentParentVariable = parentVariable || (variableMapping.get(widget.id) || null)
|
||||
|
||||
if (widget.widgetType === TopLevel){
|
||||
// for top level set it to the main variable
|
||||
// TODO: the toplevels parent should be determined other ways, suppose the top level has another toplevel
|
||||
currentParentVariable = mainVariable
|
||||
}
|
||||
|
||||
let widgetCode = widgetRef.generateCode(varName, currentParentVariable)
|
||||
|
||||
if (!(widgetCode instanceof Array)) {
|
||||
throw new Error("generateCode() 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
|
||||
if (widget.children && widget.children.length > 0) {
|
||||
// Pass down the unique names for children to prevent duplication
|
||||
const childResult = generateTkinterCodeList(widget.children, widgetRefs, varName, mainVariable, usedVariableNames)
|
||||
|
||||
// Merge child imports, requirements, and code
|
||||
imports = new Set([...imports, ...childResult.imports])
|
||||
requirements = new Set([...requirements, ...childResult.requirements])
|
||||
code.push(...childResult.code)
|
||||
|
||||
mainVariable = childResult.mainVariable || mainVariable // the main variable is the main window variable
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
imports: Array.from(imports),
|
||||
code: code,
|
||||
requirements: Array.from(requirements),
|
||||
mainVariable
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function generateTkinterCode(projectName, widgetList=[], widgetRefs=[], assetFiles){
|
||||
|
||||
console.log("widgetList and refs", projectName, widgetList, widgetRefs, assetFiles)
|
||||
|
||||
let mainWindowCount = 0
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
if (mainWindowCount > 1){
|
||||
message.error("Multiple instances of Main window found, delete one and try again.")
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (mainWindowCount === 0){
|
||||
message.error("Aborting. No instances of Main window found. Add one and try again")
|
||||
return
|
||||
}
|
||||
|
||||
// widget - {id, widgetType: widgetComponentType, children: [], parent: "", initialData: {}}
|
||||
|
||||
const generatedObject = generateTkinterCodeList(filteredWidgetList, widgetRefs, "", "")
|
||||
|
||||
const {code: codeLines, imports, requirements, mainVariable} = generatedObject
|
||||
|
||||
// TODO: avoid adding \n inside the list instead rewrite using code.join("\n")
|
||||
const code = [
|
||||
"# This code is generated by PyUIbuilder: https://github.com/PaulleDemon/PyUIBuilder",
|
||||
"\n\n",
|
||||
...imports.flatMap((item, index) => index < imports.length - 1 ? [item, "\n"] : [item]), //add \n to every line
|
||||
"\n\n",
|
||||
...codeLines,
|
||||
"\n",
|
||||
`${mainVariable}.mainloop()`,
|
||||
]
|
||||
|
||||
console.log("Code: ", code.join(""), "\n\n requirements:", requirements.join("\n"))
|
||||
|
||||
message.info("starting zipping files, download will start in a few seconds")
|
||||
|
||||
|
||||
const createFileList = [
|
||||
{
|
||||
fileData: code.join(""),
|
||||
fileName: "main.py",
|
||||
folder: ""
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
if (requirements.length > 0){
|
||||
createFileList.push({
|
||||
fileData: requirements.join("\n"),
|
||||
fileName: "requirements.txt",
|
||||
folder: ""
|
||||
})
|
||||
}
|
||||
|
||||
for (let asset of assetFiles){
|
||||
|
||||
if (asset.fileType === "image"){
|
||||
createFileList.push({
|
||||
fileData: asset.originFileObj,
|
||||
fileName: asset.name,
|
||||
folder: "assets/images"
|
||||
})
|
||||
}else if (asset.fileType === "video"){
|
||||
createFileList.push({
|
||||
fileData: asset.originFileObj,
|
||||
fileName: asset.name,
|
||||
folder: "assets/videos"
|
||||
})
|
||||
}else if (asset.fileType === "audio"){
|
||||
createFileList.push({
|
||||
fileData: asset.originFileObj,
|
||||
fileName: asset.name,
|
||||
folder: "assets/audio"
|
||||
})
|
||||
}else{
|
||||
createFileList.push({
|
||||
fileData: asset.originFileObj,
|
||||
fileName: asset.name,
|
||||
folder: "assets/others"
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
createFilesAndDownload(createFileList, projectName).then(() => {
|
||||
message.success("Download complete")
|
||||
}).catch(() => {
|
||||
message.error("Error while downloading")
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default generateTkinterCode
|
||||
27
src/frameworks/customtk/engine/componentCodes/entry.py
Normal 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()
|
||||
265
src/frameworks/customtk/plugins/analogTimepicker.js
Normal file
@@ -0,0 +1,265 @@
|
||||
import React from "react"
|
||||
|
||||
import { timePicker } from 'analogue-time-picker'
|
||||
|
||||
import Widget from "../../../canvas/widgets/base"
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
import { convertObjectToKeyValueString, removeKeyFromObject } from "../../../utils/common"
|
||||
import { TkinterBase } from "../widgets/base"
|
||||
|
||||
import "./styles/timepickerStyle.css"
|
||||
|
||||
|
||||
const Themes = {
|
||||
NAVY_BLUE: "Navy Blue",
|
||||
DRACULA: "Dracula",
|
||||
PURPLE: "Purple",
|
||||
NONE: ""
|
||||
}
|
||||
|
||||
class AnalogTimePicker extends TkinterBase{
|
||||
|
||||
static widgetType = "analog_timepicker"
|
||||
|
||||
static requiredImports = [
|
||||
...TkinterBase.requiredImports,
|
||||
'from tktimepicker import AnalogPicker, AnalogThemes, constants'
|
||||
]
|
||||
|
||||
static requirements = ["tkTimePicker"]
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.droppableTags = null
|
||||
|
||||
const newAttrs = removeKeyFromObject("layout", this.state.attrs)
|
||||
|
||||
this.timePicker = null
|
||||
|
||||
this.timePickerRef = React.createRef()
|
||||
|
||||
this.minSize = {width: 100, height: 100}
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
widgetName: "Timepicker",
|
||||
size: { width: 250, height: 350 },
|
||||
attrs: {
|
||||
...newAttrs,
|
||||
styling: {
|
||||
theme:{
|
||||
label: "Theme",
|
||||
tool: Tools.SELECT_DROPDOWN,
|
||||
toolProps: {placeholder: "select theme"},
|
||||
value: "",
|
||||
options: Object.values(Themes).map(val => ({value: val, label: val})),
|
||||
onChange: (value) => this.handleThemeChange(value)
|
||||
},
|
||||
...newAttrs.styling,
|
||||
clockColor: {
|
||||
label: "Clock Color",
|
||||
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
|
||||
value: "#EEEEEE",
|
||||
onChange: (value) => {
|
||||
this.setAttrValue("styling.clockColor", value)
|
||||
}
|
||||
},
|
||||
displayColor: {
|
||||
label: "Display Color",
|
||||
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
|
||||
value: "#000",
|
||||
onChange: (value) => {
|
||||
this.setAttrValue("styling.displayColor", value)
|
||||
}
|
||||
},
|
||||
numberColor: {
|
||||
label: "Numbers Color",
|
||||
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
|
||||
value: "#000",
|
||||
onChange: (value) => {
|
||||
this.setAttrValue("styling.numberColor", value)
|
||||
}
|
||||
},
|
||||
handleColor: {
|
||||
label: "Handle Color",
|
||||
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
|
||||
value: "#000000",
|
||||
onChange: (value) => {
|
||||
this.setAttrValue("styling.handleColor", value)
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
clockMode:{
|
||||
label: "Clock Mode",
|
||||
tool: Tools.SELECT_DROPDOWN,
|
||||
toolProps: {placeholder: "select mode", defaultValue: 12},
|
||||
value: 12,
|
||||
options: [12, 24].map(val => ({value: val, label: val})),
|
||||
onChange: (value) => {
|
||||
this.setAttrValue("clockMode", value)
|
||||
if (value === 24){
|
||||
// FIXME: the timepicker for 24 hrs also shows 12 hrs time
|
||||
|
||||
this.timePicker.set24h()
|
||||
|
||||
}else{
|
||||
this.timePicker.set12h()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
this.handleThemeChange = this.handleThemeChange.bind(this)
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.timePicker = timePicker({
|
||||
element: this.timePickerRef.current,
|
||||
mode: "12",
|
||||
width: this.state.size.width,
|
||||
// height: this.state.size.height
|
||||
})
|
||||
|
||||
// used to remove ok and cancel buttons
|
||||
const timePickerBtns = this.timePickerRef.current.getElementsByClassName("atp-clock-btn")
|
||||
for (let i = 0; i < timePickerBtns.length; i++) {
|
||||
timePickerBtns[i].remove()
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount(){
|
||||
this.timePicker.dispose()
|
||||
}
|
||||
|
||||
setResize(pos, size){
|
||||
super.setResize(pos, size)
|
||||
this.timePicker.setWidth(size.width)
|
||||
}
|
||||
|
||||
handleThemeChange(value){
|
||||
this.setAttrValue("styling.theme", value)
|
||||
|
||||
if (value === Themes.NAVY_BLUE){
|
||||
this.setAttrValue("styling.handleColor", "#009688")
|
||||
this.setAttrValue("styling.displayColor", "#009688")
|
||||
this.setAttrValue("styling.backgroundColor", "#fff")
|
||||
this.setAttrValue("styling.clockColor", "#EEEEEE")
|
||||
this.setAttrValue("styling.numberColor", "#000")
|
||||
}else if (value === Themes.DRACULA){
|
||||
this.setAttrValue("styling.handleColor", "#863434")
|
||||
this.setAttrValue("styling.displayColor", "#404040")
|
||||
this.setAttrValue("styling.backgroundColor", "#404040")
|
||||
this.setAttrValue("styling.clockColor", "#363636")
|
||||
this.setAttrValue("styling.numberColor", "#fff")
|
||||
}else if (value === Themes.PURPLE){
|
||||
this.setAttrValue("styling.handleColor", "#EE333D")
|
||||
this.setAttrValue("styling.displayColor", "#71135C")
|
||||
this.setAttrValue("styling.backgroundColor", "#4E0D3A")
|
||||
this.setAttrValue("styling.clockColor", "#71135C")
|
||||
this.setAttrValue("styling.numberColor", "#fff")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
generateCode(variableName, parent){
|
||||
|
||||
const theme = this.getAttrValue("styling.theme")
|
||||
|
||||
const mode = this.getAttrValue("clockMode")
|
||||
const bgColor = this.getAttrValue("styling.backgroundColor")
|
||||
const clockColor = this.getAttrValue("styling.clockColor")
|
||||
const displayColor = this.getAttrValue("styling.displayColor")
|
||||
const numColor = this.getAttrValue("styling.numberColor")
|
||||
const handleColor = this.getAttrValue("styling.handleColor")
|
||||
|
||||
const code = [
|
||||
`${variableName} = AnalogPicker(parent=${parent}, type=${mode===12 ? "constants.HOURS12" : "constants.HOURS24"})`,
|
||||
]
|
||||
|
||||
if (theme){
|
||||
|
||||
code.push(`${variableName}_theme = AnalogThemes(${variableName})`)
|
||||
if (theme === Themes.NAVY_BLUE){
|
||||
code.push(`${variableName}_theme.setNavyBlue()`)
|
||||
}else if (theme === Themes.DRACULA){
|
||||
code.push(`${variableName}_theme.setDracula()`)
|
||||
}else if (theme === Themes.PURPLE){
|
||||
code.push(`${variableName}_theme.setPurple()`)
|
||||
}
|
||||
|
||||
}else{
|
||||
|
||||
const configAnalog = {
|
||||
"canvas_bg": `"${bgColor}"`,
|
||||
"textcolor": `"${numColor}"`,
|
||||
"bg": `"${clockColor}"`,
|
||||
"handcolor": `"${handleColor}"`,
|
||||
"headcolor": `"${handleColor}"`
|
||||
}
|
||||
|
||||
const displayConfig = {
|
||||
bg: `"${displayColor}"`
|
||||
}
|
||||
|
||||
code.push(`${variableName}.configAnalog(${convertObjectToKeyValueString(configAnalog)})`)
|
||||
code.push(`${variableName}.configSpin(${convertObjectToKeyValueString(displayConfig)})`)
|
||||
|
||||
|
||||
// code.push(`configAnalog(canvas_bg="${bgColor}", textcolor="${numColor}",
|
||||
// bg="${clockColor}", handcolor="${handleColor}", headcolor="${handleColor}")`)
|
||||
|
||||
// code.push(`configSpin(bg="${displayColor}"`)
|
||||
}
|
||||
|
||||
return [
|
||||
...code,
|
||||
`${variableName}.${this.getLayoutCode()}`
|
||||
]
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
size: toolBarAttrs.size,
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
renderContent(){
|
||||
const timePickerStyling = {
|
||||
'--bg': this.getAttrValue("styling.backgroundColor"),
|
||||
'--clock-color': this.getAttrValue("styling.clockColor"),
|
||||
'--number-color': this.getAttrValue("styling.numberColor"),
|
||||
'--time-display': this.getAttrValue("styling.displayColor"),
|
||||
'--main-handle-color': this.getAttrValue("styling.handleColor"),
|
||||
}
|
||||
|
||||
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-h-full tw-flex tw-content-start tw-pointer-events-none"
|
||||
style={timePickerStyling}
|
||||
ref={this.timePickerRef}>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default AnalogTimePicker
|
||||
BIN
src/frameworks/customtk/plugins/assets/map.png
Normal file
|
After Width: | Height: | Size: 241 KiB |
BIN
src/frameworks/customtk/plugins/assets/video.jpg
Normal file
|
After Width: | Height: | Size: 60 KiB |
96
src/frameworks/customtk/plugins/mapview.js
Normal file
@@ -0,0 +1,96 @@
|
||||
import React from "react"
|
||||
|
||||
|
||||
import Widget from "../../../canvas/widgets/base"
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
import { removeKeyFromObject } from "../../../utils/common"
|
||||
|
||||
import MapImage from "./assets/map.png"
|
||||
import { MinusOutlined, PlayCircleFilled, PlusOutlined } from "@ant-design/icons"
|
||||
import { TkinterBase } from "../widgets/base"
|
||||
|
||||
|
||||
class MapView extends TkinterBase{
|
||||
|
||||
static widgetType = "map_view"
|
||||
|
||||
static requiredImports = [
|
||||
...TkinterBase.requiredImports,
|
||||
"import tkintermapview"
|
||||
]
|
||||
|
||||
static requirements = ["tkintermapview"]
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.droppableTags = null
|
||||
|
||||
const newAttrs = removeKeyFromObject("layout", this.state.attrs)
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
widgetName: "Map viewer",
|
||||
size: { width: 400, height: 250 },
|
||||
}
|
||||
}
|
||||
|
||||
// componentDidMount(){
|
||||
// super.componentDidMount()
|
||||
// }
|
||||
|
||||
generateCode(variableName, parent){
|
||||
|
||||
|
||||
return [
|
||||
`${variableName} = tkintermapview.TkinterMapView(master=${parent})`,
|
||||
`${variableName}.${this.getLayoutCode()}`
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
getToolbarAttrs(){
|
||||
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
size: toolBarAttrs.size,
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
renderContent(){
|
||||
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-h-full tw-content-start tw-pointer-events-none"
|
||||
style={this.state.widgetInnerStyling}>
|
||||
<div className="tw-relative tw-w-full tw-h-full">
|
||||
<div className="tw-absolute tw-left-5 tw-top-3 tw-flex tw-flex-col tw-gap-2">
|
||||
<div className="tw-text-white tw-bg-black tw-text-center
|
||||
tw-p-[2px]
|
||||
tw-w-[25px] tw-h-[25px] tw-rounded-md">
|
||||
<PlusOutlined className="tw-text-xl"/>
|
||||
</div>
|
||||
<div className="tw-text-white tw-bg-black tw-text-center
|
||||
tw-p-1
|
||||
tw-w-[25px] tw-h-[25px] tw-rounded-md">
|
||||
<MinusOutlined className="tw-text-xl"/>
|
||||
</div>
|
||||
</div>
|
||||
<img src={MapImage} className="tw-w-full tw-h-full" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default MapView
|
||||
237
src/frameworks/customtk/plugins/pandasTable.js
Normal file
@@ -0,0 +1,237 @@
|
||||
|
||||
|
||||
|
||||
|
||||
import React, { useEffect, useRef, useState } from "react"
|
||||
|
||||
import Widget from "../../../canvas/widgets/base"
|
||||
import { removeKeyFromObject } from "../../../utils/common"
|
||||
|
||||
import MapImage from "./assets/map.png"
|
||||
import { MinusOutlined, PlusOutlined } from "@ant-design/icons"
|
||||
import { TkinterBase } from "../widgets/base"
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
import { getPythonAssetPath } from "../../utils/pythonFilePath"
|
||||
|
||||
|
||||
const ResizableTable = ({minRows=5, minCols=5}) => {
|
||||
const [rows, setRows] = useState(minRows)
|
||||
const [cols, setCols] = useState(minCols)
|
||||
const containerRef = useRef(null)
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const resizeObserver = new ResizeObserver((entries) => {
|
||||
for (let entry of entries) {
|
||||
const { width, height } = entry.contentRect
|
||||
|
||||
// Set number of columns and rows based on widget width and height
|
||||
const newCols = Math.max(minCols, Math.floor(width / 100)) // each column is 100px wide
|
||||
const newRows = Math.max(minRows, Math.floor(height / 50)) // each row is 50px high
|
||||
|
||||
setCols(newCols)
|
||||
setRows(newRows)
|
||||
}
|
||||
})
|
||||
|
||||
if (containerRef.current) {
|
||||
resizeObserver.observe(containerRef.current); // Start observing the widget
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (containerRef.current) {
|
||||
resizeObserver.unobserve(containerRef.current); // Stop observing when component unmounts
|
||||
}
|
||||
}
|
||||
}, [containerRef])
|
||||
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className="tw-w-full tw-h-full tw-rounded-md tw-border tw-border-solid tw-overflow-hidden">
|
||||
<table className="tw-w-full tw-h-full">
|
||||
<tbody className="">
|
||||
{Array.from({ length: rows }).map((_, rowIndex) => (
|
||||
<tr key={rowIndex} className="">
|
||||
{Array.from({ length: cols }).map((_, colIndex) => (
|
||||
<td key={colIndex} className="tw-border tw-border-solid tw-border-gray-400 tw-p-2">
|
||||
{/* Row {rowIndex + 1}, Col {colIndex + 1} */}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
class PandasTable extends TkinterBase{
|
||||
|
||||
static widgetType = "pandas_table"
|
||||
|
||||
static requiredImports = [
|
||||
...TkinterBase.requiredImports,
|
||||
"import os",
|
||||
"from pandastable import Table",
|
||||
]
|
||||
|
||||
static requirements = ["pandastable"]
|
||||
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.droppableTags = null
|
||||
|
||||
const newAttrs = removeKeyFromObject("layout", this.state.attrs)
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
widgetName: "Pandas Table",
|
||||
size: { width: 400, height: 250 },
|
||||
attrs: {
|
||||
...newAttrs,
|
||||
styling: {
|
||||
...newAttrs.styling,
|
||||
textColor: {
|
||||
label: "Text Color",
|
||||
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
|
||||
value: "",
|
||||
onChange: (value) => {
|
||||
this.setAttrValue("styling.textColor", value)
|
||||
}
|
||||
},
|
||||
cellBg: {
|
||||
label: "Cell background",
|
||||
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
|
||||
value: "",
|
||||
onChange: (value) => {
|
||||
this.setAttrValue("styling.textColor", value)
|
||||
}
|
||||
},
|
||||
outlineColor: {
|
||||
label: "Box outline color",
|
||||
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
|
||||
value: "",
|
||||
onChange: (value) => {
|
||||
this.setAttrValue("styling.textColor", value)
|
||||
}
|
||||
},
|
||||
},
|
||||
defaultTable: {
|
||||
label: "Default table",
|
||||
tool: Tools.UPLOADED_LIST,
|
||||
toolProps: {filterOptions: ["text/csv"]},
|
||||
value: "",
|
||||
onChange: (value) => this.setAttrValue("defaultTable", value)
|
||||
},
|
||||
enableEdit: {
|
||||
label: "Enable editing",
|
||||
tool: Tools.CHECK_BUTTON,
|
||||
value: false,
|
||||
onChange: (value) => {
|
||||
this.setAttrValue("enableEdit", value)
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// componentDidMount(){
|
||||
// super.componentDidMount()
|
||||
// this.setWidgetName("Pandas Table")
|
||||
// this.setAttrValue("styling.backgroundColor", "#E4E2E2")
|
||||
// }
|
||||
|
||||
generateCode(variableName, parent){
|
||||
|
||||
const defaultTable = this.getAttrValue("defaultTable")
|
||||
|
||||
const textColor = this.getAttrValue("styling.textColor")
|
||||
const cellBg = this.getAttrValue("styling.cellBg")
|
||||
const outlineColor = this.getAttrValue("styling.outlineColor")
|
||||
|
||||
const enableEdit = this.getAttrValue("enableEdit")
|
||||
|
||||
const code = [
|
||||
`${variableName}_table_frame = tk.Frame(master=${parent})`,
|
||||
`${variableName}_table_frame.${this.getLayoutCode()}`,
|
||||
|
||||
`${variableName} = Table(parent=${variableName}_table_frame)`,
|
||||
`${variableName}.editable = ${enableEdit ? "True" : "False"}`,
|
||||
]
|
||||
|
||||
if (textColor){
|
||||
code.push(`${variableName}.textColor = "${textColor}"`)
|
||||
}
|
||||
if (cellBg){
|
||||
code.push(`${variableName}.cellbackgr = "${cellBg}"`)
|
||||
}
|
||||
|
||||
if (outlineColor){
|
||||
code.push(`${variableName}.boxoutlinecolor = "${outlineColor}"`)
|
||||
}
|
||||
|
||||
if (defaultTable){
|
||||
code.push(`${variableName}.importCSV(${getPythonAssetPath(defaultTable.name, "text/csv")})`)
|
||||
}
|
||||
|
||||
return [
|
||||
...code,
|
||||
`${variableName}.show()`,
|
||||
]
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
size: toolBarAttrs.size,
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
renderContent(){
|
||||
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-h-full tw-content-start tw-pointer-events-none"
|
||||
style={this.state.widgetInnerStyling}>
|
||||
<ResizableTable />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default PandasTable
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* {'align': 'w',
|
||||
'cellbackgr': '#F4F4F3',
|
||||
'cellwidth': 80,
|
||||
'floatprecision': 2,
|
||||
'thousandseparator': '',
|
||||
'font': 'Arial',
|
||||
'fontsize': 12,
|
||||
'fontstyle': '',
|
||||
'grid_color': '#ABB1AD',
|
||||
'linewidth': 1,
|
||||
'rowheight': 22,
|
||||
'rowselectedcolor': '#E4DED4',
|
||||
'textcolor': 'black'}
|
||||
*/
|
||||
28
src/frameworks/customtk/plugins/styles/timepickerStyle.css
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
.atp-time {
|
||||
background-color: var(--time-display, black) !important;
|
||||
text-align: center !important;
|
||||
font-size: 35px;
|
||||
}
|
||||
|
||||
.atp-clock-cnt {
|
||||
background-color: var(--bg, white) !important;
|
||||
}
|
||||
|
||||
.atp-face-color {
|
||||
background-color: var(--clock-color, #EEEEEE) !important;
|
||||
}
|
||||
|
||||
.atp-n {
|
||||
color: var(--number-color, #000) !important;
|
||||
}
|
||||
|
||||
/* .atp-color--primary {
|
||||
background-color: var(--main-handle-color, #000000c0) !important;
|
||||
} */
|
||||
|
||||
|
||||
.atp-h-cnt-cnt .atp-h-ctn, .atp-h, .atp-b, .atp-h-dot{
|
||||
/* affects only the handle*/
|
||||
background-color: var(--main-handle-color, #000000c0) !important;
|
||||
}
|
||||
126
src/frameworks/customtk/plugins/videoPlayer.js
Normal file
@@ -0,0 +1,126 @@
|
||||
import React from "react"
|
||||
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
import { removeKeyFromObject } from "../../../utils/common"
|
||||
|
||||
import VideoImage from "./assets/video.jpg"
|
||||
import { PlayCircleFilled } from "@ant-design/icons"
|
||||
import { TkinterBase } from "../widgets/base"
|
||||
import { getPythonAssetPath } from "../../utils/pythonFilePath"
|
||||
|
||||
|
||||
class VideoPlayer extends TkinterBase{
|
||||
|
||||
static widgetType = "video_player"
|
||||
|
||||
static requiredImports = [
|
||||
...TkinterBase.requiredImports,
|
||||
"import os",
|
||||
"from tkVideoPlayer import TkinterVideo"
|
||||
]
|
||||
|
||||
static requirements = ["tkvideoplayer"]
|
||||
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.droppableTags = null
|
||||
|
||||
const newAttrs = removeKeyFromObject("layout", this.state.attrs)
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
size: { width: 350, height: 200 },
|
||||
widgetName: "Video player",
|
||||
attrs: {
|
||||
...newAttrs,
|
||||
play: {
|
||||
label: "Start playing",
|
||||
tool: Tools.CHECK_BUTTON,
|
||||
value: false,
|
||||
onChange: (value) => {
|
||||
this.setAttrValue("play", value)
|
||||
}
|
||||
|
||||
},
|
||||
defaultVideo: {
|
||||
label: "Video",
|
||||
tool: Tools.UPLOADED_LIST,
|
||||
toolProps: {filterOptions: ["video/mp4", "video/webm", "video/m4v"]},
|
||||
value: "",
|
||||
onChange: (value) => this.setAttrValue("defaultVideo", value)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
// this.setAttrValue("styling.backgroundColor", "#E4E2E2")
|
||||
}
|
||||
|
||||
generateCode(variableName, parent){
|
||||
|
||||
|
||||
const defaultVideo = this.getAttrValue("defaultVideo")
|
||||
const play = this.getAttrValue("play")
|
||||
|
||||
const code = [
|
||||
`${variableName} = TkinterVideo(master=${parent}, scaled=True)`,
|
||||
]
|
||||
|
||||
if (defaultVideo){
|
||||
code.push(`${variableName}.load(${getPythonAssetPath(defaultVideo.name, "video")})`)
|
||||
}
|
||||
|
||||
if (play){
|
||||
code.push(`${variableName}.play()`)
|
||||
}
|
||||
|
||||
return [
|
||||
...code,
|
||||
`${variableName}.${this.getLayoutCode()}`
|
||||
]
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
size: toolBarAttrs.size,
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
renderContent(){
|
||||
|
||||
// const defaultVideo = this.getAttrValue("defaultVideo")
|
||||
|
||||
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-h-full tw-content-start tw-pointer-events-none"
|
||||
style={this.state.widgetInnerStyling}>
|
||||
<div className="tw-relative tw-w-full tw-h-full">
|
||||
<div className="tw-absolute tw-text-white tw-left-1/2 tw-top-1/2
|
||||
tw--translate-x-1/2 tw--translate-y-1/2">
|
||||
<PlayCircleFilled className="tw-text-4xl"/>
|
||||
</div>
|
||||
<img src={VideoImage} className="tw-w-full tw-h-full" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default VideoPlayer
|
||||
59
src/frameworks/customtk/sidebarPlugins.js
Normal file
@@ -0,0 +1,59 @@
|
||||
|
||||
import ClockImage from "./assets/widgets/plugins/clock.png"
|
||||
import VideoImage from "./assets/widgets/plugins/video.png"
|
||||
import MapImage from "./assets/widgets/plugins/map.png"
|
||||
import DataTableImage from "./assets/widgets/plugins/tables.png"
|
||||
|
||||
import AnalogTimePicker from "./plugins/analogTimepicker"
|
||||
import VideoPlayer from "./plugins/videoPlayer"
|
||||
import MapView from "./plugins/mapview"
|
||||
import PandasTable from "./plugins/pandasTable"
|
||||
|
||||
// TODO: add license for 3rd party plugins
|
||||
|
||||
const TkinterPluginWidgets = [
|
||||
{
|
||||
name: "Analog TimePicker",
|
||||
img: ClockImage,
|
||||
link: "https://github.com/PaulleDemon/tkTimePicker",
|
||||
widgetClass: AnalogTimePicker,
|
||||
license: {
|
||||
name: "MIT",
|
||||
url: ""
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Video Player",
|
||||
img: VideoImage,
|
||||
link: "https://github.com/PaulleDemon/tkVideoPlayer",
|
||||
widgetClass: VideoPlayer,
|
||||
license: {
|
||||
name: "MIT",
|
||||
url: ""
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Map viewer",
|
||||
img: MapImage,
|
||||
link: "https://github.com/TomSchimansky/TkinterMapView",
|
||||
widgetClass: MapView,
|
||||
license: {
|
||||
name: "CC0 1.0",
|
||||
url: "https://github.com/TomSchimansky/TkinterMapView/blob/main/LICENSE.txt"
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Pandas Table",
|
||||
img: DataTableImage,
|
||||
link: "https://github.com/dmnfarrell/pandastable",
|
||||
widgetClass: PandasTable,
|
||||
license: {
|
||||
name: "GPL v3",
|
||||
url: "https://github.com/dmnfarrell/pandastable/blob/master/LICENSE"
|
||||
}
|
||||
},
|
||||
|
||||
]
|
||||
|
||||
|
||||
export default TkinterPluginWidgets
|
||||
131
src/frameworks/customtk/sidebarWidgets.js
Normal file
@@ -0,0 +1,131 @@
|
||||
|
||||
|
||||
import MainWindow from "./widgets/mainWindow"
|
||||
import TopLevel from "./widgets/toplevel"
|
||||
import Frame from "./widgets/frame"
|
||||
import Label from "./widgets/label"
|
||||
import Button from "./widgets/button"
|
||||
import OptionMenu from "./widgets/optionMenu"
|
||||
import Slider from "./widgets/slider"
|
||||
import { CheckBox, RadioButton } from "./widgets/ checkButton"
|
||||
import { Input, Text } from "./widgets/input"
|
||||
import SpinBox from "./widgets/spinBox"
|
||||
|
||||
import MainWindowImage from "./assets/widgets/main/mainwindow.png"
|
||||
import TopLevelImage from "./assets/widgets/main/Toplevel.png"
|
||||
import FrameImage from "./assets/widgets/main/frame2.png"
|
||||
import LabelImage from "./assets/widgets/main/label.png"
|
||||
import ButtonImage from "./assets/widgets/main/button.png"
|
||||
import InputImage from "./assets/widgets/main/input.png"
|
||||
import TextAreaImage from "./assets/widgets/main/textarea.png"
|
||||
import SliderImage from "./assets/widgets/main/slider.png"
|
||||
import DropDownImage from "./assets/widgets/main/dropdown.png"
|
||||
import CheckButtonImage from "./assets/widgets/main/check.png"
|
||||
import RadioButtonImage from "./assets/widgets/main/radio.png"
|
||||
import SpinBoxImage from "./assets/widgets/main/spinbox.png"
|
||||
|
||||
|
||||
const TkinterWidgets = [
|
||||
{
|
||||
name: "Main window",
|
||||
img: MainWindowImage,
|
||||
link: "https://github.com",
|
||||
widgetClass: MainWindow
|
||||
},
|
||||
{
|
||||
name: "Top Level",
|
||||
img: TopLevelImage,
|
||||
link: "https://github.com",
|
||||
widgetClass: TopLevel
|
||||
},
|
||||
{
|
||||
name: "Frame",
|
||||
img: FrameImage,
|
||||
link: "https://github.com",
|
||||
widgetClass: Frame
|
||||
},
|
||||
{
|
||||
name: "Label",
|
||||
img: LabelImage,
|
||||
link: "https://github.com",
|
||||
widgetClass: Label
|
||||
},
|
||||
{
|
||||
name: "Button",
|
||||
img: ButtonImage,
|
||||
link: "https://github.com",
|
||||
widgetClass: Button
|
||||
},
|
||||
{
|
||||
name: "Entry",
|
||||
img: InputImage,
|
||||
link: "https://github.com",
|
||||
widgetClass: Input
|
||||
},
|
||||
{
|
||||
name: "Text",
|
||||
img: TextAreaImage,
|
||||
link: "https://github.com",
|
||||
widgetClass: Text
|
||||
},
|
||||
{
|
||||
name: "CheckBox",
|
||||
img: CheckButtonImage,
|
||||
link: "https://github.com",
|
||||
widgetClass: CheckBox
|
||||
},
|
||||
{
|
||||
name: "Radio button",
|
||||
img: RadioButtonImage,
|
||||
link: "https://github.com",
|
||||
widgetClass: RadioButton
|
||||
},
|
||||
{
|
||||
name: "Scale",
|
||||
img: SliderImage,
|
||||
link: "https://github.com",
|
||||
widgetClass: Slider
|
||||
},
|
||||
{
|
||||
name: "Option Menu",
|
||||
img: DropDownImage,
|
||||
link: "https://github.com",
|
||||
widgetClass: OptionMenu
|
||||
},
|
||||
{
|
||||
name: "Spinbox",
|
||||
img: SpinBoxImage,
|
||||
link: "https://github.com",
|
||||
widgetClass: SpinBox
|
||||
},
|
||||
|
||||
]
|
||||
|
||||
|
||||
export default TkinterWidgets
|
||||
|
||||
|
||||
/**
|
||||
* widgets = {
|
||||
"Tk": set(),
|
||||
"Label": set(),
|
||||
"Button": set(),
|
||||
"Entry": set(),
|
||||
"CheckButton": set(),
|
||||
"RadioButton": set(),
|
||||
"Scale": set(),
|
||||
"ListBox": set(),
|
||||
"Frame": set(),
|
||||
"LabelFrame": set(),
|
||||
"PanedWindow": set(),
|
||||
"SpinBox": set(),
|
||||
"OptionMenu": set(),
|
||||
"Canvas": set(),
|
||||
"TopLevel": set(),
|
||||
"Message": set(),
|
||||
"Menu": set(),
|
||||
"MenuButton": set(),
|
||||
"ScrollBar": set(),
|
||||
"Text": set()
|
||||
}
|
||||
*/
|
||||
240
src/frameworks/customtk/widgets/ checkButton.js
Normal file
@@ -0,0 +1,240 @@
|
||||
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
import { convertObjectToKeyValueString, removeKeyFromObject } from "../../../utils/common"
|
||||
import { CheckSquareFilled } from "@ant-design/icons"
|
||||
import { TkinterWidgetBase } from "./base"
|
||||
|
||||
|
||||
export class CheckBox extends TkinterWidgetBase{
|
||||
|
||||
static widgetType = "check_button"
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
// const {layout, ...newAttrs} = this.state.attrs // Removes the layout attribute
|
||||
|
||||
let newAttrs = removeKeyFromObject("layout", this.state.attrs)
|
||||
|
||||
this.minSize = {width: 50, height: 30}
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
size: { width: 120, height: 30 },
|
||||
widgetName: "Check box",
|
||||
attrs: {
|
||||
...newAttrs,
|
||||
styling: {
|
||||
...newAttrs.styling,
|
||||
foregroundColor: {
|
||||
label: "Foreground Color",
|
||||
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
|
||||
value: "#000",
|
||||
onChange: (value) => {
|
||||
this.setWidgetInnerStyle("color", value)
|
||||
this.setAttrValue("styling.foregroundColor", value)
|
||||
}
|
||||
}
|
||||
},
|
||||
checkLabel: {
|
||||
label: "Check Label",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: {placeholder: "Button label", maxLength: 100},
|
||||
value: "Checkbox",
|
||||
onChange: (value) => this.setAttrValue("checkLabel", value)
|
||||
},
|
||||
defaultChecked: {
|
||||
label: "Checked",
|
||||
tool: Tools.CHECK_BUTTON, // the tool to display, can be either HTML ELement or a constant string
|
||||
value: true,
|
||||
onChange: (value) => this.setAttrValue("defaultChecked", value)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
// this.setAttrValue("styling.backgroundColor", "#fff")
|
||||
this.setWidgetInnerStyle("backgroundColor", "#fff0")
|
||||
}
|
||||
|
||||
generateCode(variableName, parent){
|
||||
|
||||
const labelText = this.getAttrValue("checkLabel")
|
||||
const config = convertObjectToKeyValueString(this.getConfigCode())
|
||||
|
||||
const code = [
|
||||
`${variableName} = tk.Checkbutton(master=${parent}, text="${labelText}")`,
|
||||
`${variableName}.config(${config})`,
|
||||
]
|
||||
|
||||
if (this.getAttrValue("defaultChecked")){
|
||||
code.push(`${variableName}.select()`)
|
||||
}
|
||||
|
||||
code.push(`${variableName}.${this.getLayoutCode()}`)
|
||||
|
||||
return code
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
const attrs = this.state.attrs
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
checkLabel: attrs.checkLabel,
|
||||
size: toolBarAttrs.size,
|
||||
...attrs,
|
||||
})
|
||||
}
|
||||
|
||||
renderContent(){
|
||||
return (
|
||||
<div className="tw-flex tw-p-1 tw-w-full tw-h-full tw-rounded-md tw-overflow-hidden"
|
||||
style={this.getInnerRenderStyling()}
|
||||
>
|
||||
|
||||
<div className="tw-flex tw-gap-2 tw-w-full tw-h-full tw-place-items-center tw-place-content-center">
|
||||
<div className="tw-border-solid tw-border-[#D9D9D9] tw-border-2
|
||||
tw-min-w-[20px] tw-min-h-[20px] tw-w-[20px] tw-h-[20px]
|
||||
tw-text-blue-600 tw-flex tw-items-center tw-justify-center
|
||||
tw-rounded-md tw-overflow-hidden">
|
||||
{
|
||||
this.getAttrValue("defaultChecked") === true &&
|
||||
<CheckSquareFilled className="tw-text-[20px]" />
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
{this.getAttrValue("checkLabel")}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export class RadioButton extends TkinterWidgetBase{
|
||||
// FIXME: the radio buttons are not visible because of the default heigh provided
|
||||
|
||||
static widgetType = "radio_button"
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.minSize = {width: 50, height: 30}
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
size: { width: 80, height: 30 },
|
||||
fitContent: { width: true, height: true },
|
||||
widgetName: "Radio button",
|
||||
attrs: {
|
||||
...this.state.attrs,
|
||||
radios: {
|
||||
label: "Radio Group",
|
||||
tool: Tools.INPUT_RADIO_LIST,
|
||||
value: {inputs: ["default"], selectedRadio: -1},
|
||||
onChange: ({inputs, selectedRadio}) => {
|
||||
this.setAttrValue("radios", {inputs, selectedRadio})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
// this.setAttrValue("styling.backgroundColor", "#fff")
|
||||
this.setWidgetInnerStyle("backgroundColor", "#fff0")
|
||||
}
|
||||
|
||||
generateCode(variableName, parent){
|
||||
|
||||
const config = convertObjectToKeyValueString(this.getConfigCode())
|
||||
|
||||
|
||||
const code = [
|
||||
`${variableName}_var = tk.IntVar()`,
|
||||
]
|
||||
const radios = this.getAttrValue("radios")
|
||||
|
||||
radios.inputs.forEach((radio_text, idx) => {
|
||||
|
||||
const radioBtnVariable = `${variableName}_${idx}`
|
||||
code.push(`\n`)
|
||||
code.push(`${radioBtnVariable} = tk.Radiobutton(master=${parent}, variable=${variableName}_var, text="${radio_text}")`)
|
||||
code.push(`${radioBtnVariable}.config(${config}, value=${idx})`)
|
||||
code.push(`${radioBtnVariable}.${this.getLayoutCode()}`)
|
||||
})
|
||||
|
||||
const defaultSelected = radios.selectedRadio
|
||||
|
||||
if (defaultSelected !== -1){
|
||||
code.push(`${variableName}_var.set(${defaultSelected})`)
|
||||
}
|
||||
|
||||
|
||||
return code
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
const attrs = this.state.attrs
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
checkLabel: attrs.checkLabel,
|
||||
size: toolBarAttrs.size,
|
||||
...attrs,
|
||||
})
|
||||
}
|
||||
|
||||
renderContent(){
|
||||
|
||||
const {inputs, selectedRadio} = this.getAttrValue("radios")
|
||||
|
||||
return (
|
||||
<div className="tw-flex tw-p-1 tw-w-full tw-h-full tw-rounded-md tw-overflow-hidden"
|
||||
style={this.getInnerRenderStyling()}
|
||||
>
|
||||
<div className="tw-flex tw-flex-col tw-gap-2 tw-w-fit tw-h-fit">
|
||||
{
|
||||
inputs.map((value, index) => {
|
||||
return (
|
||||
<div key={index} className="tw-flex tw-gap-2 tw-w-full tw-h-full tw-place-items-center ">
|
||||
<div className="tw-border-solid tw-border-[#D9D9D9] tw-border-2
|
||||
tw-min-w-[20px] tw-min-h-[20px] tw-w-[20px] tw-h-[20px]
|
||||
tw-text-blue-600 tw-flex tw-items-center tw-justify-center
|
||||
tw-rounded-full tw-overflow-hidden tw-p-1">
|
||||
|
||||
{
|
||||
selectedRadio === index &&
|
||||
<div className="tw-rounded-full tw-bg-blue-600 tw-w-full tw-h-full">
|
||||
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<span className="tw-text-base" style={{color: this.state.widgetInnerStyling.foregroundColor}}>
|
||||
{value}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
601
src/frameworks/customtk/widgets/base.js
Normal file
@@ -0,0 +1,601 @@
|
||||
import { Layouts, PosType } from "../../../canvas/constants/layouts"
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
import Widget from "../../../canvas/widgets/base"
|
||||
import { convertObjectToKeyValueString, removeKeyFromObject } from "../../../utils/common"
|
||||
import { Tkinter_TO_WEB_CURSOR_MAPPING } from "../constants/cursor"
|
||||
import { Tkinter_To_GFonts } from "../constants/fontFamily"
|
||||
import { JUSTIFY, RELIEF } from "../constants/styling"
|
||||
|
||||
|
||||
|
||||
export class CustomTkBase extends Widget {
|
||||
|
||||
static requiredImports = ['import tkinter as tk']
|
||||
|
||||
static requirements = ['customtkinter']
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.getLayoutCode = this.getLayoutCode.bind(this)
|
||||
}
|
||||
|
||||
getLayoutCode(){
|
||||
const {layout: parentLayout, direction, gap, align="start"} = this.getParentLayout()
|
||||
|
||||
const absolutePositioning = this.getAttrValue("positioning")
|
||||
|
||||
let layoutManager = `pack()`
|
||||
|
||||
if (parentLayout === Layouts.PLACE || absolutePositioning){
|
||||
|
||||
const config = {
|
||||
x: this.state.pos.x,
|
||||
y: this.state.pos.y,
|
||||
}
|
||||
|
||||
config["width"] = this.state.size.width
|
||||
config["height"] = this.state.size.height
|
||||
|
||||
// if (!this.state.fitContent.width){
|
||||
// config["width"] = this.state.size.width
|
||||
// }
|
||||
// if (!this.state.fitContent.height){
|
||||
// config["height"] = this.state.size.height
|
||||
// }
|
||||
|
||||
const configStr = convertObjectToKeyValueString(config)
|
||||
|
||||
layoutManager = `place(${configStr})`
|
||||
|
||||
}else if (parentLayout === Layouts.FLEX){
|
||||
|
||||
const config = {
|
||||
side: direction === "row" ? "tk.LEFT" : "tk.TOP",
|
||||
}
|
||||
|
||||
if (gap > 0){
|
||||
config["padx"] = gap
|
||||
config["pady"] = gap
|
||||
}
|
||||
|
||||
if (align === "start"){
|
||||
config["anchor"] = "'nw'"
|
||||
}else if (align === "center"){
|
||||
config["anchor"] = "'center'"
|
||||
}else if (align === "end"){
|
||||
config["anchor"] = "'se'"
|
||||
}
|
||||
|
||||
const fillX = this.getAttrValue("flexManager.fillX")
|
||||
const fillY = this.getAttrValue("flexManager.fillY")
|
||||
const expand = this.getAttrValue("flexManager.expand")
|
||||
|
||||
if (fillX){
|
||||
config['fill'] = `"x"`
|
||||
}
|
||||
|
||||
if (fillY){
|
||||
config['fill'] = `"y"`
|
||||
}
|
||||
|
||||
if (fillX && fillY){
|
||||
config['fill'] = `"both"`
|
||||
}
|
||||
|
||||
if (expand){
|
||||
config['expand'] = "True"
|
||||
}
|
||||
|
||||
layoutManager = `pack(${convertObjectToKeyValueString(config)})`
|
||||
|
||||
}else if (parentLayout === Layouts.GRID){
|
||||
const row = this.getAttrValue("gridManager.row")
|
||||
const col = this.getAttrValue("gridManager.column")
|
||||
layoutManager = `grid(row=${row}, column=${col})`
|
||||
}
|
||||
|
||||
return layoutManager
|
||||
}
|
||||
|
||||
setParentLayout(layout){
|
||||
|
||||
if (!layout){
|
||||
return {}
|
||||
}
|
||||
|
||||
const {layout: parentLayout, direction, gap} = layout
|
||||
|
||||
// show attributes related to the layout manager
|
||||
let updates = {
|
||||
parentLayout: layout,
|
||||
}
|
||||
|
||||
// this.removeAttr("gridManager")
|
||||
// this.removeAttr("flexManager")
|
||||
// this.removeAttr("positioning")
|
||||
|
||||
// remove gridManager, flexManager positioning
|
||||
const {gridManager, flexManager, positioning, ...restAttrs} = this.state.attrs
|
||||
|
||||
if (parentLayout === Layouts.FLEX || parentLayout === Layouts.GRID) {
|
||||
|
||||
updates = {
|
||||
...updates,
|
||||
positionType: PosType.NONE,
|
||||
}
|
||||
// Allow optional absolute positioning if the parent layout is flex or grid
|
||||
const updateAttrs = {
|
||||
...restAttrs,
|
||||
positioning: {
|
||||
label: "Absolute positioning",
|
||||
tool: Tools.CHECK_BUTTON,
|
||||
value: false,
|
||||
onChange: (value) => {
|
||||
this.setAttrValue("positioning", value)
|
||||
|
||||
this.updateState({
|
||||
positionType: value ? PosType.ABSOLUTE : PosType.NONE,
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parentLayout === Layouts.FLEX){
|
||||
updates = {
|
||||
...updates,
|
||||
attrs: {
|
||||
...updateAttrs,
|
||||
flexManager: {
|
||||
label: "Flex Manager",
|
||||
display: "horizontal",
|
||||
fillX: {
|
||||
label: "Fill X",
|
||||
tool: Tools.CHECK_BUTTON,
|
||||
value: false,
|
||||
onChange: (value) => {
|
||||
this.setAttrValue("flexManager.fillX", value)
|
||||
const widgetStyle = {
|
||||
...this.state.widgetOuterStyling,
|
||||
flexGrow: value ? 1 : 0,
|
||||
}
|
||||
|
||||
this.updateState({
|
||||
widgetOuterStyling: widgetStyle,
|
||||
})
|
||||
|
||||
}
|
||||
},
|
||||
fillY: {
|
||||
label: "Fill Y",
|
||||
tool: Tools.CHECK_BUTTON,
|
||||
value: false,
|
||||
onChange: (value) => {
|
||||
this.setAttrValue("flexManager.fillY", value)
|
||||
|
||||
const widgetStyle = {
|
||||
...this.state.widgetOuterStyling,
|
||||
flexGrow: value ? 1 : 0,
|
||||
}
|
||||
this.updateState({
|
||||
widgetOuterStyling: widgetStyle,
|
||||
})
|
||||
}
|
||||
},
|
||||
expand: {
|
||||
label: "Expand",
|
||||
tool: Tools.CHECK_BUTTON,
|
||||
value: false,
|
||||
onChange: (value) => {
|
||||
this.setAttrValue("flexManager.expand", value)
|
||||
|
||||
const widgetStyle = {
|
||||
...this.state.widgetOuterStyling,
|
||||
flexGrow: value ? 1 : 0,
|
||||
}
|
||||
this.updateState({
|
||||
widgetOuterStyling: widgetStyle,
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (parentLayout === Layouts.GRID) {
|
||||
// Set attributes related to grid layout manager
|
||||
updates = {
|
||||
...updates,
|
||||
attrs: {
|
||||
...updateAttrs,
|
||||
gridManager: {
|
||||
label: "Grid manager",
|
||||
display: "horizontal",
|
||||
row: {
|
||||
label: "Row",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "width", max: 1000, min: 1 },
|
||||
value: 1,
|
||||
onChange: (value) => {
|
||||
|
||||
const previousRow = this.getWidgetOuterStyle("gridRow") || "1/1"
|
||||
|
||||
let [_row=1, rowSpan=1] = previousRow.replace(/\s+/g, '').split("/").map(Number)
|
||||
|
||||
if (value > rowSpan){
|
||||
// rowSpan should always be greater than or eq to row
|
||||
rowSpan = value
|
||||
this.setAttrValue("gridManager.rowSpan", rowSpan)
|
||||
}
|
||||
|
||||
this.setAttrValue("gridManager.row", value)
|
||||
this.setWidgetOuterStyle("gridRow", `${value+' / '+rowSpan}`)
|
||||
}
|
||||
},
|
||||
rowSpan: {
|
||||
label: "Row span",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "height", max: 1000, min: 1 },
|
||||
value: 1,
|
||||
onChange: (value) => {
|
||||
|
||||
const previousRow = this.getWidgetOuterStyle("gridRow") || "1/1"
|
||||
|
||||
const [row=1, _rowSpan=1] = previousRow.replace(/\s+/g, '').split("/").map(Number)
|
||||
|
||||
if (value < row){
|
||||
value = row + 1
|
||||
}
|
||||
|
||||
this.setAttrValue("gridManager.rowSpan", value)
|
||||
this.setWidgetOuterStyle("gridRow", `${row + ' / ' +value}`)
|
||||
}
|
||||
},
|
||||
column: {
|
||||
label: "Column",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "height", max: 1000, min: 1 },
|
||||
value: 1,
|
||||
onChange: (value) => {
|
||||
|
||||
const previousRow = this.getWidgetOuterStyle("gridColumn") || "1/1"
|
||||
|
||||
let [_col=1, colSpan=1] = previousRow.replace(/\s+/g, '').split("/").map(Number)
|
||||
|
||||
if (value > colSpan){
|
||||
// The colSpan has always be equal or greater than col
|
||||
colSpan = value
|
||||
this.setAttrValue("gridManager.columnSpan", colSpan)
|
||||
}
|
||||
|
||||
this.setAttrValue("gridManager.column", value)
|
||||
this.setWidgetOuterStyle("gridColumn", `${value +' / ' + colSpan}`)
|
||||
}
|
||||
},
|
||||
columnSpan: {
|
||||
label: "Column span",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "height", max: 1000, min: 1 },
|
||||
value: 1,
|
||||
onChange: (value) => {
|
||||
|
||||
const previousCol = this.getWidgetOuterStyle("gridColumn") || "1/1"
|
||||
|
||||
const [col=1, _colSpan=1] = previousCol.replace(/\s+/g, '').split("/").map(Number)
|
||||
|
||||
if (value < col){
|
||||
value = col + 1
|
||||
}
|
||||
|
||||
this.setAttrValue("gridManager.columnSpan", value)
|
||||
this.setWidgetOuterStyle("gridColumn", `${col + ' / ' + value}`)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if (parentLayout === Layouts.PLACE) {
|
||||
updates = {
|
||||
...updates,
|
||||
positionType: PosType.ABSOLUTE
|
||||
}
|
||||
}
|
||||
|
||||
this.updateState(updates)
|
||||
|
||||
return updates
|
||||
}
|
||||
|
||||
getInnerRenderStyling(){
|
||||
let {width, height, minWidth, minHeight} = this.getRenderSize()
|
||||
|
||||
const {layout: parentLayout, direction, gap} = this.getParentLayout() || {}
|
||||
|
||||
if (parentLayout === Layouts.FLEX){
|
||||
const fillX = this.getAttrValue("flexManager.fillX")
|
||||
const fillY = this.getAttrValue("flexManager.fillY")
|
||||
|
||||
// This is needed if fillX or fillY is true, as the parent is applied flex-grow
|
||||
|
||||
if (fillX || fillY){
|
||||
width = "100%"
|
||||
height = "100%"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const styling = {
|
||||
...this.state.widgetInnerStyling,
|
||||
width,
|
||||
height,
|
||||
minWidth,
|
||||
minHeight
|
||||
}
|
||||
return styling
|
||||
}
|
||||
|
||||
/**
|
||||
* loads the data
|
||||
* @param {object} data
|
||||
*/
|
||||
load(data, callback=null){
|
||||
|
||||
if (Object.keys(data).length === 0) return // no data to load
|
||||
|
||||
data = {...data} // create a shallow copy
|
||||
|
||||
const {attrs, parentLayout=null, ...restData} = data
|
||||
|
||||
|
||||
let layoutUpdates = {
|
||||
parentLayout: parentLayout
|
||||
}
|
||||
|
||||
if (parentLayout){
|
||||
if (parentLayout.layout === Layouts.FLEX || parentLayout.layout === Layouts.GRID){
|
||||
|
||||
layoutUpdates = {
|
||||
...layoutUpdates,
|
||||
positionType: PosType.NONE
|
||||
}
|
||||
|
||||
}else if (parentLayout.layout === Layouts.PLACE){
|
||||
layoutUpdates = {
|
||||
...layoutUpdates,
|
||||
positionType: PosType.ABSOLUTE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const newData = {
|
||||
...restData,
|
||||
...layoutUpdates
|
||||
}
|
||||
|
||||
|
||||
this.setState(newData, () => {
|
||||
let layoutAttrs = this.setParentLayout(parentLayout).attrs || {}
|
||||
|
||||
// UPdates attrs
|
||||
let newAttrs = { ...this.state.attrs, ...layoutAttrs }
|
||||
|
||||
// Iterate over each path in the updates object
|
||||
Object.entries(attrs).forEach(([path, value]) => {
|
||||
const keys = path.split('.')
|
||||
const lastKey = keys.pop()
|
||||
|
||||
// Traverse the nested object within attrs
|
||||
let nestedObject = newAttrs
|
||||
|
||||
keys.forEach(key => {
|
||||
nestedObject[key] = { ...nestedObject[key] } // Ensure immutability for each nested level
|
||||
nestedObject = nestedObject[key]
|
||||
})
|
||||
|
||||
// Set the value at the last key
|
||||
if (nestedObject[lastKey])
|
||||
nestedObject[lastKey].value = value
|
||||
})
|
||||
|
||||
|
||||
if (newAttrs?.styling?.backgroundColor){
|
||||
// TODO: find a better way to apply innerStyles
|
||||
this.setWidgetInnerStyle("backgroundColor", newAttrs.styling.backgroundColor.value)
|
||||
}
|
||||
|
||||
this.updateState({ attrs: newAttrs }, callback)
|
||||
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// base for widgets that have common base properties such as bg, fg, cursor etc
|
||||
export class CustomTkWidgetBase extends CustomTkBase{
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.droppableTags = null // disables drops
|
||||
|
||||
const newAttrs = removeKeyFromObject("layout", this.state.attrs)
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
attrs: {
|
||||
...newAttrs,
|
||||
styling: {
|
||||
...newAttrs.styling,
|
||||
foregroundColor: {
|
||||
label: "Foreground Color",
|
||||
tool: Tools.COLOR_PICKER,
|
||||
value: "#000",
|
||||
onChange: (value) => {
|
||||
this.setWidgetInnerStyle("color", value)
|
||||
this.setAttrValue("styling.foregroundColor", value)
|
||||
}
|
||||
},
|
||||
borderWidth: {
|
||||
label: "Border thickness",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: {min: 0, max: 10},
|
||||
value: 0,
|
||||
onChange: (value) => {
|
||||
this.setWidgetInnerStyle("border", `${value}px solid black`)
|
||||
this.setAttrValue("styling.borderWidth", value)
|
||||
}
|
||||
},
|
||||
borderRadius: {
|
||||
label: "Border radius",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: {min: 0, max: 140},
|
||||
value: 0,
|
||||
onChange: (value) => {
|
||||
this.setWidgetInnerStyle("borderRadius", `${value}px`)
|
||||
this.setAttrValue("styling.borderRadius", value)
|
||||
}
|
||||
}
|
||||
// justify: {
|
||||
// label: "Justify",
|
||||
// tool: Tools.SELECT_DROPDOWN,
|
||||
// options: JUSTIFY.map((val) => ({value: val, label: val})),
|
||||
// value: "",
|
||||
// onChange: (value) => {
|
||||
// this.setWidgetInnerStyle("text-align", value)
|
||||
// this.setAttrValue("styling.justify", value)
|
||||
// }
|
||||
// }
|
||||
},
|
||||
padding: {
|
||||
label: "padding",
|
||||
padX: {
|
||||
label: "Pad X",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: {min: 1, max: 140},
|
||||
value: null,
|
||||
onChange: (value) => {
|
||||
// this.setWidgetInnerStyle("paddingLeft", `${value}px`)
|
||||
// this.setWidgetInnerStyle("paddingRight", `${value}px`)
|
||||
|
||||
const widgetStyle = {
|
||||
...this.state.widgetInnerStyling,
|
||||
paddingLeft: `${value}px`,
|
||||
paddingRight: `${value}px`
|
||||
}
|
||||
this.setState({
|
||||
|
||||
widgetInnerStyling: widgetStyle
|
||||
})
|
||||
|
||||
|
||||
this.setAttrValue("padding.padX", value)
|
||||
}
|
||||
},
|
||||
padY: {
|
||||
label: "Pad Y",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: {min: 1, max: 140},
|
||||
value: null,
|
||||
onChange: (value) => {
|
||||
const widgetStyle = {
|
||||
...this.state.widgetInnerStyling,
|
||||
paddingTop: `${value}px`,
|
||||
paddingBottom: `${value}px`
|
||||
}
|
||||
this.setState({
|
||||
|
||||
widgetInnerStyling: widgetStyle
|
||||
})
|
||||
this.setAttrValue("padding.padX", value)
|
||||
}
|
||||
},
|
||||
},
|
||||
font: {
|
||||
label: "font",
|
||||
fontFamily: {
|
||||
label: "font family",
|
||||
tool: Tools.SELECT_DROPDOWN,
|
||||
options: Object.keys(Tkinter_To_GFonts).map((val) => ({value: val, label: val})),
|
||||
value: "",
|
||||
onChange: (value) => {
|
||||
this.setWidgetInnerStyle("fontFamily", Tkinter_To_GFonts[value])
|
||||
this.setAttrValue("font.fontFamily", value)
|
||||
}
|
||||
},
|
||||
fontSize: {
|
||||
label: "font size",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: {min: 3, max: 140},
|
||||
value: null,
|
||||
onChange: (value) => {
|
||||
this.setWidgetInnerStyle("fontSize", `${value}px`)
|
||||
this.setAttrValue("font.fontSize", value)
|
||||
}
|
||||
}
|
||||
},
|
||||
cursor: {
|
||||
label: "Cursor",
|
||||
tool: Tools.SELECT_DROPDOWN,
|
||||
toolProps: {placeholder: "select cursor"},
|
||||
value: "",
|
||||
options: Object.keys(Tkinter_TO_WEB_CURSOR_MAPPING).map((val) => ({value: val, label: val})),
|
||||
onChange: (value) => {
|
||||
this.setWidgetInnerStyle("cursor", Tkinter_TO_WEB_CURSOR_MAPPING[value])
|
||||
this.setAttrValue("cursor", value)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
this.getConfigCode = this.getConfigCode.bind(this)
|
||||
}
|
||||
|
||||
getConfigCode(){
|
||||
|
||||
const config = {
|
||||
fg_color: `"${this.getAttrValue("styling.backgroundColor")}"`,
|
||||
text_color: `"${this.getAttrValue("styling.foregroundColor")}"`,
|
||||
}
|
||||
|
||||
if (this.getAttrValue("styling.borderWidth"))
|
||||
config["border_width"] = this.getAttrValue("styling.borderWidth")
|
||||
|
||||
if (this.getAttrValue("font.fontFamily") || this.getAttrValue("font.fontSize")){
|
||||
config["font"] = `("${this.getAttrValue("font.fontFamily")}", ${this.getAttrValue("font.fontSize") || 12}, )`
|
||||
}
|
||||
|
||||
if (this.getAttrValue("cursor"))
|
||||
config["cursor"] = `"${this.getAttrValue("cursor")}"`
|
||||
|
||||
if (this.getAttrValue("padding.padX")){
|
||||
config["padx"] = this.getAttrValue("padding.padX")
|
||||
}
|
||||
|
||||
if (this.getAttrValue("padding.padY")){
|
||||
config["pady"] = this.getAttrValue("padding.padY")
|
||||
}
|
||||
|
||||
// FIXME: add width and height, the scales may not be correct as the width and height are based on characters in pack and grid not pixels
|
||||
|
||||
// if (!this.state.fitContent.width){
|
||||
// config["width"] = this.state.size.width
|
||||
// }
|
||||
// if (!this.state.fitContent.height){
|
||||
// config["height"] = this.state.size.height
|
||||
// }
|
||||
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
}
|
||||
84
src/frameworks/customtk/widgets/button.js
Normal file
@@ -0,0 +1,84 @@
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
import { convertObjectToKeyValueString } from "../../../utils/common"
|
||||
import { TkinterWidgetBase } from "./base"
|
||||
|
||||
|
||||
class Button extends TkinterWidgetBase{
|
||||
|
||||
static widgetType = "button"
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
size: { width: 80, height: 40 },
|
||||
widgetName: "Button",
|
||||
attrs: {
|
||||
...this.state.attrs,
|
||||
buttonLabel: {
|
||||
label: "Button Label",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: {placeholder: "Button label", maxLength: 100},
|
||||
value: "Button",
|
||||
onChange: (value) => this.setAttrValue("buttonLabel", value)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setAttrValue("styling.backgroundColor", "#E4E2E2")
|
||||
}
|
||||
|
||||
generateCode(variableName, parent){
|
||||
|
||||
const labelText = this.getAttrValue("buttonLabel")
|
||||
|
||||
const config = convertObjectToKeyValueString(this.getConfigCode())
|
||||
|
||||
return [
|
||||
`${variableName} = tk.Button(master=${parent}, text="${labelText}")`,
|
||||
`${variableName}.config(${config})`,
|
||||
`${variableName}.${this.getLayoutCode()}`
|
||||
]
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
buttonLabel: this.state.attrs.buttonLabel,
|
||||
size: toolBarAttrs.size,
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
renderContent(){
|
||||
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.getInnerRenderStyling()}>
|
||||
{/* {this.props.children} */}
|
||||
<div className="tw-text-sm" style={{color: this.getAttrValue("styling.foregroundColor")}}>
|
||||
{this.getAttrValue("buttonLabel")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default Button
|
||||
58
src/frameworks/customtk/widgets/frame.js
Normal file
@@ -0,0 +1,58 @@
|
||||
import Widget from "../../../canvas/widgets/base"
|
||||
import {TkinterBase} from "./base"
|
||||
|
||||
|
||||
class Frame extends TkinterBase{
|
||||
|
||||
static widgetType = "frame"
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.droppableTags = {
|
||||
exclude: ["image", "video", "media", "toplevel", "main_window"]
|
||||
}
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
fitContent: {width: true, height: true},
|
||||
widgetName: "Frame"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setAttrValue("styling.backgroundColor", "#EDECEC")
|
||||
}
|
||||
|
||||
generateCode(variableName, parent){
|
||||
|
||||
const bg = this.getAttrValue("styling.backgroundColor")
|
||||
|
||||
return [
|
||||
`${variableName} = tk.Frame(master=${parent})`,
|
||||
`${variableName}.config(bg="${bg}")`,
|
||||
`${variableName}.${this.getLayoutCode()}`
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
||||
renderContent(){
|
||||
// console.log("bounding rect: ", this.getBoundingRect())
|
||||
|
||||
// console.log("widget styling: ", this.state.widgetInnerStyling)
|
||||
return (
|
||||
<div className="tw-w-flex tw-flex-col tw-w-full tw-h-full tw-relative tw-rounded-md tw-overflow-hidden">
|
||||
<div className="tw-p-2 tw-w-full tw-h-full tw-content-start" style={this.getInnerRenderStyling()}>
|
||||
{this.props.children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default Frame
|
||||
150
src/frameworks/customtk/widgets/input.js
Normal file
@@ -0,0 +1,150 @@
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
import { convertObjectToKeyValueString } from "../../../utils/common"
|
||||
import { TkinterWidgetBase } from "./base"
|
||||
|
||||
|
||||
export class Input extends TkinterWidgetBase{
|
||||
|
||||
static widgetType = "entry"
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
size: { width: 120, height: 40 },
|
||||
widgetName: "Entry",
|
||||
attrs: {
|
||||
...this.state.attrs,
|
||||
placeHolder: {
|
||||
label: "PlaceHolder",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: {placeholder: "text", maxLength: 100},
|
||||
value: "placeholder text",
|
||||
onChange: (value) => this.setAttrValue("placeHolder", value)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setAttrValue("styling.backgroundColor", "#fff")
|
||||
}
|
||||
|
||||
generateCode(variableName, parent){
|
||||
|
||||
const placeHolderText = this.getAttrValue("placeHolder")
|
||||
|
||||
const config = convertObjectToKeyValueString(this.getConfigCode())
|
||||
|
||||
return [
|
||||
`${variableName} = tk.Entry(master=${parent}, text="${placeHolderText}")`,
|
||||
`${variableName}.config(${config})`,
|
||||
`${variableName}.${this.getLayoutCode()}`
|
||||
]
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
placeHolder: this.state.attrs.placeHolder,
|
||||
size: toolBarAttrs.size,
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
renderContent(){
|
||||
return (
|
||||
<div className="tw-w-flex tw-flex-col tw-w-full tw-h-full tw-rounded-md tw-overflow-hidden">
|
||||
<div className="tw-p-2 tw-w-full tw-h-full tw-flex tw-place-items-center"
|
||||
style={this.getInnerRenderStyling()}>
|
||||
<div className="tw-text-sm tw-text-gray-300">
|
||||
{this.getAttrValue("placeHolder")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export class Text extends TkinterWidgetBase{
|
||||
|
||||
static widgetType = "Text"
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
size: { width: 120, height: 80 },
|
||||
attrs: {
|
||||
...this.state.attrs,
|
||||
// placeHolder: {
|
||||
// label: "PlaceHolder",
|
||||
// tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
// toolProps: {placeholder: "text", maxLength: 100},
|
||||
// value: "placeholder text",
|
||||
// onChange: (value) => this.setAttrValue("placeHolder", value)
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setAttrValue("styling.backgroundColor", "#fff")
|
||||
this.setWidgetName("text")
|
||||
}
|
||||
|
||||
generateCode(variableName, parent){
|
||||
|
||||
const placeHolderText = this.getAttrValue("placeHolder")
|
||||
|
||||
const config = convertObjectToKeyValueString(this.getConfigCode())
|
||||
|
||||
return [
|
||||
`${variableName} = tk.Text(master=${parent})`,
|
||||
`${variableName}.config(${config})`,
|
||||
`${variableName}.${this.getLayoutCode()}`
|
||||
]
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
placeHolder: this.state.attrs.placeHolder,
|
||||
size: toolBarAttrs.size,
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
renderContent(){
|
||||
return (
|
||||
<div className="tw-w-flex tw-flex-col tw-w-full tw-h-full tw-rounded-md tw-overflow-hidden">
|
||||
<div className="tw-p-2 tw-w-full tw-h-full tw-content-start "
|
||||
style={this.getInnerRenderStyling()}>
|
||||
<div className="tw-text-sm tw-text-gray-300">
|
||||
{this.getAttrValue("placeHolder")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
142
src/frameworks/customtk/widgets/label.js
Normal file
@@ -0,0 +1,142 @@
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
import { convertObjectToKeyValueString } from "../../../utils/common"
|
||||
import { getPythonAssetPath } from "../../utils/pythonFilePath"
|
||||
import { TkinterWidgetBase } from "./base"
|
||||
|
||||
|
||||
class Label extends TkinterWidgetBase{
|
||||
|
||||
static widgetType = "label"
|
||||
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
widgetName: "Label",
|
||||
size: { width: 80, height: 40 },
|
||||
attrs: {
|
||||
...this.state.attrs,
|
||||
labelWidget: {
|
||||
label: "Text",
|
||||
tool: Tools.INPUT,
|
||||
toolProps: {placeholder: "text", maxLength: 100},
|
||||
value: "Label",
|
||||
onChange: (value) => this.setAttrValue("labelWidget", value)
|
||||
},
|
||||
imageUpload: {
|
||||
label: "Image",
|
||||
tool: Tools.UPLOADED_LIST,
|
||||
toolProps: {filterOptions: ["image/jpg", "image/jpeg", "image/png"]},
|
||||
value: "",
|
||||
onChange: (value) => this.setAttrValue("imageUpload", value)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
|
||||
|
||||
this.setAttrValue("styling.backgroundColor", "#E4E2E2")
|
||||
// this.setWidgetName("label") // Don't do this this causes issues while loading data
|
||||
|
||||
}
|
||||
|
||||
getImports(){
|
||||
const imports = super.getImports()
|
||||
|
||||
if (this.getAttrValue("imageUpload"))
|
||||
imports.push("import os", "from PIL import Image, ImageTk", )
|
||||
|
||||
return imports
|
||||
}
|
||||
|
||||
getRequirements(){
|
||||
const requirements = super.getRequirements()
|
||||
|
||||
|
||||
if (this.getAttrValue("imageUpload"))
|
||||
requirements.push("pillow")
|
||||
|
||||
return requirements
|
||||
}
|
||||
|
||||
generateCode(variableName, parent){
|
||||
|
||||
|
||||
const labelText = this.getAttrValue("labelWidget")
|
||||
const config = convertObjectToKeyValueString(this.getConfigCode())
|
||||
const image = this.getAttrValue("imageUpload")
|
||||
|
||||
let labelInitialization = `${variableName} = tk.Label(master=${parent}, text="${labelText}")`
|
||||
|
||||
const code = []
|
||||
|
||||
if (image?.name){
|
||||
code.push(`${variableName}_img = Image.open(${getPythonAssetPath(image.name, "image")})`)
|
||||
code.push(`${variableName}_img = ImageTk.PhotoImage(${variableName}_img)`)
|
||||
// code.push("\n")
|
||||
labelInitialization = `${variableName} = tk.Label(master=${parent}, image=${variableName}_img, text="${labelText}", compound=tk.TOP)`
|
||||
}
|
||||
|
||||
// code.push("\n")
|
||||
code.push(labelInitialization)
|
||||
return [
|
||||
...code,
|
||||
`${variableName}.config(${config})`,
|
||||
`${variableName}.${this.getLayoutCode()}`
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
getToolbarAttrs(){
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
labelWidget: this.state.attrs.labelWidget,
|
||||
size: toolBarAttrs.size,
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
renderContent(){
|
||||
|
||||
const image = this.getAttrValue("imageUpload")
|
||||
|
||||
return (
|
||||
<div className="tw-w-flex tw-flex-col tw-w-full tw-content-start tw-h-full tw-rounded-md tw-overflow-hidden"
|
||||
style={{
|
||||
flexGrow: 1, // Ensure the content grows to fill the parent
|
||||
minWidth: '100%', // Force the width to 100% of the parent
|
||||
minHeight: '100%', // Force the height to 100% of the parent
|
||||
}}
|
||||
>
|
||||
<div className="tw-p-2 tw-w-full tw-h-full tw-flex tw-place-content-center tw-place-items-center "
|
||||
style={this.getInnerRenderStyling()}>
|
||||
{/* {this.props.children} */}
|
||||
{
|
||||
image && (
|
||||
<img src={image.previewUrl} className="tw-bg-contain tw-w-full tw-h-full" />
|
||||
)
|
||||
}
|
||||
<div className="" style={{color: this.getAttrValue("styling.foregroundColor")}}>
|
||||
{this.getAttrValue("labelWidget")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default Label
|
||||
93
src/frameworks/customtk/widgets/mainWindow.js
Normal file
@@ -0,0 +1,93 @@
|
||||
import Widget from "../../../canvas/widgets/base"
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
import { CustomTkBase } from "./base"
|
||||
|
||||
|
||||
class MainWindow extends CustomTkBase{
|
||||
|
||||
static widgetType = "main_window"
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.droppableTags = {
|
||||
exclude: ["image", "video", "media", "main_window", "toplevel"]
|
||||
}
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
size: { width: 700, height: 400 },
|
||||
widgetName: "main",
|
||||
attrs: {
|
||||
...this.state.attrs,
|
||||
title: {
|
||||
label: "Window Title",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: {placeholder: "Window title", maxLength: 40},
|
||||
value: "Main Window",
|
||||
onChange: (value) => this.setAttrValue("title", value)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setAttrValue("styling.backgroundColor", "#E4E2E2")
|
||||
// this.setWidgetName("main") // Don't do this as this will cause conflicts while loading names
|
||||
}
|
||||
|
||||
generateCode(variableName, parent){
|
||||
|
||||
const backgroundColor = this.getAttrValue("styling.backgroundColor")
|
||||
|
||||
return [
|
||||
`${variableName} = tk.Tk()`,
|
||||
`${variableName}.config(bg="${backgroundColor}")`,
|
||||
`${variableName}.title("${this.getAttrValue("title")}")`
|
||||
]
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
title: this.state.attrs.title,
|
||||
size: toolBarAttrs.size,
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
renderContent(){
|
||||
return (
|
||||
<div className="tw-w-flex tw-flex-col tw-w-full tw-h-full tw-rounded-md tw-overflow-hidden">
|
||||
<div className="tw-flex tw-w-full tw-h-[25px] tw-bg-[#c7c7c7] tw-p-1
|
||||
tw-overflow-hidden tw-shadow-xl tw-place-items-center">
|
||||
<div className="tw-text-sm">{this.getAttrValue("title")}</div>
|
||||
<div className="tw-ml-auto tw-flex tw-gap-1 tw-place-items-center">
|
||||
<div className="tw-bg-yellow-400 tw-rounded-full tw-w-[15px] tw-h-[15px]">
|
||||
</div>
|
||||
<div className="tw-bg-blue-400 tw-rounded-full tw-w-[15px] tw-h-[15px]">
|
||||
</div>
|
||||
<div className="tw-bg-red-400 tw-rounded-full tw-w-[15px] tw-h-[15px]">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="tw-p-2 tw-w-full tw-relative tw-h-full tw-overflow-hidden tw-content-start"
|
||||
style={this.state.widgetInnerStyling}>
|
||||
{this.props.children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default MainWindow
|
||||
139
src/frameworks/customtk/widgets/optionMenu.js
Normal file
@@ -0,0 +1,139 @@
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
import { DownOutlined } from "@ant-design/icons"
|
||||
import { TkinterWidgetBase} from "./base"
|
||||
import { convertObjectToKeyValueString } from "../../../utils/common"
|
||||
|
||||
|
||||
class OptionMenu extends TkinterWidgetBase{
|
||||
|
||||
static widgetType = "option_menu"
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
// const {layout, ...newAttrs} = this.state.attrs // Removes the layout attribute
|
||||
|
||||
this.minSize = {width: 50, height: 30}
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
isDropDownOpen: false,
|
||||
widgetName: "Option menu",
|
||||
size: { width: 120, height: 'fit' },
|
||||
attrs: {
|
||||
...this.state.attrs,
|
||||
defaultValue: {
|
||||
label: "Default Value",
|
||||
tool: Tools.INPUT,
|
||||
value: "Select option",
|
||||
onChange: ({inputs, selectedRadio}) => {
|
||||
this.setAttrValue("options", {inputs, selectedRadio})
|
||||
}
|
||||
},
|
||||
widgetOptions: {
|
||||
label: "Options",
|
||||
tool: Tools.INPUT_RADIO_LIST,
|
||||
value: {inputs: ["option 1"], selectedRadio: -1},
|
||||
onChange: ({inputs, selectedRadio}) => {
|
||||
this.setAttrValue("widgetOptions", {inputs, selectedRadio})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setWidgetInnerStyle("backgroundColor", "#fff")
|
||||
}
|
||||
|
||||
generateCode(variableName, parent){
|
||||
|
||||
|
||||
const config = convertObjectToKeyValueString(this.getConfigCode())
|
||||
|
||||
const defaultValue = this.getAttrValue("defaultValue")
|
||||
const options = JSON.stringify(this.getAttrValue("widgetOptions").inputs)
|
||||
|
||||
const code = [
|
||||
`${variableName}_options = ${options}`,
|
||||
`${variableName}_var = tk.StringVar(value="${defaultValue || options.at(1) || ""}")`,
|
||||
`${variableName} = tk.OptionMenu(${parent}, ${variableName}_var, *${variableName}_options)`
|
||||
]
|
||||
|
||||
|
||||
return [
|
||||
...code,
|
||||
`${variableName}.config(${config})`,
|
||||
`${variableName}.${this.getLayoutCode()}`
|
||||
]
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
const attrs = this.state.attrs
|
||||
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
checkLabel: attrs.checkLabel,
|
||||
size: toolBarAttrs.size,
|
||||
...attrs,
|
||||
})
|
||||
}
|
||||
|
||||
toggleDropDownOpen = () => {
|
||||
this.setState((prev) => ({
|
||||
isDropDownOpen: !prev.isDropDownOpen
|
||||
}))
|
||||
}
|
||||
|
||||
renderContent(){
|
||||
|
||||
const {inputs, selectedRadio} = this.getAttrValue("widgetOptions")
|
||||
|
||||
return (
|
||||
<div className="tw-flex tw-p-1 tw-w-full tw-h-full tw-rounded-md tw-overflow-hidden"
|
||||
style={this.getInnerRenderStyling()}
|
||||
onClick={this.toggleDropDownOpen}
|
||||
>
|
||||
<div className="tw-flex tw-justify-between tw-gap-1">
|
||||
{this.getAttrValue("defaultValue")}
|
||||
|
||||
<div className="tw-text-sm">
|
||||
<DownOutlined />
|
||||
</div>
|
||||
</div>
|
||||
{this.state.isDropDownOpen &&
|
||||
<div className="tw-absolute tw-p-1 tw-bg-white tw-rounded-md tw-shadow-md tw-left-0
|
||||
tw-w-full tw-h-fit "
|
||||
style={{top: "calc(100% + 5px)"}}
|
||||
>
|
||||
{
|
||||
inputs.map((value, index) => {
|
||||
return (
|
||||
<div key={index} className="tw-flex tw-gap-2 tw-w-full tw-h-full tw-place-items-center
|
||||
hover:tw-bg-[#c5c5c573] tw-p-1">
|
||||
|
||||
<span className="tw-text-base" style={{color: this.state.widgetInnerStyling.foregroundColor}}>
|
||||
{value}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default OptionMenu
|
||||
159
src/frameworks/customtk/widgets/slider.js
Normal file
@@ -0,0 +1,159 @@
|
||||
import Widget from "../../../canvas/widgets/base"
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
import { convertObjectToKeyValueString, removeKeyFromObject } from "../../../utils/common"
|
||||
import {TkinterBase, TkinterWidgetBase} from "./base"
|
||||
|
||||
|
||||
class Slider extends TkinterWidgetBase{
|
||||
|
||||
static widgetType = "scale"
|
||||
// FIXME: You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
widgetName: "Scale",
|
||||
size: { width: 'fit', height: 'fit' },
|
||||
attrs: {
|
||||
...this.state.attrs,
|
||||
styling: {
|
||||
...this.state.attrs.styling,
|
||||
// TODO: trough color
|
||||
troughColor: {
|
||||
label: "Trough Color",
|
||||
tool: Tools.COLOR_PICKER,
|
||||
value: "#fff",
|
||||
onChange: (value) => {
|
||||
// this.setWidgetInnerStyle("color", value)
|
||||
this.setAttrValue("styling.troughColor", value)
|
||||
}
|
||||
}
|
||||
},
|
||||
scale: {
|
||||
label: "Scale",
|
||||
display: "horizontal",
|
||||
min: {
|
||||
label: "Min",
|
||||
tool: Tools.NUMBER_INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "min" },
|
||||
value: 0,
|
||||
onChange: (value) => this.setAttrValue("scale.min", value)
|
||||
},
|
||||
max: {
|
||||
label: "Max",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "max"},
|
||||
value: 100,
|
||||
onChange: (value) => this.setAttrValue("scale.max", value)
|
||||
},
|
||||
step: {
|
||||
label: "Step",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "max", stringMode: true, step: "0.1"},
|
||||
value: 1,
|
||||
onChange: (value) => this.setAttrValue("scale.step", value)
|
||||
},
|
||||
default: {
|
||||
label: "Default",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "max", stringMode: true, step: "0.1"},
|
||||
value: 0,
|
||||
onChange: (value) => this.setAttrValue("scale.default", value)
|
||||
}
|
||||
},
|
||||
orientation: {
|
||||
label: "Orientation",
|
||||
tool: Tools.SELECT_DROPDOWN,
|
||||
toolProps: {placeholder: "select orientation"},
|
||||
value: "",
|
||||
options: [{value: "horizontal", label: "horizontal"}, {value: "vertical", label: "vertical"}],
|
||||
onChange: (value) => {
|
||||
|
||||
// const widgetStyling = {
|
||||
// transformOrigin: "0 0",
|
||||
// transform: value === "horizontal" ? "rotate(0deg)" : "rotate(90deg)"
|
||||
// }
|
||||
|
||||
// this.setState((prev) => ({
|
||||
// widgetOuterStyling: {...prev, ...widgetStyling}
|
||||
// }))
|
||||
// this.setWidgetOuterStyle("transform-origin", "0 0")
|
||||
// this.setWidgetOuterStyle("transform", value === "horizontal" ? "rotate(0deg)" : "rotate(90deg)")
|
||||
this.setAttrValue("orientation", value)
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setAttrValue("styling.backgroundColor", "#fff")
|
||||
}
|
||||
|
||||
generateCode(variableName, parent){
|
||||
// TODO: add orientation
|
||||
|
||||
const config = this.getConfigCode()
|
||||
|
||||
config["from_"] = this.getAttrValue("scale.min")
|
||||
config["to"] = this.getAttrValue("scale.max")
|
||||
config["resolution"] = this.getAttrValue("scale.step")
|
||||
|
||||
if (this.getAttrValue("orientation")){
|
||||
config["orientation"] = this.getAttrValue("orientation")
|
||||
}
|
||||
|
||||
const defaultValue = this.getAttrValue("scale.default")
|
||||
|
||||
return [
|
||||
`${variableName}_var = tk.DoubleVar(value=${defaultValue})`,
|
||||
`${variableName} = tk.Scale(master=${parent}, variable=${variableName}_var)`,
|
||||
`${variableName}.config(${convertObjectToKeyValueString(config)})`,
|
||||
`${variableName}.${this.getLayoutCode()}`
|
||||
]
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
placeHolder: this.state.attrs.placeHolder,
|
||||
size: toolBarAttrs.size,
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
renderContent(){
|
||||
return (
|
||||
<div className="tw-w-flex tw-flex-col tw-w-full tw-h-full tw-rounded-md tw-overflow-hidden">
|
||||
<div className="flex flex-col items-center justify-center h-screen
|
||||
bg-gray-100" style={this.getInnerRenderStyling()}>
|
||||
<div className="w-full max-w-md">
|
||||
<input
|
||||
type="range"
|
||||
min={this.getAttrValue("scale.min")}
|
||||
max={this.getAttrValue("scale.max")}
|
||||
step={this.getAttrValue("scale.step")}
|
||||
value={this.getAttrValue("scale.default")}
|
||||
style={{backgroundColor: this.getAttrValue("styling.troughColor") }}
|
||||
className="tw-pointer-events-none w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-blue-500"
|
||||
/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default Slider
|
||||
128
src/frameworks/customtk/widgets/spinBox.js
Normal file
@@ -0,0 +1,128 @@
|
||||
import Widget from "../../../canvas/widgets/base"
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
import { convertObjectToKeyValueString, removeKeyFromObject } from "../../../utils/common"
|
||||
import { DownOutlined, UpOutlined } from "@ant-design/icons"
|
||||
import {TkinterBase, TkinterWidgetBase} from "./base"
|
||||
|
||||
|
||||
class SpinBox extends TkinterWidgetBase{
|
||||
|
||||
static widgetType = "spin_box"
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
size: { width: 70, height: 'fit' },
|
||||
widgetName: "Spin box",
|
||||
attrs: {
|
||||
...this.state.attrs,
|
||||
spinProps: {
|
||||
label: "Properties",
|
||||
display: "horizontal",
|
||||
min: {
|
||||
label: "Min",
|
||||
tool: Tools.NUMBER_INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "min" },
|
||||
value: 0,
|
||||
onChange: (value) => this.setAttrValue("spinProps.min", value)
|
||||
},
|
||||
max: {
|
||||
label: "Max",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "max"},
|
||||
value: 100,
|
||||
onChange: (value) => this.setAttrValue("spinProps.max", value)
|
||||
},
|
||||
step: {
|
||||
label: "Step",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "max", stringMode: true, step: "0.1"},
|
||||
value: 1,
|
||||
onChange: (value) => this.setAttrValue("spinProps.step", value)
|
||||
},
|
||||
default: {
|
||||
label: "Default",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "max", stringMode: true, step: "0.1"},
|
||||
value: 0,
|
||||
onChange: (value) => this.setAttrValue("spinProps.default", value)
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setAttrValue("styling.backgroundColor", "#fff")
|
||||
}
|
||||
|
||||
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.default")
|
||||
|
||||
const config = {
|
||||
from_: min,
|
||||
to: max,
|
||||
increment: step,
|
||||
value: defaultValue,
|
||||
...this.getConfigCode()
|
||||
}
|
||||
|
||||
const code = []
|
||||
let spinBox = `${variableName} = tk.Spinbox(master=${parent})`
|
||||
if (defaultValue){
|
||||
code.push(`${variableName}_var = tk.IntVar(${defaultValue})`)
|
||||
spinBox = `${variableName} = tk.Spinbox(master=${parent}, textvariable=${variableName}_var)`
|
||||
}
|
||||
code.push(spinBox)
|
||||
|
||||
return [
|
||||
...code,
|
||||
`${variableName}.config(${convertObjectToKeyValueString(config)})`,
|
||||
`${variableName}.${this.getLayoutCode()}`
|
||||
]
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
placeHolder: this.state.attrs.placeHolder,
|
||||
size: toolBarAttrs.size,
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
renderContent(){
|
||||
return (
|
||||
<div className="tw-w-flex tw-flex-col tw-w-full tw-h-full tw-rounded-md tw-overflow-hidden">
|
||||
<div className="tw-p-2 tw-w-full tw-h-full tw-flex tw-place-items-center tw-justify-between"
|
||||
style={this.getInnerRenderStyling()}>
|
||||
<div className="tw-text-sm ">
|
||||
{this.getAttrValue("spinProps.default")}
|
||||
</div>
|
||||
<div className="tw-flex tw-flex-col tw-text-black tw-gap-1 tw-text-sm">
|
||||
<UpOutlined />
|
||||
<DownOutlined />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default SpinBox
|
||||
89
src/frameworks/customtk/widgets/toplevel.js
Normal file
@@ -0,0 +1,89 @@
|
||||
import Widget from "../../../canvas/widgets/base"
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
|
||||
|
||||
class TopLevel extends Widget{
|
||||
|
||||
static widgetType = "toplevel"
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.droppableTags = {
|
||||
exclude: ["image", "video", "media", "main_window", "toplevel"]
|
||||
}
|
||||
this.maxSize = { width: 2000, height: 2000 } // disables resizing above this number
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
size: { width: 450, height: 200 },
|
||||
widgetName: "top level",
|
||||
attrs: {
|
||||
...this.state.attrs,
|
||||
title: {
|
||||
label: "Window Title",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: {placeholder: "Window title", maxLength: 40},
|
||||
value: "Top level",
|
||||
onChange: (value) => this.setAttrValue("title", value)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setAttrValue("styling.backgroundColor", "#E4E2E2")
|
||||
}
|
||||
|
||||
generateCode(variableName, parent){
|
||||
|
||||
const backgroundColor = this.getAttrValue("styling.backgroundColor")
|
||||
|
||||
return [
|
||||
`${variableName} = tk.Toplevel(master=${parent})`,
|
||||
`${variableName}.config(bg="${backgroundColor}")`,
|
||||
`${variableName}.title("${this.getAttrValue("title")}")`
|
||||
]
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
return ({
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
title: this.state.attrs.title,
|
||||
size: toolBarAttrs.size,
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
renderContent(){
|
||||
return (
|
||||
<div className="tw-w-flex tw-flex-col tw-w-full tw-h-full tw-rounded-md tw-overflow-hidden">
|
||||
<div className="tw-flex tw-w-full tw-h-[25px] tw-bg-[#c7c7c7] tw-p-1
|
||||
tw-overflow-hidden tw-shadow-xl tw-place-items-center">
|
||||
<div className="tw-text-sm">{this.getAttrValue("title")}</div>
|
||||
<div className="tw-ml-auto tw-flex tw-gap-1 tw-place-items-center">
|
||||
<div className="tw-bg-yellow-400 tw-rounded-full tw-w-[15px] tw-h-[15px]">
|
||||
</div>
|
||||
<div className="tw-bg-blue-400 tw-rounded-full tw-w-[15px] tw-h-[15px]">
|
||||
</div>
|
||||
<div className="tw-bg-red-400 tw-rounded-full tw-w-[15px] tw-h-[15px]">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="tw-p-2 tw-w-full tw-h-full tw-content-start" style={this.state.widgetInnerStyling}>
|
||||
{this.props.children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default TopLevel
|
||||
@@ -130,8 +130,6 @@ async function generateTkinterCode(projectName, widgetList=[], widgetRefs=[], as
|
||||
|
||||
message.info("starting zipping files, download will start in a few seconds")
|
||||
|
||||
return
|
||||
|
||||
const createFileList = [
|
||||
{
|
||||
fileData: code.join(""),
|
||||
|
||||
@@ -560,33 +560,41 @@ export class TkinterWidgetBase extends TkinterBase{
|
||||
|
||||
getConfigCode(){
|
||||
|
||||
const code = {
|
||||
const config = {
|
||||
bg: `"${this.getAttrValue("styling.backgroundColor")}"`,
|
||||
fg: `"${this.getAttrValue("styling.foregroundColor")}"`,
|
||||
}
|
||||
|
||||
if (this.getAttrValue("styling.borderWidth"))
|
||||
code["bd"] = this.getAttrValue("styling.borderWidth")
|
||||
config["bd"] = this.getAttrValue("styling.borderWidth")
|
||||
|
||||
if (this.getAttrValue("styling.relief"))
|
||||
code["relief"] = `"${this.getAttrValue("styling.relief")}"`
|
||||
config["relief"] = `"${this.getAttrValue("styling.relief")}"`
|
||||
|
||||
if (this.getAttrValue("font.fontFamily") || this.getAttrValue("font.fontSize")){
|
||||
code["font"] = `("${this.getAttrValue("font.fontFamily")}", ${this.getAttrValue("font.fontSize") || 12}, )`
|
||||
config["font"] = `("${this.getAttrValue("font.fontFamily")}", ${this.getAttrValue("font.fontSize") || 12}, )`
|
||||
}
|
||||
|
||||
if (this.getAttrValue("cursor"))
|
||||
code["cursor"] = `"${this.getAttrValue("cursor")}"`
|
||||
config["cursor"] = `"${this.getAttrValue("cursor")}"`
|
||||
|
||||
if (this.getAttrValue("padding.padX")){
|
||||
code["padx"] = this.getAttrValue("padding.padX")
|
||||
config["padx"] = this.getAttrValue("padding.padX")
|
||||
}
|
||||
|
||||
if (this.getAttrValue("padding.padY")){
|
||||
code["pady"] = this.getAttrValue("padding.padY")
|
||||
config["pady"] = this.getAttrValue("padding.padY")
|
||||
}
|
||||
|
||||
return code
|
||||
// FIXME: add width and height, the scales may not be correct as the width and height are based on characters in pack and grid not pixels
|
||||
// if (!this.state.fitContent.width){
|
||||
// config["width"] = this.state.size.width
|
||||
// }
|
||||
// if (!this.state.fitContent.height){
|
||||
// config["height"] = this.state.size.height
|
||||
// }
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
}
|
||||