| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- """
- Report Printer
- Handles printing of reports to physical printers using QPrinter.
- """
- from typing import Dict, List, Tuple
- from PyQt5.QtPrintSupport import QPrinter
- from PyQt5.QtCore import Qt
- from PyQt5.QtGui import QFont, QPixmap, QPainter
- from PyQt5.QtWidgets import QMessageBox
- from reportlab.lib.units import inch
- class ReportPrinter:
- """
- Handles report printing to physical printers.
- """
-
- def print_report(
- self,
- parent_widget,
- report_content: Dict,
- visualizations: List[Tuple[str, QPixmap]],
- include_visualizations: bool = True
- ) -> bool:
- """
- Print report to default printer.
-
- Args:
- parent_widget: Parent widget for error dialogs
- report_content: Dictionary with report text content
- visualizations: List of (title, QPixmap) tuples
- include_visualizations: Whether to include visualizations
-
- Returns:
- bool: True if print succeeded, False otherwise
- """
- try:
- # Create QPrinter with default printer
- printer = QPrinter(QPrinter.HighResolution)
-
- # Create painter and render report
- painter = QPainter()
- painter.begin(printer)
-
- # Get page rectangle
- page_rect = printer.pageRect(QPrinter.DevicePixel)
-
- # Render report content
- self._render_report_to_painter(painter, page_rect, report_content, include_visualizations)
-
- # Add visualizations if selected
- if include_visualizations and visualizations:
- printer.newPage()
-
- margin = 0.5 * 72 # 0.5 inch in pixels
- x_pos = int(margin)
- y_pos = int(margin)
- max_height = int(page_rect.height() - 2 * margin)
- max_width = int(page_rect.width() - 2 * margin)
-
- title_font = QFont("Arial", 12, QFont.Bold)
- painter.setFont(title_font)
-
- painter.drawText(x_pos, y_pos, max_width, 30,
- Qt.AlignLeft, "Visualizations")
- y_pos += 40
-
- for viz_title, viz_pixmap in visualizations:
- # Check if we need a new page
- if y_pos + 250 > page_rect.height():
- printer.newPage()
- y_pos = int(margin)
-
- # Draw title
- painter.drawText(x_pos, y_pos, max_width, 25,
- Qt.AlignLeft, viz_title)
- y_pos += 30
-
- # Scale and draw image
- pixmap = viz_pixmap
- if pixmap.width() > max_width:
- aspect = pixmap.height() / pixmap.width()
- pixmap = pixmap.scaledToWidth(max_width, Qt.SmoothTransformation)
- pixmap_height = int(pixmap.width() * aspect)
- if pixmap_height > max_height / 2:
- pixmap = pixmap.scaledToHeight(int(max_height / 2), Qt.SmoothTransformation)
-
- painter.drawPixmap(x_pos, y_pos, pixmap)
- y_pos += pixmap.height() + 20
-
- painter.end()
-
- QMessageBox.information(
- parent_widget,
- "Print Sent",
- "Report sent to default printer successfully."
- )
-
- return True
-
- except Exception as e:
- QMessageBox.critical(
- parent_widget,
- "Print Error",
- f"Error printing report:\n{str(e)}"
- )
- return False
-
- def _render_report_to_painter(
- self,
- painter: QPainter,
- page_rect,
- report_content: Dict,
- include_visualizations: bool = True
- ) -> int:
- """
- Render report content to a QPainter (for printing).
-
- Args:
- painter: QPainter to render to
- page_rect: Rectangle defining the printable area
- report_content: Dictionary with report text content
- include_visualizations: Whether to include images
-
- Returns:
- Number of pages needed
- """
- margin = 0.5 * inch
- content_width = page_rect.width() - 2 * margin
- y_pos = margin
- line_height = 20
-
- # Font setup
- title_font = QFont("Arial", 16, QFont.Bold)
- heading_font = QFont("Arial", 12, QFont.Bold)
- normal_font = QFont("Arial", 10)
-
- # Title
- painter.setFont(title_font)
- painter.drawText(int(margin), int(y_pos), int(content_width), int(line_height),
- Qt.AlignLeft, "Durian Analysis Report")
- y_pos += line_height + 10
-
- # Report Info
- painter.setFont(heading_font)
- painter.drawText(int(margin), int(y_pos), int(content_width), int(line_height),
- Qt.AlignLeft, "Report Information")
- y_pos += line_height
-
- painter.setFont(normal_font)
- painter.drawText(int(margin), int(y_pos), int(content_width), int(line_height),
- Qt.AlignLeft, f"Report ID: {report_content['report_id']}")
- y_pos += line_height
-
- painter.drawText(int(margin), int(y_pos), int(content_width), int(line_height),
- Qt.AlignLeft, f"Generated: {report_content['generated']}")
- y_pos += line_height + 10
-
- # Analysis Results
- painter.setFont(heading_font)
- painter.drawText(int(margin), int(y_pos), int(content_width), int(line_height),
- Qt.AlignLeft, "Analysis Results")
- y_pos += line_height
-
- painter.setFont(normal_font)
- results = report_content['results']
-
- if results.get('locule_count') is not None:
- painter.drawText(int(margin), int(y_pos), int(content_width), int(line_height),
- Qt.AlignLeft, f"Locule Count: {results['locule_count']} locules")
- y_pos += line_height
-
- if results.get('defect_status'):
- painter.drawText(int(margin), int(y_pos), int(content_width), int(line_height),
- Qt.AlignLeft, f"Defect Status: {results['defect_status']} ({results.get('total_detections', 0)} detections)")
- y_pos += line_height
-
- if results.get('shape_class'):
- painter.drawText(int(margin), int(y_pos), int(content_width), int(line_height),
- Qt.AlignLeft, f"Shape: {results['shape_class']} ({results.get('shape_confidence', 0)*100:.1f}%)")
- y_pos += line_height
-
- if results.get('maturity_class'):
- painter.drawText(int(margin), int(y_pos), int(content_width), int(line_height),
- Qt.AlignLeft, f"Maturity: {results['maturity_class']} ({results.get('maturity_confidence', 0)*100:.1f}%)")
- y_pos += line_height
-
- if results.get('ripeness_class'):
- painter.drawText(int(margin), int(y_pos), int(content_width), int(line_height),
- Qt.AlignLeft, f"Ripeness: {results['ripeness_class']} ({results.get('ripeness_confidence', 0)*100:.1f}%)")
- y_pos += line_height
-
- # Grade
- painter.setFont(heading_font)
- y_pos += 10
- painter.drawText(int(margin), int(y_pos), int(content_width), int(line_height),
- Qt.AlignLeft, f"Overall Grade: Class {report_content['grade']}")
- y_pos += line_height
-
- painter.setFont(normal_font)
- painter.drawText(int(margin), int(y_pos), int(content_width), int(line_height * 2),
- Qt.AlignLeft | Qt.TextWordWrap, report_content['grade_description'])
- y_pos += line_height * 2 + 10
-
- # Visualizations
- if include_visualizations:
- painter.setFont(heading_font)
- painter.drawText(int(margin), int(y_pos), int(content_width), int(line_height),
- Qt.AlignLeft, "Visualizations")
- y_pos += line_height + 10
-
- return 1
|