brightway-basic-explorer 0.9.dev4__tar.gz → 0.9.dev6__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.
Files changed (16) hide show
  1. {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev6}/PKG-INFO +26 -4
  2. brightway_basic_explorer-0.9.dev6/README.md +50 -0
  3. {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev6}/brightway_basic_explorer/__init__.py +210 -9
  4. {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev6}/brightway_basic_explorer.egg-info/PKG-INFO +26 -4
  5. {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev6}/pyproject.toml +1 -1
  6. brightway_basic_explorer-0.9.dev4/README.md +0 -28
  7. {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev6}/COPYING +0 -0
  8. {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev6}/brightway_basic_explorer/icons/emission.png +0 -0
  9. {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev6}/brightway_basic_explorer/icons/natural_resource.png +0 -0
  10. {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev6}/brightway_basic_explorer/icons/process.png +0 -0
  11. {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev6}/brightway_basic_explorer/icons/unknown.png +0 -0
  12. {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev6}/brightway_basic_explorer.egg-info/SOURCES.txt +0 -0
  13. {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev6}/brightway_basic_explorer.egg-info/dependency_links.txt +0 -0
  14. {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev6}/brightway_basic_explorer.egg-info/requires.txt +0 -0
  15. {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev6}/brightway_basic_explorer.egg-info/top_level.txt +0 -0
  16. {brightway_basic_explorer-0.9.dev4 → brightway_basic_explorer-0.9.dev6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: brightway-basic-explorer
3
- Version: 0.9.dev4
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
- # Sample and Documentation
32
+ # Documentation
33
33
 
34
34
  ![Sample Window](https://github.com/gschwind/brightway-basic-explorer/raw/main/doc/sample-window.png)
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
- # Close all activity windows
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
+
@@ -0,0 +1,50 @@
1
+ # Brightway Basic Explorer
2
+
3
+ Implement a basic activity explorer mostly to use in interractive python such as notebooks, ipython and spyder.
4
+
5
+ # Install
6
+
7
+ ```
8
+ pip install brightway_basic_explorer
9
+ ```
10
+
11
+ # Documentation
12
+
13
+ ![Sample Window](https://github.com/gschwind/brightway-basic-explorer/raw/main/doc/sample-window.png)
14
+
15
+ ## Show an activity
16
+
17
+ ```python
18
+ import bw2data
19
+ from brightway_basic_explorer import show_activity, close_all, search
20
+
21
+ db = bw2data.Database("ecoinvent-3.11-cutoff")
22
+
23
+ act = db.search("570kWp")
24
+
25
+ # Show the first activity
26
+ show_activity(act[0])
27
+ ```
28
+
29
+ ## Close all activities windows
30
+
31
+ ```python
32
+ close_all()
33
+ ```
34
+
35
+ ## With lca_algebraic
36
+
37
+ 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.
38
+
39
+ ```python
40
+ show_activity(act[0], {"paramter0": 10, "parameter1": 20})
41
+ ```
42
+
43
+ ## Search within a database:
44
+
45
+ You can search within a database. Search require at less a not too short keyword. Keyword are separated by spaces, for instance:
46
+
47
+ ```python
48
+ search("ecoinvent-3.11-cutoff", "570kwp GLO")
49
+ ```
50
+
@@ -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.action_copy = self.addAction("Copy Full Reference")
107
- self.action_copy.triggered.connect(self.copy_full_reference_triggered)
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.action_explore = self.addAction("Explore")
110
- self.action_explore.triggered.connect(self.explore_triggered)
123
+ self.action_copy_code = self.addAction("Copy Activity Code")
124
+ self.action_copy_code.triggered.connect(self.copy_code_triggered)
111
125
 
112
- self.action_explore = self.addAction("Explore in new window")
113
- self.action_explore.triggered.connect(self.explore_in_new_window_triggered)
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 = TableModel()
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()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: brightway-basic-explorer
3
- Version: 0.9.dev4
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
- # Sample and Documentation
32
+ # Documentation
33
33
 
34
34
  ![Sample Window](https://github.com/gschwind/brightway-basic-explorer/raw/main/doc/sample-window.png)
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
- # Close all activity windows
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
+
@@ -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.dev4"
15
+ version = "0.9.dev6"
16
16
  dependencies = [ "ipython" ]
17
17
  requires-python = ">= 3.10"
18
18
  keywords = ["lca", "ecoinvent", "brightway"]
@@ -1,28 +0,0 @@
1
- # Brightway Basic Explorer
2
-
3
- Implement a basic activity explorer mostly to use in interractive python such as notebooks, ipython and spyder.
4
-
5
- # Install
6
-
7
- ```
8
- pip install brightway_basic_explorer
9
- ```
10
-
11
- # Sample and Documentation
12
-
13
- ![Sample Window](https://github.com/gschwind/brightway-basic-explorer/raw/main/doc/sample-window.png)
14
-
15
- ```python
16
- import bw2data
17
- from brightway_basic_explorer import show_activity, close_all
18
-
19
- db = bw2data.Database("ecoinvent-3.11-cutoff")
20
-
21
- act = db.search("570kWp")
22
-
23
- # Show the first activity
24
- show_activity(act[0])
25
-
26
- # Close all activity windows
27
- close_all()
28
- ```