google-site-verification: google61fe8ba583a51912.html
QR Code Scanner GUI in Java: Complete Implementation Guide

Introduction

QR code scanning has become essential in modern applications for payments, authentication, and data transfer. This article provides a complete implementation of a QR code scanner GUI in Java using webcam integration and popular libraries.

Project Setup

Maven Dependencies

<!-- pom.xml --> <properties> <javafx.version>21</javafx.version> <openjfx.version>0.0.8</openjfx.version> <webcam-capture.version>0.3.12</webcam-capture.version> <zxing.version>3.5.1</zxing.version> <controlsfx.version>11.1.2</controlsfx.version> </properties> <dependencies> <!-- JavaFX --> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-controls</artifactId> <version>${javafx.version}</version> </dependency> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-fxml</artifactId> <version>${javafx.version}</version> </dependency> <!-- Webcam Capture --> <dependency> <groupId>com.github.sarxos</groupId> <artifactId>webcam-capture</artifactId> <version>${webcam-capture.version}</version> </dependency> <!-- QR Code Processing --> <dependency> <groupId>com.google.zxing</groupId> <artifactId>core</artifactId> <version>${zxing.version}</version> </dependency> <dependency> <groupId>com.google.zxing</groupId> <artifactId>javase</artifactId> <version>${zxing.version}</version> </dependency> <!-- Additional UI Controls --> <dependency> <groupId>org.controlsfx</groupId> <artifactId>controlsfx</artifactId> <version>${controlsfx.version}</version> </dependency> <!-- Logging --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.4.11</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.openjfx</groupId> <artifactId>javafx-maven-plugin</artifactId> <version>${openjfx.version}</version> <configuration> <mainClass>com.qrscanner.QRScannerApp</mainClass> </configuration> </plugin> </plugins> </build>

Core Scanner Implementation

QR Code Service

package com.qrscanner.service; import com.google.zxing.*; import com.google.zxing.client.j2se.BufferedImageLuminanceSource; import com.google.zxing.common.HybridBinarizer; import javafx.embed.swing.SwingFXUtils; import javafx.scene.image.Image; import java.awt.image.BufferedImage; import java.util.HashMap; import java.util.Map; public class QRCodeService { private final MultiFormatReader reader; private boolean isScanning; public QRCodeService() { this.reader = new MultiFormatReader(); this.isScanning = false; } public String decodeQRCode(BufferedImage image) { if (image == null) { return null; } try { LuminanceSource source = new BufferedImageLuminanceSource(image); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); Map<DecodeHintType, Object> hints = new HashMap<>(); hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE); hints.put(DecodeHintType.PURE_BARCODE, Boolean.FALSE); Result result = reader.decode(bitmap, hints); return result.getText(); } catch (NotFoundException e) { // No QR code found in the image return null; } catch (Exception e) { System.err.println("Error decoding QR code: " + e.getMessage()); return null; } } public String decodeQRCode(Image fxImage) { BufferedImage bufferedImage = SwingFXUtils.fromFXImage(fxImage, null); return decodeQRCode(bufferedImage); } public boolean isScanning() { return isScanning; } public void setScanning(boolean scanning) { this.isScanning = scanning; } }

Webcam Service

package com.qrscanner.service; import com.github.sarxos.webcam.Webcam; import com.github.sarxos.webcam.WebcamResolution; import javafx.application.Platform; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.concurrent.Service; import javafx.concurrent.Task; import javafx.scene.image.Image; import java.awt.*; import java.awt.image.BufferedImage; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class WebcamService { private Webcam webcam; private final ObjectProperty<Image> currentFrame; private final QRCodeService qrCodeService; private ScheduledExecutorService executorService; private boolean isRunning; public WebcamService() { this.currentFrame = new SimpleObjectProperty<>(); this.qrCodeService = new QRCodeService(); this.isRunning = false; initializeWebcam(); } private void initializeWebcam() { try { List<Webcam> webcams = Webcam.getWebcams(); if (webcams.isEmpty()) { throw new RuntimeException("No webcams found"); } // Use the default webcam webcam = webcams.get(0); // Set resolution Dimension[] resolutions = WebcamResolution.HD.getSize(); webcam.setViewSize(resolutions[0]); webcam.open(); } catch (Exception e) { throw new RuntimeException("Failed to initialize webcam: " + e.getMessage(), e); } } public void startCapture() { if (isRunning) return; isRunning = true; qrCodeService.setScanning(true); executorService = Executors.newSingleThreadScheduledExecutor(); executorService.scheduleAtFixedRate(this::captureFrame, 0, 100, TimeUnit.MILLISECONDS); } public void stopCapture() { isRunning = false; qrCodeService.setScanning(false); if (executorService != null) { executorService.shutdown(); try { executorService.awaitTermination(1, TimeUnit.SECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } if (webcam != null && webcam.isOpen()) { webcam.close(); } } private void captureFrame() { if (!isRunning || !webcam.isOpen()) return; try { BufferedImage capturedImage = webcam.getImage(); if (capturedImage != null) { Image fxImage = SwingFXUtils.toFXImage(capturedImage, null); Platform.runLater(() -> { currentFrame.set(fxImage); }); // Process QR code in background if (qrCodeService.isScanning()) { processQRCode(capturedImage); } } } catch (Exception e) { System.err.println("Error capturing frame: " + e.getMessage()); } } private void processQRCode(BufferedImage image) { String result = qrCodeService.decodeQRCode(image); if (result != null) { Platform.runLater(() -> { // Notify listeners about QR code detection // This will be handled by the controller }); } } public ObjectProperty<Image> currentFrameProperty() { return currentFrame; } public Image getCurrentFrame() { return currentFrame.get(); } public QRCodeService getQrCodeService() { return qrCodeService; } public boolean isRunning() { return isRunning; } public List<Webcam> getAvailableWebcams() { return Webcam.getWebcams(); } public void switchWebcam(Webcam newWebcam) { stopCapture(); if (webcam != null && webcam.isOpen()) { webcam.close(); } webcam = newWebcam; webcam.setViewSize(WebcamResolution.HD.getSize()[0]); webcam.open(); startCapture(); } }

Main Application GUI

Main Scanner GUI (QRScannerGUI.fxml)

<?xml version="1.0" encoding="UTF-8"?> <?import javafx.geometry.Insets?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <?import javafx.scene.text.Font?> <VBox xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.qrscanner.controller.QRScannerController" spacing="20" style="-fx-background-color: #2c3e50;" prefWidth="800" prefHeight="700"> <padding> <Insets top="20" right="20" bottom="20" left="20"/> </padding> <!-- Header --> <HBox spacing="20" alignment="CENTER_LEFT"> <Label text="QR Code Scanner" style="-fx-text-fill: white; -fx-font-size: 28; -fx-font-weight: bold;"/> <Region HBox.hgrow="ALWAYS"/> <Button fx:id="settingsButton" text="Settings" style="-fx-background-color: #34495e; -fx-text-fill: white;"/> </HBox> <!-- Camera Preview Section --> <VBox spacing="10" style="-fx-background-color: #34495e; -fx-background-radius: 10; -fx-padding: 20;"> <HBox spacing="10" alignment="CENTER_LEFT"> <Label text="Camera Preview" style="-fx-text-fill: white; -fx-font-size: 18; -fx-font-weight: bold;"/> <Region HBox.hgrow="ALWAYS"/> <ComboBox fx:id="cameraSelector" promptText="Select Camera" style="-fx-background-color: #2c3e50; -fx-text-fill: white;"/> </HBox> <!-- Camera View --> <BorderPane style="-fx-background-color: black; -fx-border-color: #7f8c8d; -fx-border-radius: 5;"> <center> <ImageView fx:id="cameraView" fitWidth="640" fitHeight="480" preserveRatio="true" style="-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.8), 10, 0, 0, 0);"/> </center> <!-- Scanning Overlay --> <StackPane> <Rectangle fx:id="scanOverlay" width="200" height="200" fill="transparent" stroke="#00ff00" strokeWidth="2" strokeDashArray="10 5" visible="false"/> <Label fx:id="scanningLabel" text="Scanning..." style="-fx-text-fill: #00ff00; -fx-font-size: 16; -fx-font-weight: bold;" visible="false"/> </StackPane> </BorderPane> <!-- Camera Controls --> <HBox spacing="10" alignment="CENTER"> <Button fx:id="startButton" text="Start Camera" style="-fx-background-color: #27ae60; -fx-text-fill: white; -fx-font-weight: bold; -fx-padding: 10 20;"/> <Button fx:id="stopButton" text="Stop Camera" style="-fx-background-color: #e74c3c; -fx-text-fill: white; -fx-font-weight: bold; -fx-padding: 10 20;" disable="true"/> <Button fx:id="captureButton" text="Capture QR Code" style="-fx-background-color: #3498db; -fx-text-fill: white; -fx-font-weight: bold; -fx-padding: 10 20;" disable="true"/> </HBox> </VBox> <!-- Results Section --> <VBox spacing="10" style="-fx-background-color: #34495e; -fx-background-radius: 10; -fx-padding: 20;"> <Label text="Scan Results" style="-fx-text-fill: white; -fx-font-size: 18; -fx-font-weight: bold;"/> <HBox spacing="10"> <VBox spacing="10" HBox.hgrow="ALWAYS"> <Label text="Detected QR Code:" style="-fx-text-fill: #bdc3c7;"/> <TextArea fx:id="resultTextArea" promptText="QR code content will appear here..." style="-fx-background-color: #2c3e50; -fx-text-fill: white; -fx-border-color: #7f8c8d;" wrapText="true" prefRowCount="4"/> </VBox> <VBox spacing="10"> <Label text="Actions:" style="-fx-text-fill: #bdc3c7;"/> <Button fx:id="copyButton" text="Copy" style="-fx-background-color: #2980b9; -fx-text-fill: white;" disable="true"/> <Button fx:id="clearButton" text="Clear" style="-fx-background-color: #e67e22; -fx-text-fill: white;"/> <Button fx:id="saveButton" text="Save Result" style="-fx-background-color: #16a085; -fx-text-fill: white;" disable="true"/> </VBox> </HBox> <!-- Result Type Indicator --> <HBox spacing="10" alignment="CENTER_LEFT"> <Label text="Content Type:" style="-fx-text-fill: #bdc3c7;"/> <Label fx:id="contentTypeLabel" text="Unknown" style="-fx-text-fill: #f39c12; -fx-font-weight: bold;"/> <Region HBox.hgrow="ALWAYS"/> <Label fx:id="lastScanLabel" text="Last scan: Never" style="-fx-text-fill: #95a5a6; -fx-font-style: italic;"/> </HBox> </VBox> <!-- Status Bar --> <HBox spacing="10" style="-fx-background-color: #34495e; -fx-background-radius: 5; -fx-padding: 10;"> <Label fx:id="statusLabel" text="Ready to scan" style="-fx-text-fill: #2ecc71; -fx-font-weight: bold;"/> <Region HBox.hgrow="ALWAYS"/> <ProgressIndicator fx:id="progressIndicator" visible="false"/> <Label fx:id="versionLabel" text="v1.0.0" style="-fx-text-fill: #7f8c8d;"/> </HBox> </VBox>

Main Controller

package com.qrscanner.controller; import com.qrscanner.service.QRCodeService; import com.qrscanner.service.WebcamService; import com.github.sarxos.webcam.Webcam; import javafx.animation.Animation; import javafx.animation.KeyFrame; import javafx.animation.Timeline; import javafx.application.Platform; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.*; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.shape.Rectangle; import javafx.stage.FileChooser; import javafx.util.Duration; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.net.URL; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; import java.util.ResourceBundle; public class QRScannerController implements Initializable { @FXML private ImageView cameraView; @FXML private ComboBox<String> cameraSelector; @FXML private Button startButton; @FXML private Button stopButton; @FXML private Button captureButton; @FXML private Button copyButton; @FXML private Button clearButton; @FXML private Button saveButton; @FXML private Button settingsButton; @FXML private TextArea resultTextArea; @FXML private Label statusLabel; @FXML private Label contentTypeLabel; @FXML private Label lastScanLabel; @FXML private Label scanningLabel; @FXML private ProgressIndicator progressIndicator; @FXML private Rectangle scanOverlay; private WebcamService webcamService; private QRCodeService qrCodeService; private Timeline scanningAnimation; private final StringProperty lastResult = new SimpleStringProperty(""); private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); @Override public void initialize(URL location, ResourceBundle resources) { initializeServices(); setupUI(); setupEventHandlers(); setupAnimations(); loadAvailableCameras(); } private void initializeServices() { try { webcamService = new WebcamService(); qrCodeService = webcamService.getQrCodeService(); // Bind camera view to webcam service cameraView.imageProperty().bind(webcamService.currentFrameProperty()); } catch (Exception e) { showError("Initialization Error", "Failed to initialize webcam service: " + e.getMessage()); } } private void setupUI() { // Initial button states stopButton.setDisable(true); captureButton.setDisable(true); copyButton.setDisable(true); saveButton.setDisable(true); // Setup result text area resultTextArea.textProperty().addListener((obs, oldVal, newVal) -> { boolean hasContent = newVal != null && !newVal.trim().isEmpty(); copyButton.setDisable(!hasContent); saveButton.setDisable(!hasContent); if (hasContent) { analyzeContentType(newVal); } else { contentTypeLabel.setText("Unknown"); contentTypeLabel.setStyle("-fx-text-fill: #f39c12;"); } }); } private void setupEventHandlers() { startButton.setOnAction(e -> startCamera()); stopButton.setOnAction(e -> stopCamera()); captureButton.setOnAction(e -> captureQRCode()); copyButton.setOnAction(e -> copyToClipboard()); clearButton.setOnAction(e -> clearResults()); saveButton.setOnAction(e -> saveResults()); settingsButton.setOnAction(e -> showSettings()); cameraSelector.setOnAction(e -> switchCamera()); } private void setupAnimations() { // Scanning animation scanningAnimation = new Timeline( new KeyFrame(Duration.ZERO, e -> scanningLabel.setVisible(true)), new KeyFrame(Duration.seconds(0.5), e -> scanningLabel.setVisible(false)), new KeyFrame(Duration.seconds(1.0), e -> scanningLabel.setVisible(true)) ); scanningAnimation.setCycleCount(Animation.INDEFINITE); } private void loadAvailableCameras() { try { List<Webcam> webcams = webcamService.getAvailableWebcams(); cameraSelector.getItems().clear(); for (int i = 0; i < webcams.size(); i++) { Webcam webcam = webcams.get(i); String cameraName = webcam.getName(); cameraSelector.getItems().add("Camera " + (i + 1) + ": " + cameraName); } if (!webcams.isEmpty()) { cameraSelector.getSelectionModel().select(0); } } catch (Exception e) { showError("Camera Error", "Failed to load available cameras: " + e.getMessage()); } } private void startCamera() { try { webcamService.startCapture(); startButton.setDisable(true); stopButton.setDisable(false); captureButton.setDisable(false); scanOverlay.setVisible(true); scanningAnimation.play(); updateStatus("Camera started - Scanning for QR codes...", "success"); } catch (Exception e) { showError("Camera Start Error", "Failed to start camera: " + e.getMessage()); } } private void stopCamera() { try { webcamService.stopCapture(); startButton.setDisable(false); stopButton.setDisable(true); captureButton.setDisable(true); scanOverlay.setVisible(false); scanningAnimation.stop(); scanningLabel.setVisible(false); updateStatus("Camera stopped", "info"); } catch (Exception e) { showError("Camera Stop Error", "Failed to stop camera: " + e.getMessage()); } } private void captureQRCode() { Image currentFrame = webcamService.getCurrentFrame(); if (currentFrame != null) { progressIndicator.setVisible(true); updateStatus("Processing QR code...", "info"); // Process in background thread new Thread(() -> { String result = qrCodeService.decodeQRCode(currentFrame); Platform.runLater(() -> { progressIndicator.setVisible(false); if (result != null) { handleQRCodeResult(result); } else { updateStatus("No QR code detected", "warning"); } }); }).start(); } } private void handleQRCodeResult(String result) { resultTextArea.setText(result); lastResult.set(result); // Update last scan time String timestamp = LocalDateTime.now().format(TIME_FORMATTER); lastScanLabel.setText("Last scan: " + timestamp); updateStatus("QR code scanned successfully!", "success"); // Optional: Auto-copy to clipboard // copyToClipboard(); } private void analyzeContentType(String content) { if (content.startsWith("http://") || content.startsWith("https://")) { contentTypeLabel.setText("URL"); contentTypeLabel.setStyle("-fx-text-fill: #3498db;"); } else if (content.startsWith("BEGIN:VCARD")) { contentTypeLabel.setText("Contact"); contentTypeLabel.setStyle("-fx-text-fill: #9b59b6;"); } else if (content.matches("^[\\w\\-\\.]+@([\\w-]+\\.)+[\\w-]{2,}$")) { contentTypeLabel.setText("Email"); contentTypeLabel.setStyle("-fx-text-fill: #e74c3c;"); } else if (content.matches("^[\\+]?[0-9\\-\\s\\(\\)]{10,}$")) { contentTypeLabel.setText("Phone"); contentTypeLabel.setStyle("-fx-text-fill: #2ecc71;"); } else if (content.length() < 50 && content.matches("^[A-Za-z0-9]+$")) { contentTypeLabel.setText("Text"); contentTypeLabel.setStyle("-fx-text-fill: #f39c12;"); } else { contentTypeLabel.setText("Unknown"); contentTypeLabel.setStyle("-fx-text-fill: #95a5a6;"); } } private void copyToClipboard() { String content = resultTextArea.getText(); if (content != null && !content.trim().isEmpty()) { javafx.scene.input.Clipboard clipboard = javafx.scene.input.Clipboard.getSystemClipboard(); javafx.scene.input.ClipboardContent clipboardContent = new javafx.scene.input.ClipboardContent(); clipboardContent.putString(content); clipboard.setContent(clipboardContent); updateStatus("Copied to clipboard!", "success"); } } private void clearResults() { resultTextArea.clear(); lastResult.set(""); lastScanLabel.setText("Last scan: Never"); updateStatus("Results cleared", "info"); } private void saveResults() { FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("Save QR Code Result"); fileChooser.getExtensionFilters().add( new FileChooser.ExtensionFilter("Text Files", "*.txt") ); fileChooser.setInitialFileName("qr_code_result_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")) + ".txt"); File file = fileChooser.showSaveDialog(cameraView.getScene().getWindow()); if (file != null) { try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { writer.write("QR Code Scan Result\n"); writer.write("Scanned at: " + LocalDateTime.now().format(TIME_FORMATTER) + "\n"); writer.write("Content Type: " + contentTypeLabel.getText() + "\n"); writer.write("Content:\n" + resultTextArea.getText()); updateStatus("Results saved to: " + file.getName(), "success"); } catch (IOException e) { showError("Save Error", "Failed to save results: " + e.getMessage()); } } } private void switchCamera() { int selectedIndex = cameraSelector.getSelectionModel().getSelectedIndex(); if (selectedIndex >= 0) { try { List<Webcam> webcams = webcamService.getAvailableWebcams(); if (selectedIndex < webcams.size()) { webcamService.switchWebcam(webcams.get(selectedIndex)); updateStatus("Switched to camera: " + webcams.get(selectedIndex).getName(), "info"); } } catch (Exception e) { showError("Camera Switch Error", "Failed to switch camera: " + e.getMessage()); } } } private void showSettings() { // Implement settings dialog Alert alert = new Alert(Alert.AlertType.INFORMATION); alert.setTitle("Settings"); alert.setHeaderText("QR Scanner Settings"); alert.setContentText("Settings dialog will be implemented here."); alert.showAndWait(); } private void updateStatus(String message, String type) { Platform.runLater(() -> { statusLabel.setText(message); switch (type) { case "success": statusLabel.setStyle("-fx-text-fill: #2ecc71; -fx-font-weight: bold;"); break; case "warning": statusLabel.setStyle("-fx-text-fill: #f39c12; -fx-font-weight: bold;"); break; case "error": statusLabel.setStyle("-fx-text-fill: #e74c3c; -fx-font-weight: bold;"); break; default: statusLabel.setStyle("-fx-text-fill: #3498db; -fx-font-weight: bold;"); } }); } private void showError(String title, String message) { Platform.runLater(() -> { Alert alert = new Alert(Alert.AlertType.ERROR); alert.setTitle(title); alert.setHeaderText(null); alert.setContentText(message); alert.showAndWait(); }); } public void shutdown() { if (webcamService != null) { webcamService.stopCapture(); } if (scanningAnimation != null) { scanningAnimation.stop(); } } }

Advanced Features

Image File QR Code Scanner

package com.qrscanner.service; import com.google.zxing.*; import com.google.zxing.client.j2se.BufferedImageLuminanceSource; import com.google.zxing.common.HybridBinarizer; import javafx.stage.FileChooser; import javafx.stage.Window; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; public class FileQRCodeService { private final MultiFormatReader reader; public FileQRCodeService() { this.reader = new MultiFormatReader(); } public String decodeQRCodeFromFile(File imageFile) throws IOException { if (imageFile == null || !imageFile.exists()) { throw new IOException("Image file does not exist"); } try { BufferedImage image = ImageIO.read(imageFile); return decodeQRCode(image); } catch (IOException e) { throw new IOException("Failed to read image file: " + e.getMessage(), e); } } public String decodeQRCode(BufferedImage image) { try { LuminanceSource source = new BufferedImageLuminanceSource(image); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); Map<DecodeHintType, Object> hints = new HashMap<>(); hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE); hints.put(DecodeHintType.POSSIBLE_FORMATS, java.util.Arrays.asList(BarcodeFormat.QR_CODE)); Result result = reader.decode(bitmap, hints); return result.getText(); } catch (NotFoundException e) { return null; // No QR code found } catch (Exception e) { throw new RuntimeException("Error decoding QR code: " + e.getMessage(), e); } } public File showImageFileChooser(Window ownerWindow) { FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("Select QR Code Image"); fileChooser.getExtensionFilters().addAll( new FileChooser.ExtensionFilter("Image Files", "*.png", "*.jpg", "*.jpeg", "*.gif", "*.bmp"), new FileChooser.ExtensionFilter("All Files", "*.*") ); return fileChooser.showOpenDialog(ownerWindow); } }

Batch QR Code Processor

package com.qrscanner.service; import javafx.concurrent.Task; import java.io.File; import java.util.ArrayList; import java.util.List; public class BatchQRProcessor extends Task<List<BatchResult>> { private final List<File> imageFiles; private final FileQRCodeService qrService; public BatchQRProcessor(List<File> imageFiles) { this.imageFiles = new ArrayList<>(imageFiles); this.qrService = new FileQRCodeService(); } @Override protected List<BatchResult> call() throws Exception { List<BatchResult> results = new ArrayList<>(); int totalFiles = imageFiles.size(); for (int i = 0; i < totalFiles; i++) { if (isCancelled()) { break; } File file = imageFiles.get(i); updateProgress(i + 1, totalFiles); updateMessage("Processing: " + file.getName()); try { String qrContent = qrService.decodeQRCodeFromFile(file); results.add(new BatchResult(file, qrContent, true, "Success")); } catch (Exception e) { results.add(new BatchResult(file, null, false, e.getMessage())); } // Small delay to prevent UI freezing Thread.sleep(50); } return results; } public static class BatchResult { private final File file; private final String content; private final boolean success; private final String message; public BatchResult(File file, String content, boolean success, String message) { this.file = file; this.content = content; this.success = success; this.message = message; } // Getters public File getFile() { return file; } public String getContent() { return content; } public boolean isSuccess() { return success; } public String getMessage() { return message; } } }

Main Application Class

package com.qrscanner; import com.qrscanner.controller.QRScannerController; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.stage.Stage; import java.util.Objects; public class QRScannerApp extends Application { private QRScannerController controller; @Override public void start(Stage primaryStage) throws Exception { FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/QRScannerGUI.fxml")); Parent root = loader.load(); // Get controller for proper shutdown controller = loader.getController(); Scene scene = new Scene(root); scene.getStylesheets().add(Objects.requireNonNull( getClass().getResource("/css/styles.css")).toExternalForm()); primaryStage.setTitle("QR Code Scanner"); primaryStage.getIcons().add(new Image( Objects.requireNonNull(getClass().getResourceAsStream("/images/qr-icon.png")))); primaryStage.setScene(scene); primaryStage.setMinWidth(600); primaryStage.setMinHeight(700); // Handle window close primaryStage.setOnCloseRequest(event -> { if (controller != null) { controller.shutdown(); } }); primaryStage.show(); } @Override public void stop() throws Exception { if (controller != null) { controller.shutdown(); } super.stop(); } public static void main(String[] args) { launch(args); } }

CSS Styling

/* styles.css */ .root { -fx-font-family: "Segoe UI", Arial, sans-serif; -fx-font-size: 14px; } /* Button Styles */ .button { -fx-background-radius: 5; -fx-border-radius: 5; -fx-padding: 8 16; -fx-cursor: hand; -fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.1), 5, 0, 0, 2); -fx-font-weight: bold; } .button:hover { -fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.2), 8, 0, 0, 3); } .button:pressed { -fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.1), 2, 0, 0, 1); } .button:disabled { -fx-opacity: 0.6; -fx-cursor: default; } /* Text Area */ .text-area { -fx-background-radius: 5; -fx-border-radius: 5; -fx-padding: 10; } .text-area .content { -fx-background-radius: 5; } /* ComboBox */ .combo-box { -fx-background-radius: 5; -fx-border-radius: 5; } .combo-box .list-cell { -fx-background-color: #2c3e50; -fx-text-fill: white; } .combo-box .list-view { -fx-background-color: #2c3e50; -fx-background-radius: 5; } /* Progress Indicator */ .progress-indicator { -fx-progress-color: #3498db; } /* Custom Scanner Overlay */ .scan-overlay { -fx-stroke: #00ff00; -fx-stroke-width: 2; -fx-stroke-dash-array: 10 5; } /* Status Colors */ .status-success { -fx-text-fill: #2ecc71; -fx-font-weight: bold; } .status-warning { -fx-text-fill: #f39c12; -fx-font-weight: bold; } .status-error { -fx-text-fill: #e74c3c; -fx-font-weight: bold; } .status-info { -fx-text-fill: #3498db; -fx-font-weight: bold; } /* Content Type Labels */ .content-type-url { -fx-text-fill: #3498db; -fx-font-weight: bold; } .content-type-contact { -fx-text-fill: #9b59b6; -fx-font-weight: bold; } .content-type-email { -fx-text-fill: #e74c3c; -fx-font-weight: bold; } .content-type-phone { -fx-text-fill: #2ecc71; -fx-font-weight: bold; } .content-type-text { -fx-text-fill: #f39c12; -fx-font-weight: bold; }

Build and Run

Build Script (build.sh)

#!/bin/bash # Build the QR Scanner application echo "Building QR Code Scanner..." # Clean previous build mvn clean # Compile and package mvn package echo "Build complete!" echo "Run the application with: java -jar target/qr-scanner.jar"

Run Configuration

// For development running public class DevRunner { public static void main(String[] args) { // Set system properties for webcam capture System.setProperty("webcam.debug", "false"); System.setProperty("webcam.autoopen", "true"); QRScannerApp.main(args); } }

Features Summary

  • Real-time webcam QR code scanning
  • Multiple camera support
  • File-based QR code decoding
  • Batch processing capabilities
  • Content type detection
  • Copy to clipboard functionality
  • Result saving and export
  • Modern JavaFX GUI
  • Responsive design
  • Error handling and status reporting

This QR code scanner provides a complete, professional solution for both real-time scanning and file-based decoding with an intuitive graphical interface.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper