Fix Windows browse, status label mapping, and repo cleanup
This commit is contained in:
@@ -24,6 +24,7 @@ import time
|
||||
import datetime
|
||||
import glob
|
||||
import configparser
|
||||
import subprocess
|
||||
from print_label import print_label_standalone, get_available_printers
|
||||
from kivy.clock import Clock
|
||||
from watchdog.observers import Observer
|
||||
@@ -645,6 +646,14 @@ printer = PDF
|
||||
|
||||
def browse_file(self, instance):
|
||||
"""Open file browser to select file to monitor"""
|
||||
# On Windows, use native dialog to avoid missing-library issues with Kivy FileChooser
|
||||
if platform.system() == 'Windows':
|
||||
selected_file = self._browse_file_windows_native()
|
||||
if selected_file:
|
||||
self.file_input.text = selected_file
|
||||
self.save_config()
|
||||
return
|
||||
|
||||
content = BoxLayout(orientation='vertical', spacing=10, padding=10)
|
||||
|
||||
# File chooser
|
||||
@@ -680,6 +689,79 @@ printer = PDF
|
||||
|
||||
content.add_widget(buttons)
|
||||
popup.open()
|
||||
|
||||
def _browse_file_windows_native(self):
|
||||
"""Open a native Windows file dialog via PowerShell/.NET and return selected file path."""
|
||||
ps_script = (
|
||||
"Add-Type -AssemblyName System.Windows.Forms; "
|
||||
"$dialog = New-Object System.Windows.Forms.OpenFileDialog; "
|
||||
"$dialog.Title = 'Select File to Monitor'; "
|
||||
"$dialog.Filter = 'Text files (*.txt)|*.txt|All files (*.*)|*.*'; "
|
||||
"$dialog.Multiselect = $false; "
|
||||
"if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { "
|
||||
"[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; "
|
||||
"Write-Output $dialog.FileName "
|
||||
"}"
|
||||
)
|
||||
|
||||
try:
|
||||
creationflags = getattr(subprocess, 'CREATE_NO_WINDOW', 0)
|
||||
result = subprocess.run(
|
||||
['powershell', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', ps_script],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
creationflags=creationflags
|
||||
)
|
||||
|
||||
selected = result.stdout.strip()
|
||||
if selected and os.path.isfile(selected):
|
||||
return selected
|
||||
|
||||
if result.stderr.strip():
|
||||
print(f"Windows file dialog error: {result.stderr.strip()}")
|
||||
except Exception as e:
|
||||
print(f"Native Windows browse failed: {e}")
|
||||
|
||||
# Fallback: manual path entry popup (no external dependencies)
|
||||
self.show_manual_path_popup()
|
||||
return None
|
||||
|
||||
def show_manual_path_popup(self):
|
||||
"""Fallback popup for manually entering monitored file path."""
|
||||
content = BoxLayout(orientation='vertical', spacing=8, padding=10)
|
||||
|
||||
input_box = TextInput(
|
||||
text=self.file_input.text.strip(),
|
||||
multiline=False,
|
||||
hint_text='Enter full file path, e.g. C:\\Users\\Public\\Documents\\check.txt'
|
||||
)
|
||||
content.add_widget(Label(text='Browse unavailable. Enter file path manually:'))
|
||||
content.add_widget(input_box)
|
||||
|
||||
buttons = BoxLayout(size_hint_y=0.35, spacing=8)
|
||||
popup = Popup(title='Set Monitor File', content=content, size_hint=(0.9, 0.4))
|
||||
|
||||
def apply_path(_instance):
|
||||
path = input_box.text.strip()
|
||||
if path and os.path.isfile(path):
|
||||
self.file_input.text = path
|
||||
self.save_config()
|
||||
popup.dismiss()
|
||||
self.show_popup('Success', f'File set:\n{path}', auto_dismiss_after=2)
|
||||
else:
|
||||
self.show_popup('Error', 'Invalid file path', auto_dismiss_after=3)
|
||||
|
||||
ok_btn = Button(text='Apply')
|
||||
ok_btn.bind(on_press=apply_path)
|
||||
buttons.add_widget(ok_btn)
|
||||
|
||||
cancel_btn = Button(text='Cancel')
|
||||
cancel_btn.bind(on_press=popup.dismiss)
|
||||
buttons.add_widget(cancel_btn)
|
||||
|
||||
content.add_widget(buttons)
|
||||
popup.open()
|
||||
|
||||
def toggle_monitoring(self, instance):
|
||||
"""Start or stop file monitoring"""
|
||||
@@ -767,12 +849,12 @@ printer = PDF
|
||||
|
||||
def read_file_variables(self):
|
||||
"""Read variables from monitored file
|
||||
Expected format: article;nr_art;serial;template_type;count
|
||||
template_type: 0=OK, 1=NOK
|
||||
Expected format: article;nr_art;serial;status;count
|
||||
status: 1=OK, 0=NOK
|
||||
count: number of labels to print (default=1)
|
||||
"""
|
||||
if not self.monitored_file or not os.path.exists(self.monitored_file):
|
||||
return None, None, None, 0, 1
|
||||
return None, None, None, '1', 1
|
||||
|
||||
try:
|
||||
with open(self.monitored_file, 'r', encoding='utf-8') as f:
|
||||
@@ -780,7 +862,7 @@ printer = PDF
|
||||
|
||||
# Skip if file is empty or only contains "-" (cleared marker)
|
||||
if not content or content == '-':
|
||||
return None, None, None, 0, 1
|
||||
return None, None, None, '1', 1
|
||||
|
||||
# Parse file content - expecting format: article;nr_art;serial;template_type;count
|
||||
# or key=value pairs on separate lines
|
||||
@@ -788,7 +870,7 @@ printer = PDF
|
||||
article = ""
|
||||
nr_art = ""
|
||||
serial = ""
|
||||
template_type = 0 # Default to OK template
|
||||
status_flag = "1"
|
||||
count = 1 # Default to 1 label
|
||||
|
||||
# Try semicolon-separated format first
|
||||
@@ -801,10 +883,7 @@ printer = PDF
|
||||
if len(parts) >= 3:
|
||||
serial = parts[2].strip()
|
||||
if len(parts) >= 4:
|
||||
try:
|
||||
template_type = int(parts[3].strip())
|
||||
except ValueError:
|
||||
template_type = 0
|
||||
status_flag = parts[3].strip()
|
||||
if len(parts) >= 5:
|
||||
try:
|
||||
count = int(parts[4].strip())
|
||||
@@ -828,11 +907,8 @@ printer = PDF
|
||||
nr_art = value
|
||||
elif key in ['serial', 'serial_no', 'serial-no', 'serialno']:
|
||||
serial = value
|
||||
elif key in ['template', 'template_type', 'type']:
|
||||
try:
|
||||
template_type = int(value)
|
||||
except ValueError:
|
||||
template_type = 0
|
||||
elif key in ['status', 'result', 'quality', 'ok', 'is_ok', 'nok']:
|
||||
status_flag = value
|
||||
elif key in ['count', 'quantity', 'copies']:
|
||||
try:
|
||||
count = int(value)
|
||||
@@ -842,16 +918,23 @@ printer = PDF
|
||||
count = 100
|
||||
except ValueError:
|
||||
count = 1
|
||||
|
||||
# Normalize status flag: 1 = OK label, 0 = NOK label
|
||||
normalized_status = str(status_flag).strip().lower()
|
||||
if normalized_status in ['0', 'nok', 'no', 'false', 'rejected', 'refused', 'fail']:
|
||||
status_flag = '0'
|
||||
else:
|
||||
status_flag = '1'
|
||||
|
||||
return article, nr_art, serial, template_type, count
|
||||
return article, nr_art, serial, status_flag, count
|
||||
except Exception as e:
|
||||
print(f"Error reading file: {e}")
|
||||
return None, None, None
|
||||
return None, None, None, '1', 1
|
||||
|
||||
def print_label(self, instance):
|
||||
"""Handle print button press - read from file and print"""
|
||||
# Read variables from file including template type and count
|
||||
article, nr_art, serial, template_type, count = self.read_file_variables()
|
||||
# Read variables from file including status and count
|
||||
article, nr_art, serial, status_flag, count = self.read_file_variables()
|
||||
|
||||
# Resolve display name to full printer name
|
||||
printer = self._get_full_printer_name(self.printer_spinner.text)
|
||||
@@ -861,8 +944,8 @@ printer = PDF
|
||||
self.show_popup("Error", "No data in file or file not set", auto_dismiss_after=3)
|
||||
return
|
||||
|
||||
# Select template based on template_type
|
||||
if template_type == 1:
|
||||
# Select template based on status_flag (1=OK, 0=NOK)
|
||||
if status_flag == '0':
|
||||
template_path = os.path.join('conf', 'label_template_nok.svg')
|
||||
template_name = "NOK"
|
||||
else:
|
||||
@@ -875,7 +958,8 @@ printer = PDF
|
||||
template_name = "DEFAULT"
|
||||
|
||||
# Create combined label text using semicolon separator
|
||||
label_text = f"{article};{nr_art};{serial}"
|
||||
# status_flag: 1 = OK label, 0 = NOK label
|
||||
label_text = f"{article};{nr_art};{serial};{status_flag}"
|
||||
|
||||
# Show loading popup with count info
|
||||
popup = Popup(
|
||||
|
||||
Reference in New Issue
Block a user