Skip to content

Commit 93124b1

Browse files
committed
Updated
1 parent 135e7fb commit 93124b1

2 files changed

Lines changed: 142 additions & 31 deletions

File tree

examples/official/camera_file/file.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
continue
3636

3737
result_array = cvr_instance.capture_multi_pages(
38-
image_path, EnumPresetTemplate.PT_READ_BARCODES_READ_RATE_FIRST.value)
38+
image_path, EnumPresetTemplate.PT_READ_BARCODES.value)
3939

4040
results = result_array.get_results()
4141
if results is None or len(results) == 0:

examples/official/camera_file/gui.py

Lines changed: 141 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
)
3232
from PySide6.QtWidgets import (
3333
QApplication,
34+
QComboBox,
3435
QFileDialog,
3536
QFrame,
3637
QGraphicsItem,
@@ -80,6 +81,14 @@
8081
PDF_DETECT_DPI = 300
8182
PDF_DISPLAY_DPI = 150
8283

84+
TEMPLATES: List[Tuple[str, str]] = [
85+
("Read Barcodes (default)", EnumPresetTemplate.PT_READ_BARCODES.value),
86+
("Speed First", EnumPresetTemplate.PT_READ_BARCODES_SPEED_FIRST.value),
87+
("Read Rate First", EnumPresetTemplate.PT_READ_BARCODES_READ_RATE_FIRST.value),
88+
("Single Barcode", EnumPresetTemplate.PT_READ_SINGLE_BARCODE.value),
89+
]
90+
DEFAULT_TEMPLATE_INDEX = 0 # Read Barcodes (default)
91+
8392
OVERLAY_COLOR = QColor(0, 200, 80)
8493
OVERLAY_FILL = QColor(0, 200, 80, 60)
8594
OVERLAY_TEXT_BG = QColor(0, 0, 0, 180)
@@ -175,15 +184,82 @@ class ScannerSignals(QObject):
175184

176185

177186
class ScannerThread(QThread):
178-
def __init__(self, files: List[str], parent=None) -> None:
187+
def __init__(
188+
self,
189+
files: List[str],
190+
template: str,
191+
cached_pages: Optional[dict] = None,
192+
parent=None,
193+
) -> None:
179194
super().__init__(parent)
180195
self.files = list(files)
196+
self.template = template
197+
# {(file_path, page_idx) -> PageData} - rendered pages we can reuse
198+
# so a template-switch re-decode does not re-render PDFs/TIFFs.
199+
self.cached_pages = cached_pages or {}
181200
self.signals = ScannerSignals()
182201
self._stop = False
183202

184203
def stop(self) -> None:
185204
self._stop = True
186205

206+
def _prepare_pages(self, file_path: str) -> List[PageData]:
207+
"""Render pages or reuse cached renders. Emits fileStarted (only when
208+
the file was not previously loaded) and a placeholder pageReady for
209+
every page. Returns the list of PageData."""
210+
cached_keys = sorted(k for k in self.cached_pages if k[0] == file_path)
211+
if cached_keys:
212+
n_total = cached_keys[-1][1] + 1
213+
if all((file_path, i) in self.cached_pages for i in range(n_total)):
214+
records: List[PageData] = []
215+
for i in range(n_total):
216+
old = self.cached_pages[(file_path, i)]
217+
records.append(
218+
PageData(
219+
file_path=file_path,
220+
page_index=i,
221+
total_pages=n_total,
222+
image=old.image,
223+
detect_width=old.detect_width,
224+
detect_height=old.detect_height,
225+
barcodes=[],
226+
error=None,
227+
)
228+
)
229+
# No fileStarted - the tree item already exists.
230+
for rec in records:
231+
self.signals.pageReady.emit(rec)
232+
return records
233+
234+
try:
235+
rendered = render_pages(file_path)
236+
except Exception as exc:
237+
self.signals.error.emit(
238+
file_path, f"Failed to render: {exc}\n{traceback.format_exc()}"
239+
)
240+
return []
241+
242+
total_pages = len(rendered)
243+
if total_pages == 0:
244+
self.signals.error.emit(file_path, "No pages rendered.")
245+
return []
246+
247+
self.signals.fileStarted.emit(file_path, total_pages)
248+
249+
records = []
250+
for idx, (img, dw, dh) in enumerate(rendered):
251+
rec = PageData(
252+
file_path=file_path,
253+
page_index=idx,
254+
total_pages=total_pages,
255+
image=img,
256+
detect_width=dw,
257+
detect_height=dh,
258+
)
259+
records.append(rec)
260+
self.signals.pageReady.emit(rec)
261+
return records
262+
187263
def run(self) -> None:
188264
try:
189265
cvr = CaptureVisionRouter()
@@ -192,40 +268,16 @@ def run(self) -> None:
192268
self.signals.allFinished.emit()
193269
return
194270

195-
template = EnumPresetTemplate.PT_READ_BARCODES_READ_RATE_FIRST.value
271+
template = self.template
196272

197273
for file_path in self.files:
198274
if self._stop:
199275
break
200-
try:
201-
rendered = render_pages(file_path)
202-
except Exception as exc:
203-
self.signals.error.emit(
204-
file_path, f"Failed to render: {exc}\n{traceback.format_exc()}"
205-
)
206-
continue
207276

208-
total_pages = len(rendered)
209-
if total_pages == 0:
210-
self.signals.error.emit(file_path, "No pages rendered.")
277+
page_records = self._prepare_pages(file_path)
278+
if not page_records:
211279
continue
212-
213-
self.signals.fileStarted.emit(file_path, total_pages)
214-
215-
# Emit placeholder pages first so the UI is responsive while the
216-
# scan runs; barcodes will be filled in after decode finishes.
217-
page_records: List[PageData] = []
218-
for idx, (img, dw, dh) in enumerate(rendered):
219-
rec = PageData(
220-
file_path=file_path,
221-
page_index=idx,
222-
total_pages=total_pages,
223-
image=img,
224-
detect_width=dw,
225-
detect_height=dh,
226-
)
227-
page_records.append(rec)
228-
self.signals.pageReady.emit(rec)
280+
total_pages = len(page_records)
229281

230282
if self._stop:
231283
break
@@ -472,6 +524,7 @@ def __init__(self) -> None:
472524
self._license_ok = False
473525
self._barcode_total = 0
474526
self._auto_select_target: Optional[str] = None
527+
self._current_template: str = TEMPLATES[DEFAULT_TEMPLATE_INDEX][1]
475528

476529
self._build_ui()
477530
self._init_license()
@@ -520,6 +573,20 @@ def _build_ui(self) -> None:
520573
self._fit_act.triggered.connect(lambda: self.viewer.fit_to_window())
521574
toolbar.addAction(self._fit_act)
522575

576+
toolbar.addSeparator()
577+
toolbar.addWidget(QLabel(" Template: "))
578+
self._template_combo = QComboBox()
579+
for label, _value in TEMPLATES:
580+
self._template_combo.addItem(label)
581+
self._template_combo.setCurrentIndex(DEFAULT_TEMPLATE_INDEX)
582+
self._template_combo.setMinimumWidth(180)
583+
self._template_combo.setToolTip(
584+
"Capture Vision preset template. Changing it re-decodes the "
585+
"currently selected file."
586+
)
587+
self._template_combo.currentIndexChanged.connect(self._on_template_changed)
588+
toolbar.addWidget(self._template_combo)
589+
523590
toolbar.addSeparator()
524591
spacer = QWidget()
525592
spacer.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
@@ -669,6 +736,50 @@ def _on_open_folder(self) -> None:
669736
if folder:
670737
self._enqueue_paths([folder])
671738

739+
def _on_template_changed(self, index: int) -> None:
740+
if index < 0 or index >= len(TEMPLATES):
741+
return
742+
label, value = TEMPLATES[index]
743+
self._current_template = value
744+
745+
file_path = self._current_file_path()
746+
if not file_path:
747+
self._status_label.setText(f"Template -> {label}")
748+
return
749+
750+
# Re-decode the currently displayed file with the new template.
751+
# Cached page renders are reused so PDFs/TIFFs don't have to be
752+
# rasterised again.
753+
cached = {
754+
key: page for key, page in self._pages.items() if key[0] == file_path
755+
}
756+
if self._scanner and self._scanner.isRunning():
757+
self._scanner.stop()
758+
self._scanner.wait(2000)
759+
760+
self._auto_select_target = None # keep the current selection
761+
self._progress.setVisible(True)
762+
self._status_label.setText(
763+
f"Re-decoding {os.path.basename(file_path)} with '{label}'..."
764+
)
765+
766+
self._scanner = ScannerThread(
767+
[file_path], self._current_template, cached_pages=cached, parent=self
768+
)
769+
self._scanner.signals.fileStarted.connect(self._on_file_started)
770+
self._scanner.signals.pageReady.connect(self._on_page_ready)
771+
self._scanner.signals.fileFinished.connect(self._on_file_finished)
772+
self._scanner.signals.allFinished.connect(self._on_all_finished)
773+
self._scanner.signals.error.connect(self._on_scan_error)
774+
self._scanner.start()
775+
776+
def _current_file_path(self) -> Optional[str]:
777+
item = self.tree.currentItem()
778+
if item is None:
779+
return None
780+
path = item.data(0, self.FILE_ROLE)
781+
return path if path else None
782+
672783
def _on_clear(self) -> None:
673784
if self._scanner and self._scanner.isRunning():
674785
self._scanner.stop()
@@ -713,7 +824,7 @@ def _enqueue_paths(self, paths: List[str]) -> None:
713824
self._progress.setVisible(True)
714825
self._status_label.setText(f"Scanning {len(new_files)} file(s)...")
715826

716-
self._scanner = ScannerThread(new_files, self)
827+
self._scanner = ScannerThread(new_files, self._current_template, parent=self)
717828
self._scanner.signals.fileStarted.connect(self._on_file_started)
718829
self._scanner.signals.pageReady.connect(self._on_page_ready)
719830
self._scanner.signals.fileFinished.connect(self._on_file_finished)

0 commit comments

Comments
 (0)