brightway-basic-explorer 0.9.dev4__py3-none-any.whl → 0.9.dev6__py3-none-any.whl
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/__init__.py +210 -9
- {brightway_basic_explorer-0.9.dev4.dist-info → brightway_basic_explorer-0.9.dev6.dist-info}/METADATA +26 -4
- {brightway_basic_explorer-0.9.dev4.dist-info → brightway_basic_explorer-0.9.dev6.dist-info}/RECORD +6 -6
- {brightway_basic_explorer-0.9.dev4.dist-info → brightway_basic_explorer-0.9.dev6.dist-info}/WHEEL +0 -0
- {brightway_basic_explorer-0.9.dev4.dist-info → brightway_basic_explorer-0.9.dev6.dist-info}/licenses/COPYING +0 -0
- {brightway_basic_explorer-0.9.dev4.dist-info → brightway_basic_explorer-0.9.dev6.dist-info}/top_level.txt +0 -0
|
@@ -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
|
+
def warn_dialog(msg):
|
|
56
|
+
print(f"WARNING: {msg}")
|
|
57
|
+
w = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Warning, "WARNING", msg)
|
|
58
|
+
w.show()
|
|
59
|
+
w.setMaximumSize(800, 600)
|
|
60
|
+
w.exec()
|
|
61
|
+
|
|
62
|
+
def fatal_dialog(msg):
|
|
63
|
+
print(f"ERROR: {msg}")
|
|
64
|
+
w = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Critical, "ERROR", msg)
|
|
65
|
+
w.show()
|
|
66
|
+
w.setMaximumSize(800, 600)
|
|
67
|
+
w.exec()
|
|
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)
|
|
@@ -103,14 +117,19 @@ class ActionMenu(QtGui.QMenu):
|
|
|
103
117
|
self.action_copy = self.addAction("Copy")
|
|
104
118
|
self.action_copy.triggered.connect(self.copy_triggered)
|
|
105
119
|
|
|
106
|
-
self.
|
|
107
|
-
self.
|
|
120
|
+
self.action_copy_ref = self.addAction("Copy Full Reference")
|
|
121
|
+
self.action_copy_ref.triggered.connect(self.copy_full_reference_triggered)
|
|
108
122
|
|
|
109
|
-
self.
|
|
110
|
-
self.
|
|
123
|
+
self.action_copy_code = self.addAction("Copy Activity Code")
|
|
124
|
+
self.action_copy_code.triggered.connect(self.copy_code_triggered)
|
|
111
125
|
|
|
112
|
-
|
|
113
|
-
|
|
126
|
+
if hasattr(self.parentWidget(), "explore"):
|
|
127
|
+
self.action_explore = self.addAction("Explore")
|
|
128
|
+
self.action_explore.triggered.connect(self.explore_triggered)
|
|
129
|
+
|
|
130
|
+
if hasattr(self.parentWidget(), "explore_in_new_window"):
|
|
131
|
+
self.action_explore = self.addAction("Explore in new window")
|
|
132
|
+
self.action_explore.triggered.connect(self.explore_in_new_window_triggered)
|
|
114
133
|
|
|
115
134
|
def copy_triggered(self):
|
|
116
135
|
QtGui.QGuiApplication.clipboard().setText(self.index.data())
|
|
@@ -121,6 +140,12 @@ class ActionMenu(QtGui.QMenu):
|
|
|
121
140
|
text = (v["database"], v["name"], v.get("location", None), v.get("categories", tuple()), v.get("unit", None))
|
|
122
141
|
QtGui.QGuiApplication.clipboard().setText(str(text))
|
|
123
142
|
|
|
143
|
+
def copy_code_triggered(self):
|
|
144
|
+
parent = self.parentWidget()
|
|
145
|
+
v = parent.model.root.child(self.index.row(), 0).data()
|
|
146
|
+
text = (v["database"], v["code"])
|
|
147
|
+
QtGui.QGuiApplication.clipboard().setText(str(text))
|
|
148
|
+
|
|
124
149
|
def explore_triggered(self):
|
|
125
150
|
self.parentWidget().explore(self.index)
|
|
126
151
|
|
|
@@ -223,7 +248,7 @@ class ActivityTab(QtGui.QWidget):
|
|
|
223
248
|
self.tree_view.setSortingEnabled(True)
|
|
224
249
|
self.tree_view.setAlternatingRowColors(True)
|
|
225
250
|
|
|
226
|
-
self.model =
|
|
251
|
+
self.model = ExchangeModel()
|
|
227
252
|
self.model.load(activity_json["exchanges"])
|
|
228
253
|
self.tree_view.setModel(self.model)
|
|
229
254
|
self.tree_view.doubleClicked.connect(self.doubleCliked)
|
|
@@ -271,6 +296,154 @@ class ActivityTab(QtGui.QWidget):
|
|
|
271
296
|
act = bw2data.get_activity((v["database"], v["code"]))
|
|
272
297
|
show_activity(act, self.xparent.params)
|
|
273
298
|
|
|
299
|
+
class SearchModel(QtGui.QStandardItemModel):
|
|
300
|
+
def __init__(self, parent=None):
|
|
301
|
+
super().__init__(parent)
|
|
302
|
+
self.setColumnCount(4)
|
|
303
|
+
self.setHeaderData(0, QtCore.Qt.Orientation.Horizontal, "Name")
|
|
304
|
+
self.setHeaderData(1, QtCore.Qt.Orientation.Horizontal, "Unit")
|
|
305
|
+
self.setHeaderData(2, QtCore.Qt.Orientation.Horizontal, "Category")
|
|
306
|
+
self.setHeaderData(3, QtCore.Qt.Orientation.Horizontal, "Location")
|
|
307
|
+
|
|
308
|
+
def load(self, data):
|
|
309
|
+
self.exchanges = [e for e in data]
|
|
310
|
+
self.root = self.invisibleRootItem()
|
|
311
|
+
for e in data:
|
|
312
|
+
row = [
|
|
313
|
+
QStandardItemRO(str(e.get(k, "-")), data=e)
|
|
314
|
+
for k in ["name", "unit", "categories", "location"]
|
|
315
|
+
]
|
|
316
|
+
|
|
317
|
+
etype = e.get("type", "unknown")
|
|
318
|
+
if etype == "emission":
|
|
319
|
+
path = os.path.join(os.path.dirname(__file__), "icons", "emission.png")
|
|
320
|
+
elif etype in {"process", "processwithreferenceproduct"}:
|
|
321
|
+
path = os.path.join(os.path.dirname(__file__), "icons", "process.png")
|
|
322
|
+
elif etype == "natural resource":
|
|
323
|
+
path = os.path.join(os.path.dirname(__file__), "icons", "natural_resource.png")
|
|
324
|
+
else:
|
|
325
|
+
path = os.path.join(os.path.dirname(__file__), "icons", "unknown.png")
|
|
326
|
+
|
|
327
|
+
row[0].setIcon(QtGui.QIcon(path))
|
|
328
|
+
self.root.appendRow(row)
|
|
329
|
+
|
|
330
|
+
class SearchWindow(QtGui.QMainWindow):
|
|
331
|
+
def __init__(self, db, keywords=""):
|
|
332
|
+
super().__init__()
|
|
333
|
+
|
|
334
|
+
self.db = db
|
|
335
|
+
|
|
336
|
+
self.setWindowTitle("Search Activity")
|
|
337
|
+
self.setMinimumSize(QtCore.QSize(800, 600))
|
|
338
|
+
|
|
339
|
+
central_widget = QtGui.QWidget()
|
|
340
|
+
self.setCentralWidget(central_widget)
|
|
341
|
+
layout = QtGui.QVBoxLayout()
|
|
342
|
+
central_widget.setLayout(layout)
|
|
343
|
+
|
|
344
|
+
grid = QtGui.QGridLayout()
|
|
345
|
+
layout.addLayout(grid)
|
|
346
|
+
|
|
347
|
+
next_row = grid.rowCount()
|
|
348
|
+
grid.addWidget(QtGui.QLabel("database:"), next_row, 0)
|
|
349
|
+
grid.addWidget(QtGui.QLabel(self.db.name), next_row, 1)
|
|
350
|
+
|
|
351
|
+
# Keyword query
|
|
352
|
+
next_row = grid.rowCount()
|
|
353
|
+
grid.addWidget(QtGui.QLabel("keywords:"), next_row, 0)
|
|
354
|
+
self.keywords = QtGui.QLineEdit("")
|
|
355
|
+
self.keywords.setText(keywords)
|
|
356
|
+
self.keywords.returnPressed.connect(self.update_search)
|
|
357
|
+
grid.addWidget(self.keywords, next_row, 1)
|
|
358
|
+
self.keywords_button = QtGui.QPushButton("Update")
|
|
359
|
+
self.keywords_button.clicked.connect(self.update_search)
|
|
360
|
+
|
|
361
|
+
grid.addWidget(self.keywords_button, next_row, 2)
|
|
362
|
+
|
|
363
|
+
next_row = grid.rowCount()
|
|
364
|
+
grid.addWidget(QtGui.QLabel("filter:"), next_row, 0)
|
|
365
|
+
self.filter_edit = QtGui.QLineEdit("")
|
|
366
|
+
self.filter_edit.textChanged.connect(self.update_filter)
|
|
367
|
+
grid.addWidget(self.filter_edit, next_row, 1)
|
|
368
|
+
grid.setColumnStretch(0, 0)
|
|
369
|
+
grid.setColumnStretch(1, 1)
|
|
370
|
+
self.ignore_case = QtGui.QCheckBox("Ignore Case")
|
|
371
|
+
self.ignore_case.setChecked(True)
|
|
372
|
+
if hasattr(self.ignore_case, "checkStateChanged"):
|
|
373
|
+
self.ignore_case.checkStateChanged.connect(self.update_filter)
|
|
374
|
+
else:
|
|
375
|
+
self.ignore_case.stateChanged.connect(self.update_filter)
|
|
376
|
+
next_row = grid.rowCount()
|
|
377
|
+
grid.addWidget(self.ignore_case, next_row, 1)
|
|
378
|
+
|
|
379
|
+
# Create tree view
|
|
380
|
+
self.tree_view = QtGui.QTreeView()
|
|
381
|
+
layout.addWidget(self.tree_view)
|
|
382
|
+
self.tree_view.setSortingEnabled(True)
|
|
383
|
+
self.tree_view.setAlternatingRowColors(True)
|
|
384
|
+
self.tree_view.doubleClicked.connect(self.doubleCliked)
|
|
385
|
+
self.tree_view.setExpandsOnDoubleClick(False)
|
|
386
|
+
self.tree_view.setSelectionBehavior(QtGui.QAbstractItemView.SelectionBehavior.SelectItems)
|
|
387
|
+
self.tree_view.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.CustomContextMenu)
|
|
388
|
+
self.tree_view.customContextMenuRequested.connect(self.context_menu)
|
|
389
|
+
|
|
390
|
+
self.model = None
|
|
391
|
+
|
|
392
|
+
self.update_search()
|
|
393
|
+
|
|
394
|
+
def update_search(self):
|
|
395
|
+
text = self.keywords.text()
|
|
396
|
+
keywords = [re.sub("[^a-zA-Z0-9_-]", "", s) for s in text.split(" ") if len(s) > 0]
|
|
397
|
+
if all(len(x) < 3 for x in keywords):
|
|
398
|
+
warn_dialog("Too smalls keywords")
|
|
399
|
+
return
|
|
400
|
+
|
|
401
|
+
acts = self.db.search(" ".join(keywords), proxy=True, limit=200)
|
|
402
|
+
acts = [dict(a) for a in acts]
|
|
403
|
+
|
|
404
|
+
old_model = self.model
|
|
405
|
+
self.model = SearchModel()
|
|
406
|
+
self.model.load(acts)
|
|
407
|
+
self.tree_view.setModel(self.model)
|
|
408
|
+
if old_model is not None:
|
|
409
|
+
old_model.deleteLater()
|
|
410
|
+
self.tree_view.setColumnWidth(0, 400)
|
|
411
|
+
|
|
412
|
+
def context_menu(self, point):
|
|
413
|
+
index = self.tree_view.indexAt(point)
|
|
414
|
+
self.action_menu = ActionMenu(self, index)
|
|
415
|
+
self.action_menu.exec(self.tree_view.viewport().mapToGlobal(point))
|
|
416
|
+
|
|
417
|
+
def update_filter(self, *args):
|
|
418
|
+
if self.model is None:
|
|
419
|
+
return
|
|
420
|
+
|
|
421
|
+
text = self.filter_edit.text()
|
|
422
|
+
if self.ignore_case.isChecked():
|
|
423
|
+
text = text.lower()
|
|
424
|
+
index = self.model.root.index()
|
|
425
|
+
for i in range(self.model.rowCount()):
|
|
426
|
+
v = self.model.root.child(i, 0).data()
|
|
427
|
+
self.tree_view.setRowHidden(i, index, text not in v["name"].lower())
|
|
428
|
+
else:
|
|
429
|
+
index = self.model.root.index()
|
|
430
|
+
for i in range(self.model.rowCount()):
|
|
431
|
+
v = self.model.root.child(i, 0).data()
|
|
432
|
+
self.tree_view.setRowHidden(i, index, text not in v["name"])
|
|
433
|
+
|
|
434
|
+
def doubleCliked(self, index):
|
|
435
|
+
self.explore_in_new_window(index)
|
|
436
|
+
|
|
437
|
+
def explore_in_new_window(self, index):
|
|
438
|
+
r = index.row()
|
|
439
|
+
v = self.model.root.child(r, 0).data()
|
|
440
|
+
act = bw2data.get_activity((v["database"], v["code"]))
|
|
441
|
+
show_activity(act)
|
|
442
|
+
|
|
443
|
+
def show(self, *args, **kwargs):
|
|
444
|
+
self.tree_view.setColumnWidth(0, 400)
|
|
445
|
+
super().show(*args, **kwargs)
|
|
446
|
+
|
|
274
447
|
|
|
275
448
|
# Replace IPython version to one compatible with Qt6
|
|
276
449
|
def start_event_loop_qt4(app=None):
|
|
@@ -297,6 +470,9 @@ def show_activity(act, params=None):
|
|
|
297
470
|
|
|
298
471
|
app = get_app_qt4()
|
|
299
472
|
|
|
473
|
+
if isinstance(act, tuple):
|
|
474
|
+
act = bw2data.get_activity(act)
|
|
475
|
+
|
|
300
476
|
if params is None:
|
|
301
477
|
activity_json = activity_to_json(act)
|
|
302
478
|
else:
|
|
@@ -311,6 +487,31 @@ def show_activity(act, params=None):
|
|
|
311
487
|
|
|
312
488
|
window.activateWindow()
|
|
313
489
|
|
|
490
|
+
def search(database, keywords=""):
|
|
491
|
+
|
|
492
|
+
if 'matplotlib' in sys.modules:
|
|
493
|
+
import matplotlib
|
|
494
|
+
if hasattr(matplotlib.backends, "backend"):
|
|
495
|
+
if 'qt' not in matplotlib.backends.backend:
|
|
496
|
+
print("WARNING: ActivityGUI will block, use `%matplotlib qt` to avoid blocking")
|
|
497
|
+
|
|
498
|
+
app = get_app_qt4()
|
|
499
|
+
|
|
500
|
+
if isinstance(database, str):
|
|
501
|
+
if database not in bw2data.databases:
|
|
502
|
+
fatal_dialog(f"Database {database} not found !")
|
|
503
|
+
return
|
|
504
|
+
database = bw2data.Database(database)
|
|
505
|
+
|
|
506
|
+
window = SearchWindow(database, keywords)
|
|
507
|
+
window.show()
|
|
508
|
+
|
|
509
|
+
if not is_event_loop_running_qt4(app):
|
|
510
|
+
start_event_loop_qt4(app)
|
|
511
|
+
|
|
512
|
+
window.activateWindow()
|
|
513
|
+
return window
|
|
514
|
+
|
|
314
515
|
def close_all():
|
|
315
516
|
for w in list(ActivityWindow.keep):
|
|
316
517
|
w.close()
|
{brightway_basic_explorer-0.9.dev4.dist-info → brightway_basic_explorer-0.9.dev6.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: brightway-basic-explorer
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.dev6
|
|
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>
|
|
@@ -29,13 +29,15 @@ Implement a basic activity explorer mostly to use in interractive python such as
|
|
|
29
29
|
pip install brightway_basic_explorer
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
#
|
|
32
|
+
# Documentation
|
|
33
33
|
|
|
34
34
|

|
|
35
35
|
|
|
36
|
+
## Show an activity
|
|
37
|
+
|
|
36
38
|
```python
|
|
37
39
|
import bw2data
|
|
38
|
-
from brightway_basic_explorer import show_activity, close_all
|
|
40
|
+
from brightway_basic_explorer import show_activity, close_all, search
|
|
39
41
|
|
|
40
42
|
db = bw2data.Database("ecoinvent-3.11-cutoff")
|
|
41
43
|
|
|
@@ -43,7 +45,27 @@ act = db.search("570kWp")
|
|
|
43
45
|
|
|
44
46
|
# Show the first activity
|
|
45
47
|
show_activity(act[0])
|
|
48
|
+
```
|
|
46
49
|
|
|
47
|
-
|
|
50
|
+
## Close all activities windows
|
|
51
|
+
|
|
52
|
+
```python
|
|
48
53
|
close_all()
|
|
49
54
|
```
|
|
55
|
+
|
|
56
|
+
## With lca_algebraic
|
|
57
|
+
|
|
58
|
+
If you are ussing lca_algebraic, you can specify parameters. Parameters are completed by default values and used in formula to compute the corresponding amount.
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
show_activity(act[0], {"paramter0": 10, "parameter1": 20})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Search within a database:
|
|
65
|
+
|
|
66
|
+
You can search within a database. Search require at less a not too short keyword. Keyword are separated by spaces, for instance:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
search("ecoinvent-3.11-cutoff", "570kwp GLO")
|
|
70
|
+
```
|
|
71
|
+
|
{brightway_basic_explorer-0.9.dev4.dist-info → brightway_basic_explorer-0.9.dev6.dist-info}/RECORD
RENAMED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
brightway_basic_explorer/__init__.py,sha256=
|
|
1
|
+
brightway_basic_explorer/__init__.py,sha256=XOlZ3f7fbGJmHwzPz3PAtkLC3G_AzD2lXW56Ifx-Fm8,19198
|
|
2
2
|
brightway_basic_explorer/icons/emission.png,sha256=js9yLt3jihZ1x7pXfuL-cXQ0HtC57Nrfxg58YfbWpfU,154
|
|
3
3
|
brightway_basic_explorer/icons/natural_resource.png,sha256=vbXPQful4vuibRNUoM4qze4hV2-eW3OkmkePUOnWsRI,162
|
|
4
4
|
brightway_basic_explorer/icons/process.png,sha256=zlJJ3J-2geqybqiL6YwGHFka0kqEIwKZqTeUzijPcE0,240
|
|
5
5
|
brightway_basic_explorer/icons/unknown.png,sha256=92LyvebtGvewcaLjEojQOWXJzeIicIVgWqvov9XhOrc,165
|
|
6
|
-
brightway_basic_explorer-0.9.
|
|
7
|
-
brightway_basic_explorer-0.9.
|
|
8
|
-
brightway_basic_explorer-0.9.
|
|
9
|
-
brightway_basic_explorer-0.9.
|
|
10
|
-
brightway_basic_explorer-0.9.
|
|
6
|
+
brightway_basic_explorer-0.9.dev6.dist-info/licenses/COPYING,sha256=b8nnCcy_4Nd_v_okJ6mDKCvi64jkexzbSfIag7TR5mU,13827
|
|
7
|
+
brightway_basic_explorer-0.9.dev6.dist-info/METADATA,sha256=L0x8SVO98MBYaOuhMaXlPSUSJffQlP7cVj_4muV4_tw,2118
|
|
8
|
+
brightway_basic_explorer-0.9.dev6.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
9
|
+
brightway_basic_explorer-0.9.dev6.dist-info/top_level.txt,sha256=YrW7Qnd1cgs1TDGBcL5wNmDg7XSiiwYsFymP7cfL0hU,25
|
|
10
|
+
brightway_basic_explorer-0.9.dev6.dist-info/RECORD,,
|
{brightway_basic_explorer-0.9.dev4.dist-info → brightway_basic_explorer-0.9.dev6.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|