activity-browser
activity-browser copied to clipboard
Re-ordering items in calculation setup broken
Updating AB
- [X] Yes, I have updated AB and still experience this issue
What happened?
The re-ordering of items in the calculation setup tables (#719) is broken
Broken in master and 2.9.2
I tried down to 2.7.6, but also broken there
Expected behaviour is described in #719, but it's just that we want to be able to re-order items in the table by drag/drop, and that's not working anymore.
Relevant errors
None
Operating system
Windows 10
Conda environment
Not relevant
Also we can prob. remove https://github.com/LCA-ActivityBrowser/activity-browser/blame/61bf99e50596e40336d9f4432b169b89c6f011f1/activity_browser/ui/tables/LCA_setup.py#L108 and https://github.com/LCA-ActivityBrowser/activity-browser/blame/61bf99e50596e40336d9f4432b169b89c6f011f1/activity_browser/ui/tables/LCA_setup.py#L170
because they are in the superclass CSGenericTable (though do copy over the SHIFT key, which isn't in the original)
Perhaps also the def dropEvent and def dragEnterEvent if we move the allowed sources to a variable created in __init__. Though we need to check if self breaks things by drag/dropping between the tables (so it reads from the superclass). If so, we also need to write a var that in the __init__
+ review this class descriptor
https://github.com/LCA-ActivityBrowser/activity-browser/blame/61bf99e50596e40336d9f4432b169b89c6f011f1/activity_browser/ui/tables/LCA_setup.py#L46
+ move the dropevent logs to debug level
Current progress in improving the file, but main problem not resolved yet:
# -*- coding: utf-8 -*-
import brightway2 as bw
from PySide2.QtCore import Slot, Qt
from PySide2 import QtWidgets
from activity_browser.signals import signals
from ..icons import qicons
from .delegates import FloatDelegate
from .impact_categories import MethodsTable, MethodsTree
from .models import CSMethodsModel, CSActivityModel, ScenarioImportModel
from .views import ABDataFrameView
import logging
from activity_browser.logger import ABHandler
logger = logging.getLogger('ab_logs')
log = ABHandler.setup_with_logger(logger, __name__)
class CSList(QtWidgets.QComboBox):
def __init__(self, parent=None):
super(CSList, self).__init__(parent)
# Runs even if selection doesn't change
self.activated['QString'].connect(self.set_cs)
signals.calculation_setup_selected.connect(self.sync)
def sync(self, name):
self.blockSignals(True)
self.clear()
keys = sorted(bw.calculation_setups)
self.insertItems(0, keys)
self.blockSignals(False)
self.setCurrentIndex(keys.index(name))
@staticmethod
def set_cs(name: str):
signals.calculation_setup_selected.emit(name)
@property
def name(self) -> str:
return self.currentText()
class CSGenericTable(ABDataFrameView):
""" Generic class to enable internal re-ordering of items in table.
Items commented out (blass below + first line of init) are intended to help
with showing a 'drop indicator' where the dragged item would end up.
This doesn't work yet
See also comments on PR here: https://github.com/LCA-ActivityBrowser/activity-browser/pull/719
See also this stackoverflow page: https://stackoverflow.com/questions/61387248/in-pyqt5-how-do-i-properly-move-rows-in-a-qtableview-using-dragdrop
"""
def __init__(self, parent=None):
super().__init__(parent)
self.setSelectionBehavior(QtWidgets.QTableView.SelectRows)
self.setSelectionMode(QtWidgets.QTableView.SingleSelection)
self.setAcceptDrops(True)
self.setDragDropMode(QtWidgets.QTableView.InternalMove)
self.setDragDropOverwriteMode(False)
self.setToolTip("Drag impact categories from the impact categories tree/table to include them \n"
"Click and drag to re-order individual rows of the table\n"
"Hold CTRL or SHIFT and click to select multiple rows to open or delete them.")
def mousePressEvent(self, event):
""" Check whether left mouse is pressed and whether CTRL or SHIFT are pressed to change selection mode"""
if event.button() == Qt.LeftButton:
if event.modifiers() & Qt.ControlModifier or event.modifiers() & Qt.ShiftModifier:
self.setSelectionMode(QtWidgets.QTableView.ExtendedSelection)
self.setDragDropMode(QtWidgets.QTableView.DropOnly)
else:
self.setSelectionMode(QtWidgets.QTableView.SingleSelection)
self.setDragDropMode(QtWidgets.QTableView.InternalMove)
ABDataFrameView.mousePressEvent(self, event)
def dragMoveEvent(self, event):
pass
def re_order_table_items(self, event):
"""Re order items in the table (triggered by dragging within self)."""
selection = self.selectedIndexes()
from_index = selection[0].row() if selection else -1
to_index = self.indexAt(event.pos()).row()
if (0 <= from_index < self.model.rowCount() and
0 <= to_index < self.model.rowCount() and
from_index != to_index):
self.model.relocateRow(from_index, to_index)
class CSActivityTable(CSGenericTable):
def __init__(self, parent=None):
super().__init__(parent)
self.model = CSActivityModel(self)
self.setItemDelegateForColumn(0, FloatDelegate(self))
self.model.updated.connect(self.update_proxy_model)
self.model.updated.connect(self.custom_view_sizing)
@Slot(name="resizeView")
def custom_view_sizing(self):
self.setColumnHidden(6, True)
self.resizeColumnsToContents()
self.resizeRowsToContents()
@Slot(name="openActivities")
def open_activities(self) -> None:
for proxy in self.selectedIndexes():
act = self.model.get_key(proxy)
signals.safe_open_activity_tab.emit(act)
signals.add_activity_to_history.emit(act)
@Slot(name="deleteRows")
def delete_rows(self):
self.model.delete_rows(self.selectedIndexes())
def to_python(self) -> list:
return self.model.activities
def contextMenuEvent(self, event) -> None:
if self.indexAt(event.pos()).row() == -1:
return
menu = QtWidgets.QMenu()
menu.addAction(qicons.right, "Open activity", self.open_activities)
menu.addAction(qicons.delete, "Remove row", self.delete_rows)
menu.exec_(event.globalPos())
def dragEnterEvent(self, event):
if getattr(event.source(), "technosphere", False)\
or event.source() is self:
event.accept()
def dropEvent(self, event):
event.accept()
source = event.source()
if getattr(event.source(), "technosphere", False):
log.debug('Dropevent from:', source)
self.model.include_activities({source.get_key(p): 1.0} for p in source.selectedIndexes())
elif event.source() is self:
log.debug('Dropevent from:', source)
self.re_order_table_items(event)
class CSMethodsTable(CSGenericTable):
def __init__(self, parent=None):
super().__init__(parent)
self.model = CSMethodsModel(self)
self.model.updated.connect(self.update_proxy_model)
self.model.updated.connect(self.custom_view_sizing)
@Slot(name="resizeView")
def custom_view_sizing(self):
self.setColumnHidden(3, True)
self.resizeColumnsToContents()
self.resizeRowsToContents()
def to_python(self):
return self.model.methods
def contextMenuEvent(self, event) -> None:
if self.indexAt(event.pos()).row() == -1:
return
menu = QtWidgets.QMenu()
menu.addAction(
qicons.delete, "Remove row",
lambda: self.model.delete_rows(self.selectedIndexes())
)
menu.exec_(event.globalPos())
def dragEnterEvent(self, event):
if isinstance(event.source(), (MethodsTable, MethodsTree))\
or event.source() is self:
event.accept()
def dropEvent(self, event):
event.accept()
source = event.source()
if isinstance(event.source(), (MethodsTable, MethodsTree)):
log.debug('Dropevent from:', source)
self.model.include_methods(event.source().selected_methods())
elif event.source() is self:
log.debug('Dropevent from:', source)
self.re_order_table_items(event)
class ScenarioImportTable(ABDataFrameView):
"""Self-contained widget that shows the scenario headers for a given
scenario template dataframe.
"""
def __init__(self, parent=None):
super().__init__(parent=parent)
self.model = ScenarioImportModel(self)
self.model.updated.connect(self.update_proxy_model)
self.model.updated.connect(self.custom_view_sizing)
def sync(self, names: list):
self.model.sync(names)