13 Commits

Author SHA1 Message Date
paul
bdd3bab3a5 corrected grid weight and grid config position in toolbar 2025-03-30 05:46:03 +05:30
paul
53aaa8a670 fix: fixed grid row col on load 2025-03-30 05:23:18 +05:30
paul
64631caaaa fix: fixed setState warning 2025-03-29 21:03:02 +05:30
paul
8e1f042350 fix: fixed parentLayout taking place on changing layout 2025-03-29 21:00:45 +05:30
paul
128a7c49b9 updated readme 2025-03-28 18:48:41 +05:30
paul
e0fa421459 Merge branch 'customtk-fixes' 2025-03-28 17:14:51 +05:30
paul
0438480620 fixes: corrected tk to ctk in customtk 2025-03-28 17:14:03 +05:30
Art/Paul
d64f87847c Merge pull request #12 from PaulleDemon/customtk-fixes
fixing customtk layout
2025-03-28 17:13:21 +05:30
paul
c6fd4a8275 fixing customtk layout 2025-03-28 15:56:17 +05:30
paul
bfeb1c55b9 added discord invite 2025-03-28 11:17:48 +05:30
paul
9949fb8335 updated manifest 2025-03-27 18:43:56 +05:30
paul
86f84ef998 updated premium message 2025-03-27 17:18:30 +05:30
paul
fc68d407b7 added discord invite 2025-03-27 15:31:27 +05:30
17 changed files with 1126 additions and 191 deletions

View File

@@ -30,7 +30,7 @@ https://github.com/user-attachments/assets/ac91aa98-843d-4578-b646-88e66bc113de
<sub>**Don't like background music? fell free to mute it**</sub> <sub>**Don't like background music? fell free to mute it**</sub>
## Try PyUIBuilder ## Try PyUIBuilder
Try [PyUIBuilder](https://pyuibuilder.pages.dev/) Try [PyUIBuilder](https://pyuibuilder.com)
## Table of contents ## Table of contents
@@ -53,7 +53,7 @@ Try [PyUIBuilder](https://pyuibuilder.pages.dev/)
## Docs - Getting started ## Docs - Getting started
Read the docs on the [Docs page](https://pyuibuilder-docs.pages.dev/) Read the docs on the [Docs page](https://docs.pyuibuilder.com/)
## Example app ## Example app
@@ -109,7 +109,7 @@ While there are a lot of features, here are few you need to know.
* Framework agnostic - Can outputs code in multiple frameworks. * Framework agnostic - Can outputs code in multiple frameworks.
* Pre-built UI widgets * Pre-built UI widgets
* Plugins to extend 3rd party UI libraries * Plugins to extend 3rd party UI libraries
* Supports layout managers, such as flex, grid and absolute positioning [read docs](https://pyuibuilder-docs.pages.dev/) * Supports layout managers, such as flex, grid and absolute positioning [read docs](https://docs.pyuibuilder.com/)
* Generates python Code. * Generates python Code.
* Support to upload local assets. * Support to upload local assets.
* Generates requirements.txt file when needed * Generates requirements.txt file when needed

View File

@@ -249,7 +249,7 @@
</ul> </ul>
<a <a
href="https://pyuibuilder.pages.dev/" href="https://pyuibuilder.com"
target="_blank" target="_blank"
rel="noreferrer noopener" rel="noreferrer noopener"
class=" !tw-bg-[#0F1727] tw-duration-[0.3s] hover:tw-transition-transform hover:tw-scale-[1.01] !tw-mt-auto !tw-text-white tw-gap-2 tw-text-lg tw-rounded-md tw-w-full tw-flex tw-place-content-center tw-p-2 tw-mx-2" class=" !tw-bg-[#0F1727] tw-duration-[0.3s] hover:tw-transition-transform hover:tw-scale-[1.01] !tw-mt-auto !tw-text-white tw-gap-2 tw-text-lg tw-rounded-md tw-w-full tw-flex tw-place-content-center tw-p-2 tw-mx-2"

View File

@@ -1,6 +1,6 @@
{ {
"short_name": "React App", "short_name": "PyUiBuilder",
"name": "Create React App Sample", "name": "PyUiBuilder - Python GUI Builder",
"icons": [ "icons": [
{ {
"src": "favicon.ico", "src": "favicon.ico",

View File

@@ -61,21 +61,23 @@ function App() {
useEffect(() => { useEffect(() => {
if (shownNotChromiumAlert){ if (!shownNotChromiumAlert){
return // this modal may rerender twice only in dev mode because of how react works
isChromium().then((isChrome) => {
if (!isChrome){
Modal.warning({
title: "Use Chromium browser",
onOk: () => setShownNotChromiumAlert(true),
content: (<span>We recommend using Chromium based browser such as Chrome, Brave, Edge etc for best results.
<br />
Join us on
<a href="https://discord.gg/dHXjrrCA7G" target='_blank' rel='noreferrer noopener'> Discord</a> for help and updates</span>)
})
}
})
} }
setShownNotChromiumAlert(true)
isChromium().then((isChrome) => {
if (!isChrome){
Modal.warning({
title: "Not Chromium browser",
content: "We recommend using Chromium based browser such as Chrome, Brave, Edge etc."
})
setShownNotChromiumAlert(true)
}
})
}, [shownNotChromiumAlert]) }, [shownNotChromiumAlert])
@@ -123,7 +125,6 @@ function App() {
const widgetCenterX = (TkMainWindow.initialSize.width - canvasBoundingBox.left) / 2 const widgetCenterX = (TkMainWindow.initialSize.width - canvasBoundingBox.left) / 2
const widgetCenterY = (TkMainWindow.initialSize.height - canvasBoundingBox.top) / 2 const widgetCenterY = (TkMainWindow.initialSize.height - canvasBoundingBox.top) / 2
canvasRef?.current?.createWidget(TkMainWindow, {x: canvasCenterX - widgetCenterX, y: canvasCenterY - widgetCenterY}, ({id, widgetRef}) => { canvasRef?.current?.createWidget(TkMainWindow, {x: canvasCenterX - widgetCenterX, y: canvasCenterY - widgetCenterY}, ({id, widgetRef}) => {
// center the widget when adding to canvas // center the widget when adding to canvas
@@ -140,7 +141,12 @@ function App() {
}) })
}else if (UIFramework === FrameWorks.CUSTOMTK){ }else if (UIFramework === FrameWorks.CUSTOMTK){
canvasRef?.current?.createWidget(CTkMainWindow, ({id, widgetRef}) => {
const widgetCenterX = (TkMainWindow.initialSize.width - canvasBoundingBox.left) / 2
const widgetCenterY = (TkMainWindow.initialSize.height - canvasBoundingBox.top) / 2
canvasRef?.current?.createWidget(CTkMainWindow, {x: canvasCenterX - widgetCenterX, y: canvasCenterY - widgetCenterY}, ({id, widgetRef}) => {
// center the widget when adding to canvas // center the widget when adding to canvas
if (!widgetRef.current){ if (!widgetRef.current){
@@ -152,7 +158,7 @@ function App() {
const widgetCenterY = (widgetBoundingBox.height - widgetBoundingBox.top) / 2 const widgetCenterY = (widgetBoundingBox.height - widgetBoundingBox.top) / 2
widgetRef.current?.setPos(canvasCenterX-widgetCenterX, canvasCenterY-widgetCenterY) // widgetRef.current?.setPos(canvasCenterX-widgetCenterX, canvasCenterY-widgetCenterY)
}) })
} }

View File

@@ -214,6 +214,7 @@ class Widget extends React.Component {
this.stateUpdateCallback = null // allowing other components such as toolbar to subscribe to changes in this widget this.stateUpdateCallback = null // allowing other components such as toolbar to subscribe to changes in this widget
this.resizeObserver = null this.resizeObserver = null
} }
@@ -704,8 +705,7 @@ class Widget extends React.Component {
} }
getLayout(){ getLayout(){
return this.getAttrValue("layout") || {layout: Layouts.PLACE}
return this.getAttrValue("layout") || Layouts.PLACE
} }
setLayout(value) { setLayout(value) {

View File

@@ -2,6 +2,7 @@
export const Tkinter_TO_WEB_CURSOR_MAPPING = { export const Tkinter_TO_WEB_CURSOR_MAPPING = {
"": "",
"arrow": "default", "arrow": "default",
"circle": "wait", "circle": "wait",
"clock": "wait", "clock": "wait",

View File

@@ -20,5 +20,25 @@ export const ANCHOR = [
"s", "s",
"e", "e",
"w", "w",
"center" "center",
] "ne",
"se",
"sw",
"nw",
]
export const GRID_STICKY = {
N: "n",
S: "s",
E: "e",
W: "w",
WE: "we",
NS: "ns",
NW: "nw",
NE: "ne",
SW: "sw",
SE: "se",
NEWS: "news",
NONE: "",
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,5 @@
import { Layouts } from "../../../canvas/constants/layouts"
import Tools from "../../../canvas/constants/tools"
import Widget from "../../../canvas/widgets/base" import Widget from "../../../canvas/widgets/base"
import { CustomTkBase } from "./base" import { CustomTkBase } from "./base"
@@ -17,7 +19,99 @@ class Frame extends CustomTkBase{
this.state = { this.state = {
...this.state, ...this.state,
fitContent: {width: true, height: true}, fitContent: {width: true, height: true},
widgetName: "Frame" widgetName: "Frame",
attrs: {
...this.state.attrs,
padding: {
label: "padding",
padX: {
label: "Pad X",
tool: Tools.NUMBER_INPUT,
toolProps: {min: 0, max: 140},
value: null,
onChange: (value) => {
// this.setWidgetInnerStyle("paddingLeft", `${value}px`)
// this.setWidgetInnerStyle("paddingRight", `${value}px`)
// const widgetStyle = {
// }
this.setState((prevState) => ({
widgetInnerStyling: {
...prevState.widgetInnerStyling,
paddingLeft: `${value}px`,
paddingRight: `${value}px`
}
}))
this.setAttrValue("padding.padX", value)
}
},
padY: {
label: "Pad Y",
tool: Tools.NUMBER_INPUT,
toolProps: {min: 0, max: 140},
value: null,
onChange: (value) => {
this.setState((prevState) => ({
widgetInnerStyling: {
...prevState.widgetInnerStyling,
paddingTop: `${value}px`,
paddingBottom: `${value}px`
}
}))
// this.setState({
// widgetInnerStyling: widgetStyle
// })
this.setAttrValue("padding.padY", value)
}
},
},
margin: {
label: "Margin",
marginX: {
label: "Margin X",
tool: Tools.NUMBER_INPUT,
toolProps: {min: 0, max: 140},
value: null,
onChange: (value) => {
this.updateState((prev) => ({
widgetOuterStyling: {
...prev.widgetOuterStyling,
marginLeft: `${value}px`,
marginRight: `${value}px`
},
}))
this.setAttrValue("margin.marginX", value)
}
},
marginY: {
label: "Margin Y",
tool: Tools.NUMBER_INPUT,
toolProps: {min: 0, max: 140},
value: null,
onChange: (value) => {
this.updateState((prev) => ({
widgetOuterStyling: {
...prev.widgetOuterStyling,
marginTop: `${value}px`,
marginBottom: `${value}px`
},
}))
this.setAttrValue("margin.marginY", value)
}
},
},
}
} }
} }
@@ -27,6 +121,34 @@ class Frame extends CustomTkBase{
this.setAttrValue("styling.backgroundColor", "#EDECEC") this.setAttrValue("styling.backgroundColor", "#EDECEC")
} }
getConfigCode(){
const bg = this.getAttrValue("styling.backgroundColor")
const fitWidth = this.state.fitContent.width
const fitHeight = this.state.fitContent.height
const {width, height} = this.getSize()
const {layout} = this.getParentLayout()
const config = {
bg: `"${bg}"`
}
if (layout !== Layouts.PLACE){
if (!fitWidth){
config['width'] = width
}
if (!fitHeight){
config['height'] = height
}
}
return config
}
generateCode(variableName, parent){ generateCode(variableName, parent){
const bg = this.getAttrValue("styling.backgroundColor") const bg = this.getAttrValue("styling.backgroundColor")
@@ -34,12 +156,26 @@ class Frame extends CustomTkBase{
return [ return [
`${variableName} = ctk.CTkFrame(master=${parent})`, `${variableName} = ctk.CTkFrame(master=${parent})`,
`${variableName}.configure(fg_color="${bg}")`, `${variableName}.configure(fg_color="${bg}")`,
`${variableName}.${this.getLayoutCode()}` `${variableName}.${this.getLayoutCode()}`,
...this.getGridLayoutConfigurationCode(variableName)
] ]
} }
getToolbarAttrs(){
const {layout, gridConfig, gridWeights, ...toolBarAttrs} = super.getToolbarAttrs()
// places layout at the end
return ({
id: this.__id,
...toolBarAttrs,
padding: this.state.attrs.padding,
margin: this.state.attrs.margin,
layout,
gridConfig,
gridWeights
})
}
renderContent(){ renderContent(){
// console.log("bounding rect: ", this.getBoundingRect()) // console.log("bounding rect: ", this.getBoundingRect())
@@ -49,7 +185,7 @@ class Frame extends CustomTkBase{
<div className="tw-p-2 tw-w-full tw-h-full tw-content-start" <div className="tw-p-2 tw-w-full tw-h-full tw-content-start"
ref={this.styleAreaRef} ref={this.styleAreaRef}
style={this.getInnerRenderStyling()}> style={this.getInnerRenderStyling()}>
{this.props.children} {this.renderTkinterLayout()}
</div> </div>
</div> </div>
) )

View File

@@ -116,6 +116,22 @@ class Label extends CustomTkWidgetBase{
}) })
} }
getAnchorStyle = (anchor) => {
const anchorStyles = {
n: { justifyContent: 'center', alignItems: 'flex-start' },
s: { justifyContent: 'center', alignItems: 'flex-end' },
e: { justifyContent: 'flex-end', alignItems: 'center' },
w: { justifyContent: 'flex-start', alignItems: 'center' },
ne: { justifyContent: 'flex-end', alignItems: 'flex-start' },
se: { justifyContent: 'flex-end', alignItems: 'flex-end' },
nw: { justifyContent: 'flex-start', alignItems: 'flex-start' },
sw: { justifyContent: 'flex-start', alignItems: 'flex-end' },
center: { justifyContent: 'center', alignItems: 'center' }
}
return anchorStyles[anchor] || anchorStyles["w"];
}
renderContent(){ renderContent(){
const image = this.getAttrValue("imageUpload") const image = this.getAttrValue("imageUpload")
@@ -137,7 +153,10 @@ class Label extends CustomTkWidgetBase{
<img src={image.previewUrl} className="tw-bg-contain tw-w-full tw-h-full" /> <img src={image.previewUrl} className="tw-bg-contain tw-w-full tw-h-full" />
) )
} }
<div className="" style={{color: this.getAttrValue("styling.foregroundColor")}}> <div className={`tw-flex ${!image ? "tw-w-full tw-h-full" : ""}`} style={{
color: this.getAttrValue("styling.foregroundColor"),
...this.getAnchorStyle(this.getAttrValue("styling.anchor"))
}}>
{this.getAttrValue("labelWidget")} {this.getAttrValue("labelWidget")}
</div> </div>
</div> </div>

View File

@@ -1,6 +1,7 @@
import Widget from "../../../canvas/widgets/base" import Widget from "../../../canvas/widgets/base"
import Tools from "../../../canvas/constants/tools" import Tools from "../../../canvas/constants/tools"
import { CustomTkBase } from "./base" import { CustomTkBase } from "./base"
import { getPythonAssetPath } from "../../utils/pythonFilePath"
class MainWindow extends CustomTkBase{ class MainWindow extends CustomTkBase{
@@ -8,6 +9,13 @@ class MainWindow extends CustomTkBase{
static widgetType = "main_window" static widgetType = "main_window"
static displayName = "Main Window" static displayName = "Main Window"
static initialSize = {
width: 700,
height: 400
}
constructor(props) { constructor(props) {
super(props) super(props)
@@ -27,6 +35,13 @@ class MainWindow extends CustomTkBase{
toolProps: {placeholder: "Window title", maxLength: 40}, toolProps: {placeholder: "Window title", maxLength: 40},
value: "Main Window", value: "Main Window",
onChange: (value) => this.setAttrValue("title", value) onChange: (value) => this.setAttrValue("title", value)
},
logo: {
label: "Window Logo",
tool: Tools.UPLOADED_LIST,
toolProps: {filterOptions: ["image/jpg", "image/jpeg", "image/png"]},
value: "",
onChange: (value) => this.setAttrValue("logo", value)
} }
} }
@@ -43,12 +58,48 @@ class MainWindow extends CustomTkBase{
generateCode(variableName, parent){ generateCode(variableName, parent){
const backgroundColor = this.getAttrValue("styling.backgroundColor") const backgroundColor = this.getAttrValue("styling.backgroundColor")
const logo = this.getAttrValue("logo")
return [ const {width, height} = this.getSize()
`${variableName} = ctk.CTk()`,
`${variableName}.configure(fg_color="${backgroundColor}")`, const code = [
`${variableName}.title("${this.getAttrValue("title")}")` `${variableName} = ctk.CTk()`,
] `${variableName}.configure(fg_color="${backgroundColor}")`,
`${variableName}.title("${this.getAttrValue("title")}")`,
`${variableName}.geometry("${width}x${height}")`,
...this.getGridLayoutConfigurationCode(variableName)
]
if (logo?.name){
// code.push(`\n`)
code.push(`${variableName}_img = Image.open(${getPythonAssetPath(logo.name, "image")})`)
code.push(`${variableName}_img = ImageTk.PhotoImage(${variableName}_img)`)
code.push(`${variableName}.iconphoto(False, ${variableName}_img)`)
// code.push("\n")
}
return code
}
getImports(){
const imports = super.getImports()
if (this.getAttrValue("logo"))
imports.push("import os", "from PIL import Image, ImageTk", )
return imports
}
getRequirements(){
const requirements = super.getRequirements()
if (this.getAttrValue("logo"))
requirements.push("pillow")
return requirements
} }
getToolbarAttrs(){ getToolbarAttrs(){
@@ -58,8 +109,8 @@ class MainWindow extends CustomTkBase{
id: this.__id, id: this.__id,
widgetName: toolBarAttrs.widgetName, widgetName: toolBarAttrs.widgetName,
title: this.state.attrs.title, title: this.state.attrs.title,
logo: this.state.attrs.logo,
size: toolBarAttrs.size, size: toolBarAttrs.size,
...this.state.attrs, ...this.state.attrs,
}) })
@@ -83,7 +134,7 @@ class MainWindow extends CustomTkBase{
<div className="tw-p-2 tw-w-full tw-relative tw-h-full tw-overflow-hidden tw-content-start" <div className="tw-p-2 tw-w-full tw-relative tw-h-full tw-overflow-hidden tw-content-start"
ref={this.styleAreaRef} ref={this.styleAreaRef}
style={this.state.widgetInnerStyling}> style={this.state.widgetInnerStyling}>
{this.props.children} {this.renderTkinterLayout()}
</div> </div>
</div> </div>
) )

View File

@@ -1,8 +1,10 @@
import Widget from "../../../canvas/widgets/base" import Widget from "../../../canvas/widgets/base"
import Tools from "../../../canvas/constants/tools" import Tools from "../../../canvas/constants/tools"
import { getPythonAssetPath } from "../../utils/pythonFilePath"
import { CustomTkBase } from "./base"
class TopLevel extends Widget{ class TopLevel extends CustomTkBase{
static widgetType = "toplevel" static widgetType = "toplevel"
static displayName = "Top Level" static displayName = "Top Level"
@@ -27,6 +29,13 @@ class TopLevel extends Widget{
toolProps: {placeholder: "Window title", maxLength: 40}, toolProps: {placeholder: "Window title", maxLength: 40},
value: "Top level", value: "Top level",
onChange: (value) => this.setAttrValue("title", value) onChange: (value) => this.setAttrValue("title", value)
},
logo: {
label: "Toplevel Logo",
tool: Tools.UPLOADED_LIST,
toolProps: {filterOptions: ["image/jpg", "image/jpeg", "image/png"]},
value: "",
onChange: (value) => this.setAttrValue("logo", value)
} }
} }
@@ -42,11 +51,49 @@ class TopLevel extends Widget{
const backgroundColor = this.getAttrValue("styling.backgroundColor") const backgroundColor = this.getAttrValue("styling.backgroundColor")
return [ const logo = this.getAttrValue("logo")
`${variableName} = ctk.CTkToplevel(master=${parent})`,
`${variableName}.configure(fg_color="${backgroundColor}")`, const {width, height} = this.getSize()
`${variableName}.title("${this.getAttrValue("title")}")`
] const code = [
`${variableName} = ctk.CTkToplevel(master=${parent})`,
`${variableName}.configure(fg_color="${backgroundColor}")`,
`${variableName}.title("${this.getAttrValue("title")}")`,
`${variableName}.geometry("${width}x${height}")`,
...this.getGridLayoutConfigurationCode(variableName)
]
if (logo?.name){
// code.push(`\n`)
code.push(`${variableName}_img = Image.open(${getPythonAssetPath(logo.name, "image")})`)
code.push(`${variableName}_img = ImageTk.PhotoImage(${variableName}_img)`)
code.push(`${variableName}.iconphoto(False, ${variableName}_img)`)
// code.push("\n")
}
return code
}
getImports(){
const imports = super.getImports()
if (this.getAttrValue("logo"))
imports.push("import os", "from PIL import Image, ImageTk", )
return imports
}
getRequirements(){
const requirements = super.getRequirements()
if (this.getAttrValue("logo"))
requirements.push("pillow")
return requirements
} }
getToolbarAttrs(){ getToolbarAttrs(){
@@ -55,8 +102,8 @@ class TopLevel extends Widget{
return ({ return ({
widgetName: toolBarAttrs.widgetName, widgetName: toolBarAttrs.widgetName,
title: this.state.attrs.title, title: this.state.attrs.title,
logo: this.state.attrs.logo,
size: toolBarAttrs.size, size: toolBarAttrs.size,
...this.state.attrs, ...this.state.attrs,
}) })
@@ -80,7 +127,7 @@ class TopLevel extends Widget{
<div className="tw-p-2 tw-w-full tw-h-full tw-content-start" <div className="tw-p-2 tw-w-full tw-h-full tw-content-start"
ref={this.styleAreaRef} ref={this.styleAreaRef}
style={this.state.widgetInnerStyling}> style={this.state.widgetInnerStyling}>
{this.props.children} {this.renderTkinterLayout()}
</div> </div>
</div> </div>
) )

View File

@@ -2,6 +2,7 @@
export const Tkinter_TO_WEB_CURSOR_MAPPING = { export const Tkinter_TO_WEB_CURSOR_MAPPING = {
"": "",
"arrow": "default", "arrow": "default",
"circle": "wait", "circle": "wait",
"clock": "wait", "clock": "wait",

View File

@@ -659,7 +659,7 @@ export class TkinterBase extends Widget {
{this.renderPackWidgetsRecursively(widgets, index + 1, side, previousExpandValue)} {this.renderPackWidgetsRecursively(widgets, index + 1, side, previousExpandValue)}
</div> </div>
); );
}; }
@@ -687,9 +687,13 @@ export class TkinterBase extends Widget {
setLayout(value) { setLayout(value) {
const { layout, direction, grid = { rows: 1, cols: 1 }, gap = 10, align } = value const { layout, direction, grid = { rows: 1, cols: 1 }, gap = 10, align } = value
const gridRow = this.getAttrValue("gridConfig.noOfRows") || 3 // suppose its loaded using this.load, we need this
const gridCol = this.getAttrValue("gridConfig.noOfCols") || 3
if (layout === Layouts.GRID){ if (layout === Layouts.GRID){
const rowWeight = this.getAttrValue("gridWeights.rowWeights") || undefined // suppose its loaded via this.load
const colWeight = this.getAttrValue("gridWeights.colWeights") || undefined // suppose its loaded via this.load
const {...restAttrs} = this.state.attrs const {...restAttrs} = this.state.attrs
@@ -703,7 +707,7 @@ export class TkinterBase extends Widget {
label: "No of rows", label: "No of rows",
tool: Tools.NUMBER_INPUT, tool: Tools.NUMBER_INPUT,
toolProps: { placeholder: "no of rows", max: 1000, min: 1 }, toolProps: { placeholder: "no of rows", max: 1000, min: 1 },
value: 3, value: gridRow,
onChange: (value) => { onChange: (value) => {
this.setAttrValue("gridConfig.noOfRows", value) this.setAttrValue("gridConfig.noOfRows", value)
@@ -729,7 +733,7 @@ export class TkinterBase extends Widget {
label: "No of cols", label: "No of cols",
tool: Tools.NUMBER_INPUT, tool: Tools.NUMBER_INPUT,
toolProps: { placeholder: "no of cols", max: 1000, min: 1 }, toolProps: { placeholder: "no of cols", max: 1000, min: 1 },
value: 3, value: gridCol,
onChange: (value) => { onChange: (value) => {
this.setAttrValue("gridConfig.noOfCols", value) this.setAttrValue("gridConfig.noOfCols", value)
@@ -753,7 +757,7 @@ export class TkinterBase extends Widget {
// placeholder: "weight", // placeholder: "weight",
// defaultWeightMapping: this.getAttrValue("gridWeights.rowWeights"), // defaultWeightMapping: this.getAttrValue("gridWeights.rowWeights"),
}, },
value: undefined, value: rowWeight,
Component: DynamicGridWeightInput, Component: DynamicGridWeightInput,
onChange: (value) => { onChange: (value) => {
@@ -781,7 +785,7 @@ export class TkinterBase extends Widget {
// placeholder: "weight", // placeholder: "weight",
// defaultWeightMapping: {0: {weight: 0, gridNo: 0}} // defaultWeightMapping: {0: {weight: 0, gridNo: 0}}
}, },
value: undefined, value: colWeight,
Component: DynamicGridWeightInput, Component: DynamicGridWeightInput,
onChange: (value) => { onChange: (value) => {
@@ -812,8 +816,6 @@ export class TkinterBase extends Widget {
}else if (layout === Layouts.FLEX){ }else if (layout === Layouts.FLEX){
const {gridConfig, gridWeights, ...restAttrs} = this.state.attrs const {gridConfig, gridWeights, ...restAttrs} = this.state.attrs
console.log("Flex: ", restAttrs)
this.updateState((prevState) => ({attrs: {...restAttrs}})) this.updateState((prevState) => ({attrs: {...restAttrs}}))
} }
@@ -827,8 +829,8 @@ export class TkinterBase extends Widget {
flexDirection: "column", flexDirection: "column",
// flexDirection: direction, // flexDirection: direction,
gap: `${gap}px`, gap: `${gap}px`,
gridTemplateColumns: "repeat(3, max-content)", gridTemplateColumns: `repeat(${gridRow}, max-content)`,
gridTemplateRows: "repeat(3, max-content)", gridTemplateRows: `repeat(${gridCol}, max-content)`,
// gridTemplateColumns: "repeat(auto-fill, minmax(100px, auto))", // gridTemplateColumns: "repeat(auto-fill, minmax(100px, auto))",
// gridTemplateRows: "repeat(auto-fill, minmax(100px, auto))", // gridTemplateRows: "repeat(auto-fill, minmax(100px, auto))",
} }
@@ -842,18 +844,6 @@ export class TkinterBase extends Widget {
const {layout: parentLayout, direction, gap} = this.getParentLayout() || {} 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 = { const styling = {
...this.state.widgetInnerStyling, ...this.state.widgetInnerStyling,
@@ -900,6 +890,19 @@ export class TkinterBase extends Widget {
} }
getToolbarAttrs(){
const {layout, gridConfig, gridWeights, ...toolBarAttrs} = super.getToolbarAttrs()
// places layout at the end
return ({
id: this.__id,
...toolBarAttrs,
layout,
gridConfig,
gridWeights
})
}
serialize(){ serialize(){
return ({ return ({
...super.serialize(), ...super.serialize(),
@@ -950,8 +953,10 @@ export class TkinterBase extends Widget {
...layoutUpdates, ...layoutUpdates,
pos pos
} }
const {layout} = attrs
this.setState(newData, () => { this.setState(newData, () => {
let layoutAttrs = this.setParentLayout(parentLayout).attrs || {} let layoutAttrs = this.setParentLayout(parentLayout).attrs || {}
@@ -977,15 +982,21 @@ export class TkinterBase extends Widget {
if (newAttrs?.styling?.backgroundColor){ if (newAttrs?.styling?.backgroundColor){
// TODO: find a better way to apply innerStyles // TODO: find a better way to apply innerStyles (we may not need this anymore)
this.setWidgetInnerStyle("backgroundColor", newAttrs.styling.backgroundColor.value) this.setWidgetInnerStyle("backgroundColor", newAttrs.styling.backgroundColor.value)
} }
this.updateState({ attrs: newAttrs }, callback) this.updateState({ attrs: newAttrs }, callback)
// FIXME: when changing layouts all the widgets are being selected // FIXME: when changing layouts all the widgets are being selected
if (selected){ if (selected){
this.select() this.select()
} }
if (layout){
this.setLayout(layout)
}
}) })

View File

@@ -161,7 +161,20 @@ class Frame extends TkinterBase{
] ]
} }
getToolbarAttrs(){
const {layout, gridConfig, gridWeights, ...toolBarAttrs} = super.getToolbarAttrs()
// places layout at the end
return ({
id: this.__id,
...toolBarAttrs,
padding: this.state.attrs.padding,
margin: this.state.attrs.margin,
layout,
gridConfig,
gridWeights
})
}
renderContent(){ renderContent(){
// console.log("bounding rect: ", this.getBoundingRect()) // console.log("bounding rect: ", this.getBoundingRect())

View File

@@ -4,7 +4,7 @@
*/ */
import { useEffect, useRef, useMemo, useState } from "react"; import { useEffect, useRef, useMemo, useState } from "react";
import { BookOutlined, CloseCircleFilled, CrownFilled, GithubFilled, ShareAltOutlined } from "@ant-design/icons"; import { BookOutlined, CloseCircleFilled, CrownFilled, DiscordFilled, GithubFilled, ShareAltOutlined } from "@ant-design/icons";
import KO_FI from "../assets/logo/ko-fi.png" import KO_FI from "../assets/logo/ko-fi.png"
import Premium from "./utils/premium"; import Premium from "./utils/premium";
@@ -88,6 +88,11 @@ function Sidebar({tabs}){
} }
<div className="tw-flex tw-flex-col tw-place-content-items tw-place-items-center tw-gap-3 tw-mt-auto"> <div className="tw-flex tw-flex-col tw-place-content-items tw-place-items-center tw-gap-3 tw-mt-auto">
<a href="https://discord.gg/dHXjrrCA7G" target="_blank"
title="discord invite"
rel="noopener noreferrer" className="tw-text-3xl tw-cursor-pointer tw-text-[#5562EA]">
<DiscordFilled />
</a>
<Premium className="tw-text-2xl tw-bg-purple-700 tw-text-center <Premium className="tw-text-2xl tw-bg-purple-700 tw-text-center
tw-w-[35px] tw-h-[35px] tw-rounded-md tw-w-[35px] tw-h-[35px] tw-rounded-md
tw-cursor-pointer tw-text-white tw-cursor-pointer tw-text-white
@@ -107,9 +112,9 @@ function Sidebar({tabs}){
rel="noopener noreferrer" className="tw-text-2xl tw-cursor-pointer tw-text-black"> rel="noopener noreferrer" className="tw-text-2xl tw-cursor-pointer tw-text-black">
<GithubFilled /> <GithubFilled />
</a> </a>
<a href="https://ko-fi.com/artpaul" className="tw-cursor-pointer "> {/* <a href="https://ko-fi.com/artpaul" className="tw-cursor-pointer ">
<img src={KO_FI} alt="ko-fi" className="tw-w-[30px] tw-h-[30px]"/> <img src={KO_FI} alt="ko-fi" className="tw-w-[30px] tw-h-[30px]"/>
</a> </a> */}
</div> </div>
</div> </div>

View File

@@ -38,11 +38,7 @@ function Premium({ children, className = "" }) {
If you find this tool useful and want to fund and support it's development, consider buying a <b>one time license</b>. If you find this tool useful and want to fund and support it's development, consider buying a <b>one time license</b>.
<br /> <br />
<br /> <br />
I am working on PYUI builder full time to help you easily build GUI's in python. However as a solo-dev, its super hard By pre-ordering license, you get discounted price, advance features, priority support, early access, upcoming features, and &nbsp;
to keep working on it without enough funding.
<br />
<br />
By buying pre-order license, I get to complete this faster and you get discounted price, advance features, priority support, early access, upcoming features, and &nbsp;
<a <a
href="https://github.com/PaulleDemon/PyUIBuilder/blob/main/roadmap.md" href="https://github.com/PaulleDemon/PyUIBuilder/blob/main/roadmap.md"
target="_blank" target="_blank"
@@ -145,7 +141,7 @@ function Premium({ children, className = "" }) {
Support open-source development 🚀. Plus, get added benefits. Support open-source development 🚀. Plus, get added benefits.
</p> </p>
<hr /> <hr />
<ul className="tw-mt-4 tw-flex tw-flex-col tw-gap-3 tw-text-xl tw-text-gray-600"> <ul className="tw-mt-4 tw-flex tw-flex-col tw-gap-3 tw-text-xl tw-text-black">
<li className="tw-flex tw-place-items-center tw-gap-2"> <li className="tw-flex tw-place-items-center tw-gap-2">
<i className="bi bi-check-circle-fill tw-text-green-600 tw-text-base"></i> <i className="bi bi-check-circle-fill tw-text-green-600 tw-text-base"></i>
<span>Access to web-based editor</span> <span>Access to web-based editor</span>