Carregar ficheiros para "/"

This commit is contained in:
2024-12-19 09:03:36 -08:00
parent 30b1c5b775
commit 39dbb95672
5 changed files with 1102 additions and 0 deletions

286
document_compare.py Normal file
View File

@ -0,0 +1,286 @@
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)

228
document_form.py Normal file
View File

@ -0,0 +1,228 @@
from PySide6.QtWidgets import (QWidget, QFormLayout, QLineEdit, QComboBox,
QTextEdit, QPushButton, QFileDialog, QSpinBox,
QVBoxLayout, QLabel, QMessageBox)
from database import get_session
from models import Document, Folder
import os
import shutil
from datetime import datetime
from preferences import Preferences
class DocumentForm(QWidget):
def __init__(self, folder_id=None, parent=None):
super().__init__(parent)
self.folder_id = folder_id
self.selected_file = None
self.preferences = Preferences()
self.setup_ui()
# Create documents directory if it doesn't exist
self.docs_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "documentos")
if not os.path.exists(self.docs_dir):
os.makedirs(self.docs_dir)
def setup_ui(self):
layout = QVBoxLayout(self)
form_layout = QFormLayout()
# File selection
self.file_button = QPushButton("Selecionar Arquivo")
self.file_button.clicked.connect(self.select_file)
self.file_label = QLabel("Nenhum arquivo selecionado")
form_layout.addRow("Arquivo:", self.file_button)
form_layout.addRow("", self.file_label)
# Required fields
self.marca_edit = QLineEdit()
self.modelo_edit = QLineEdit()
self.ano_spin = QSpinBox()
self.ano_spin.setRange(1900, 2100)
self.ano_spin.setValue(2024)
form_layout.addRow("Marca*:", self.marca_edit)
form_layout.addRow("Modelo*:", self.modelo_edit)
form_layout.addRow("Ano*:", self.ano_spin)
# Optional fields
self.cilindrada_edit = QLineEdit()
self.codigo_motor_edit = QLineEdit()
form_layout.addRow("Cilindrada:", self.cilindrada_edit)
form_layout.addRow("Código Motor:", self.codigo_motor_edit)
# Document type
self.tipo_combo = QComboBox()
self.tipo_combo.addItems(["Elétrico", "Mecânico"])
form_layout.addRow("Tipo de Documento*:", self.tipo_combo)
# Variant
self.variante_combo = QComboBox()
self.variante_combo.addItems([
"Esquema", "Esquema OEM", "TSB",
"Esquemas Varios", "Documentação de Instruções"
])
form_layout.addRow("Variante*:", self.variante_combo)
# Observations
self.observacoes_edit = QTextEdit()
form_layout.addRow("Observações:", self.observacoes_edit)
layout.addLayout(form_layout)
# Save button
self.save_button = QPushButton("Salvar")
self.save_button.clicked.connect(self.save_document)
layout.addWidget(self.save_button)
def get_save_location(self, filename):
"""Prompt user for save location"""
base_path = self.preferences.get_save_path()
print(f"Base save path from preferences: {base_path}") # Debug print
if not base_path:
QMessageBox.warning(self, "Erro", "Por favor, configure o diretório de salvamento nas preferências primeiro.")
return None
# Get current folder path if folder_id is set
if self.folder_id:
print(f"Getting path for folder ID: {self.folder_id}") # Debug print
session = get_session()
folder = session.query(Folder).get(self.folder_id)
if folder:
print(f"Found folder: {folder.name}") # Debug print
current_folder = folder
path_parts = []
while current_folder:
path_parts.insert(0, current_folder.name)
current_folder = current_folder.parent
suggested_path = os.path.join(base_path, *path_parts, filename)
print(f"Suggested save path: {suggested_path}") # Debug print
else:
print(f"No folder found for ID: {self.folder_id}") # Debug print
suggested_path = os.path.join(base_path, filename)
session.close()
else:
print("No folder ID set") # Debug print
suggested_path = os.path.join(base_path, filename)
save_path, _ = QFileDialog.getSaveFileName(
self,
"Salvar Documento",
suggested_path,
"Todos os arquivos (*.*)"
)
print(f"User selected save path: {save_path}") # Debug print
return save_path
def select_file(self):
file_path, _ = QFileDialog.getOpenFileName(
self,
"Selecionar Documento",
"",
"Todos os arquivos (*.*);;Documentos PDF (*.pdf);;Imagens (*.png *.jpg *.jpeg)"
)
if file_path:
self.selected_file = file_path
self.file_label.setText(os.path.basename(file_path))
def save_document(self):
if not self.selected_file:
QMessageBox.warning(self, "Erro", "Por favor, selecione um arquivo primeiro.")
return
if not self.validate_form():
return
print(f"Starting document save process...") # Debug print
print(f"Current folder ID: {self.folder_id}") # Debug print
print(f"Selected file: {self.selected_file}") # Debug print
# Get save location from user
filename = os.path.basename(self.selected_file)
save_path = self.get_save_location(filename)
if not save_path:
print("Save cancelled - no path selected") # Debug print
return
try:
# Create directory if it doesn't exist
save_dir = os.path.dirname(save_path)
print(f"Creating directory: {save_dir}") # Debug print
os.makedirs(save_dir, exist_ok=True)
# Copy file to new location
print(f"Copying file from {self.selected_file} to {save_path}") # Debug print
shutil.copy2(self.selected_file, save_path)
# Create document record
session = get_session()
# Double check folder exists
if self.folder_id:
folder = session.query(Folder).get(self.folder_id)
print(f"Verified folder: {folder.name if folder else 'Not Found'}") # Debug print
document = Document(
file_path=save_path,
file_name=os.path.basename(save_path),
marca=self.marca_edit.text(),
modelo=self.modelo_edit.text(),
ano=self.ano_spin.value(),
cilindrada=self.cilindrada_edit.text(),
codigo_motor=self.codigo_motor_edit.text(),
tipo_documento=self.tipo_combo.currentText(),
variante=self.variante_combo.currentText(),
observacoes=self.observacoes_edit.toPlainText(),
folder_id=self.folder_id
)
print(f"Created document object with attributes:") # Debug print
print(f" file_path: {document.file_path}")
print(f" file_name: {document.file_name}")
print(f" folder_id: {document.folder_id}")
session.add(document)
session.commit()
print(f"Document saved to database with ID: {document.id}") # Debug print
session.close()
QMessageBox.information(self, "Sucesso", "Documento salvo com sucesso!")
self.clear_form()
except Exception as e:
print(f"Error saving document: {str(e)}") # Debug print
print(f"Error type: {type(e)}") # Debug print
import traceback
print(f"Stack trace: {traceback.format_exc()}") # Debug print
QMessageBox.critical(self, "Erro", f"Erro ao salvar documento: {str(e)}")
def validate_form(self):
if not self.selected_file:
QMessageBox.warning(self, "Validação",
"Por favor, selecione um arquivo.")
return False
if not self.marca_edit.text():
QMessageBox.warning(self, "Validação",
"Por favor, preencha a marca.")
return False
if not self.modelo_edit.text():
QMessageBox.warning(self, "Validação",
"Por favor, preencha o modelo.")
return False
return True
def clear_form(self):
self.selected_file = None
self.file_label.setText("Nenhum arquivo selecionado")
self.marca_edit.clear()
self.modelo_edit.clear()
self.ano_spin.setValue(2024)
self.cilindrada_edit.clear()
self.codigo_motor_edit.clear()
self.tipo_combo.setCurrentIndex(0)
self.variante_combo.setCurrentIndex(0)
self.observacoes_edit.clear()

248
document_list.py Normal file
View File

@ -0,0 +1,248 @@
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QTableWidget,
QTableWidgetItem, QHeaderView, QMenu,
QMessageBox, QPushButton, QHBoxLayout)
from PySide6.QtCore import Qt, Signal
from database import get_session
from models import Document, Folder
import os
from preferences import Preferences
class DocumentListView(QWidget):
document_selected = Signal(int) # Signal emitted when document is selected
def __init__(self, parent=None):
super().__init__(parent)
self.current_folder_id = None
self.setup_ui()
def setup_ui(self):
layout = QVBoxLayout(self)
# Toolbar
toolbar = QHBoxLayout()
self.refresh_btn = QPushButton("Atualizar")
self.refresh_btn.clicked.connect(self.refresh_documents)
toolbar.addWidget(self.refresh_btn)
self.delete_btn = QPushButton("Excluir Selecionados")
self.delete_btn.clicked.connect(self.delete_selected)
toolbar.addWidget(self.delete_btn)
layout.addLayout(toolbar)
# Documents table
self.docs_table = QTableWidget()
self.docs_table.setColumnCount(7)
self.docs_table.setHorizontalHeaderLabels([
"Marca", "Modelo", "Ano", "Tipo", "Variante",
"Nome do Arquivo", "Observações"
])
header = self.docs_table.horizontalHeader()
header.setSectionResizeMode(QHeaderView.ResizeToContents)
header.setSectionResizeMode(6, QHeaderView.Stretch) # Observações column stretches
self.docs_table.setSortingEnabled(True)
self.docs_table.setSelectionBehavior(QTableWidget.SelectRows)
self.docs_table.setSelectionMode(QTableWidget.SingleSelection)
self.docs_table.setContextMenuPolicy(Qt.CustomContextMenu)
self.docs_table.customContextMenuRequested.connect(self.show_context_menu)
self.docs_table.doubleClicked.connect(self.open_document)
layout.addWidget(self.docs_table)
def set_folder(self, folder_id):
self.current_folder_id = folder_id
self.refresh_documents()
def refresh_documents(self):
session = get_session()
try:
# First, let's check all documents and fix their folder associations
all_documents = session.query(Document).all()
for doc in all_documents:
# Check if the document exists
if os.path.exists(doc.file_path):
# Always try to find the most specific folder
folders = session.query(Folder).all()
best_folder = self.find_best_matching_folder(doc.file_path, folders)
if best_folder:
if doc.folder_id != best_folder.id:
doc.folder_id = best_folder.id
session.add(doc)
else:
session.delete(doc)
# Commit any changes made
session.commit()
# Now get documents for the current folder
if self.current_folder_id is not None:
documents = session.query(Document).filter(
Document.folder_id == self.current_folder_id
).all()
else:
documents = session.query(Document).all()
# Update the table
self.docs_table.setRowCount(len(documents))
for row, doc in enumerate(documents):
self.docs_table.setItem(row, 0, QTableWidgetItem(doc.marca))
self.docs_table.setItem(row, 1, QTableWidgetItem(doc.modelo))
self.docs_table.setItem(row, 2, QTableWidgetItem(str(doc.ano)))
self.docs_table.setItem(row, 3, QTableWidgetItem(doc.tipo_documento))
self.docs_table.setItem(row, 4, QTableWidgetItem(doc.variante))
self.docs_table.setItem(row, 5, QTableWidgetItem(doc.file_name))
self.docs_table.setItem(row, 6, QTableWidgetItem(doc.observacoes))
# Store document ID in the first column
self.docs_table.item(row, 0).setData(Qt.UserRole, doc.id)
except Exception as e:
QMessageBox.critical(self, "Erro", f"Erro ao atualizar documentos: {str(e)}")
finally:
session.close()
def get_folder_path(self, folder):
"""Get the full path for a folder"""
try:
path_parts = []
current = folder
while current:
path_parts.insert(0, current.name)
current = current.parent
# Get base path from preferences
preferences = Preferences()
base_path = preferences.get_save_path()
if base_path:
# Make sure we're using forward slashes and the correct base path
# Only add 'Root' if it's not already in the path
if path_parts and path_parts[0] != "Root":
path_parts.insert(0, "Root")
full_path = os.path.join(base_path, *path_parts)
# Convert to forward slashes for consistency
return full_path.replace("\\", "/")
except Exception as e:
print(f"Error getting folder path: {str(e)}")
return None
def find_best_matching_folder(self, doc_path, folders):
"""Find the most specific matching folder for a document path"""
doc_path = doc_path.replace("\\", "/")
best_match = None
longest_match = 0
for folder in folders:
folder_path = self.get_folder_path(folder)
if folder_path:
folder_path = folder_path.replace("\\", "/")
# Make sure paths end with slash for exact directory matching
folder_path = folder_path.rstrip("/") + "/"
doc_dir = os.path.dirname(doc_path).rstrip("/") + "/"
if doc_dir.startswith(folder_path):
match_length = len(folder_path)
if match_length > longest_match:
longest_match = match_length
best_match = folder
return best_match
def show_context_menu(self, position):
menu = QMenu()
open_action = menu.addAction("Abrir Documento")
open_folder_action = menu.addAction("Abrir Pasta")
menu.addSeparator()
delete_action = menu.addAction("Excluir")
action = menu.exec_(self.docs_table.mapToGlobal(position))
current_row = self.docs_table.currentRow()
if current_row >= 0:
doc_id = self.docs_table.item(current_row, 0).data(Qt.UserRole)
if action == open_action:
self.open_document()
elif action == open_folder_action:
self.open_document_folder(doc_id)
elif action == delete_action:
self.delete_document(doc_id)
def open_document(self, index=None):
"""Open the selected document"""
if index:
row = index.row()
else:
row = self.docs_table.currentRow()
if row >= 0:
doc_id = self.docs_table.item(row, 0).data(Qt.UserRole)
session = get_session()
try:
doc = session.query(Document).get(doc_id)
if doc and os.path.exists(doc.file_path):
# Use the default system application to open the file
os.startfile(doc.file_path)
else:
QMessageBox.warning(self, "Erro", "Arquivo não encontrado.")
except Exception as e:
QMessageBox.critical(self, "Erro", f"Erro ao abrir documento: {str(e)}")
finally:
session.close()
def open_document_folder(self, doc_id):
session = get_session()
document = session.query(Document).get(doc_id)
if document and os.path.exists(document.file_path):
folder_path = os.path.dirname(document.file_path)
os.startfile(folder_path)
else:
QMessageBox.warning(self, "Erro",
"Não foi possível abrir a pasta do documento.")
session.close()
def delete_document(self, doc_id):
reply = QMessageBox.question(
self,
"Confirmar Exclusão",
"Tem certeza que deseja excluir este documento?",
QMessageBox.Yes | QMessageBox.No
)
if reply == QMessageBox.Yes:
session = get_session()
document = session.query(Document).get(doc_id)
if document:
session.delete(document)
session.commit()
session.close()
self.refresh_documents()
def delete_selected(self):
selected_rows = set(item.row() for item in self.docs_table.selectedItems())
if not selected_rows:
return
reply = QMessageBox.question(
self,
"Confirmar Exclusão",
f"Tem certeza que deseja excluir {len(selected_rows)} documento(s)?",
QMessageBox.Yes | QMessageBox.No
)
if reply == QMessageBox.Yes:
session = get_session()
for row in selected_rows:
doc_id = self.docs_table.item(row, 0).data(Qt.UserRole)
document = session.query(Document).get(doc_id)
if document:
session.delete(document)
session.commit()
session.close()
self.refresh_documents()

164
document_viewer.py Normal file
View File

@ -0,0 +1,164 @@
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
QLabel, QScrollArea, QSplitter, QTabWidget)
from PySide6.QtCore import Qt, Signal
from PySide6.QtGui import QImage, QPixmap
import fitz
from database import get_session
from models import Document
from comments import CommentsList
from preferences import Preferences
class DocumentViewer(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.current_document = None
self.current_page = 0
self.preferences = Preferences()
self.setup_ui()
def setup_ui(self):
layout = QVBoxLayout(self)
# Toolbar
toolbar = QHBoxLayout()
self.prev_btn = QPushButton("Anterior")
self.prev_btn.clicked.connect(self.prev_page)
toolbar.addWidget(self.prev_btn)
self.page_label = QLabel("Página 0 de 0")
toolbar.addWidget(self.page_label)
self.next_btn = QPushButton("Próxima")
self.next_btn.clicked.connect(self.next_page)
toolbar.addWidget(self.next_btn)
toolbar.addStretch()
self.zoom_in_btn = QPushButton("Zoom +")
self.zoom_in_btn.clicked.connect(self.zoom_in)
toolbar.addWidget(self.zoom_in_btn)
self.zoom_out_btn = QPushButton("Zoom -")
self.zoom_out_btn.clicked.connect(self.zoom_out)
toolbar.addWidget(self.zoom_out_btn)
layout.addLayout(toolbar)
# Main content area
splitter = QSplitter(Qt.Horizontal)
# Document view
self.scroll_area = QScrollArea()
self.scroll_area.setWidgetResizable(True)
self.document_label = QLabel()
self.document_label.setAlignment(Qt.AlignCenter)
self.scroll_area.setWidget(self.document_label)
splitter.addWidget(self.scroll_area)
# Side panel with tabs
side_panel = QTabWidget()
# Comments tab
self.comments_list = CommentsList()
side_panel.addTab(self.comments_list, "Comentários")
splitter.addWidget(side_panel)
# Set initial splitter sizes
splitter.setSizes([int(self.width() * 0.7), int(self.width() * 0.3)])
layout.addWidget(splitter)
self.zoom_level = 1.0
self.doc = None
self.update_navigation()
def load_document(self, doc_id):
try:
session = get_session()
document = session.query(Document).get(doc_id)
if document and document.file_path:
self.current_document = doc_id
self.doc = fitz.open(document.file_path)
self.current_page = 0
self.zoom_level = 1.0
self.update_page()
self.update_navigation()
# Load comments
self.comments_list.load_document(doc_id)
# Add to recent documents if auto_preview is enabled
if self.preferences.get("auto_preview"):
from preferences import RecentDocuments
recent = RecentDocuments(self.preferences)
recent.add_document(doc_id, document.file_name)
session.close()
except Exception as e:
print(f"Error loading document: {str(e)}")
def update_page(self):
if not self.doc:
return
try:
page = self.doc[self.current_page]
zoom = self.zoom_level
# Adjust zoom based on preview size preference
preview_size = self.preferences.get("preview_size")
if preview_size == "small":
zoom *= 0.8
elif preview_size == "large":
zoom *= 1.2
mat = fitz.Matrix(zoom, zoom)
pix = page.get_pixmap(matrix=mat)
img = QImage(pix.samples, pix.width, pix.height,
pix.stride, QImage.Format_RGB888)
pixmap = QPixmap.fromImage(img)
self.document_label.setPixmap(pixmap)
self.page_label.setText(f"Página {self.current_page + 1} de {len(self.doc)}")
except Exception as e:
print(f"Error updating page: {str(e)}")
def update_navigation(self):
has_doc = self.doc is not None
has_prev = has_doc and self.current_page > 0
has_next = has_doc and self.current_page < len(self.doc) - 1
self.prev_btn.setEnabled(has_prev)
self.next_btn.setEnabled(has_next)
self.zoom_in_btn.setEnabled(has_doc)
self.zoom_out_btn.setEnabled(has_doc)
def prev_page(self):
if self.doc and self.current_page > 0:
self.current_page -= 1
self.update_page()
self.update_navigation()
def next_page(self):
if self.doc and self.current_page < len(self.doc) - 1:
self.current_page += 1
self.update_page()
self.update_navigation()
def zoom_in(self):
self.zoom_level *= 1.2
self.update_page()
def zoom_out(self):
self.zoom_level /= 1.2
self.update_page()
def closeEvent(self, event):
if self.doc:
self.doc.close()
super().closeEvent(event)

176
folder_view.py Normal file
View File

@ -0,0 +1,176 @@
from PySide6.QtWidgets import (QTreeView, QFileDialog, QMenu, QInputDialog,
QMessageBox)
from PySide6.QtCore import Qt, QModelIndex, Signal
from PySide6.QtGui import QStandardItemModel, QStandardItem
from database import get_session
from models import Folder
import os
from preferences import Preferences
class FolderTreeView(QTreeView):
folder_selected = Signal(int) # Signal for folder selection
def __init__(self, parent=None):
super().__init__(parent)
self.model = QStandardItemModel()
self.model.setHorizontalHeaderLabels(['Pastas'])
self.setModel(self.model)
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self.show_context_menu)
self.preferences = Preferences()
self.load_folders()
# Connect selection changed signal
self.selectionModel().selectionChanged.connect(self.on_folder_selected)
def load_folders(self):
self.model.clear()
self.model.setHorizontalHeaderLabels(['Pastas'])
session = get_session()
root_folders = session.query(Folder).filter(Folder.parent_id == None).all()
for folder in root_folders:
item = QStandardItem(folder.name)
item.setData(folder.id)
self.model.appendRow(item)
self.load_subfolders(folder, item)
session.close()
def load_subfolders(self, folder, parent_item):
session = get_session()
subfolders = session.query(Folder).filter(Folder.parent_id == folder.id).all()
for subfolder in subfolders:
item = QStandardItem(subfolder.name)
item.setData(subfolder.id)
parent_item.appendRow(item)
self.load_subfolders(subfolder, item)
session.close()
def get_physical_path(self, folder):
"""Get the physical path for a folder"""
base_path = self.preferences.get_save_path()
if not base_path:
QMessageBox.warning(self, "Erro", "Por favor, configure o diretório de salvamento nas preferências primeiro.")
return None
path_parts = []
current = folder
while current:
path_parts.insert(0, current.name)
current = current.parent
# Make sure we're using forward slashes and the correct base path
# Only add 'Root' if it's not already in the path
if path_parts and path_parts[0] != "Root":
path_parts.insert(0, "Root")
full_path = os.path.join(base_path, *path_parts)
# Convert to forward slashes for consistency
return full_path.replace("\\", "/")
def create_physical_folder(self, folder):
"""Create the physical folder on disk"""
physical_path = self.get_physical_path(folder)
if physical_path and not os.path.exists(physical_path):
try:
os.makedirs(physical_path)
return True
except Exception as e:
QMessageBox.warning(self, "Erro", f"Erro ao criar pasta física: {str(e)}")
return False
return True
def show_context_menu(self, position):
menu = QMenu()
add_action = menu.addAction("Nova Pasta")
delete_action = menu.addAction("Excluir Pasta")
rename_action = menu.addAction("Renomear")
action = menu.exec_(self.mapToGlobal(position))
if action == add_action:
self.add_folder()
elif action == delete_action:
self.delete_folder()
elif action == rename_action:
self.rename_folder()
def on_folder_selected(self, selected, deselected):
indexes = selected.indexes()
if indexes:
current_item = self.model.itemFromIndex(indexes[0])
folder_id = current_item.data()
self.folder_selected.emit(folder_id)
def add_folder(self):
name, ok = QInputDialog.getText(self, "Nova Pasta", "Nome da pasta:")
if ok and name:
session = get_session()
current_index = self.currentIndex()
new_folder = Folder(name=name)
if current_index.isValid():
parent_id = self.model.itemFromIndex(current_index).data()
parent_folder = session.query(Folder).get(parent_id)
new_folder.parent_id = parent_id
new_folder.parent = parent_folder
session.add(new_folder)
session.commit()
# Create physical folder
if not self.create_physical_folder(new_folder):
session.delete(new_folder)
session.commit()
session.close()
self.load_folders()
def delete_folder(self):
current_index = self.currentIndex()
if not current_index.isValid():
return
reply = QMessageBox.question(self, "Confirmar Exclusão",
"Tem certeza que deseja excluir esta pasta?\nIsso também excluirá a pasta física do disco.",
QMessageBox.Yes | QMessageBox.No)
if reply == QMessageBox.Yes:
session = get_session()
folder_id = self.model.itemFromIndex(current_index).data()
folder = session.query(Folder).get(folder_id)
if folder:
# Delete physical folder
physical_path = self.get_physical_path(folder)
if physical_path and os.path.exists(physical_path):
try:
os.rmdir(physical_path) # Only removes if empty
except Exception as e:
QMessageBox.warning(self, "Aviso", f"Não foi possível excluir a pasta física: {str(e)}")
session.delete(folder)
session.commit()
session.close()
self.load_folders()
def rename_folder(self):
current_index = self.currentIndex()
if not current_index.isValid():
return
current_name = self.model.itemFromIndex(current_index).text()
name, ok = QInputDialog.getText(self, "Renomear Pasta",
"Novo nome:", text=current_name)
if ok and name:
session = get_session()
folder_id = self.model.itemFromIndex(current_index).data()
folder = session.query(Folder).get(folder_id)
if folder:
folder.name = name
session.commit()
session.close()
self.load_folders()