Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,63 +1,118 @@
|
|
|
|
|
| 1 |
import gradio as gr
|
|
|
|
|
|
|
| 2 |
import pandas as pd
|
| 3 |
|
| 4 |
-
def
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
# Ubah nama kolom yang tidak informatif (misalnya 'Unnamed: ...') menjadi nama generik
|
| 18 |
-
df.columns = [f"Column_{i}" if "Unnamed" in col else col for i, col in enumerate(df.columns)]
|
| 19 |
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
|
|
|
| 23 |
|
| 24 |
-
|
| 25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
# 📊 Google Sheet Viewer
|
| 31 |
-
**Cara Penggunaan:**
|
| 32 |
-
1. Buka Google Sheet anda
|
| 33 |
-
2. Pilih *File > Share > Publish to web*
|
| 34 |
-
3. Pilih format **CSV** dan publish
|
| 35 |
-
4. Salin URL CSV yang dihasilkan
|
| 36 |
-
""")
|
| 37 |
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
|
| 45 |
-
#
|
| 46 |
-
|
| 47 |
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
|
| 62 |
if __name__ == "__main__":
|
| 63 |
-
|
|
|
|
| 1 |
+
# app.py
|
| 2 |
import gradio as gr
|
| 3 |
+
from PIL import Image, ImageDraw
|
| 4 |
+
from rectpack import newPacker, PackingMode, PackingAlgorithm
|
| 5 |
import pandas as pd
|
| 6 |
|
| 7 |
+
def urut_dan_nesting(lebar_roll, daftar_banner):
|
| 8 |
+
tinggi_maks_roll = 2000
|
| 9 |
+
packer = newPacker(
|
| 10 |
+
mode=PackingMode.Rectangle,
|
| 11 |
+
bin_algo=PackingAlgorithm.MaxRectsBl,
|
| 12 |
+
rotation=True
|
| 13 |
+
)
|
| 14 |
+
packer.add_bin(lebar_roll, tinggi_maks_roll)
|
| 15 |
+
|
| 16 |
+
for banner in daftar_banner:
|
| 17 |
+
w, h, nama = banner
|
| 18 |
+
packer.add_rect(int(w), int(h), rid=str(nama))
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
+
packer.pack()
|
| 21 |
+
|
| 22 |
+
hasil_layout = []
|
| 23 |
+
tinggi_pakai_maksimal = 0
|
| 24 |
|
| 25 |
+
for rect in packer.rect_list():
|
| 26 |
+
b_idx, x, y, w, h, rid = rect
|
| 27 |
+
hasil_layout.append({
|
| 28 |
+
"nama": rid,
|
| 29 |
+
"x": x,
|
| 30 |
+
"y": y,
|
| 31 |
+
"lebar_cetak": w,
|
| 32 |
+
"tinggi_cetak": h
|
| 33 |
+
})
|
| 34 |
+
if y + h > tinggi_pakai_maksimal:
|
| 35 |
+
tinggi_pakai_maksimal = y + h
|
| 36 |
+
|
| 37 |
+
return hasil_layout, max(tinggi_pakai_maksimal, 10)
|
| 38 |
|
| 39 |
+
def proses_nesting(lebar_roll, df_order):
|
| 40 |
+
if df_order is None or df_order.empty:
|
| 41 |
+
return None, "❌ Daftar banner masih kosong."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
|
| 43 |
+
daftar_banner = []
|
| 44 |
+
for index, row in df_order.iterrows():
|
| 45 |
+
try:
|
| 46 |
+
w = float(row.iloc[0])
|
| 47 |
+
h = float(row.iloc[1])
|
| 48 |
+
nama = str(row.iloc[2])
|
| 49 |
+
if w > 0 and h > 0:
|
| 50 |
+
daftar_banner.append((w, h, nama))
|
| 51 |
+
except Exception:
|
| 52 |
+
continue
|
| 53 |
+
|
| 54 |
+
if not daftar_banner:
|
| 55 |
+
return None, "❌ Format data salah. Pastikan kolom Lebar dan Tinggi berupa angka."
|
| 56 |
+
|
| 57 |
+
layout, total_panjang_bahan = urut_dan_nesting(lebar_roll, daftar_banner)
|
| 58 |
+
|
| 59 |
+
SKALA = 4
|
| 60 |
+
img_w = int(lebar_roll * SKALA)
|
| 61 |
+
img_h = int(total_panjang_bahan * SKALA)
|
| 62 |
+
|
| 63 |
+
canvas = Image.new("RGB", (img_w, img_h), "#F0F0F0")
|
| 64 |
+
draw = ImageDraw.Draw(canvas)
|
| 65 |
+
|
| 66 |
+
for item in layout:
|
| 67 |
+
x1 = item["x"] * SKALA
|
| 68 |
+
y1 = (total_panjang_bahan - item["y"] - item["tinggi_cetak"]) * SKALA
|
| 69 |
+
x2 = x1 + (item["lebar_cetak"] * SKALA)
|
| 70 |
+
y2 = y1 + (item["tinggi_cetak"] * SKALA)
|
| 71 |
|
| 72 |
+
draw.rectangle([x1, y1, x2, y2], fill="#A8DADC", outline="#1D3557", width=2)
|
| 73 |
+
draw.text((x1 + 10, y1 + 10), f"{item['nama']}\n({item['lebar_cetak']}x{item['tinggi_cetak']})", fill="#1D3557")
|
| 74 |
|
| 75 |
+
info = f"✅ **Berhasil dihitung!**\n- Lebar Roll: **{lebar_roll} cm**\n- Panjang Bahan Terpakai: **{total_panjang_bahan} cm**"
|
| 76 |
+
return canvas, info
|
| 77 |
+
|
| 78 |
+
# Data awal default untuk demo
|
| 79 |
+
data_default = pd.DataFrame([
|
| 80 |
+
[100, 100, "Banner_A_Agus"],
|
| 81 |
+
[60, 90, "Banner_B_Budi_1"],
|
| 82 |
+
[60, 90, "Banner_B_Budi_2"],
|
| 83 |
+
[40, 80, "Banner_C_Cici"],
|
| 84 |
+
[110, 50, "Banner_D_Deni"],
|
| 85 |
+
[30, 30, "Logo_Kecil_1"],
|
| 86 |
+
[30, 30, "Logo_Kecil_2"],
|
| 87 |
+
[50, 70, "Banner_E_Eka"]
|
| 88 |
+
], columns=["Lebar (cm)", "Tinggi (cm)", "Nama / ID Order"])
|
| 89 |
+
|
| 90 |
+
app = gr.Blocks(theme=gr.themes.Soft())
|
| 91 |
+
|
| 92 |
+
with app:
|
| 93 |
+
gr.Markdown("# 🖨️ Kalkulator Nesting & Layout Banner Otomatis")
|
| 94 |
+
gr.Markdown("Atur ukuran roll dan masukkan daftar pesanan banner untuk mengoptimalkan pemotongan bahan secara otomatis.")
|
| 95 |
|
| 96 |
+
with gr.Row():
|
| 97 |
+
with gr.Column(scale=1):
|
| 98 |
+
lebar_roll_input = gr.Number(value=120, label="Lebar Roll Mesin (cm)")
|
| 99 |
+
tabel_order = gr.Dataframe(
|
| 100 |
+
value=data_default,
|
| 101 |
+
headers=["Lebar (cm)", "Tinggi (cm)", "Nama / ID Order"],
|
| 102 |
+
datatype=["number", "number", "str"],
|
| 103 |
+
row_count=(5, "dynamic"),
|
| 104 |
+
col_count=(3, "fixed"),
|
| 105 |
+
label="Daftar Order Banner"
|
| 106 |
+
)
|
| 107 |
+
btn_proses = gr.Button("🚀 Hitung & Generate Layout", variant="primary")
|
| 108 |
+
|
| 109 |
+
with gr.Column(scale=1):
|
| 110 |
+
output_info = gr.Markdown(label="Informasi Hasil")
|
| 111 |
+
output_image = gr.Image(label="Pratinjau Layout Operator", type="pil")
|
| 112 |
+
|
| 113 |
+
btn_proses.fn = proses_nesting
|
| 114 |
+
btn_proses.inputs = [lebar_roll_input, tabel_order]
|
| 115 |
+
btn_proses.outputs = [output_image, output_info]
|
| 116 |
|
| 117 |
if __name__ == "__main__":
|
| 118 |
+
app.launch()
|