added webpack and fixed ImageLabel
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
.env-cmdrc.json
|
.env-cmdrc.json
|
||||||
build
|
build
|
||||||
|
dist
|
||||||
|
|
||||||
python-tests/
|
python-tests/
|
||||||
|
|
||||||
|
|||||||
2205
package-lock.json
generated
2205
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -35,8 +35,8 @@
|
|||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "env-cmd -e development cross-env NODE_ENV=development webpack serve",
|
||||||
"build": "GENERATE_SOURCEMAP=false react-scripts build",
|
"build": "env-cmd -e production cross-env NODE_ENV=production webpack",
|
||||||
"build:local": "GENERATE_SOURCEMAP=false env-cmd -e production react-scripts build",
|
"build:local": "GENERATE_SOURCEMAP=false env-cmd -e production react-scripts build",
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject",
|
"eject": "react-scripts eject",
|
||||||
@@ -66,9 +66,11 @@
|
|||||||
"@babel/core": "^7.26.10",
|
"@babel/core": "^7.26.10",
|
||||||
"@babel/preset-env": "^7.26.9",
|
"@babel/preset-env": "^7.26.9",
|
||||||
"@babel/preset-react": "^7.26.3",
|
"@babel/preset-react": "^7.26.3",
|
||||||
|
"@svgr/webpack": "^8.1.0",
|
||||||
"ajv": "^7.2.4",
|
"ajv": "^7.2.4",
|
||||||
"babel-loader": "^10.0.0",
|
"babel-loader": "^10.0.0",
|
||||||
"clean-webpack-plugin": "^4.0.0",
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
|
"copy-webpack-plugin": "^13.0.0",
|
||||||
"css-loader": "^7.1.2",
|
"css-loader": "^7.1.2",
|
||||||
"css-minimizer-webpack-plugin": "^7.0.2",
|
"css-minimizer-webpack-plugin": "^7.0.2",
|
||||||
"docsify-cli": "^4.4.4",
|
"docsify-cli": "^4.4.4",
|
||||||
|
|||||||
@@ -2,31 +2,31 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
<link rel="icon" href="favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
content="A python GUI builder. Create tkinter, Customtk, Kivy and PySide using GUI builder"
|
content="A python GUI builder. Create tkinter, Customtk, Kivy and PySide using GUI builder"
|
||||||
/>
|
/>
|
||||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
<link rel="apple-touch-icon" href="logo192.png" />
|
||||||
<!--
|
<!--
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
-->
|
-->
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
<link rel="manifest" href="manifest.json" />
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.11.3/font/bootstrap-icons.min.css" integrity="sha512-dPXYcDub/aeb08c63jRq/k6GaKccl256JQy/AnOq7CAnEZ9FzSL9wSbcZkMp4R26vBsMLFYH4kQ67/bbV8XaCQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.11.3/font/bootstrap-icons.min.css" integrity="sha512-dPXYcDub/aeb08c63jRq/k6GaKccl256JQy/AnOq7CAnEZ9FzSL9wSbcZkMp4R26vBsMLFYH4kQ67/bbV8XaCQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||||
|
|
||||||
<script async src="https://tally.so/widgets/embed.js"></script>
|
<script async src="https://tally.so/widgets/embed.js"></script>
|
||||||
|
|
||||||
<!-- Google tag (gtag.js) -->
|
<!-- Google tag (gtag.js) -->
|
||||||
<script async src="https://www.googletagmanager.com/gtag/js?id=%REACT_APP_ANALYTICS_SCRIPT_ID%"></script>
|
<script async src="https://www.googletagmanager.com/gtag/js?id=<%= REACT_APP_ANALYTICS_SCRIPT_ID %>"></script>
|
||||||
<script>
|
<script>
|
||||||
window.dataLayer = window.dataLayer || [];
|
window.dataLayer = window.dataLayer || [];
|
||||||
function gtag(){dataLayer.push(arguments);}
|
function gtag(){dataLayer.push(arguments);}
|
||||||
gtag('js', new Date());
|
gtag('js', new Date());
|
||||||
|
|
||||||
gtag('config', '%REACT_APP_ANALYTICS_SCRIPT_ID%');
|
gtag('config', '<%= REACT_APP_ANALYTICS_SCRIPT_ID %>');
|
||||||
</script>
|
</script>
|
||||||
<!--
|
<!--
|
||||||
Notice the use of %PUBLIC_URL% in the tags above.
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ import { removeDuplicateObjects } from "../utils/common"
|
|||||||
|
|
||||||
|
|
||||||
// import DotsBackground from "../assets/background/dots.svg"
|
// import DotsBackground from "../assets/background/dots.svg"
|
||||||
import { ReactComponent as DotsBackground } from "../assets/background/dots.svg"
|
// import { ReactComponent as DotsBackground } from "../assets/background/dots.svg"
|
||||||
|
import DotsBackground from "../assets/background/dots.svg";
|
||||||
|
|
||||||
import DroppableWrapper from "../components/draggable/droppable"
|
import DroppableWrapper from "../components/draggable/droppable"
|
||||||
|
|
||||||
|
|||||||
@@ -159,7 +159,6 @@ async function generateTkinterCode(projectName, widgetList=[], widgetRefs=[], as
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add empty __init__ file
|
|
||||||
for (let customWidget of customPythonWidgets){
|
for (let customWidget of customPythonWidgets){
|
||||||
|
|
||||||
let [fileName, extension] = customWidget.split(".")
|
let [fileName, extension] = customWidget.split(".")
|
||||||
@@ -169,9 +168,8 @@ async function generateTkinterCode(projectName, widgetList=[], widgetRefs=[], as
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const fileContent = pythonFiles(`./${fileName}`);
|
const fileContent = pythonFiles(`./${fileName}`).default
|
||||||
|
|
||||||
console.log("file name: ", fileContent.default, pythonFiles(`./${fileName}`))
|
|
||||||
createFileList.push({
|
createFileList.push({
|
||||||
fileData: new Blob([fileContent], { type: "text/plain" }),
|
fileData: new Blob([fileContent], { type: "text/plain" }),
|
||||||
fileName: fileName,
|
fileName: fileName,
|
||||||
@@ -179,6 +177,14 @@ async function generateTkinterCode(projectName, widgetList=[], widgetRefs=[], as
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (customPythonWidgets.length > 0){
|
||||||
|
createFileList.push({
|
||||||
|
fileData: new Blob([''], { type: "text/plain" }),
|
||||||
|
fileName: '__init__.py',
|
||||||
|
folder: "customWidgets"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
for (let asset of assetFiles){
|
for (let asset of assetFiles){
|
||||||
|
|
||||||
if (asset.fileType === "image"){
|
if (asset.fileType === "image"){
|
||||||
|
|||||||
@@ -1,26 +1,43 @@
|
|||||||
|
# Author: Paul: https://github.com/PaulleDemon
|
||||||
|
# Made using PyUibuilder: https://pyuibuilder.com
|
||||||
|
# MIT License - keep the copy of this license
|
||||||
|
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from PIL import Image, ImageTk
|
from PIL import Image, ImageTk
|
||||||
|
|
||||||
|
|
||||||
class ImageLabel(tk.Label):
|
class ImageLabel(tk.Label):
|
||||||
def __init__(self, master, image_path, mode="fit", *args, **kwargs):
|
def __init__(self, master, image_path=None, mode="cover", width=100, height=100, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
mode:
|
mode:
|
||||||
- "fit" -> Keeps aspect ratio, fits inside label
|
- "fit" -> Keeps aspect ratio, fits inside label
|
||||||
- "cover" -> Covers label fully, cropping excess
|
- "cover" -> Covers label fully, cropping excess
|
||||||
"""
|
"""
|
||||||
super().__init__(master, *args, **kwargs)
|
super().__init__(master, width=width, height=height, *args, **kwargs)
|
||||||
self.parent = master # Store parent reference
|
self.parent = master
|
||||||
self.image_path = image_path
|
self.image_path = image_path
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.original_image = Image.open(image_path)
|
self.original_image = None
|
||||||
self.photo = None
|
self.photo = None
|
||||||
self.resize_job = None # Debounce job reference
|
self.resize_job = None # Debounce job reference
|
||||||
|
|
||||||
self.force_resize() # Initial resize
|
if mode not in ['fit', 'cover']:
|
||||||
self.after(100, self.init_events) # Delay event binding slightly
|
raise Exception("Mode can only be fit or cover.")
|
||||||
|
|
||||||
|
if image_path:
|
||||||
|
try:
|
||||||
|
self.original_image = Image.open(image_path)
|
||||||
|
self.photo = ImageTk.PhotoImage(self.original_image)
|
||||||
|
self.config(image=self.photo)
|
||||||
|
|
||||||
|
self.force_resize()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error loading image: {e}")
|
||||||
|
|
||||||
|
self.after(100, self.init_events)
|
||||||
|
|
||||||
def init_events(self):
|
def init_events(self):
|
||||||
self.parent.bind("<Configure>", self.on_resize) # Bind resize to parent
|
self.parent.bind("<Configure>", self.on_resize)
|
||||||
|
|
||||||
def on_resize(self, event=None):
|
def on_resize(self, event=None):
|
||||||
"""Debounce resizing to prevent rapid execution."""
|
"""Debounce resizing to prevent rapid execution."""
|
||||||
@@ -30,8 +47,13 @@ class ImageLabel(tk.Label):
|
|||||||
|
|
||||||
def force_resize(self):
|
def force_resize(self):
|
||||||
"""Resize image using actual widget size."""
|
"""Resize image using actual widget size."""
|
||||||
|
|
||||||
|
if self.original_image is None:
|
||||||
|
return # Do nothing if no image is loaded
|
||||||
|
|
||||||
width = self.winfo_width()
|
width = self.winfo_width()
|
||||||
height = self.winfo_height()
|
height = self.winfo_height()
|
||||||
|
|
||||||
if width < 5 or height < 5:
|
if width < 5 or height < 5:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -53,6 +75,7 @@ class ImageLabel(tk.Label):
|
|||||||
else:
|
else:
|
||||||
new_width = int(height * aspect_ratio)
|
new_width = int(height * aspect_ratio)
|
||||||
new_height = height
|
new_height = height
|
||||||
|
|
||||||
resized = self.original_image.resize((new_width, new_height), Image.LANCZOS)
|
resized = self.original_image.resize((new_width, new_height), Image.LANCZOS)
|
||||||
|
|
||||||
# Crop excess
|
# Crop excess
|
||||||
|
|||||||
@@ -562,6 +562,7 @@ export class TkinterBase extends Widget {
|
|||||||
|
|
||||||
// console.log("rerendering:", side, expand);
|
// console.log("rerendering:", side, expand);
|
||||||
|
|
||||||
|
|
||||||
const directionMap = {
|
const directionMap = {
|
||||||
top: "column",
|
top: "column",
|
||||||
bottom: "column-reverse",
|
bottom: "column-reverse",
|
||||||
@@ -587,7 +588,7 @@ export class TkinterBase extends Widget {
|
|||||||
expandValue = 1
|
expandValue = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: if previous expand value is greater than 0 and current doesn't have expand then it should be 0
|
// TODO: if the child widget as fillx or y use flex grow
|
||||||
|
|
||||||
if ((expand && !isSameSide) || (expand && previousExpandValue === 0)){
|
if ((expand && !isSameSide) || (expand && previousExpandValue === 0)){
|
||||||
previousExpandValue = expandValue;
|
previousExpandValue = expandValue;
|
||||||
@@ -610,6 +611,9 @@ export class TkinterBase extends Widget {
|
|||||||
|
|
||||||
|
|
||||||
const stretchClass = isVertical ? "tw-flex-grow" : "tw-h-full"; // Allow only horizontal growth for top/bottom
|
const stretchClass = isVertical ? "tw-flex-grow" : "tw-h-full"; // Allow only horizontal growth for top/bottom
|
||||||
|
// TODO: if previous expand value is greater than 0 and current doesn't have expand then it should be 0
|
||||||
|
|
||||||
|
// const fill = this.getAttrValue("flexManager.fillX") || this.getAttrValue("flexManager.fillY")
|
||||||
|
|
||||||
if (isSameSide) {
|
if (isSameSide) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class Label extends TkinterWidgetBase{
|
|||||||
static widgetType = "label"
|
static widgetType = "label"
|
||||||
static displayName = "Label"
|
static displayName = "Label"
|
||||||
|
|
||||||
static requiredCustomPyFiles = ["imageLabel"]
|
// static requiredCustomPyFiles = ["imageLabel"]
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
@@ -104,11 +104,20 @@ class Label extends TkinterWidgetBase{
|
|||||||
const imports = super.getImports()
|
const imports = super.getImports()
|
||||||
|
|
||||||
if (this.getAttrValue("imageUpload"))
|
if (this.getAttrValue("imageUpload"))
|
||||||
imports.push("import os", "from PIL import Image, ImageTk", )
|
imports.push("import os", "from PIL import Image, ImageTk", "from customWidgets.imageLabel import ImageLabel")
|
||||||
|
|
||||||
return imports
|
return imports
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getRequiredCustomPyFiles(){
|
||||||
|
const requiredCustomFiles = super.getRequiredCustomPyFiles()
|
||||||
|
|
||||||
|
if (this.getAttrValue("imageUpload"))
|
||||||
|
requiredCustomFiles.push("imageLabel")
|
||||||
|
|
||||||
|
return requiredCustomFiles
|
||||||
|
}
|
||||||
|
|
||||||
getRequirements(){
|
getRequirements(){
|
||||||
const requirements = super.getRequirements()
|
const requirements = super.getRequirements()
|
||||||
|
|
||||||
@@ -161,10 +170,10 @@ class Label extends TkinterWidgetBase{
|
|||||||
const code = []
|
const code = []
|
||||||
|
|
||||||
if (image?.name){
|
if (image?.name){
|
||||||
code.push(`${variableName}_img = Image.open(${getPythonAssetPath(image.name, "image")})`)
|
// code.push(`${variableName}_img = Image.open(${getPythonAssetPath(image.name, "image")})`)
|
||||||
code.push(`${variableName}_img = ImageTk.PhotoImage(${variableName}_img)`)
|
// code.push(`${variableName}_img = ImageTk.PhotoImage(${variableName}_img)`)
|
||||||
// code.push("\n")
|
// code.push("\n")
|
||||||
labelInitialization = `${variableName} = tk.Label(master=${parent}, image=${variableName}_img, text="${labelText}", compound=tk.TOP)`
|
labelInitialization = `${variableName} = ImageLabel(master=${parent}, image_path=${getPythonAssetPath(image.name, "image")}, text="${labelText}", compound=tk.TOP, mode="${this.getAttrValue("imageSize.mode")}")`
|
||||||
}
|
}
|
||||||
|
|
||||||
// code.push("\n")
|
// code.push("\n")
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ import "./styles/index.css";
|
|||||||
|
|
||||||
import { FileUploadProvider } from "./contexts/fileUploadContext";
|
import { FileUploadProvider } from "./contexts/fileUploadContext";
|
||||||
|
|
||||||
|
window.React = React
|
||||||
|
|
||||||
|
|
||||||
const originalSetItem = localStorage.setItem;
|
const originalSetItem = localStorage.setItem;
|
||||||
// triggers itemsChaned event whenever the item in localstorage is chanegd.
|
// triggers itemsChaned event whenever the item in localstorage is chanegd.
|
||||||
localStorage.setItem = function (key, value) {
|
localStorage.setItem = function (key, value) {
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ function Premium({ children, className = "" }) {
|
|||||||
>
|
>
|
||||||
more.
|
more.
|
||||||
</a>
|
</a>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Premium features will start rolling out phase wise from mid of April, after which there would be a price increase.
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,110 @@
|
|||||||
|
const webpack = require('webpack');
|
||||||
|
|
||||||
|
const path = require("path");
|
||||||
|
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||||
|
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||||
|
const CopyWebpackPlugin = require("copy-webpack-plugin");
|
||||||
|
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
|
||||||
|
|
||||||
|
const dotenv = require('dotenv');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const isProduction = process.env.NODE_ENV === "production";
|
||||||
|
|
||||||
|
// Load .env-cmdrc.json based on NODE_ENV
|
||||||
|
const envFile = `.env-cmdrc.json`;
|
||||||
|
const envConfig = JSON.parse(fs.readFileSync(envFile, 'utf8'))[process.env.NODE_ENV] || {};
|
||||||
|
|
||||||
|
// Convert JSON to a format Webpack understands
|
||||||
|
const envKeys = Object.keys(envConfig).reduce((prev, next) => {
|
||||||
|
prev[`process.env.${next}`] = JSON.stringify(envConfig[next]);
|
||||||
|
return prev;
|
||||||
|
}, {});
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
mode: isProduction ? "production" : "development",
|
||||||
|
|
||||||
|
watch: !isProduction,
|
||||||
|
watchOptions: {
|
||||||
|
ignored: /node_modules/,
|
||||||
|
},
|
||||||
|
|
||||||
|
entry: "./src/index.js",
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, "dist"),
|
||||||
|
filename: isProduction ? "js/[name].[contenthash].js" : "js/[name].js",
|
||||||
|
publicPath: "/",
|
||||||
|
},
|
||||||
|
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.(js|jsx)$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: {
|
||||||
|
loader: "babel-loader",
|
||||||
|
options: {
|
||||||
|
presets: ["@babel/preset-env", "@babel/preset-react"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader",],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
test: /\.py$/,
|
test: /\.py$/,
|
||||||
use: "raw-loader",
|
use: "raw-loader", // Load Python files as raw text
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.svg$/,
|
||||||
|
use: ["@svgr/webpack"], // Enables importing SVGs as React components
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(png|jpe?g|gif|ico)$/,
|
||||||
|
type: "asset/resource", // Handles image files
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
new webpack.DefinePlugin(envKeys),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: "./public/index.html",
|
||||||
|
minify: isProduction,
|
||||||
|
inject: true,
|
||||||
|
templateParameters: envConfig
|
||||||
|
}),
|
||||||
|
(isProduction ? new MiniCssExtractPlugin({ filename: 'styles.[contenthash].css' }) : new MiniCssExtractPlugin()),
|
||||||
|
new CopyWebpackPlugin({
|
||||||
|
patterns: [
|
||||||
|
{
|
||||||
|
from: path.resolve(__dirname, "src/assets"),
|
||||||
|
to: "assets", // Copies to `dist/assets`
|
||||||
|
noErrorOnMissing: true, // Prevents errors if the folder is missing
|
||||||
|
},
|
||||||
|
{ from: 'public', to: '', noErrorOnMissing: true, globOptions: { ignore: ['**/index.html'] }}, // Copies everything else from public to dist
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
new webpack.EnvironmentPlugin({
|
||||||
|
REACT_APP_ANALYTICS_SCRIPT_ID: process.env.REACT_APP_ANALYTICS_SCRIPT_ID || '', // Default empty value
|
||||||
|
API_URL: 'https://default.api.com'
|
||||||
|
})
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
optimization: isProduction
|
||||||
|
? {
|
||||||
|
minimize: true,
|
||||||
|
minimizer: [new CssMinimizerPlugin()],
|
||||||
|
}
|
||||||
|
: {},
|
||||||
|
|
||||||
|
devServer: {
|
||||||
|
static: path.join(__dirname, "public"),
|
||||||
|
port: 3000,
|
||||||
|
hot: true,
|
||||||
|
historyApiFallback: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user