diff --git a/capa/ida/plugin/item.py b/capa/ida/plugin/item.py index 3292de1c0..4e6db7a55 100644 --- a/capa/ida/plugin/item.py +++ b/capa/ida/plugin/item.py @@ -21,7 +21,7 @@ import capa.ida.helpers from capa.features.address import Address, FileOffsetAddress, AbsoluteVirtualAddress -from capa.ida.plugin.qt_compat import QtCore, qt_get_item_flag_tristate +from capa.ida.plugin.qt_compat import QtCore, qt_get_item_flag_tristate, flag_val def info_to_name(display): @@ -52,10 +52,10 @@ def __init__(self, parent: Optional["CapaExplorerDataItem"], data: list[str], ca self._can_check = can_check # default state for item - self.flags = QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable + self.flags = flag_val(QtCore.Qt.ItemIsEnabled) | flag_val(QtCore.Qt.ItemIsSelectable) if self._can_check: - self.flags = self.flags | QtCore.Qt.ItemIsUserCheckable | qt_get_item_flag_tristate() + self.flags = self.flags | flag_val(QtCore.Qt.ItemIsUserCheckable) | qt_get_item_flag_tristate() if self.pred: self.pred.appendChild(self) @@ -66,9 +66,9 @@ def setIsEditable(self, isEditable=False): @param isEditable: True, can edit, False cannot edit """ if isEditable: - self.flags |= QtCore.Qt.ItemIsEditable + self.flags |= flag_val(QtCore.Qt.ItemIsEditable) else: - self.flags &= ~QtCore.Qt.ItemIsEditable + self.flags &= ~flag_val(QtCore.Qt.ItemIsEditable) def setChecked(self, checked): """set item as checked diff --git a/capa/ida/plugin/qt_compat.py b/capa/ida/plugin/qt_compat.py index 7b3858a71..1f3d44edf 100644 --- a/capa/ida/plugin/qt_compat.py +++ b/capa/ida/plugin/qt_compat.py @@ -41,6 +41,12 @@ Qt = QtCore.Qt +def flag_val(flag): + if hasattr(flag, 'value'): + return flag.value + return flag + + def qt_get_item_flag_tristate(): """ Get the tristate item flag compatible with Qt5 and Qt6. @@ -51,20 +57,12 @@ def qt_get_item_flag_tristate(): ItemIsAutoTristate automatically manages tristate based on child checkboxes, matching the original ItemIsTristate behavior where parent checkboxes reflect the check state of their children. - - Returns: - int: The appropriate flag value for the Qt version - - Raises: - AttributeError: If the tristate flag cannot be found in the Qt library - """ +def qt_get_item_flag_tristate(): if QT_LIBRARY == "PySide6": - # Qt6: ItemIsTristate was removed, replaced with ItemIsAutoTristate - # Try different possible locations (API varies slightly across PySide6 versions) if hasattr(Qt, "ItemIsAutoTristate"): - return Qt.ItemIsAutoTristate + return flag_val(Qt.ItemIsAutoTristate) elif hasattr(Qt, "ItemFlag") and hasattr(Qt.ItemFlag, "ItemIsAutoTristate"): - return Qt.ItemFlag.ItemIsAutoTristate + return flag_val(Qt.ItemFlag.ItemIsAutoTristate) else: raise AttributeError( "Cannot find ItemIsAutoTristate in PySide6. " @@ -72,8 +70,7 @@ def qt_get_item_flag_tristate(): + f"Available Qt attributes: {[attr for attr in dir(Qt) if 'Item' in attr]}" ) else: - # Qt5: Use the original ItemIsTristate flag - return Qt.ItemIsTristate + return flag_val(Qt.ItemIsTristate) -__all__ = ["qt_get_item_flag_tristate", "Signal", "QAction", "QtGui", "QtCore", "QtWidgets"] +__all__ = ["flag_val", "qt_get_item_flag_tristate", "Signal", "QAction", "QtGui", "QtCore", "QtWidgets"] diff --git a/capa/ida/plugin/view.py b/capa/ida/plugin/view.py index 7a7759164..7254c1b6e 100644 --- a/capa/ida/plugin/view.py +++ b/capa/ida/plugin/view.py @@ -26,7 +26,7 @@ from capa.ida.plugin.item import CapaExplorerFunctionItem from capa.features.address import FileOffsetAddress, AbsoluteVirtualAddress, _NoAddress from capa.ida.plugin.model import CapaExplorerDataModel -from capa.ida.plugin.qt_compat import QtGui, QtCore, Signal, QAction, QtWidgets +from capa.ida.plugin.qt_compat import QtGui, QtCore, Signal, QAction, QtWidgets, flag_val MAX_SECTION_SIZE = 750 @@ -484,9 +484,9 @@ def slot_item_double_clicked(self, o, column): CapaExplorerRulegenEditor.get_column_comment_index(), CapaExplorerRulegenEditor.get_column_description_index(), ): - o.setFlags(o.flags() | QtCore.Qt.ItemIsEditable) + o.setFlags(int(o.flags()) | flag_val(QtCore.Qt.ItemIsEditable)) self.editItem(o, column) - o.setFlags(o.flags() & ~QtCore.Qt.ItemIsEditable) + o.setFlags(int(o.flags()) & ~flag_val(QtCore.Qt.ItemIsEditable)) self.is_editing = True def update_preview(self): @@ -601,13 +601,13 @@ def set_expression_node(self, o): def set_feature_node(self, o): """ """ setattr(o, "capa_type", CapaExplorerRulegenEditor.get_node_type_feature()) - o.setFlags(o.flags() & ~QtCore.Qt.ItemIsDropEnabled) + o.setFlags(int(o.flags()) & ~flag_val(QtCore.Qt.ItemIsDropEnabled)) self.style_feature_node(o) def set_comment_node(self, o): """ """ setattr(o, "capa_type", CapaExplorerRulegenEditor.get_node_type_comment()) - o.setFlags(o.flags() & ~QtCore.Qt.ItemIsDropEnabled) + o.setFlags(int(o.flags()) & ~flag_val(QtCore.Qt.ItemIsDropEnabled)) self.style_comment_node(o) @@ -1009,7 +1009,7 @@ def style_leaf_node(self, o): def set_parent_node(self, o): """ """ - o.setFlags(o.flags() & ~QtCore.Qt.ItemIsSelectable) + o.setFlags(int(o.flags()) & ~flag_val(QtCore.Qt.ItemIsSelectable)) setattr(o, "capa_type", CapaExplorerRulegenFeatures.get_node_type_parent()) self.style_parent_node(o)