@@ -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