fixed shit. now threading works fine and it looks better
This commit is contained in:
@@ -727,7 +727,7 @@ class PfandCalculator:
|
|||||||
about_uscan,
|
about_uscan,
|
||||||
text=(
|
text=(
|
||||||
"µScan - Der bessere Barcode Scanner\n"
|
"µScan - Der bessere Barcode Scanner\n"
|
||||||
"Version 2.2.2\n"
|
"Version 2.3.5\n"
|
||||||
"µScan erfordert einen UI Reload (Strg+R) in der Root Anwendung\n"
|
"µScan erfordert einen UI Reload (Strg+R) in der Root Anwendung\n"
|
||||||
"µScan ist für mehrere Barcodes gemacht, die in einer kurzen Zeit gescannt werden sollten\n"
|
"µScan ist für mehrere Barcodes gemacht, die in einer kurzen Zeit gescannt werden sollten\n"
|
||||||
"Beachte das µScan eine Kamera benötigt die mindestens 30FPS aufnehmen kann (Process-FPS können eingestellt werden!)"
|
"Beachte das µScan eine Kamera benötigt die mindestens 30FPS aufnehmen kann (Process-FPS können eingestellt werden!)"
|
||||||
|
|||||||
@@ -44,22 +44,28 @@ class PfandScanner:
|
|||||||
# Collapsible scan list state
|
# Collapsible scan list state
|
||||||
self.scan_list_collapsed = tk.BooleanVar(value=False)
|
self.scan_list_collapsed = tk.BooleanVar(value=False)
|
||||||
|
|
||||||
|
# Threading control
|
||||||
|
self.running = True
|
||||||
|
self.camera_thread = None
|
||||||
|
self.process_thread = None
|
||||||
|
|
||||||
self.init_gui()
|
self.init_gui()
|
||||||
self.init_camera()
|
self.init_camera()
|
||||||
|
|
||||||
self.queue = queue.Queue()
|
self.queue = queue.Queue()
|
||||||
|
self.frame_queue = queue.Queue(maxsize=2) # Limit queue size to prevent memory issues
|
||||||
self.pfand_values = {
|
self.pfand_values = {
|
||||||
"EINWEG": 0.25,
|
"EINWEG": 0.25,
|
||||||
"MEHRWEG": 0.15,
|
"MEHRWEG": 0.15,
|
||||||
"DOSE": 0.25,
|
"DOSE": 0.25,
|
||||||
}
|
}
|
||||||
|
|
||||||
self.update_preview()
|
# Start threads
|
||||||
|
self.start_threads()
|
||||||
|
|
||||||
self.window.protocol("WM_DELETE_WINDOW", self.on_closing)
|
self.window.protocol("WM_DELETE_WINDOW", self.on_closing)
|
||||||
self.process_queue()
|
|
||||||
|
|
||||||
def setup_styles(self):
|
def setup_styles(self):
|
||||||
"""Setup custom styles for a modern look"""
|
|
||||||
self.style = ttk.Style()
|
self.style = ttk.Style()
|
||||||
self.style.theme_use('clam')
|
self.style.theme_use('clam')
|
||||||
|
|
||||||
@@ -84,7 +90,6 @@ class PfandScanner:
|
|||||||
self.style.configure('Camera.TFrame', relief='solid', borderwidth=2)
|
self.style.configure('Camera.TFrame', relief='solid', borderwidth=2)
|
||||||
|
|
||||||
def load_data(self):
|
def load_data(self):
|
||||||
"""Load products from products.json and quantities from quantities.json"""
|
|
||||||
# Load products
|
# Load products
|
||||||
if os.path.exists(self.products_file):
|
if os.path.exists(self.products_file):
|
||||||
try:
|
try:
|
||||||
@@ -132,10 +137,10 @@ class PfandScanner:
|
|||||||
header_frame = ttk.Frame(self.main_frame)
|
header_frame = ttk.Frame(self.main_frame)
|
||||||
header_frame.grid(row=0, column=0, columnspan=3, sticky="ew", pady=(0, 10))
|
header_frame.grid(row=0, column=0, columnspan=3, sticky="ew", pady=(0, 10))
|
||||||
|
|
||||||
title_label = ttk.Label(header_frame, text="µScan V2.3.0", style='Title.TLabel')
|
title_label = ttk.Label(header_frame, text="µScan", style='Title.TLabel')
|
||||||
title_label.pack(side="left")
|
title_label.pack(side="left")
|
||||||
|
|
||||||
status_label = ttk.Label(header_frame, text="Erweiterte Barcode-Scanner", style='Info.TLabel')
|
status_label = ttk.Label(header_frame, text="v2.3.5", style='Info.TLabel')
|
||||||
status_label.pack(side="right")
|
status_label.pack(side="right")
|
||||||
|
|
||||||
# Control Panel (Left)
|
# Control Panel (Left)
|
||||||
@@ -417,11 +422,25 @@ class PfandScanner:
|
|||||||
|
|
||||||
return frame
|
return frame
|
||||||
|
|
||||||
def update_preview(self):
|
def start_threads(self):
|
||||||
|
"""Start camera and processing threads"""
|
||||||
|
self.camera_thread = threading.Thread(target=self.camera_worker, daemon=True)
|
||||||
|
self.camera_thread.start()
|
||||||
|
|
||||||
|
self.process_thread = threading.Thread(target=self.process_worker, daemon=True)
|
||||||
|
self.process_thread.start()
|
||||||
|
|
||||||
|
# Start UI update loop
|
||||||
|
self.update_preview()
|
||||||
|
self.process_queue()
|
||||||
|
|
||||||
|
def camera_worker(self):
|
||||||
|
"""Worker thread for camera capture and processing"""
|
||||||
|
while self.running:
|
||||||
try:
|
try:
|
||||||
if not hasattr(self, 'cap') or not self.cap.isOpened():
|
if not hasattr(self, 'cap') or not self.cap.isOpened():
|
||||||
self.window.after(100, self.update_preview)
|
time.sleep(0.1)
|
||||||
return
|
continue
|
||||||
|
|
||||||
ret, frame = self.cap.read()
|
ret, frame = self.cap.read()
|
||||||
if ret:
|
if ret:
|
||||||
@@ -442,8 +461,74 @@ class PfandScanner:
|
|||||||
# Draw barcode outlines
|
# Draw barcode outlines
|
||||||
frame_with_outlines = self.draw_barcode_outline(frame.copy(), self.current_barcodes)
|
frame_with_outlines = self.draw_barcode_outline(frame.copy(), self.current_barcodes)
|
||||||
|
|
||||||
|
# Put frame in queue for UI thread
|
||||||
|
try:
|
||||||
|
if self.frame_queue.full():
|
||||||
|
self.frame_queue.get_nowait() # Discard old frame
|
||||||
|
self.frame_queue.put(frame_with_outlines, timeout=0.1)
|
||||||
|
except queue.Full:
|
||||||
|
pass # Skip this frame if queue is full
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Fehler in der Kamera-Verarbeitung: {e}")
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
def process_worker(self):
|
||||||
|
"""Worker thread for barcode processing"""
|
||||||
|
while self.running:
|
||||||
|
try:
|
||||||
|
barcode_data = self.queue.get(timeout=0.1)
|
||||||
|
now = datetime.now()
|
||||||
|
|
||||||
|
# Rate limiting
|
||||||
|
timestamps = self.barcode_times.get(barcode_data, [])
|
||||||
|
timestamps = [t for t in timestamps if now - t <= timedelta(seconds=5)]
|
||||||
|
if len(timestamps) >= 3:
|
||||||
|
continue
|
||||||
|
timestamps.append(now)
|
||||||
|
self.barcode_times[barcode_data] = timestamps
|
||||||
|
|
||||||
|
current_time = now.strftime("%H:%M:%S")
|
||||||
|
pfand_type = "EINWEG" if len(barcode_data) == 13 else "MEHRWEG" if len(barcode_data) == 8 else "DOSE"
|
||||||
|
deposit = self.pfand_values.get(pfand_type, 0.00)
|
||||||
|
|
||||||
|
# Update UI in main thread
|
||||||
|
self.window.after(0, self.add_to_treeview, current_time, barcode_data, pfand_type, deposit)
|
||||||
|
|
||||||
|
# Show product selection dialog
|
||||||
|
if barcode_data not in self.prompted_barcodes:
|
||||||
|
self.prompted_barcodes.add(barcode_data)
|
||||||
|
self.window.after(0, self.show_product_selection, barcode_data)
|
||||||
|
|
||||||
|
except queue.Empty:
|
||||||
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Fehler in der Warteschlangenverarbeitung: {e}")
|
||||||
|
|
||||||
|
def add_to_treeview(self, current_time, barcode_data, pfand_type, deposit):
|
||||||
|
"""Add item to treeview (called from main thread)"""
|
||||||
|
# Determine row color
|
||||||
|
row_count = len(self.tree.get_children())
|
||||||
|
tag = 'evenrow' if row_count % 2 == 0 else 'oddrow'
|
||||||
|
|
||||||
|
self.tree.insert("", 0, values=(current_time, barcode_data, pfand_type, f"€{deposit:.2f}"), tags=(tag,))
|
||||||
|
|
||||||
|
# Update statistics
|
||||||
|
self.total_scans += 1
|
||||||
|
self.update_statistics()
|
||||||
|
|
||||||
|
# Sound notification
|
||||||
|
if self.beep_var.get():
|
||||||
|
self.window.bell()
|
||||||
|
|
||||||
|
def update_preview(self):
|
||||||
|
"""Update camera preview (called from main thread)"""
|
||||||
|
try:
|
||||||
|
# Get frame from queue
|
||||||
|
frame = self.frame_queue.get_nowait()
|
||||||
|
|
||||||
# Convert and display
|
# Convert and display
|
||||||
cv2image = cv2.cvtColor(frame_with_outlines, cv2.COLOR_BGR2RGBA)
|
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
|
||||||
img = Image.fromarray(cv2image)
|
img = Image.fromarray(cv2image)
|
||||||
|
|
||||||
# Resize to fit label while maintaining aspect ratio
|
# Resize to fit label while maintaining aspect ratio
|
||||||
@@ -456,13 +541,14 @@ class PfandScanner:
|
|||||||
self.camera_label.imgtk = imgtk
|
self.camera_label.imgtk = imgtk
|
||||||
self.camera_label.configure(image=imgtk)
|
self.camera_label.configure(image=imgtk)
|
||||||
|
|
||||||
# Update session time
|
except queue.Empty:
|
||||||
self.update_statistics()
|
pass # No new frame available
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Fehler in der Video-Vorschau: {e}")
|
print(f"Fehler in der Video-Vorschau: {e}")
|
||||||
|
|
||||||
self.window.after(10, self.update_preview)
|
# Schedule next update
|
||||||
|
if self.running:
|
||||||
|
self.window.after(30, self.update_preview)
|
||||||
|
|
||||||
def update_statistics(self):
|
def update_statistics(self):
|
||||||
session_time = datetime.now() - self.session_start
|
session_time = datetime.now() - self.session_start
|
||||||
@@ -481,76 +567,96 @@ class PfandScanner:
|
|||||||
self.product_win = tk.Toplevel(self.window)
|
self.product_win = tk.Toplevel(self.window)
|
||||||
self.product_win.title("Produkt auswählen")
|
self.product_win.title("Produkt auswählen")
|
||||||
self.product_win.geometry("500x400")
|
self.product_win.geometry("500x400")
|
||||||
|
self.product_win.minsize(450, 350)
|
||||||
self.product_win.resizable(True, True)
|
self.product_win.resizable(True, True)
|
||||||
|
|
||||||
# Center the window
|
# Center the window
|
||||||
self.product_win.transient(self.window)
|
self.product_win.transient(self.window)
|
||||||
self.product_win.grab_set()
|
self.product_win.grab_set()
|
||||||
|
|
||||||
# Configure grid weights
|
# Configure grid weights for proper expansion
|
||||||
self.product_win.columnconfigure(0, weight=1)
|
self.product_win.columnconfigure(0, weight=1)
|
||||||
self.product_win.rowconfigure(1, weight=1)
|
self.product_win.rowconfigure(1, weight=1)
|
||||||
|
|
||||||
# Header frame
|
# Header frame
|
||||||
header_frame = ttk.Frame(self.product_win, padding="20")
|
header_frame = ttk.Frame(self.product_win, padding="10")
|
||||||
header_frame.grid(row=0, column=0, sticky="ew")
|
header_frame.grid(row=0, column=0, sticky="ew", padx=10, pady=(10, 5))
|
||||||
|
header_frame.columnconfigure(0, weight=1)
|
||||||
|
|
||||||
ttk.Label(header_frame, text="Produkt für Barcode auswählen:", style='Heading.TLabel').pack(pady=(0, 5))
|
ttk.Label(header_frame, text="Produkt für Barcode auswählen:",
|
||||||
ttk.Label(header_frame, text=f"'{barcode_data}'", style='Info.TLabel', font=('Courier', 10)).pack(pady=(0, 10))
|
style='Heading.TLabel').grid(row=0, column=0, sticky="w")
|
||||||
|
ttk.Label(header_frame, text=f"'{barcode_data}'",
|
||||||
|
style='Info.TLabel', font=('Courier', 10)).grid(row=1, column=0, sticky="w", pady=(0, 5))
|
||||||
|
|
||||||
# Main content frame
|
# Main content frame with scrollable area
|
||||||
content_frame = ttk.Frame(self.product_win, padding="20")
|
content_frame = ttk.Frame(self.product_win)
|
||||||
content_frame.grid(row=1, column=0, sticky="nsew")
|
content_frame.grid(row=1, column=0, sticky="nsew", padx=10, pady=5)
|
||||||
content_frame.columnconfigure(0, weight=1)
|
content_frame.columnconfigure(0, weight=1)
|
||||||
content_frame.rowconfigure(0, weight=1)
|
content_frame.rowconfigure(0, weight=1)
|
||||||
|
|
||||||
selected_product = tk.StringVar()
|
selected_product = tk.StringVar()
|
||||||
|
|
||||||
if self.products:
|
# Create a canvas with scrollbar for the product list
|
||||||
# Create scrollable frame for products
|
# Create a canvas with scrollbar for the product list
|
||||||
canvas = tk.Canvas(content_frame, bg='white')
|
canvas = tk.Canvas(content_frame, bg='white', highlightthickness=0)
|
||||||
scrollbar = ttk.Scrollbar(content_frame, orient="vertical", command=canvas.yview)
|
scrollbar = ttk.Scrollbar(content_frame, orient="vertical", command=canvas.yview)
|
||||||
scrollable_frame = ttk.Frame(canvas)
|
canvas.configure(yscrollcommand=scrollbar.set)
|
||||||
|
|
||||||
|
# Frame inside canvas
|
||||||
|
scrollable_frame = ttk.Frame(canvas)
|
||||||
|
window_id = canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
|
||||||
|
|
||||||
|
# Update scrollregion when frame contents change
|
||||||
scrollable_frame.bind(
|
scrollable_frame.bind(
|
||||||
"<Configure>",
|
"<Configure>",
|
||||||
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
|
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
|
||||||
)
|
)
|
||||||
|
|
||||||
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
|
# Resize inner frame width when canvas resizes
|
||||||
canvas.configure(yscrollcommand=scrollbar.set)
|
def on_canvas_configure(event):
|
||||||
|
canvas.itemconfig(window_id, width=event.width)
|
||||||
|
|
||||||
|
canvas.bind("<Configure>", on_canvas_configure)
|
||||||
|
|
||||||
|
# Place canvas + scrollbar
|
||||||
|
canvas.grid(row=0, column=0, sticky="nsew")
|
||||||
|
scrollbar.grid(row=0, column=1, sticky="ns")
|
||||||
|
|
||||||
|
# Add products to scrollable frame
|
||||||
|
if self.products:
|
||||||
for i, product in enumerate(self.products):
|
for i, product in enumerate(self.products):
|
||||||
current_quantity = self.quantities.get(product, 0)
|
current_quantity = self.quantities.get(product, 0)
|
||||||
price = self.prices.get(product, 0.00)
|
price = self.prices.get(product, 0.00)
|
||||||
|
|
||||||
product_frame = ttk.Frame(scrollable_frame, padding="5")
|
product_frame = ttk.Frame(scrollable_frame, padding="5")
|
||||||
product_frame.pack(fill="x", pady=2)
|
product_frame.pack(fill="x", pady=2)
|
||||||
|
product_frame.columnconfigure(1, weight=1)
|
||||||
|
|
||||||
ttk.Radiobutton(
|
ttk.Radiobutton(
|
||||||
product_frame,
|
product_frame,
|
||||||
text=f"{product}",
|
text=product,
|
||||||
variable=selected_product,
|
variable=selected_product,
|
||||||
value=product
|
value=product
|
||||||
).pack(side="left")
|
).grid(row=0, column=0, sticky="w")
|
||||||
|
|
||||||
info_text = f"(Aktuell: {current_quantity}, Preis: €{price:.2f})"
|
info_text = f"Aktuell: {current_quantity}, Preis: €{price:.2f}"
|
||||||
ttk.Label(product_frame, text=info_text, style='Info.TLabel').pack(side="right")
|
ttk.Label(product_frame, text=info_text, style='Info.TLabel').grid(row=0, column=1, sticky="e", padx=(10, 0))
|
||||||
|
|
||||||
canvas.grid(row=0, column=0, sticky="nsew")
|
|
||||||
scrollbar.grid(row=0, column=1, sticky="ns")
|
|
||||||
else:
|
else:
|
||||||
ttk.Label(content_frame, text="Noch keine Produkte definiert.", style='Info.TLabel').pack(pady=20)
|
no_products_label = ttk.Label(scrollable_frame, text="Noch keine Produkte definiert.",
|
||||||
|
style='Info.TLabel', justify="center")
|
||||||
|
no_products_label.pack(pady=20)
|
||||||
|
|
||||||
# Button frame
|
# Button frame at the bottom
|
||||||
button_frame = ttk.Frame(self.product_win, padding="20")
|
button_frame = ttk.Frame(self.product_win, padding="10")
|
||||||
button_frame.grid(row=2, column=0, sticky="ew")
|
button_frame.grid(row=2, column=0, sticky="ew", padx=10, pady=(5, 10))
|
||||||
|
|
||||||
# Configure button frame columns
|
# Configure button frame columns for proper spacing
|
||||||
button_frame.columnconfigure(0, weight=1)
|
button_frame.columnconfigure(0, weight=1)
|
||||||
button_frame.columnconfigure(1, weight=0)
|
button_frame.columnconfigure(1, weight=0)
|
||||||
button_frame.columnconfigure(2, weight=0)
|
button_frame.columnconfigure(2, weight=0)
|
||||||
|
button_frame.columnconfigure(3, weight=0)
|
||||||
|
|
||||||
|
# Helper functions for buttons
|
||||||
def confirm():
|
def confirm():
|
||||||
product = selected_product.get()
|
product = selected_product.get()
|
||||||
if product:
|
if product:
|
||||||
@@ -573,7 +679,6 @@ class PfandScanner:
|
|||||||
def cancel():
|
def cancel():
|
||||||
self.product_win.destroy()
|
self.product_win.destroy()
|
||||||
|
|
||||||
# Add new product button
|
|
||||||
def add_new_product():
|
def add_new_product():
|
||||||
new_product = simpledialog.askstring("Neues Produkt", "Name des neuen Produkts:")
|
new_product = simpledialog.askstring("Neues Produkt", "Name des neuen Produkts:")
|
||||||
if new_product and new_product.strip():
|
if new_product and new_product.strip():
|
||||||
@@ -603,66 +708,39 @@ class PfandScanner:
|
|||||||
else:
|
else:
|
||||||
messagebox.showwarning("Produkt existiert", "Dieses Produkt ist bereits vorhanden.")
|
messagebox.showwarning("Produkt existiert", "Dieses Produkt ist bereits vorhanden.")
|
||||||
|
|
||||||
# Buttons with better layout
|
# Buttons with proper layout
|
||||||
ttk.Button(button_frame, text="Neues Produkt", command=add_new_product).grid(row=0, column=0, sticky="w", padx=5)
|
ttk.Button(button_frame, text="Neues Produkt", command=add_new_product).grid(row=0, column=0, sticky="w", padx=5)
|
||||||
ttk.Button(button_frame, text="Abbrechen", command=cancel).grid(row=0, column=1, padx=5)
|
ttk.Button(button_frame, text="Abbrechen", command=cancel).grid(row=0, column=2, padx=5)
|
||||||
ttk.Button(button_frame, text="Bestätigen", command=confirm).grid(row=0, column=2, padx=5)
|
ttk.Button(button_frame, text="Bestätigen", command=confirm).grid(row=0, column=3, padx=5)
|
||||||
|
|
||||||
# Bind Enter key to confirm
|
# Bind Enter key to confirm
|
||||||
self.product_win.bind('<Return>', lambda e: confirm())
|
self.product_win.bind('<Return>', lambda e: confirm())
|
||||||
self.product_win.bind('<Escape>', lambda e: cancel())
|
self.product_win.bind('<Escape>', lambda e: cancel())
|
||||||
|
|
||||||
|
# Set focus to the window
|
||||||
|
self.product_win.focus_set()
|
||||||
|
|
||||||
def process_queue(self):
|
def process_queue(self):
|
||||||
try:
|
try:
|
||||||
barcode_data = self.queue.get(timeout=0.1)
|
# This method is now handled by the process_worker thread
|
||||||
now = datetime.now()
|
|
||||||
|
|
||||||
# Rate limiting
|
|
||||||
timestamps = self.barcode_times.get(barcode_data, [])
|
|
||||||
timestamps = [t for t in timestamps if now - t <= timedelta(seconds=5)]
|
|
||||||
if len(timestamps) >= 3:
|
|
||||||
return
|
|
||||||
timestamps.append(now)
|
|
||||||
self.barcode_times[barcode_data] = timestamps
|
|
||||||
|
|
||||||
current_time = now.strftime("%H:%M:%S")
|
|
||||||
pfand_type = "EINWEG" if len(barcode_data) == 13 else "MEHRWEG" if len(barcode_data) == 8 else "DOSE"
|
|
||||||
deposit = self.pfand_values.get(pfand_type, 0.00)
|
|
||||||
|
|
||||||
# Determine row color
|
|
||||||
row_count = len(self.tree.get_children())
|
|
||||||
tag = 'evenrow' if row_count % 2 == 0 else 'oddrow'
|
|
||||||
|
|
||||||
self.tree.insert("", 0, values=(current_time, barcode_data, pfand_type, f"€{deposit:.2f}"), tags=(tag,))
|
|
||||||
|
|
||||||
# Update statistics
|
|
||||||
self.total_scans += 1
|
|
||||||
# Note: Total value is updated in product selection dialog
|
|
||||||
|
|
||||||
# Sound notification
|
|
||||||
if self.beep_var.get():
|
|
||||||
self.window.bell()
|
|
||||||
|
|
||||||
# Show product selection dialog
|
|
||||||
if barcode_data not in self.prompted_barcodes:
|
|
||||||
self.prompted_barcodes.add(barcode_data)
|
|
||||||
self.window.after(0, self.show_product_selection, barcode_data)
|
|
||||||
|
|
||||||
except queue.Empty:
|
|
||||||
pass
|
pass
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Fehler in der Warteschlangenverarbeitung: {e}")
|
print(f"Fehler in der Warteschlangenverarbeitung: {e}")
|
||||||
finally:
|
|
||||||
|
# Schedule next check
|
||||||
|
if self.running:
|
||||||
self.window.after(100, self.process_queue)
|
self.window.after(100, self.process_queue)
|
||||||
|
|
||||||
def on_closing(self):
|
def on_closing(self):
|
||||||
"""Clean shutdown of the application"""
|
|
||||||
self.running = False
|
self.running = False
|
||||||
|
|
||||||
# Wait for camera thread to finish
|
# Wait for camera thread to finish
|
||||||
if self.camera_thread and self.camera_thread.is_alive():
|
if self.camera_thread and self.camera_thread.is_alive():
|
||||||
self.camera_thread.join(timeout=1.0)
|
self.camera_thread.join(timeout=1.0)
|
||||||
|
|
||||||
|
if self.process_thread and self.process_thread.is_alive():
|
||||||
|
self.process_thread.join(timeout=1.0)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if hasattr(self, 'cap') and self.cap and self.cap.isOpened():
|
if hasattr(self, 'cap') and self.cap and self.cap.isOpened():
|
||||||
self.cap.release()
|
self.cap.release()
|
||||||
@@ -674,9 +752,9 @@ class PfandScanner:
|
|||||||
if __name__ != "__main__":
|
if __name__ != "__main__":
|
||||||
def launch_pfand_scanner():
|
def launch_pfand_scanner():
|
||||||
scanner_window = tk.Toplevel()
|
scanner_window = tk.Toplevel()
|
||||||
PfandScanner(scanner_window, "µScan V2.3.0 - Verbesserte Deutsche Version")
|
PfandScanner(scanner_window, "µScan V2.3.5")
|
||||||
else:
|
else:
|
||||||
# For standalone testing
|
# For standalone testing
|
||||||
root = tk.Tk()
|
root = tk.Tk()
|
||||||
app = PfandScanner(root, "µScan V2.3.0 - Verbesserte Deutsche Version")
|
app = PfandScanner(root, "µScan V2.3.5")
|
||||||
root.mainloop()
|
root.mainloop()
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"Flaschen": 7,
|
|
||||||
"Bierflasche": 2,
|
|
||||||
"Kasten": 2,
|
|
||||||
"Dose": 3,
|
|
||||||
"Plastikflasche": 2,
|
|
||||||
"Monster": 2,
|
|
||||||
"Joghurt Glas": 2,
|
|
||||||
"nigger": 1
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user