from PySide6.QtCore import Qt
from PySide6.QtCore import Signal
from PySide6.QtWidgets import (QLabel, QVBoxLayout, QScrollArea, QWidget,
QSizePolicy, QPushButton, QGridLayout)
from tommy.controller.config_controller import ConfigController
from tommy.controller.corpus_controller import CorpusController
from tommy.controller.file_import.metadata import Metadata
from tommy.controller.topic_modelling_controller import \
TopicModellingController
from tommy.support.constant_variables import (
heading_font, prim_col_red,
hover_prim_col_red, scrollbar_style, title_label_font,
collapse_button_font)
from tommy.support.types import Document_topics
from tommy.view.imported_files_view.file_label import FileLabel
from tommy.view.topic_view.topic_entity_component.topic_entity import \
TopicEntity
[docs]
class ImportedFilesView(QWidget):
"""The ImportedFileDisplay class that shows the imported files."""
fileClicked = Signal(object)
[docs]
def __init__(self, corpus_controller: CorpusController,
topic_modelling_controller: TopicModellingController,
config_controller: ConfigController) -> None:
"""Initialize the ImportedFileDisplay"""
super().__init__()
# Set reference to the corpus controller and subscribe to the metadata
self._corpus_controller = corpus_controller
self.config_controller = config_controller
corpus_controller.metadata_changed_event.subscribe(
self.on_metadata_changed)
topic_modelling_controller.model_trained_event.subscribe(
lambda _: self.display_files())
topic_modelling_controller.calculate_topic_documents_event.subscribe(
self.on_document_topics_calculated)
config_controller.config_switched_event.subscribe(
lambda _: self.display_files())
# Initialize widget properties
self.setMinimumHeight(200)
self.setMaximumHeight(300)
self.setStyleSheet("background-color: rgba(230, 230, 230, 230);")
# Initialize layout for the entire widget
self.layout = QVBoxLayout(self)
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.setAlignment(Qt.AlignmentFlag.AlignTop)
self.layout.setSpacing(0)
# Initialize title label
self.title_widget = None
self.initialize_title_widget()
# Initialize scroll area and its layout
self.scroll_area = QScrollArea()
self.scroll_area.setWidgetResizable(True)
self.scroll_widget = QWidget()
self.scroll_layout = QVBoxLayout(self.scroll_widget)
self.scroll_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
self.scroll_area.setWidget(self.scroll_widget)
self.scroll_area.setStyleSheet(scrollbar_style)
self.layout.addWidget(self.scroll_area)
self.scroll_area.setVisible(True)
self.setSizePolicy(QSizePolicy.Policy.Expanding,
QSizePolicy.Policy.Expanding)
self.metadata: list[Metadata] = []
self.document_topics: Document_topics = []
self.selected_label = None
self.selected_file = None
# Add scroll options
self.scroll_area.setVerticalScrollBarPolicy(
Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
self.scroll_area.setHorizontalScrollBarPolicy(
Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.scroll_area.setWidgetResizable(True)
[docs]
def display_files(self) -> None:
"""
Display the metadata from files in the layout
:return: None
"""
# Clear the layout except for the title label
# Start from 1 to keep the title label
for i in reversed(range(0, self.scroll_layout.count())):
self.scroll_layout.itemAt(i).widget().deleteLater()
sorted_files = sorted(self.metadata, key=lambda x: x.name)
# Add the file labels to the layout
for file in sorted_files:
file_label = FileLabel(file, self.scroll_area)
file_label.clicked.connect(self.label_clicked)
self.scroll_layout.addWidget(file_label)
[docs]
def display_files_for_topic(self, topic: TopicEntity) -> None:
"""
Display the metadata from files in the layout
Sorted by topic document correspondence
:return: None
"""
index = topic.index
# Clear the layout except for the title label
# Start from 1 to keep the title label
for i in reversed(range(0, self.scroll_layout.count())):
self.scroll_layout.itemAt(i).widget().deleteLater()
sorted_files = sorted(self.document_topics, key=lambda x: x[1][index],
reverse=True)
# Add the file labels to the layout
for (metadata, topic_correspondence) in sorted_files:
file_label = FileLabel(metadata, self.scroll_area,
topic_correspondence=
topic_correspondence[index])
file_label.clicked.connect(self.label_clicked)
self.scroll_layout.addWidget(file_label)
[docs]
def deselect_all_files(self) -> None:
"""
Deselect all the files
:return: None
"""
for i in range(self.scroll_layout.count()):
file_label = self.scroll_layout.itemAt(i).widget()
file_label.deselect()
[docs]
def label_clicked(self, clicked_label: FileLabel) -> None:
"""
Handle the click event on a file label
:param clicked_label: The label that was clicked
:return: None
"""
# Deselect the previously selected label
self.deselect_all_files()
# Select the clicked label
if self.selected_label == clicked_label:
self.selected_label = None
clicked_label.enterEvent(None)
else:
self.selected_label = clicked_label
clicked_label.select()
# Display the file stats
self.fileClicked.emit(clicked_label)
[docs]
def toggle_collapse(self, clicked_header) -> None:
"""
Toggle visibility of the scroll area and adjust layout accordingly.
"""
self.collapse_component()
self.change_button_appearance()
[docs]
def collapse_component(self) -> None:
"""
Collapse the imported files display.
"""
if self.scroll_area.isVisible():
# Hide the scroll area
self.scroll_area.setVisible(False)
# Move the header to the bottom of the layout
self.layout.addStretch(0.1)
self.layout.addWidget(self.title_widget)
# Fix widget size to allow entire layout to be moved to
self.setFixedHeight(self.title_widget.height())
else:
# Show the scroll area
self.scroll_area.setVisible(True)
# Remove the stretch from the layout to move the header
# back to its original position
self.layout.removeWidget(self.title_widget)
self.layout.insertWidget(0, self.title_widget)
self.layout.removeItem(self.layout.itemAt(self.layout.count() - 1))
# Restore beginning height
self.setMinimumHeight(200)
self.setMaximumHeight(300)
[docs]
def on_document_topics_calculated(
self,
document_topics: Document_topics) -> None:
"""
Update stored topic document correspondence reference.
:param document_topics: The list of documents related to topics
:return: None
"""
self.document_topics = document_topics
[docs]
def on_topic_selected(self, topic: TopicEntity) -> None:
"""
Update the files tab including topic document correspondence.
:param topic: the current selected topic
:return: None
"""
if not self.document_topics:
print("No document topics correspondence was calculated, skipping"
"displaying files for topic.")
return
self.display_files_for_topic(topic)
"""
This program has been developed by students from the bachelor Computer Science
at Utrecht University within the Software Project course.
© Copyright Utrecht University
(Department of Information and Computing Sciences)
"""