Volt egy olyan a bakancslistámon, hogy ebben az életben még egyszer szeretnék programozást művelni (kb 26 év után újra). Remélem ezek nem olyan bezáró-zárójelek, amelyekkel egyes életszakaszaimat zárom le, de ez most mindegy is.
Kapóra jött, hogy volt a cégnél egy régóta megoldandó feladat, plusz a MI fejlődése is lehetővé teszi már, hogy bárki programozóként tetszeleghessen. Nem mellesleg, emiatt mennek tönkre programozói állások. Ahogyan a fotográfia több rendbéli szekularizálódása is mindannyiszor keményen bevágott a fotós szakmának.
A braille írások domborító kliséinek előkészítéséről, és nyilvántartásáról van szó. Évekig mondogattam, hogy a csomagolóipar egyetlen (EU-s) releváns húzása a braille rányomtatása a dobozra, mert a csillivilli aranyfóliák, meg amúgy a külcsíny, ellene megy minden józan felfogásnak a reklámról, meg a kapitalizmusról általában, de a braille írás legalább alkalmas arra, hogy a vakok ne vegyenek be hashajtóra nyugtatót.
Aztán szembesültem azzal az adattal, hogy a vakok alig 10 százaléka érti a braille írást. Nabazmeg.
Szóval, a feladat az volt, hogy a Braille-készítő célgép által fogadott és felismert dxf-eket előállítsuk. Eddig ezeket QCadben csináltuk, a nyilvántartást pedig nyögvenyelősen Illustratorban, Corelben, szemmel ellenőrizve és egérrel összevillogtatva. Na, ezt akarjuk megkönnyíteni, hogy az egyes klisékkel eltöltött átlagos 10 percet, 1-2 perc környékére redukáljuk.
A 6 pontos braille írás szabványa:
- a pontok 1,6mm átmérőjűek, 2,5 milliméterre vannak egymástól, a braille karakterek távolsága 6mm, a soroké pedig 10mm. 20*4 braille karakterrel dolgozunk, ez 480 pontot jelent.
A részfeladatokat külön programokra bontottuk, a könnyebb tesztelhetőség érdekében, illetve néhány modulra, csak egyszer, illetve csak a tesztidőszakban van szükség.
A felület Tkinter, a dxf fileokhoz szükség van a ezdxf python-csomagra.
Bulldózer
import osimport tkinter as tkfrom tkinter import filedialog, scrolledtextimport ezdxffrom ezdxf import recoverimport math# --- 1. MATEMATIKAI MOTOR ÉS KÓDOLÓ ---class BrailleEngine:def __init__(self):# Alapértékek (mm)self.dot_dist = 2.5self.char_dist = 6.0self.row_dist = 10.0self.tolerance = 0.5# Base64 ábécé a 6-bites Braille cellákhozself.b64_chars = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")def bits_to_base64(self, bit_string):"""480 bitből 80 darab Base64 karaktert csinál (6 bitenként)."""b64_result = ""for i in range(0, 480, 6):six_bits = bit_string[i : i + 6]val = 0for bit_pos, bit in enumerate(six_bits):if bit == "1":val += 1 << bit_posb64_result += self.b64_chars[val]return b64_resultdef get_theoretical_grid(self):"""Legenerálja a 480 rácspont elméleti koordinátáját."""grid = []for row in range(4):for char in range(20):for dot_col in range(2):for dot_row in range(3):x = (char * self.char_dist) + (dot_col * self.dot_dist)y = -((row * self.row_dist) + (dot_row * self.dot_dist))grid.append((x, y))return griddef fit_points(self, points):"""Rácsra illesztés és Base64 kódolás."""if not points:return "A" * 80min_x = min(p[0] for p in points)max_y = max(p[1] for p in points)norm_points = [(p[0] - min_x, p[1] - max_y) for p in points]theoretical_grid = self.get_theoretical_grid()# ez az offset-lista nem mukodott fuggoleges eltolasban, offsets = [(0, 0), (2.5, 0), (0, -2.5), (2.5, -2.5), (0, -5.0), (2.5, -5.0)]# ez az offset-lista nem azonos a BraiLuca offsetlistaajaval, pont mast tologatunk ittoffsets = [(0, 0), # Alap(2.5, 0), # Ha hiányzik a bal oszlop, a pontokat jobbra toljuk a rács 2. oszlopára(0, -2.5), # Ha hiányzik a felső sor, a pontokat lejjebb toljuk a rács 2. sorára(2.5, -2.5), # Mindkettő hiányzik(0, -5.0), # Felső két sor hiányzik(2.5, -5.0) # Bal oszlop és felső két sor hiányzik]for ox, oy in offsets:fingerprint_bits = ["0"] * 480matched_count = 0for px, py in norm_points:tx, ty = px + ox, py + oyfor i, (gx, gy) in enumerate(theoretical_grid):dist = math.sqrt((tx - gx) ** 2 + (ty - gy) ** 2)if dist <= self.tolerance:fingerprint_bits[i] = "1"matched_count += 1breakif matched_count == len(points):return self.bits_to_base64("".join(fingerprint_bits))return None# --- 2. GUI ÉS FÁJLKEZELŐ ---class BrailleBulldozer:def __init__(self, root):self.root = rootself.root.title("Braille Buldózer v1.4 - Base64 Expert")self.root.geometry("800x600")self.engine = BrailleEngine()self.selected_path = ""# UI Felépítéseself.btn_browse = tk.Button(root,text="Forrásmappa kiválasztása",command=self.browse_folder,bg="#e1e1e1",height=2,)self.btn_browse.pack(pady=10, fill=tk.X, padx=20)self.lbl_path = tk.Label(root, text="Nincs mappa kiválasztva", fg="gray")self.lbl_path.pack()self.log_area = scrolledtext.ScrolledText(root, width=90, height=25, font=("Consolas", 9))self.log_area.pack(pady=10, padx=20)self.btn_start = tk.Button(root,text="Feldolgozás és Base64 CSV mentés",command=self.process_files,state=tk.DISABLED,bg="#4CAF50",fg="white",height=2,)self.btn_start.pack(pady=10, fill=tk.X, padx=20)def log(self, message):self.log_area.insert(tk.END, message + "\n")self.log_area.see(tk.END)self.root.update_idletasks()def browse_folder(self):"""Ez a függvény hiányzott vagy elcsúszott az előbb!"""self.selected_path = filedialog.askdirectory()if self.selected_path:self.lbl_path.config(text=self.selected_path, fg="black")self.btn_start.config(state=tk.NORMAL)self.log(f"Kiválasztott mappa: {self.selected_path}")def manual_dxf_parse(self, dxf_path):"""Szöveges DXF feldolgozás, ha az ezdxf elakad."""points = []try:with open(dxf_path, "r", encoding="utf-8", errors="ignore") as f:content = f.read().splitlines()i = 0while i < len(content):line = content[i].strip()if (line == "0"and i + 1 < len(content)and content[i + 1].strip() == "CIRCLE"):current_layer, x, y = "", None, Nonei += 2while i < len(content) and content[i].strip() != "0":code, val = content[i].strip(), content[i + 1].strip()if code == "8":current_layer = valif code == "10":x = float(val)if code == "20":y = float(val)i += 2if (current_layer.upper() == "LAYER1"and x is not Noneand y is not None):points.append((x, y))continuei += 1return pointsexcept:return Nonedef get_layer1_circles(self, dxf_path):"""Hibrid olvasó: ezdxf -> manual."""try:doc, auditor = recover.readfile(dxf_path)msp = doc.modelspace()# Itt egy egyszerűbb lekérdezést használunk a stabilitásértpts = []for e in msp:if e.dxftype() == "CIRCLE" and e.dxf.layer.upper() == "LAYER1":pts.append(e.dxf.center[:2])return ptsexcept:return self.manual_dxf_parse(dxf_path)def process_files(self):self.log("\n--- FELDOLGOZÁS INDÍTÁSA (Base64 mód) ---")results = []stats = {"total": 0, "success": 0, "no_grid": 0, "empty": 0}for root_dir, dirs, files in os.walk(self.selected_path):for file in files:if file.lower().endswith(".dxf"):stats["total"] += 1full_path = os.path.join(root_dir, file)circles = self.get_layer1_circles(full_path)if circles is not None and len(circles) > 0:b64_fprint = self.engine.fit_points(circles)if b64_fprint:id_name = os.path.splitext(file)[0]results.append(f"{id_name};{b64_fprint}")self.log(f"OK: {file} -> [ {b64_fprint[:20]}... ]")stats["success"] += 1else:self.log(f"HIBA: {file} -> Nem illeszkedik a rácsra!")stats["no_grid"] += 1else:self.log(f"ÜRES: {file} -> Nincs kör a Layer1-en.")stats["empty"] += 1if results:csv_path = os.path.join(self.selected_path, "ujjlenyomatok_b64.csv")with open(csv_path, "w", encoding="utf-8") as f:f.write("\n".join(results))self.log(f"\n" + "=" * 40)self.log(f"ÖSSZESÍTÉS:")self.log(f" Talált DXF: {stats['total']}")self.log(f" Sikeres ujjlenyomat: {stats['success']}")self.log(f" Rossz rács/zaj: {stats['no_grid']}")self.log(f" Üres fájl: {stats['empty']}")self.log(f"CSV mentve: {csv_path}")self.log("=" * 40)else:self.log("\n--- KÉSZ! (Nem született eredmény) ---")if __name__ == "__main__":root = tk.Tk()app = BrailleBulldozer(root)root.mainloop()
BraiLuca
# INSUNIT mm-re alllitva, SPLINE es CIRCLE elemeket keresimport osimport tkinter as tkfrom tkinter import filedialog, messageboximport ezdxffrom ezdxf import recoverimport math# --- BRAILLE MOTOR ---class BrailleEngine:def __init__(self):self.dot_dist = 2.5self.char_dist = 6.0self.row_dist = 10.0self.tolerance = 0.8 # Kicsit engedékenyebb az új kliséknélself.b64_chars = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")def bits_to_base64(self, bit_string):b64_result = ""for i in range(0, 480, 6):six_bits = bit_string[i : i + 6]val = 0for bit_pos, bit in enumerate(six_bits):if bit == "1":val += 1 << bit_posb64_result += self.b64_chars[val]return b64_resultdef get_theoretical_grid(self, offset_x=0, offset_y=0):"""Generál egy rácsot adott eltolással."""grid = []for row in range(4):for char in range(20):for dot_col in range(2):for dot_row in range(3):gx = ((char * self.char_dist)+ (dot_col * self.dot_dist)+ offset_x)gy = (-((row * self.row_dist) + (dot_row * self.dot_dist))+ offset_y)grid.append((gx, gy))return griddef get_best_fit(self, points):if not points:return None, None, Falsemin_x = min(p[0] for p in points)max_y = max(p[1] for p in points)offsets = [(0, 0), (2.5, 0), (0, 2.5), (2.5, 2.5), (0, 5.0), (2.5, 5.0)]for ox, oy in offsets:theoretical_grid = self.get_theoretical_grid(min_x - ox, max_y + oy)fingerprint_bits = ["0"] * 480snapped_points = []matched_count = 0for px, py in points:for i, (gx, gy) in enumerate(theoretical_grid):dist = math.sqrt((px - gx) ** 2 + (py - gy) ** 2)if dist <= self.tolerance:fingerprint_bits[i] = "1"snapped_points.append((gx, gy))matched_count += 1breakif matched_count == len(points):is_vertical_shifted = (oy != 0)return self.bits_to_base64("".join(fingerprint_bits)), snapped_points, is_vertical_shiftedreturn None, None, False# --- BRAI LUCA GUI (v1.4 - Spline & Circle Support) ---class BraiLuca:def __init__(self, root):self.root = rootself.root.title("BraiLuca v1.4 - Multi-Entity Support")self.root.geometry("500x450")self.engine = BrailleEngine()self.snapped_points = []self.fingerprint = ""tk.Label(root, text="BraiLuca - Klisé Tisztító", font=("Arial", 12, "bold")).pack(pady=10)self.btn_open = tk.Button(root, text="DXF Megnyitása", command=self.load_file, height=2, width=30)self.btn_open.pack(pady=10)self.info_frame = tk.LabelFrame(root, text="Státusz", padx=10, pady=10)self.info_frame.pack(pady=10, padx=20, fill="x")self.lbl_dots = tk.Label(self.info_frame, text="Pontok: -")self.lbl_dots.pack(anchor="w")self.lbl_fprint = tk.Label(self.info_frame, text="Ujjlenyomat: -", font=("Consolas", 9), wraplength=400)self.lbl_fprint.pack(anchor="w")self.btn_copy = tk.Button(root,text="Ujjlenyomat másolása",command=self.copy_to_clipboard,state="disabled",)self.btn_copy.pack(pady=5)self.btn_save = tk.Button(root,text="Tökéletes DXF mentése (Circle)",command=self.save_cleaned_dxf,state="disabled",bg="#4CAF50",fg="white",height=2,)self.btn_save.pack(pady=10)def get_points_from_entity(self, entity, points_list):"""Kinyeri a pontokat SPLINE középpontként vagy CIRCLE középpontként."""dxftype = entity.dxftype()# KÖRÖK KEZELÉSEif dxftype == "CIRCLE":cp = entity.dxf.center# Kényszerítsük sima tuple formátumra: (x, y)points_list.append((float(cp.x), float(cp.y)))# SPLINE-OK KEZELÉSEelif dxftype == "SPLINE":cp = entity.control_pointsif cp:# Itt már eleve tuple-t generálsz, ez rendben vanavg_x = sum(p[0] for p in cp) / len(cp)avg_y = sum(p[1] for p in cp) / len(cp)points_list.append((avg_x, avg_y))# BLOKKOK KEZELÉSEelif dxftype == "INSERT":# A virtual_entities() jó megoldás a beágyazott elemekrefor sub_entity in entity.virtual_entities():self.get_points_from_entity(sub_entity, points_list)def load_file(self):path = filedialog.askopenfilename(filetypes=[("DXF fájlok", "*.dxf")])if not path:returnfilename = os.path.basename(path)try:doc = ezdxf.readfile(path)msp = doc.modelspace()all_raw_points = []# Minden entitást megvizsgálunk (Spline és Circle is bekerül)for entity in msp:self.get_points_from_entity(entity, all_raw_points)if not all_raw_points:messagebox.showwarning("Figyelem", "Nem találtam sem SPLINE, sem CIRCLE elemet a fájlban!")return# --- DEDUPLIKÁCIÓ ---unique_points = []dup_count = 0for p in all_raw_points:is_duplicate = Falsefor u in unique_points:if math.dist(p, u) < 0.1:is_duplicate = Truebreakif is_duplicate:dup_count += 1else:unique_points.append(p)status_text = f"Fájl: {filename}\nPontok: {len(unique_points)} db"if dup_count > 0:status_text += f" ({dup_count} duplikátum törölve)"self.lbl_dots.config(text=status_text, fg="black")fprint, snapped, is_shifted = self.engine.get_best_fit(unique_points)if fprint:self.fingerprint = fprintself.snapped_points = snappedif is_shifted:display_text = f"Ujjlenyomat: {self.fingerprint}\n⚠️ FÜGGŐLEGES ELTOLÁS! Célgépen kézi bevitel szükséges!"self.lbl_fprint.config(text=display_text, fg="red")else:self.lbl_fprint.config(text=f"Ujjlenyomat: {self.fingerprint}", fg="black")self.btn_copy.config(state="normal")self.btn_save.config(state="normal")else:self.lbl_fprint.config(text="HIBA: Nem illeszkedik a rácsra!", fg="red")self.btn_save.config(state="disabled")except Exception as e:messagebox.showerror("Hiba", f"Beolvasási hiba: {str(e)}")def copy_to_clipboard(self):self.root.clipboard_clear()self.root.clipboard_append(self.fingerprint)messagebox.showinfo("Siker", "Vágólapra másolva!")def save_cleaned_dxf(self):path = filedialog.asksaveasfilename(defaultextension=".dxf", filetypes=[("DXF fájlok", "*.dxf")])if not path:returnnew_doc = ezdxf.new("R2000")try:new_doc.header['$INSUNITS'] = 4new_doc.header['$MEASUREMENT'] = 1except:try:new_doc.header.set('$INSUNIT', 4)except:passnew_doc.layers.new(name="Layer1", dxfattribs={"color": 7})msp = new_doc.modelspace()for p in self.snapped_points:msp.add_circle(p, radius=0.8, dxfattribs={"layer": "Layer1"})new_doc.saveas(path)messagebox.showinfo("Siker", "A klisé tisztítva és körökké alakítva mentve!")if __name__ == "__main__":root = tk.Tk()app = BraiLuca(root)root.mainloop()
Ez a progi a kiválasztott dxf filet értelmezi, Spline és Circle elemeket keres benne (mivel a régebbi Illustrator és a CorelDraw nem tud Circle elemeket exportálni, viszont az újabb Illustrátorok már csak ezt tudnak), ezeket felfeszíti a képzeletbeli 480 pontos gridre, ha kell (mert például a legbaloldalibb karakter bal sorában nincs egyetlen pont se, vagy a legfölső sor legfelső pontsorában sincs) akkor addig offseteli, amíg minden Spline egybeesik a képzeletbeli grid megfelelő pontjaival (minimális tolerancián belül) Ezeknek ujjlenyomatot gyárt (Base64), amire rá tudok keresni az adatbázisunkban, hogy nem e létezik már ugyanilyen klisé. Ha nem találtunk egyezőséget (a másik programban), a program engedi elmenteni a dxf-et új néven, amiben kitakarítja a Splineokat (akkor is ha egymáson több van) és Circle elemekre cseréli a Layer1 rétegre, mert ezt keresi a CNC-gép. Továbbá riportot ad, a pontok számáról, illetve ha függőleges offsetre került sor, ugyanis az acélklisé gyártó szoftver nem tud offsetelni, az olyan kliséket a saját terminálján kell begépelni.
BrAlajos
import osimport reimport tkinter as tkfrom tkinter import filedialog, messagebox, scrolledtextimport ezdxffrom ezdxf import recoverimport mathimport traceback# --- 1. MOTOR ---class AlajosEngine:def __init__(self):self.tolerance = 0.8def get_grid_info(self, points):if not points:return Nonegrid_base = []for r in range(4):for c in range(20):for dc in range(2):for dr in range(3):grid_base.append((float(c * 6.0 + dc * 2.5), float(-(r * 10.0 + dr * 2.5))))min_x, max_x = float(min(p[0] for p in points)), float(max(p[0] for p in points))max_y = float(max(p[1] for p in points))for ox, oy in [(0, 0), (2.5, 0)]:matched = 0for px, py in points:tx, ty = float(px - min_x + ox), float(py - max_y + oy)for gx, gy in grid_base:if math.sqrt((tx - gx) ** 2 + (ty - gy) ** 2) <= self.tolerance:matched += 1breakif matched == len(points):return {"ref_x": float(min_x - ox),"ref_y": float(max_y - oy),"max_x": float(max_x),"min_x_orig": min_x,"max_y_orig": max_y,}return None# --- 2. GUI ---class BrAlajos:def __init__(self, root):self.root = rootself.root.title("BrAlajos v1.7.1 - Ultimate Hybrid")self.root.geometry("600x750")self.engine = AlajosEngine()self.current_dxf = ""self.grid_data = Noneself.snapped_points = []tk.Label(root, text="BrAlajos - Finalizáló", font=("Arial", 12, "bold")).pack(pady=10)btn_frame = tk.Frame(root)btn_frame.pack(fill="x", padx=20)tk.Button(btn_frame, text="DXF Megnyitása", command=self.load_dxf, height=2).pack(side="left", expand=True, fill="x")tk.Button(btn_frame,text="Vágólap Beolvasása",command=self.parse_clipboard,height=2,bg="#e1f5fe",).pack(side="left", expand=True, fill="x")self.fields = []labels = ["1. Filenév maradvány:","2. St: azonosító:","3. Terméknév (- C.):","4. BT dátum:",]for i in range(4):f_frame = tk.Frame(root)f_frame.pack(fill="x", padx=20, pady=5)tk.Label(f_frame, text=labels[i], width=20, anchor="w").pack(side="left")ent = tk.Entry(f_frame, font=("Arial", 10))ent.pack(side="right", expand=True, fill="x")self.fields.append(ent)self.info_area = scrolledtext.ScrolledText(root, height=15, font=("Consolas", 9), bg="#f0f0f0")self.info_area.pack(pady=10, padx=20, fill="both")self.btn_save = tk.Button(root,text="DXF FINALIZÁLÁSA",command=self.save_dxf,state="disabled",bg="#4CAF50",fg="white",height=2,)self.btn_save.pack(pady=10, fill="x", padx=20)def is_pac_mode(self, path):"""Központosított PAC ellenőrzés: névben VAGY végződésben."""name = os.path.basename(path).upper()# Igaz, ha benne van a -PAC- VAGY a kiterjesztés előtt -PAC vanreturn "-PAC-" in name or name.replace(".DXF", "").endswith("-PAC")def log(self, msg, color="black"):tag = f"tag_{color}"self.info_area.tag_configure(tag, foreground=color)self.info_area.insert(tk.END, msg + "\n", tag)self.info_area.see(tk.END)self.root.update()def load_dxf(self):path = filedialog.askopenfilename(filetypes=[("DXF fájlok", "*.dxf")])if not path:returnself.current_dxf = pathself.btn_save.config(state="disabled")try:try:doc, auditor = recover.readfile(path)except:doc = ezdxf.readfile(path)msp = doc.modelspace()circles = [(float(e.dxf.center.x), float(e.dxf.center.y))for e in mspif e.dxftype() == "CIRCLE" and e.dxf.layer.upper() == "LAYER1"]if not circles:self.log("HIBA: Layer1 üres!", "red")returnself.snapped_points = circlesself.grid_data = self.engine.get_grid_info(circles)if self.grid_data:mode_str = "PAC-MÓD" if self.is_pac_mode(path) else "NORMÁL-MÓD"self.log(f"Fájl: {os.path.basename(path)}\nMód: {mode_str}")self.btn_save.config(state="normal")parts = os.path.basename(path).replace(".dxf", "").split("-", 1)if len(parts) > 1:self.fields[0].delete(0, tk.END)self.fields[0].insert(0, parts[1])self.fields[1].delete(0, tk.END)self.fields[1].insert(0, f"St: {parts[0]}")else:self.log("HIBA: Nem rács-szerkezet!", "red")except:self.log(traceback.format_exc(), "red")def parse_clipboard(self):try:text = self.root.clipboard_get()if "- C." in text:part = text.split("- C.")[1].strip().split("\n")[0]self.fields[2].delete(0, tk.END)self.fields[2].insert(0, part[:15])date_match = re.search(r"BT:\s*(\d{2}\.\d{2}\.\d{4})", text)if date_match:self.fields[3].delete(0, tk.END)self.fields[3].insert(0, f"BT: {date_match.group(1)}")self.log("Vágólap OK.")except:self.log("Vágólap hiba.")def find_text_space(self, points, box_w, box_h):min_x = self.grid_data["min_x_orig"]right_limit = self.grid_data["max_x"] + 5.0row_coords = sorted(list(set(round(p[1], 1) for p in points)), reverse=True)base_y = row_coords[0] if row_coords else self.grid_data["max_y_orig"]y_corridors = [base_y - (i * 10.0) - 5.0 for i in range(4)]curr_x = min_x - 3.0while (curr_x + box_w) <= right_limit:for mid_y in y_corridors:top, bottom = mid_y + (box_h / 2), mid_y - (box_h / 2)collision = any(curr_x <= p[0] <= curr_x + box_w and bottom <= p[1] <= topfor p in points)if not collision:return (curr_x, mid_y + (box_h / 2))curr_x += 2.0return Nonedef save_dxf(self):if not self.current_dxf or not self.grid_data:returntry:doc = ezdxf.readfile(self.current_dxf)doc.header["$DWGCODEPAGE"] = "UTF-8"msp = doc.modelspace()is_pac = self.is_pac_mode(self.current_dxf)y_top_pac = float(self.grid_data["max_y_orig"] + 4.0)for i in range(4):val = self.fields[i].get().strip()if not val:continuebox_w, box_h = (len(val) * 2.75) + 3.0, 8.0if is_pac:space = self.find_text_space(self.snapped_points, box_w, box_h)if space:tx, ty = spacefinal_pos = (float(tx + 1.5), float(ty - box_h + 1.5))else:# Mentőöv: Felső vonalra pakolás eltolvaself.log(f"Vigyázat: '{val}' a keret fölé került.", "orange")final_pos = (float(self.grid_data["min_x_orig"] + (i * 35)),float(y_top_pac + 1.0),)else:# NORMÁL MÓD: Fix pozíciókrx, ry = self.grid_data["ref_x"], self.grid_data["ref_y"]positions = [(rx + 364, ry - 10),(rx + 364, ry - 20),(rx + 364, ry - 30),(rx + 364, ry - 40),]final_pos = positions[i]t = msp.add_text(val, dxfattribs={"layer": "Layer1", "height": 3.5})t.set_placement(final_pos)if is_pac:x_s, x_e = float(self.grid_data["min_x_orig"] - 3.0), float(self.grid_data["max_x"] + 5.0)for y in [y_top_pac, y_top_pac - 42.9]:msp.add_line((x_s, y), (x_e, y), dxfattribs={"layer": "Layer1"})doc.saveas(self.current_dxf, encoding="utf-8")self.log("Sikeres mentés.")messagebox.showinfo("Kész", "DXF frissítve!")self.btn_save.config(state="disabled")except Exception as e:self.log(f"Hiba: {str(e)}", "red")if __name__ == "__main__":root = tk.Tk()app = BrAlajos(root)root.mainloop()
Ez a progi a BraiLuca által kicsinosított dxf filet készíti elő véglegesre. A filenévből (ami az azonosító is) illetve vágólapról kapott metaadattal felcímkézi a dxf-et. Négy editálható szövegdobozba gyűjti ki az adatokat, ezek utólag szerkeszthetőek, ha szükség lenne rá, illetve mentésnél a megfelelő pozícióba írja ezeket a dxf-be. A célprogram kétféle módot képes kezelni, ezért a BrAlajos is megkülönbözteti ezeket.
A normál módot a célgép úgy értelmezi, hogy egy hosszú, fix méretű acéllemez-abroncsra kétszer teszi rá a pontfelhőt és a klisé végére marja az azonosítókat. Mellesleg rotációs torzítást is alkalmaz.
A kivételes módban a klisé szélességét két vízszintes vonallal kell a dxf-ben reprezentálni, a klisé hossza pedig a pontfelhő hosszától dinamikusan függ. Ezeket a vonalakat is berajzolja a program, illetve szövegelemnek (hosszát a karakterek számából saccolja meg), helyet keres a klisé mind a négy sorában. Ha nem talál, figyelmeztet, kézi beavatkozás szükségességére.
Az ujjlenyomatkezelő
import tkinter as tkfrom tkinter import messagebox, scrolledtext, filedialogimport sqlite3import csv# --- KONFIGURÁCIÓ ---# Ha hálózati meghajtón használjátok, ide írd az elérést, pl: "Z:/adatok/ujjlenyomatok.db"DB_PATH = "Z:\Temp\!!!INSTALL\Makrok_Hasznos_Kiskesek\BraiLucaBrAlajos\ujjlenyomatok.db"def init_db():conn = sqlite3.connect(DB_PATH)c = conn.cursor()c.execute("""CREATE TABLE IF NOT EXISTS fingerprints(identifier TEXT PRIMARY KEY, fingerprint TEXT)""")conn.commit()conn.close()def import_csv_to_db():file_path = filedialog.askopenfilename(filetypes=[("CSV fájlok", "*.csv")])if not file_path:returnif not messagebox.askyesno("Megerősítés", "Töröljek minden adatot és betöltsem a CSV-t?"):returntry:conn = sqlite3.connect(DB_PATH)c = conn.cursor()c.execute("DELETE FROM fingerprints")with open(file_path, mode="r", encoding="utf-8-sig") as f:reader = csv.reader(f, delimiter=";")count = 0for row in reader:if len(row) >= 2:c.execute("INSERT OR REPLACE INTO fingerprints (identifier, fingerprint) VALUES (?, ?)",(row[0].strip(), row[1].strip()),)count += 1conn.commit()conn.close()messagebox.showinfo("Siker", f"Beöltött sorok: {count}")except Exception as e:messagebox.showerror("Hiba", str(e))def search_fingerprint():fp = entry_fp.get().strip()if len(fp) != 80:messagebox.showwarning("Figyelem", "Az ujjlenyomat 80 karakter kell legyen!")returnconn = sqlite3.connect(DB_PATH)c = conn.cursor()c.execute("SELECT identifier FROM fingerprints WHERE fingerprint = ?", (fp,))results = c.fetchall()conn.close()entry_res.config(state="normal")entry_res.delete(0, tk.END)if results:ids = ", ".join([r[0] for r in results])entry_res.insert(0, ids)# Nem szürkítjük ki a gombot, mert lehet, hogy új azonosítóval is felvinnékelse:entry_res.insert(0, "NINCS MÉG ILYEN UJJLENYOMAT")entry_res.config(state="readonly")def save_new():fp = entry_fp.get().strip()new_id = entry_new_id.get().strip()if not new_id or len(fp) != 80:messagebox.showerror("Hiba", "Hiányzó azonosító vagy rossz ujjlenyomat hossz!")returnconn = sqlite3.connect(DB_PATH)c = conn.cursor()try:# Ellenőrizzük, hogy ez az ujjlenyomat már létezik-e más néven (csak tájékoztatás)c.execute("SELECT identifier FROM fingerprints WHERE fingerprint = ?", (fp,))exists = c.fetchall()if exists:msg = f"Ez az ujjlenyomat már szerepel a következő azonosítókkal: {', '.join([x[0] for x in exists])}\n\nBiztosan felveszed új azonosítóval ({new_id}) is?"if not messagebox.askyesno("Duplikátum figyelmeztetés", msg):returnc.execute("INSERT INTO fingerprints (identifier, fingerprint) VALUES (?, ?)",(new_id, fp),)conn.commit()messagebox.showinfo("Siker", "Adat elmentve!")entry_new_id.delete(0, tk.END)search_fingerprint() # Frissítjük a nézetetexcept sqlite3.IntegrityError:messagebox.showerror("Hiba","Ez az AZONOSÍTÓ már foglalt! Minden ujjlenyomatnak egyedi azonosító kell.",)finally:conn.close()def check_duplicates():conn = sqlite3.connect(DB_PATH)c = conn.cursor()query = """SELECT fingerprint, GROUP_CONCAT(identifier, ' | ')FROM fingerprintsGROUP BY fingerprintHAVING COUNT(identifier) > 1"""c.execute(query)dupes = c.fetchall()conn.close()top = tk.Toplevel(root)top.title("Duplikátumok")txt = scrolledtext.ScrolledText(top, width=100, height=20)txt.pack(padx=10, pady=10)if not dupes:txt.insert(tk.END, "Nincs duplikált ujjlenyomat.")else:for f, ids in dupes:txt.insert(tk.END, f"{f} -> {ids}\n\n")# --- UI ---root = tk.Tk()root.title("Ujjlenyomat Kezelő")root.geometry("700x500")# Kereséstk.Label(root, text="Ujjlenyomat (80 karakter):").pack(pady=5)entry_fp = tk.Entry(root, width=90)entry_fp.pack(pady=5)tk.Button(root, text="ELLENŐRZÉS", command=search_fingerprint, bg="#d1e7ff").pack(pady=5)# Eredménytk.Label(root, text="Jelenlegi azonosítók az adatbázisban:").pack(pady=5)entry_res = tk.Entry(root, width=70, font=("Arial", 10, "bold"), justify="center")entry_res.config(state="readonly")entry_res.pack(pady=5)# Mentéstk.Frame(root, height=2, bd=1, relief="sunken").pack(fill="x", padx=20, pady=10)tk.Label(root, text="Új azonosító rögzítése (ha szükséges):").pack(pady=5)entry_new_id = tk.Entry(root, width=40)entry_new_id.pack(pady=5)tk.Button(root, text="MENTÉS ÚJ AZONOSÍTÓKÉNT", command=save_new, bg="#d4edda").pack(pady=5)# Adminadmin_frame = tk.Frame(root)admin_frame.pack(side="bottom", fill="x", pady=20)tk.Button(admin_frame, text="Duplikátum riport", command=check_duplicates).pack(side="left", padx=20)tk.Button(admin_frame, text="CSV Import", command=import_csv_to_db, bg="#f8d7da").pack(side="right", padx=20)init_db()root.mainloop()
Az ujjlenyomatot is kezelő BraiLuca
Mivel nagyon hosszas és beszédes hallgatást tapasztaltunk a vezetőség oldaláról, ezért elengedtük azt a lehetőséget, hogy az ujjlenyomatkezelést a céges PHP-ba implementáljuk. Ezért a BraiLucába belekerült az Ujjlenyomatkezelő néhány funkciója, az azonosítás és az adatbázisba mentés. Így nincs szükség külön program elindítására. A duplikátumkezelést, CSV importot nem tartottuk meg, mert minek, illetve az adatbázis szerkesztgetését (például elgépelési hibák javítása), egy külső ingyenes adatbáziskezelővel (SQLiteDatabaseBrowserPortable) oldjuk majd meg.A módosítást ezennel a Claude AI végezte, második nekifutásra jó is lett. Sőt cuki volt, mert az ikon-mizériát (ha nincs az exe mellett crashel a program) is megoldotta csendben (try: except:). Ezt alkalomadtán a BrAlajosba is bele kellene kompillálni.
# BraiLuca v11 - Spline & Circle vadász + Ujjlenyomat adatbázis# Egyesített verzió: BraiLuca v8 + Ujjlenyomatkereső funkcióiimport osimport tkinter as tkfrom tkinter import filedialog, messageboximport ezdxfimport mathimport sqlite3# --- KONFIGURÁCIÓ ---DB_PATH = (r"Z:\Temp\!!!INSTALL\Makrok_Hasznos_Kiskesek\BraiLucaBrAlajos\ujjlenyomatok.db")# --- ADATBÁZIS INICIALIZÁLÁS ---def init_db():try:conn = sqlite3.connect(DB_PATH)c = conn.cursor()c.execute("""CREATE TABLE IF NOT EXISTS fingerprints(identifier TEXT PRIMARY KEY, fingerprint TEXT)""")conn.commit()conn.close()except Exception as e:# Ha az adatbázis nem elérhető, csak figyelmeztetünk, nem állunk leprint(f"DB inicializálási hiba: {e}")# --- BRAILLE MOTOR (VÁLTOZATLAN) ---class BrailleEngine:def __init__(self):self.dot_dist = 2.5self.char_dist = 6.0self.row_dist = 10.0self.tolerance = 0.8self.b64_chars = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")def bits_to_base64(self, bit_string):b64_result = ""for i in range(0, 480, 6):six_bits = bit_string[i : i + 6]val = 0for bit_pos, bit in enumerate(six_bits):if bit == "1":val += 1 << bit_posb64_result += self.b64_chars[val]return b64_resultdef get_theoretical_grid(self, offset_x=0, offset_y=0):grid = []for row in range(4):for char in range(20):for dot_col in range(2):for dot_row in range(3):gx = ((char * self.char_dist)+ (dot_col * self.dot_dist)+ offset_x)gy = (-((row * self.row_dist) + (dot_row * self.dot_dist))+ offset_y)grid.append((gx, gy))return griddef get_best_fit(self, points):if not points:return None, None, Falsemin_x = min(p[0] for p in points)max_y = max(p[1] for p in points)offsets = [(0, 0), (2.5, 0), (0, 2.5), (2.5, 2.5), (0, 5.0), (2.5, 5.0)]for ox, oy in offsets:theoretical_grid = self.get_theoretical_grid(min_x - ox, max_y + oy)fingerprint_bits = ["0"] * 480snapped_points = []matched_count = 0for px, py in points:for i, (gx, gy) in enumerate(theoretical_grid):dist = math.sqrt((px - gx) ** 2 + (py - gy) ** 2)if dist <= self.tolerance:fingerprint_bits[i] = "1"snapped_points.append((gx, gy))matched_count += 1breakif matched_count == len(points):is_vertical_shifted = oy != 0return (self.bits_to_base64("".join(fingerprint_bits)),snapped_points,is_vertical_shifted,)return None, None, False# --- EGYESÍTETT GUI ---class BraiLuca:def __init__(self, root):self.root = rootself.root.title("BraiLuca v11 - Klisé Tisztító & Ujjlenyomat Kezelő")self.root.geometry("520x620")self.engine = BrailleEngine()self.snapped_points = []self.fingerprint = ""# ── Fejléc ──────────────────────────────────────────────────tk.Label(root, text="BraiLuca - Klisé Tisztító", font=("Arial", 12, "bold")).pack(pady=8)# ── DXF megnyitás ────────────────────────────────────────────self.btn_open = tk.Button(root, text="DXF Megnyitása", command=self.load_file, height=2, width=30)self.btn_open.pack(pady=8)# ── Státusz keret ────────────────────────────────────────────self.info_frame = tk.LabelFrame(root, text="Státusz", padx=10, pady=6)self.info_frame.pack(pady=6, padx=20, fill="x")self.lbl_dots = tk.Label(self.info_frame, text="Pontok: -")self.lbl_dots.pack(anchor="w")self.lbl_fprint = tk.Label(self.info_frame,text="Ujjlenyomat: -",font=("Consolas", 9),wraplength=440,)self.lbl_fprint.pack(anchor="w")# Ujjlenyomat vágólapra gomb (megmarad)self.btn_copy = tk.Button(root,text="Ujjlenyomat másolása vágólapra",command=self.copy_to_clipboard,state="disabled",)self.btn_copy.pack(pady=4)# ── Adatbázis találat keret ──────────────────────────────────db_frame = tk.LabelFrame(root, text="Adatbázis találat", padx=10, pady=6)db_frame.pack(pady=6, padx=20, fill="x")self.entry_res = tk.Entry(db_frame,width=60,font=("Arial", 10, "bold"),justify="center",state="readonly",)self.entry_res.pack(pady=4)# ── Új azonosító rögzítése ───────────────────────────────────save_frame = tk.LabelFrame(root, text="Új azonosító rögzítése", padx=10, pady=6)save_frame.pack(pady=6, padx=20, fill="x")id_row = tk.Frame(save_frame)id_row.pack(fill="x")tk.Label(id_row, text="Azonosító:").pack(side="left")self.entry_new_id = tk.Entry(id_row, width=35)self.entry_new_id.pack(side="left", padx=6)self.btn_save_db = tk.Button(save_frame,text="MENTÉS ÚJ AZONOSÍTÓKÉNT",command=self.save_new_to_db,bg="#d4edda",state="disabled",)self.btn_save_db.pack(pady=4)# ── DXF mentés ───────────────────────────────────────────────self.btn_save_dxf = tk.Button(root,text="Tökéletes DXF mentése (Circle)",command=self.save_cleaned_dxf,state="disabled",bg="#4CAF50",fg="white",height=2,)self.btn_save_dxf.pack(pady=10)# ── SEGÉDFÜGGVÉNYEK (VÁLTOZATLAN) ───────────────────────────────def get_points_from_entity(self, entity, points_list):dxftype = entity.dxftype()if dxftype == "CIRCLE":cp = entity.dxf.centerpoints_list.append((float(cp.x), float(cp.y)))elif dxftype == "SPLINE":cp = entity.control_pointsif cp:avg_x = sum(p[0] for p in cp) / len(cp)avg_y = sum(p[1] for p in cp) / len(cp)points_list.append((avg_x, avg_y))elif dxftype == "INSERT":for sub_entity in entity.virtual_entities():self.get_points_from_entity(sub_entity, points_list)# ── FÁJL BETÖLTÉS (logika változatlan, + auto DB lekérdezés) ────def load_file(self):path = filedialog.askopenfilename(filetypes=[("DXF fájlok", "*.dxf")])if not path:returnfilename = os.path.basename(path)try:doc = ezdxf.readfile(path)msp = doc.modelspace()all_raw_points = []for entity in msp:self.get_points_from_entity(entity, all_raw_points)if not all_raw_points:messagebox.showwarning("Figyelem", "Nem találtam sem SPLINE, sem CIRCLE elemet a fájlban!")return# Deduplikáció (változatlan)unique_points = []dup_count = 0for p in all_raw_points:is_duplicate = Falsefor u in unique_points:if math.dist(p, u) < 0.1:is_duplicate = Truebreakif is_duplicate:dup_count += 1else:unique_points.append(p)status_text = f"Fájl: {filename}\nPontok: {len(unique_points)} db"if dup_count > 0:status_text += f" ({dup_count} duplikátum törölve)"self.lbl_dots.config(text=status_text, fg="black")fprint, snapped, is_shifted = self.engine.get_best_fit(unique_points)if fprint:self.fingerprint = fprintself.snapped_points = snappedif is_shifted:display_text = f"Ujjlenyomat: {self.fingerprint}\n⚠️ FÜGGŐLEGES ELTOLÁS! Célgépen kézi bevitel szükséges!"self.lbl_fprint.config(text=display_text, fg="red")else:self.lbl_fprint.config(text=f"Ujjlenyomat: {self.fingerprint}", fg="black")self.btn_copy.config(state="normal")self.btn_save_dxf.config(state="normal")self.btn_save_db.config(state="normal")# ── ÚJ: automatikus DB lekérdezés ──self._search_in_db(self.fingerprint)else:self.lbl_fprint.config(text="HIBA: Nem illeszkedik a rácsra!", fg="red")self.btn_save_dxf.config(state="disabled")self.btn_save_db.config(state="disabled")self._clear_db_result()except Exception as e:messagebox.showerror("Hiba", f"Beolvasási hiba: {str(e)}")# ── VÁGÓLAP (VÁLTOZATLAN) ────────────────────────────────────────def copy_to_clipboard(self):self.root.clipboard_clear()self.root.clipboard_append(self.fingerprint)messagebox.showinfo("Siker", "Vágólapra másolva!")# ── DB LEKÉRDEZÉS (a második programból áthozva, metódusba szervezve) ──def _clear_db_result(self):self.entry_res.config(state="normal")self.entry_res.delete(0, tk.END)self.entry_res.config(state="readonly")def _search_in_db(self, fingerprint):"""Lekérdezi az ujjlenyomatot az adatbázisból és megjeleníti az eredményt."""self._clear_db_result()try:conn = sqlite3.connect(DB_PATH)c = conn.cursor()c.execute("SELECT identifier FROM fingerprints WHERE fingerprint = ?",(fingerprint,),)results = c.fetchall()conn.close()self.entry_res.config(state="normal")if results:ids = ", ".join([r[0] for r in results])self.entry_res.insert(0, ids)self.entry_res.config(fg="navy")else:self.entry_res.insert(0, "NINCS MÉG ILYEN UJJLENYOMAT")self.entry_res.config(fg="gray")self.entry_res.config(state="readonly")except Exception as e:self.entry_res.config(state="normal")self.entry_res.insert(0, f"DB hiba: {e}")self.entry_res.config(state="readonly", fg="red")# ── DB MENTÉS (a második programból áthozva) ─────────────────────def save_new_to_db(self):"""Elmenti az ujjlenyomatot az adatbázisba az új azonosítóval.Visszatér True-val ha sikeres, False-szal ha nem."""fp = self.fingerprintnew_id = self.entry_new_id.get().strip()if not new_id:messagebox.showerror("Hiba", "Kérlek add meg az azonosítót!")return Falseif len(fp) != 80:messagebox.showerror("Hiba", "Érvénytelen ujjlenyomat (nem 80 karakter)!")return Falsetry:conn = sqlite3.connect(DB_PATH)c = conn.cursor()# Duplikátum figyelmeztetés (ha ez az ujjlenyomat már létezik)c.execute("SELECT identifier FROM fingerprints WHERE fingerprint = ?", (fp,))exists = c.fetchall()if exists:msg = (f"Ez az ujjlenyomat már szerepel a következő azonosítókkal: "f"{', '.join([x[0] for x in exists])}\n\n"f"Biztosan felveszed új azonosítóval ({new_id}) is?")if not messagebox.askyesno("Duplikátum figyelmeztetés", msg):conn.close()return Falsec.execute("INSERT INTO fingerprints (identifier, fingerprint) VALUES (?, ?)",(new_id, fp),)conn.commit()conn.close()messagebox.showinfo("Siker", f"'{new_id}' elmentve az adatbázisba!")self._search_in_db(fp) # Frissítjük a találat mezőtreturn Trueexcept sqlite3.IntegrityError:messagebox.showerror("Hiba","Ez az AZONOSÍTÓ már foglalt! Minden ujjlenyomatnak egyedi azonosító kell.",)return Falseexcept Exception as e:messagebox.showerror("Hiba", f"DB mentési hiba: {str(e)}")return False# ── DXF MENTÉS (Circle logika változatlan, mentési rész bővítve) ─def save_cleaned_dxf(self):path = filedialog.asksaveasfilename(defaultextension=".dxf", filetypes=[("DXF fájlok", "*.dxf")])if not path:returnnew_doc = ezdxf.new("R2000")try:new_doc.header["$INSUNITS"] = 4new_doc.header["$MEASUREMENT"] = 1except:try:new_doc.header.set("$INSUNIT", 4)except:passnew_doc.layers.new(name="Layer1", dxfattribs={"color": 7})msp = new_doc.modelspace()for p in self.snapped_points:msp.add_circle(p, radius=0.8, dxfattribs={"layer": "Layer1"})new_doc.saveas(path)messagebox.showinfo("Siker", "A klisé tisztítva és körökké alakítva mentve!")# ── BELÉPÉSI PONT ────────────────────────────────────────────────────if __name__ == "__main__":init_db()root = tk.Tk()try:root.iconbitmap("BrLu.ico")except:pass # Ha nincs ico fájl, csendben továbblépünkapp = BraiLuca(root)root.mainloop()