from PyQt5.QtWidgets import QWidget, QVBoxLayout, QCheckBox, QToolTip, QTreeWidget, QTreeWidgetItem, QHBoxLayout, QLabel, QScrollArea from PyQt5.QtCore import Qt from PyQt5.QtGui import QColor import logging from logline_leviathan.database.database_manager import EntitiesTable, DistinctEntitiesTable, EntityTypesTable, FileMetadata, session_scope class CustomCheckBox(QCheckBox): def __init__(self, *args, **kwargs): super(CustomCheckBox, self).__init__(*args, **kwargs) self.setMouseTracking(True) # Enable mouse tracking self.setStyleSheet("QCheckBox { color: white; }") def mouseMoveEvent(self, event): QToolTip.showText(event.globalPos(), self.toolTip()) # Show tooltip at mouse position super(CustomCheckBox, self).mouseMoveEvent(event) class FileCheckboxItem(QWidget): def __init__(self, text, parent=None): super(FileCheckboxItem, self).__init__(parent) layout = QHBoxLayout(self) self.checkBox = QCheckBox() self.checkBox.setChecked(True) self.label = QLabel(text) self.label.setStyleSheet("QLabel { color: white; }") # Set text color layout.addWidget(self.checkBox) layout.addWidget(self.label) layout.addStretch(1) # Add stretch factor to push content to the left self.setLayout(layout) class CheckboxPanel(QWidget): def __init__(self): super().__init__() layout = QVBoxLayout(self) self.treeWidget = QTreeWidget() self.treeWidget.setHeaderHidden(True) self.treeWidget.setStyleSheet(""" QTreeWidget::branch {color: white; /* White color for branches */ } """) layout.addWidget(self.treeWidget) def _addChildren(self, parentItem, parent_entity_type, db_session, used_ids, depth=0): try: # Log the depth of recursion #logging.debug(f"Adding children at depth: {depth}, parent entity type: {parent_entity_type}") child_entity_types = db_session.query(EntityTypesTable).filter(EntityTypesTable.parent_type == parent_entity_type).all() for child_entity_type in child_entity_types: count = db_session.query(EntitiesTable).filter(EntitiesTable.entity_types_id == child_entity_type.entity_type_id).count() text = f"{child_entity_type.gui_name}" childItem = QTreeWidgetItem(parentItem) isCheckable = not child_entity_type.entity_type.startswith("category_") childItem.setFlags(childItem.flags() | Qt.ItemIsUserCheckable) if isCheckable else None childItem.setCheckState(0, Qt.Unchecked) if isCheckable else None text += f" ({count} Erwähnungen)" if isCheckable else "" childItem.setText(0, text) childItem.setToolTip(0, child_entity_type.gui_tooltip) childItem.entity_type_id = child_entity_type.entity_type_id childItem.entity_type = child_entity_type.entity_type if child_entity_type.entity_type_id in used_ids and not child_entity_type.parser_enabled == False: color = QColor('green') elif not child_entity_type.entity_type.startswith("category_") and not child_entity_type.parser_enabled == True: color = QColor('red') else: color = QColor('white') childItem.setForeground(0, color) # Recursive call with increased depth depth = depth + 1 self._addChildren(childItem, child_entity_type.entity_type, db_session, used_ids, depth) except Exception as e: logging.error(f"Error adding children: {e}") def updateCheckboxes(self, db_session): #logging.info("Updating checkboxes with database content") with session_scope() as db_session: try: # Query database for entity types entity_types = db_session.query(EntityTypesTable).all() used_ids = {d.entity_types_id for d in db_session.query(DistinctEntitiesTable.entity_types_id).distinct()} #logging.debug(f"Used IDs: {used_ids}") # Clear existing items self.treeWidget.clear() rootItems = {} # Construct hierarchical tree structure for entity_type in entity_types: if entity_type.parent_type != 'root': # Skip non-root items continue count = db_session.query(EntitiesTable).filter(EntitiesTable.entity_types_id == entity_type.entity_type_id).count() text = f"{entity_type.gui_name}" treeItem = QTreeWidgetItem() treeItem.setToolTip(0, entity_type.gui_tooltip) treeItem.entity_type_id = entity_type.entity_type_id treeItem.entity_type = entity_type.entity_type if not entity_type.entity_type.startswith("category_"): treeItem.setFlags(treeItem.flags() | Qt.ItemIsUserCheckable) treeItem.setCheckState(0, Qt.Unchecked) text = f"{entity_type.gui_name} ({count} Erwähnungen)" treeItem.setText(0, text) # Add item to tree widget self.treeWidget.addTopLevelItem(treeItem) rootItems[entity_type.entity_type_id] = treeItem # Call recursive function to add children self._addChildren(treeItem, entity_type.entity_type, db_session, used_ids) # Optionally expand all tree items self.treeWidget.expandAll() except Exception as e: logging.error("Error updating checkboxes", exc_info=True) def filterCheckboxes(self, filter_text): def filterTreeItem(treeItem): # Check if the current item or any of its properties match the filter text try: match = filter_text.lower() in treeItem.text(0).lower() or filter_text.lower() in treeItem.toolTip(0).lower() except Exception as e: logging.error(f"Error checking filter match for tree item: {e}") match = False # Recursively check child items and set 'childMatch' if any child matches childMatch = False for j in range(treeItem.childCount()): if filterTreeItem(treeItem.child(j)): childMatch = True # Unhide the item and its parents if there's a match in the item or its children if match or childMatch: treeItem.setHidden(False) parent = treeItem.parent() while parent: parent.setHidden(False) parent = parent.parent() return True else: treeItem.setHidden(True) return False # Filter all top-level items for i in range(self.treeWidget.topLevelItemCount()): filterTreeItem(self.treeWidget.topLevelItem(i)) def checkAllVisible(self): with session_scope() as db_session: used_ids = self.getUsedIds(db_session) self._setCheckStateForVisibleItems(Qt.Checked, used_ids) def uncheckAllVisible(self): with session_scope() as db_session: used_ids = self.getUsedIds(db_session) self._setCheckStateForVisibleItems(Qt.Unchecked, used_ids) def _setCheckStateForVisibleItems(self, state, used_ids): def setCheckState(item): try: if (item.flags() & Qt.ItemIsUserCheckable) and not item.isHidden(): # and item.parent(): # Check if entity_type_id is in used_ids if hasattr(item, 'entity_type_id') and item.entity_type_id in used_ids: item.setCheckState(0, state) #logging.debug(f"Set check state for item with entity_type_id: {item.entity_type_id}") #else: #logging.debug(f"Item with entity_type_id: {getattr(item, 'entity_type_id', 'N/A')} skipped") for i in range(item.childCount()): childItem = item.child(i) setCheckState(childItem) except Exception as e: logging.error(f"Error in setCheckState: {e}") try: for i in range(self.treeWidget.topLevelItemCount()): topItem = self.treeWidget.topLevelItem(i) setCheckState(topItem) except Exception as e: logging.error(f"Error in _setCheckStateForVisibleItems: {e}") def getUsedIds(self, db_session): # Assuming db_session is your database session object try: used_ids = {d.entity_types_id for d in db_session.query(DistinctEntitiesTable.entity_types_id).distinct()} return used_ids except Exception as e: logging.error(f"Error in getUsedIds: {e}") return set() def expandAllTreeItems(self): for i in range(self.treeWidget.topLevelItemCount()): self._expandCollapseRecursive(self.treeWidget.topLevelItem(i), True) def collapseAllTreeItems(self): for i in range(self.treeWidget.topLevelItemCount()): self._expandCollapseRecursive(self.treeWidget.topLevelItem(i), False) def _expandCollapseRecursive(self, treeItem, expand=True): if treeItem is not None: treeItem.setExpanded(expand) for j in range(treeItem.childCount()): self._expandCollapseRecursive(treeItem.child(j), expand) class DatabasePanel(QWidget): def __init__(self): super().__init__() layout = QVBoxLayout(self) self.treeWidget = QTreeWidget() self.treeWidget.setHeaderHidden(True) # Hide the header self.treeWidget.setStyleSheet("""QTreeWidget::branch {color: white; /* White color for branches */}""") layout.addWidget(self.treeWidget) def _getTotalCountForChildren(self, entity_type, db_session): # Recursive function to get total count total_count = db_session.query(EntitiesTable).filter(EntitiesTable.entity_types_id == entity_type.entity_type_id).count() child_entity_types = db_session.query(EntityTypesTable).filter(EntityTypesTable.parent_type == entity_type.entity_type).all() for child_entity_type in child_entity_types: total_count += self._getTotalCountForChildren(child_entity_type, db_session) return total_count def _addChildren(self, parentItem, parent_entity_type, db_session, used_ids, depth=0): try: # Log the depth of recursion #logging.debug(f"Adding children at depth: {depth}, parent entity type: {parent_entity_type}") child_entity_types = db_session.query(EntityTypesTable).filter(EntityTypesTable.parent_type == parent_entity_type).all() for child_entity_type in child_entity_types: if not child_entity_type.entity_type.startswith("category_"): count = db_session.query(EntitiesTable).filter(EntitiesTable.entity_types_id == child_entity_type.entity_type_id).count() text = f" {count} - {child_entity_type.gui_name} ({child_entity_type.entity_type})" else: # Use the new method to get the total count for this category total_count = self._getTotalCountForChildren(child_entity_type, db_session) text = f" {total_count} - {child_entity_type.gui_name} (Total)" childItem = QTreeWidgetItem(parentItem) childItem.setText(0, text) childItem.setToolTip(0, child_entity_type.gui_tooltip) childItem.entity_type_id = child_entity_type.entity_type_id childItem.entity_type = child_entity_type.entity_type if child_entity_type.entity_type_id in used_ids and child_entity_type.parser_enabled: color = QColor('green') elif not child_entity_type.entity_type.startswith("category_") and not child_entity_type.parser_enabled: color = QColor('red') else: color = QColor('white') childItem.setForeground(0, color) # Recursive call with increased depth depth = depth + 1 self._addChildren(childItem, child_entity_type.entity_type, db_session, used_ids, depth) except Exception as e: logging.error(f"Error in _addChildren: {e}") def updateTree(self, db_session): #logging.info("Updating checkboxes with database content") with session_scope() as db_session: try: # Query database for entity types entity_types = db_session.query(EntityTypesTable).all() used_ids = {d.entity_types_id for d in db_session.query(DistinctEntitiesTable.entity_types_id).distinct()} #logging.debug(f"Used IDs: {used_ids}") # Clear existing items self.treeWidget.clear() rootItems = {} # Construct hierarchical tree structure for entity_type in entity_types: if entity_type.parent_type != 'root': # Skip non-root items continue if not entity_type.entity_type.startswith("category_"): count = db_session.query(EntitiesTable).filter(EntitiesTable.entity_types_id == entity_type.entity_type_id).count() text = f"{count} - {entity_type.gui_name} {entity_type.entity_type}" else: # Use the new method to get the total count for this category total_count = self._getTotalCountForChildren(entity_type, db_session) text = f"{total_count} - {entity_type.gui_name} (Total)" treeItem = QTreeWidgetItem() treeItem.setText(0, text) treeItem.setToolTip(0, entity_type.gui_tooltip) treeItem.entity_type_id = entity_type.entity_type_id treeItem.entity_type = entity_type.entity_type if entity_type.entity_type_id in used_ids and entity_type.parser_enabled: color = QColor('green') elif not entity_type.entity_type.startswith("category_") and not entity_type.parser_enabled: color = QColor('red') else: color = QColor('white') treeItem.setForeground(0, color) self.treeWidget.addTopLevelItem(treeItem) # Call recursive function to add children self._addChildren(treeItem, entity_type.entity_type, db_session, used_ids) # Optionally expand all tree items self.treeWidget.expandAll() except Exception as e: logging.error("Error updating database tree", exc_info=True) def expandAllTreeItems(self): for i in range(self.treeWidget.topLevelItemCount()): self._expandCollapseRecursive(self.treeWidget.topLevelItem(i), True) def collapseAllTreeItems(self): for i in range(self.treeWidget.topLevelItemCount()): self._expandCollapseRecursive(self.treeWidget.topLevelItem(i), False) def _expandCollapseRecursive(self, treeItem, expand=True): if treeItem is not None: treeItem.setExpanded(expand) for j in range(treeItem.childCount()): self._expandCollapseRecursive(treeItem.child(j), expand) class FileCheckboxPanel(QWidget): def __init__(self): super().__init__() self.mainLayout = QVBoxLayout(self) self.scrollArea = QScrollArea(self) self.scrollArea.setWidgetResizable(True) self.scrollAreaContents = QWidget() self.scrollLayout = QVBoxLayout(self.scrollAreaContents) self.scrollArea.setWidget(self.scrollAreaContents) self.mainLayout.addWidget(self.scrollArea) self.items = [] # Keep track of the custom widgets def updateCheckboxes(self, db_session): try: # Clear existing items for item in self.items: item.deleteLater() self.items.clear() with session_scope() as db_session: try: file_metadata = db_session.query(FileMetadata).all() for file in file_metadata: entity_count = db_session.query(EntitiesTable).filter(EntitiesTable.file_id == file.file_id).count() item_text = f"{file.file_name} ({entity_count})" custom_widget = FileCheckboxItem(item_text) self.scrollLayout.addWidget(custom_widget) self.items.append(custom_widget) # Add a stretch to push everything up self.scrollLayout.addStretch(1) except Exception as e: logging.error("Error updating file checkboxes", exc_info=True) except Exception as e: logging.error("Error updating file checkboxes", exc_info=True) def filterCheckboxes(self, filter_text): for item in self.items: if filter_text.lower() in item.label.text().lower(): item.show() else: item.hide() def checkAllVisible(self): for item in self.items: if not item.isHidden(): item.checkBox.setChecked(True) def uncheckAllVisible(self): for item in self.items: if not item.isHidden(): item.checkBox.setChecked(False) def getCheckedFiles(self): checked_files = [] for custom_widget in self.items: if custom_widget.checkBox.isChecked(): # Extract the file name from the item text file_name = custom_widget.label.text().split(" (")[0] checked_files.append(file_name) return checked_files def _setCheckStateForVisibleItems(self, state): for custom_widget in self.items: if not custom_widget.isHidden(): custom_widget.checkBox.setChecked(state)