Skip to content

Commit cde57e8

Browse files
committed
Support loading images from clipboard
1 parent 401d95c commit cde57e8

File tree

1 file changed

+119
-1
lines changed

1 file changed

+119
-1
lines changed

examples/official/dcv/main.py

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1035,7 +1035,7 @@ def __init__(self):
10351035

10361036
def show_placeholder(self):
10371037
"""Show placeholder text when no image is loaded."""
1038-
self.setText("🖼️ Drag and drop files here\n\n📁 Supported formats:\n• Images: JPG, PNG, BMP, TIFF, WEBP\n• PDF files (native support)\n\n🖱️ Or click 'Load File' button")
1038+
self.setText("🖼️ Drag and drop files here\n\n📁 Supported formats:\n• Images: JPG, PNG, BMP, TIFF, WEBP\n• PDF files (native support)\n\n🖱️ Options:\n• Click 'Load File' button\n• Click 'Paste from Clipboard' button\n• Press Ctrl+V to paste")
10391039
self.setStyleSheet("border: 2px dashed #ccc; background-color: #f9f9f9; color: #666; font-size: 14px;")
10401040

10411041
def set_image(self, cv_image, detection_items=None):
@@ -1760,6 +1760,13 @@ def create_control_panel(self):
17601760
self.load_button.clicked.connect(self.load_file)
17611761
file_layout.addWidget(self.load_button)
17621762

1763+
# Paste from clipboard button
1764+
self.paste_button = QPushButton("📋 Paste from Clipboard")
1765+
self.paste_button.setMinimumHeight(35)
1766+
self.paste_button.clicked.connect(self.paste_from_clipboard)
1767+
self.paste_button.setToolTip("Paste an image from clipboard (Ctrl+V)")
1768+
file_layout.addWidget(self.paste_button)
1769+
17631770
# File info
17641771
info_group = QGroupBox("File Information")
17651772
info_layout = QVBoxLayout(info_group)
@@ -2016,6 +2023,11 @@ def setup_menu_bar(self):
20162023
open_action.triggered.connect(self.load_file)
20172024
file_menu.addAction(open_action)
20182025

2026+
paste_action = QAction("Paste from Clipboard", self)
2027+
paste_action.setShortcut("Ctrl+V")
2028+
paste_action.triggered.connect(self.paste_from_clipboard)
2029+
file_menu.addAction(paste_action)
2030+
20192031
file_menu.addSeparator()
20202032

20212033
exit_action = QAction("Exit", self)
@@ -2554,10 +2566,28 @@ def closeEvent(self, event):
25542566
if hasattr(self, 'camera_widget'):
25552567
self.camera_widget.cleanup()
25562568

2569+
# Cleanup temporary clipboard files
2570+
if hasattr(self, 'temp_clipboard_files'):
2571+
for temp_file in self.temp_clipboard_files:
2572+
try:
2573+
if os.path.exists(temp_file):
2574+
os.unlink(temp_file)
2575+
except:
2576+
pass # Ignore cleanup errors
2577+
25572578
event.accept()
25582579
else:
25592580
event.ignore()
25602581

2582+
def keyPressEvent(self, event):
2583+
"""Handle key press events."""
2584+
# Handle Ctrl+V for paste from clipboard
2585+
if event.key() == Qt.Key.Key_V and event.modifiers() == Qt.KeyboardModifier.ControlModifier:
2586+
self.paste_from_clipboard()
2587+
event.accept()
2588+
else:
2589+
super().keyPressEvent(event)
2590+
25612591
def log_message(self, message):
25622592
"""Log a message to status bar and results panel."""
25632593
timestamp = datetime.datetime.now().strftime("%H:%M:%S")
@@ -2584,6 +2614,83 @@ def load_file(self):
25842614
self.update_last_used_directory(file_path)
25852615
self.load_file_path(file_path)
25862616

2617+
def paste_from_clipboard(self):
2618+
"""Paste an image from the system clipboard."""
2619+
try:
2620+
clipboard = QApplication.clipboard()
2621+
mime_data = clipboard.mimeData()
2622+
2623+
# Check if clipboard contains image data
2624+
if mime_data.hasImage():
2625+
# Get the image from clipboard
2626+
qimage = clipboard.image()
2627+
2628+
if qimage.isNull():
2629+
QMessageBox.information(self, "No Image", "No valid image found in clipboard.")
2630+
return
2631+
2632+
self.log_message("📋 Loading image from clipboard")
2633+
2634+
# Convert QImage to a format we can work with
2635+
# Save temporarily to process it like a regular file
2636+
import tempfile
2637+
# Create a persistent temporary file that won't be auto-deleted
2638+
temp_fd, temp_path = tempfile.mkstemp(suffix=".png", prefix="clipboard_")
2639+
os.close(temp_fd) # Close the file descriptor, but keep the file
2640+
2641+
# Save the QImage to temporary file
2642+
success = qimage.save(temp_path, "PNG")
2643+
if not success:
2644+
# Clean up the temporary file if save failed
2645+
try:
2646+
os.unlink(temp_path)
2647+
except:
2648+
pass
2649+
QMessageBox.critical(self, "Error", "Failed to process clipboard image.")
2650+
return
2651+
2652+
# Store the temp path for cleanup later
2653+
if not hasattr(self, 'temp_clipboard_files'):
2654+
self.temp_clipboard_files = []
2655+
self.temp_clipboard_files.append(temp_path)
2656+
2657+
# Load the temporary file using standard file loading
2658+
# This ensures consistency with regular file loading
2659+
self.load_file_path(temp_path)
2660+
2661+
# Override the file info to show it's from clipboard
2662+
width, height = qimage.width(), qimage.height()
2663+
self.file_info_label.setText(f"📋 Clipboard Image ({width}x{height})")
2664+
2665+
self.log_message("✅ Clipboard image loaded successfully")
2666+
2667+
elif mime_data.hasUrls():
2668+
# Handle file URLs from clipboard (copied file paths)
2669+
urls = mime_data.urls()
2670+
if urls:
2671+
file_path = urls[0].toLocalFile()
2672+
if file_path and os.path.exists(file_path):
2673+
# Check if it's a supported image format
2674+
supported_exts = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif', '.webp', '.pdf'}
2675+
file_ext = os.path.splitext(file_path)[1].lower()
2676+
if file_ext in supported_exts:
2677+
self.log_message(f"📋 Loading file from clipboard: {os.path.basename(file_path)}")
2678+
self.load_file_path(file_path)
2679+
else:
2680+
QMessageBox.information(self, "Unsupported Format",
2681+
f"File format '{file_ext}' is not supported.")
2682+
else:
2683+
QMessageBox.information(self, "File Not Found",
2684+
"The file path from clipboard could not be found.")
2685+
else:
2686+
QMessageBox.information(self, "No Image",
2687+
"No image or supported file found in clipboard.\n\n"
2688+
"Try copying an image or screenshot first.")
2689+
2690+
except Exception as e:
2691+
self.log_message(f"❌ Clipboard error: {e}")
2692+
QMessageBox.critical(self, "Clipboard Error", f"Failed to paste from clipboard: {e}")
2693+
25872694
def load_file_path(self, file_path):
25882695
"""Load a file from the given path."""
25892696
try:
@@ -2672,12 +2779,23 @@ def load_pdf_file(self, file_path):
26722779
def process_current_file(self):
26732780
"""Process the current file for detection."""
26742781
if not self.current_file_path or self.is_processing:
2782+
self.log_message(f"❌ Cannot process: file_path={self.current_file_path}, is_processing={self.is_processing}")
2783+
return
2784+
2785+
# Check if the file exists (important for clipboard temp files)
2786+
if not os.path.exists(self.current_file_path):
2787+
self.log_message(f"❌ File not found: {self.current_file_path}")
2788+
QMessageBox.critical(self, "File Error", f"The image file could not be found:\n{self.current_file_path}")
26752789
return
26762790

26772791
# Get current detection mode from the combo box
26782792
current_mode_text = self.picture_detection_mode_combo.currentText()
26792793
mode_name = current_mode_text.split(" - ")[0]
26802794

2795+
self.log_message(f"🔍 Processing file: {os.path.basename(self.current_file_path)}")
2796+
self.log_message(f"📁 Full path: {self.current_file_path}")
2797+
self.log_message(f"🎯 Detection mode: {mode_name}")
2798+
26812799
# Manage intermediate receiver based on detection mode to avoid deadlocks
26822800
# Only use receiver for barcode detection - remove for document/MRZ modes
26832801
if mode_name == "Barcode":

0 commit comments

Comments
 (0)