Files
Inf_Tec_Auto/document_compare.py
2024-12-19 09:03:36 -08:00

287 lines
9.9 KiB
Python

from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
QLabel, QComboBox, QScrollArea, QSplitter,
QMessageBox)
from PySide6.QtCore import Qt
from PySide6.QtGui import QImage, QPixmap
import fitz
from database import get_session
from models import Document, DocumentVersion
import difflib
import os
from PIL import Image
import numpy as np
import cv2
class DocumentCompare(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.doc1 = None
self.doc2 = None
self.current_page = 0
self.setup_ui()
def setup_ui(self):
layout = QVBoxLayout(self)
# Document selection
select_layout = QHBoxLayout()
# First document
doc1_layout = QVBoxLayout()
doc1_layout.addWidget(QLabel("Documento 1:"))
self.doc1_combo = QComboBox()
self.doc1_combo.currentIndexChanged.connect(self.load_comparison)
doc1_layout.addWidget(self.doc1_combo)
select_layout.addLayout(doc1_layout)
# Second document
doc2_layout = QVBoxLayout()
doc2_layout.addWidget(QLabel("Documento 2:"))
self.doc2_combo = QComboBox()
self.doc2_combo.currentIndexChanged.connect(self.load_comparison)
doc2_layout.addWidget(self.doc2_combo)
select_layout.addLayout(doc2_layout)
layout.addLayout(select_layout)
# Navigation
nav_layout = QHBoxLayout()
self.prev_btn = QPushButton("Anterior")
self.prev_btn.clicked.connect(self.prev_page)
nav_layout.addWidget(self.prev_btn)
self.page_label = QLabel("Página 0 de 0")
nav_layout.addWidget(self.page_label)
self.next_btn = QPushButton("Próxima")
self.next_btn.clicked.connect(self.next_page)
nav_layout.addWidget(self.next_btn)
nav_layout.addStretch()
self.diff_mode_combo = QComboBox()
self.diff_mode_combo.addItems(["Visual", "Texto", "Híbrido"])
self.diff_mode_combo.currentTextChanged.connect(self.load_comparison)
nav_layout.addWidget(self.diff_mode_combo)
layout.addLayout(nav_layout)
# Comparison view
self.splitter = QSplitter(Qt.Horizontal)
# Left document
self.left_scroll = QScrollArea()
self.left_scroll.setWidgetResizable(True)
self.left_label = QLabel()
self.left_label.setAlignment(Qt.AlignCenter)
self.left_scroll.setWidget(self.left_label)
self.splitter.addWidget(self.left_scroll)
# Right document
self.right_scroll = QScrollArea()
self.right_scroll.setWidgetResizable(True)
self.right_label = QLabel()
self.right_label.setAlignment(Qt.AlignCenter)
self.right_scroll.setWidget(self.right_label)
self.splitter.addWidget(self.right_scroll)
# Difference view
self.diff_scroll = QScrollArea()
self.diff_scroll.setWidgetResizable(True)
self.diff_label = QLabel()
self.diff_label.setAlignment(Qt.AlignCenter)
self.diff_scroll.setWidget(self.diff_label)
self.splitter.addWidget(self.diff_scroll)
layout.addWidget(self.splitter)
# Difference summary
self.diff_summary = QLabel()
layout.addWidget(self.diff_summary)
self.refresh_documents()
def refresh_documents(self):
try:
session = get_session()
documents = session.query(Document).all()
# Update combo boxes
self.doc1_combo.clear()
self.doc2_combo.clear()
for doc in documents:
item_text = f"{doc.file_name} ({doc.marca} {doc.modelo})"
self.doc1_combo.addItem(item_text, doc.id)
self.doc2_combo.addItem(item_text, doc.id)
session.close()
except Exception as e:
QMessageBox.critical(self, "Erro",
f"Erro ao carregar documentos: {str(e)}")
def load_comparison(self):
if (self.doc1_combo.currentData() is None or
self.doc2_combo.currentData() is None):
return
try:
session = get_session()
# Load documents
doc1 = session.query(Document).get(self.doc1_combo.currentData())
doc2 = session.query(Document).get(self.doc2_combo.currentData())
if doc1 and doc2:
self.doc1 = fitz.open(doc1.file_path)
self.doc2 = fitz.open(doc2.file_path)
self.current_page = 0
self.update_comparison()
session.close()
except Exception as e:
QMessageBox.critical(self, "Erro",
f"Erro ao carregar documentos: {str(e)}")
def update_comparison(self):
if not self.doc1 or not self.doc2:
return
try:
# Update page navigation
max_pages = min(len(self.doc1), len(self.doc2))
self.page_label.setText(f"Página {self.current_page + 1} de {max_pages}")
# Get pages
page1 = self.doc1[self.current_page]
page2 = self.doc2[self.current_page]
# Process based on comparison mode
mode = self.diff_mode_combo.currentText()
if mode == "Visual":
self.compare_visual(page1, page2)
elif mode == "Texto":
self.compare_text(page1, page2)
else: # Hybrid
self.compare_hybrid(page1, page2)
except Exception as e:
QMessageBox.critical(self, "Erro",
f"Erro ao comparar páginas: {str(e)}")
def compare_visual(self, page1, page2):
# Convert pages to images
pix1 = page1.get_pixmap()
pix2 = page2.get_pixmap()
# Convert to numpy arrays
img1 = np.frombuffer(pix1.samples, dtype=np.uint8).reshape(
pix1.height, pix1.width, 3)
img2 = np.frombuffer(pix2.samples, dtype=np.uint8).reshape(
pix2.height, pix2.width, 3)
# Resize images to same size
height = max(img1.shape[0], img2.shape[0])
width = max(img1.shape[1], img2.shape[1])
img1 = cv2.resize(img1, (width, height))
img2 = cv2.resize(img2, (width, height))
# Calculate difference
diff = cv2.absdiff(img1, img2)
diff_highlighted = img2.copy()
diff_highlighted[diff.mean(axis=2) > 30] = [0, 0, 255] # Red for differences
# Convert to QPixmap and display
self.display_image(img1, self.left_label)
self.display_image(img2, self.right_label)
self.display_image(diff_highlighted, self.diff_label)
# Update summary
diff_percentage = (diff.mean(axis=2) > 30).mean() * 100
self.diff_summary.setText(
f"Diferença visual: {diff_percentage:.2f}% da página")
def compare_text(self, page1, page2):
# Extract text
text1 = page1.get_text()
text2 = page2.get_text()
# Compare text
diff = difflib.unified_diff(
text1.splitlines(),
text2.splitlines(),
lineterm=''
)
# Format differences
diff_html = []
for line in diff:
if line.startswith('+'):
diff_html.append(f'<span style="background-color: #aaffaa">{line}</span>')
elif line.startswith('-'):
diff_html.append(f'<span style="background-color: #ffaaaa">{line}</span>')
else:
diff_html.append(line)
# Display original texts and differences
self.left_label.setText(f"<pre>{text1}</pre>")
self.right_label.setText(f"<pre>{text2}</pre>")
self.diff_label.setText(f"<pre>{'<br>'.join(diff_html)}</pre>")
# Update summary
changes = len([l for l in diff_html if l.startswith('<span')])
self.diff_summary.setText(
f"Diferenças encontradas: {changes} linhas modificadas")
def compare_hybrid(self, page1, page2):
# Combine visual and text comparison
self.compare_visual(page1, page2)
# Add text comparison summary
text1 = page1.get_text()
text2 = page2.get_text()
# Calculate text similarity
similarity = difflib.SequenceMatcher(None, text1, text2).ratio()
# Update summary
current_summary = self.diff_summary.text()
self.diff_summary.setText(
f"{current_summary}\nSimilaridade do texto: {similarity:.2f}%")
def display_image(self, img, label):
height, width = img.shape[:2]
bytes_per_line = 3 * width
q_img = QImage(img.data, width, height, bytes_per_line, QImage.Format_RGB888)
pixmap = QPixmap.fromImage(q_img)
# Scale to fit while maintaining aspect ratio
scaled_pixmap = pixmap.scaled(
label.size(),
Qt.KeepAspectRatio,
Qt.SmoothTransformation
)
label.setPixmap(scaled_pixmap)
def prev_page(self):
if self.current_page > 0:
self.current_page -= 1
self.update_comparison()
def next_page(self):
if (self.doc1 and self.doc2 and
self.current_page < min(len(self.doc1), len(self.doc2)) - 1):
self.current_page += 1
self.update_comparison()
def closeEvent(self, event):
if self.doc1:
self.doc1.close()
if self.doc2:
self.doc2.close()
super().closeEvent(event)