brightway-basic-explorer 0.9.dev4__tar.gz → 0.9.dev5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev5}/PKG-INFO +1 -1
- {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev5}/brightway_basic_explorer/__init__.py +200 -7
- {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev5}/brightway_basic_explorer.egg-info/PKG-INFO +1 -1
- {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev5}/pyproject.toml +1 -1
- {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev5}/COPYING +0 -0
- {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev5}/README.md +0 -0
- {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev5}/brightway_basic_explorer/icons/emission.png +0 -0
- {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev5}/brightway_basic_explorer/icons/natural_resource.png +0 -0
- {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev5}/brightway_basic_explorer/icons/process.png +0 -0
- {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev5}/brightway_basic_explorer/icons/unknown.png +0 -0
- {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev5}/brightway_basic_explorer.egg-info/SOURCES.txt +0 -0
- {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev5}/brightway_basic_explorer.egg-info/dependency_links.txt +0 -0
- {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev5}/brightway_basic_explorer.egg-info/requires.txt +0 -0
- {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev5}/brightway_basic_explorer.egg-info/top_level.txt +0 -0
- {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev5}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: brightway-basic-explorer
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.dev5
|
|
4
4
|
Summary: Implement basic GUI to explorer brightway ativities
|
|
5
5
|
Author-email: Benoît GSCHWIND <benoit.gschwind@minesparis.psl.eu>, OIE - Mines Paris PSL <benoit.gschwind@minesparis.psl.eu>
|
|
6
6
|
Maintainer-email: Benoît GSCHWIND <benoit.gschwind@minesparis.psl.eu>
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from IPython.external.qt_for_kernel import QtGui, QtCore
|
|
4
4
|
from IPython.lib.guisupport import get_app_qt4, is_event_loop_running_qt4
|
|
5
5
|
|
|
6
|
+
import re
|
|
6
7
|
import os
|
|
7
8
|
import sys
|
|
8
9
|
import bw2data
|
|
@@ -51,6 +52,20 @@ def activity_to_json_with_params(act, params):
|
|
|
51
52
|
a["exchanges"] = exs
|
|
52
53
|
return a
|
|
53
54
|
|
|
55
|
+
class MessageDialog(QtGui.QDialog):
|
|
56
|
+
def __init__(self, msg):
|
|
57
|
+
super().__init__()
|
|
58
|
+
self.msg = msg
|
|
59
|
+
|
|
60
|
+
layout = QtGui.QVBoxLayout()
|
|
61
|
+
self.setLayout(layout)
|
|
62
|
+
layout.addWidget(QtGui.QLabel(msg))
|
|
63
|
+
|
|
64
|
+
self.button = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.StandardButton.Ok)
|
|
65
|
+
layout.addWidget(self.button)
|
|
66
|
+
self.button.accepted.connect(self.close)
|
|
67
|
+
|
|
68
|
+
|
|
54
69
|
class QStandardItemRO(QtGui.QStandardItem):
|
|
55
70
|
def __init__(self, *args, data=None, **kwargs):
|
|
56
71
|
super().__init__(*args, **kwargs)
|
|
@@ -58,8 +73,7 @@ class QStandardItemRO(QtGui.QStandardItem):
|
|
|
58
73
|
self.setSelectable(True)
|
|
59
74
|
self.setData(data)
|
|
60
75
|
|
|
61
|
-
|
|
62
|
-
class TableModel(QtGui.QStandardItemModel):
|
|
76
|
+
class ExchangeModel(QtGui.QStandardItemModel):
|
|
63
77
|
def __init__(self, parent=None):
|
|
64
78
|
super().__init__(parent)
|
|
65
79
|
self.setColumnCount(6)
|
|
@@ -106,11 +120,13 @@ class ActionMenu(QtGui.QMenu):
|
|
|
106
120
|
self.action_copy = self.addAction("Copy Full Reference")
|
|
107
121
|
self.action_copy.triggered.connect(self.copy_full_reference_triggered)
|
|
108
122
|
|
|
109
|
-
|
|
110
|
-
|
|
123
|
+
if hasattr(self.parentWidget(), "explore"):
|
|
124
|
+
self.action_explore = self.addAction("Explore")
|
|
125
|
+
self.action_explore.triggered.connect(self.explore_triggered)
|
|
111
126
|
|
|
112
|
-
|
|
113
|
-
|
|
127
|
+
if hasattr(self.parentWidget(), "explore_in_new_window"):
|
|
128
|
+
self.action_explore = self.addAction("Explore in new window")
|
|
129
|
+
self.action_explore.triggered.connect(self.explore_in_new_window_triggered)
|
|
114
130
|
|
|
115
131
|
def copy_triggered(self):
|
|
116
132
|
QtGui.QGuiApplication.clipboard().setText(self.index.data())
|
|
@@ -223,7 +239,7 @@ class ActivityTab(QtGui.QWidget):
|
|
|
223
239
|
self.tree_view.setSortingEnabled(True)
|
|
224
240
|
self.tree_view.setAlternatingRowColors(True)
|
|
225
241
|
|
|
226
|
-
self.model =
|
|
242
|
+
self.model = ExchangeModel()
|
|
227
243
|
self.model.load(activity_json["exchanges"])
|
|
228
244
|
self.tree_view.setModel(self.model)
|
|
229
245
|
self.tree_view.doubleClicked.connect(self.doubleCliked)
|
|
@@ -271,6 +287,156 @@ class ActivityTab(QtGui.QWidget):
|
|
|
271
287
|
act = bw2data.get_activity((v["database"], v["code"]))
|
|
272
288
|
show_activity(act, self.xparent.params)
|
|
273
289
|
|
|
290
|
+
class SearchModel(QtGui.QStandardItemModel):
|
|
291
|
+
def __init__(self, parent=None):
|
|
292
|
+
super().__init__(parent)
|
|
293
|
+
self.setColumnCount(4)
|
|
294
|
+
self.setHeaderData(0, QtCore.Qt.Orientation.Horizontal, "Name")
|
|
295
|
+
self.setHeaderData(1, QtCore.Qt.Orientation.Horizontal, "Unit")
|
|
296
|
+
self.setHeaderData(2, QtCore.Qt.Orientation.Horizontal, "Category")
|
|
297
|
+
self.setHeaderData(3, QtCore.Qt.Orientation.Horizontal, "Location")
|
|
298
|
+
|
|
299
|
+
def load(self, data):
|
|
300
|
+
self.exchanges = [e for e in data]
|
|
301
|
+
self.root = self.invisibleRootItem()
|
|
302
|
+
for e in data:
|
|
303
|
+
row = [
|
|
304
|
+
QStandardItemRO(str(e.get(k, "-")), data=e)
|
|
305
|
+
for k in ["name", "unit", "categories", "location"]
|
|
306
|
+
]
|
|
307
|
+
|
|
308
|
+
etype = e.get("type", "unknown")
|
|
309
|
+
if etype == "emission":
|
|
310
|
+
path = os.path.join(os.path.dirname(__file__), "icons", "emission.png")
|
|
311
|
+
elif etype in {"process", "processwithreferenceproduct"}:
|
|
312
|
+
path = os.path.join(os.path.dirname(__file__), "icons", "process.png")
|
|
313
|
+
elif etype == "natural resource":
|
|
314
|
+
path = os.path.join(os.path.dirname(__file__), "icons", "natural_resource.png")
|
|
315
|
+
else:
|
|
316
|
+
path = os.path.join(os.path.dirname(__file__), "icons", "unknown.png")
|
|
317
|
+
|
|
318
|
+
row[0].setIcon(QtGui.QIcon(path))
|
|
319
|
+
self.root.appendRow(row)
|
|
320
|
+
|
|
321
|
+
class SearchWindow(QtGui.QMainWindow):
|
|
322
|
+
def __init__(self, db, keywords=""):
|
|
323
|
+
super().__init__()
|
|
324
|
+
|
|
325
|
+
self.db = db
|
|
326
|
+
|
|
327
|
+
self.setWindowTitle("Search Activity")
|
|
328
|
+
self.setMinimumSize(QtCore.QSize(800, 600))
|
|
329
|
+
|
|
330
|
+
central_widget = QtGui.QWidget()
|
|
331
|
+
self.setCentralWidget(central_widget)
|
|
332
|
+
layout = QtGui.QVBoxLayout()
|
|
333
|
+
central_widget.setLayout(layout)
|
|
334
|
+
|
|
335
|
+
grid = QtGui.QGridLayout()
|
|
336
|
+
layout.addLayout(grid)
|
|
337
|
+
|
|
338
|
+
next_row = grid.rowCount()
|
|
339
|
+
grid.addWidget(QtGui.QLabel("database:"), next_row, 0)
|
|
340
|
+
grid.addWidget(QtGui.QLabel(self.db.name), next_row, 1)
|
|
341
|
+
|
|
342
|
+
# Keyword query
|
|
343
|
+
next_row = grid.rowCount()
|
|
344
|
+
grid.addWidget(QtGui.QLabel("keywords:"), next_row, 0)
|
|
345
|
+
self.keywords = QtGui.QLineEdit("")
|
|
346
|
+
self.keywords.setText(keywords)
|
|
347
|
+
self.keywords.returnPressed.connect(self.update_search)
|
|
348
|
+
grid.addWidget(self.keywords, next_row, 1)
|
|
349
|
+
self.keywords_button = QtGui.QPushButton("Update")
|
|
350
|
+
self.keywords_button.clicked.connect(self.update_search)
|
|
351
|
+
|
|
352
|
+
grid.addWidget(self.keywords_button, next_row, 2)
|
|
353
|
+
|
|
354
|
+
next_row = grid.rowCount()
|
|
355
|
+
grid.addWidget(QtGui.QLabel("filter:"), next_row, 0)
|
|
356
|
+
self.filter_edit = QtGui.QLineEdit("")
|
|
357
|
+
self.filter_edit.textChanged.connect(self.update_filter)
|
|
358
|
+
grid.addWidget(self.filter_edit, next_row, 1)
|
|
359
|
+
grid.setColumnStretch(0, 0)
|
|
360
|
+
grid.setColumnStretch(1, 1)
|
|
361
|
+
self.ignore_case = QtGui.QCheckBox("Ignore Case")
|
|
362
|
+
self.ignore_case.setChecked(True)
|
|
363
|
+
if hasattr(self.ignore_case, "checkStateChanged"):
|
|
364
|
+
self.ignore_case.checkStateChanged.connect(self.update_filter)
|
|
365
|
+
else:
|
|
366
|
+
self.ignore_case.stateChanged.connect(self.update_filter)
|
|
367
|
+
next_row = grid.rowCount()
|
|
368
|
+
grid.addWidget(self.ignore_case, next_row, 1)
|
|
369
|
+
|
|
370
|
+
# Create tree view
|
|
371
|
+
self.tree_view = QtGui.QTreeView()
|
|
372
|
+
layout.addWidget(self.tree_view)
|
|
373
|
+
self.tree_view.setSortingEnabled(True)
|
|
374
|
+
self.tree_view.setAlternatingRowColors(True)
|
|
375
|
+
self.tree_view.doubleClicked.connect(self.doubleCliked)
|
|
376
|
+
self.tree_view.setExpandsOnDoubleClick(False)
|
|
377
|
+
self.tree_view.setSelectionBehavior(QtGui.QAbstractItemView.SelectionBehavior.SelectItems)
|
|
378
|
+
self.tree_view.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.CustomContextMenu)
|
|
379
|
+
self.tree_view.customContextMenuRequested.connect(self.context_menu)
|
|
380
|
+
|
|
381
|
+
self.model = None
|
|
382
|
+
|
|
383
|
+
self.update_search()
|
|
384
|
+
|
|
385
|
+
def update_search(self):
|
|
386
|
+
text = self.keywords.text()
|
|
387
|
+
keywords = [re.sub("[^a-zA-Z0-9_-]", "", s) for s in text.split(" ") if len(s) > 0]
|
|
388
|
+
if all(len(x) < 3 for x in keywords):
|
|
389
|
+
w = MessageDialog("Too smalls keywords")
|
|
390
|
+
w.setWindowTitle("WARNING")
|
|
391
|
+
w.exec()
|
|
392
|
+
return
|
|
393
|
+
|
|
394
|
+
acts = self.db.search(" ".join(keywords), proxy=True, limit=200)
|
|
395
|
+
acts = [dict(a) for a in acts]
|
|
396
|
+
|
|
397
|
+
old_model = self.model
|
|
398
|
+
self.model = SearchModel()
|
|
399
|
+
self.model.load(acts)
|
|
400
|
+
self.tree_view.setModel(self.model)
|
|
401
|
+
if old_model is not None:
|
|
402
|
+
old_model.deleteLater()
|
|
403
|
+
self.tree_view.setColumnWidth(0, 400)
|
|
404
|
+
|
|
405
|
+
def context_menu(self, point):
|
|
406
|
+
index = self.tree_view.indexAt(point)
|
|
407
|
+
self.action_menu = ActionMenu(self, index)
|
|
408
|
+
self.action_menu.exec(self.tree_view.viewport().mapToGlobal(point))
|
|
409
|
+
|
|
410
|
+
def update_filter(self, *args):
|
|
411
|
+
if self.model is None:
|
|
412
|
+
return
|
|
413
|
+
|
|
414
|
+
text = self.filter_edit.text()
|
|
415
|
+
if self.ignore_case.isChecked():
|
|
416
|
+
text = text.lower()
|
|
417
|
+
index = self.model.root.index()
|
|
418
|
+
for i in range(self.model.rowCount()):
|
|
419
|
+
v = self.model.root.child(i, 0).data()
|
|
420
|
+
self.tree_view.setRowHidden(i, index, text not in v["name"].lower())
|
|
421
|
+
else:
|
|
422
|
+
index = self.model.root.index()
|
|
423
|
+
for i in range(self.model.rowCount()):
|
|
424
|
+
v = self.model.root.child(i, 0).data()
|
|
425
|
+
self.tree_view.setRowHidden(i, index, text not in v["name"])
|
|
426
|
+
|
|
427
|
+
def doubleCliked(self, index):
|
|
428
|
+
self.explore_in_new_window(index)
|
|
429
|
+
|
|
430
|
+
def explore_in_new_window(self, index):
|
|
431
|
+
r = index.row()
|
|
432
|
+
v = self.model.root.child(r, 0).data()
|
|
433
|
+
act = bw2data.get_activity((v["database"], v["code"]))
|
|
434
|
+
show_activity(act)
|
|
435
|
+
|
|
436
|
+
def show(self, *args, **kwargs):
|
|
437
|
+
self.tree_view.setColumnWidth(0, 400)
|
|
438
|
+
super().show(*args, **kwargs)
|
|
439
|
+
|
|
274
440
|
|
|
275
441
|
# Replace IPython version to one compatible with Qt6
|
|
276
442
|
def start_event_loop_qt4(app=None):
|
|
@@ -311,6 +477,33 @@ def show_activity(act, params=None):
|
|
|
311
477
|
|
|
312
478
|
window.activateWindow()
|
|
313
479
|
|
|
480
|
+
def search(database, keywords=""):
|
|
481
|
+
|
|
482
|
+
if 'matplotlib' in sys.modules:
|
|
483
|
+
import matplotlib
|
|
484
|
+
if hasattr(matplotlib.backends, "backend"):
|
|
485
|
+
if 'qt' not in matplotlib.backends.backend:
|
|
486
|
+
print("WARNING: ActivityGUI will block, use `%matplotlib qt` to avoid blocking")
|
|
487
|
+
|
|
488
|
+
app = get_app_qt4()
|
|
489
|
+
|
|
490
|
+
if isinstance(database, str):
|
|
491
|
+
if database not in bw2data.databases:
|
|
492
|
+
w = MessageDialog(f"Database {database} not found !")
|
|
493
|
+
w.setWindowTitle("WARNING")
|
|
494
|
+
w.exec()
|
|
495
|
+
return
|
|
496
|
+
database = bw2data.Database(database)
|
|
497
|
+
|
|
498
|
+
window = SearchWindow(database, keywords)
|
|
499
|
+
window.show()
|
|
500
|
+
|
|
501
|
+
if not is_event_loop_running_qt4(app):
|
|
502
|
+
start_event_loop_qt4(app)
|
|
503
|
+
|
|
504
|
+
window.activateWindow()
|
|
505
|
+
return window
|
|
506
|
+
|
|
314
507
|
def close_all():
|
|
315
508
|
for w in list(ActivityWindow.keep):
|
|
316
509
|
w.close()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: brightway-basic-explorer
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.dev5
|
|
4
4
|
Summary: Implement basic GUI to explorer brightway ativities
|
|
5
5
|
Author-email: Benoît GSCHWIND <benoit.gschwind@minesparis.psl.eu>, OIE - Mines Paris PSL <benoit.gschwind@minesparis.psl.eu>
|
|
6
6
|
Maintainer-email: Benoît GSCHWIND <benoit.gschwind@minesparis.psl.eu>
|
|
@@ -12,7 +12,7 @@ name = "brightway-basic-explorer"
|
|
|
12
12
|
description = "Implement basic GUI to explorer brightway ativities"
|
|
13
13
|
readme = "README.md"
|
|
14
14
|
license-files = ["COPYING"]
|
|
15
|
-
version = "0.9.
|
|
15
|
+
version = "0.9.dev5"
|
|
16
16
|
dependencies = [ "ipython" ]
|
|
17
17
|
requires-python = ">= 3.10"
|
|
18
18
|
keywords = ["lca", "ecoinvent", "brightway"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|