brightway-basic-explorer 0.9.dev3__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.dev3 → brightway_basic_explorer-0.9.dev5}/PKG-INFO +1 -1
- brightway_basic_explorer-0.9.dev5/brightway_basic_explorer/__init__.py +509 -0
- {brightway_basic_explorer-0.9.dev3 → brightway_basic_explorer-0.9.dev5}/brightway_basic_explorer.egg-info/PKG-INFO +1 -1
- {brightway_basic_explorer-0.9.dev3 → brightway_basic_explorer-0.9.dev5}/pyproject.toml +1 -1
- brightway_basic_explorer-0.9.dev3/brightway_basic_explorer/__init__.py +0 -279
- {brightway_basic_explorer-0.9.dev3 → brightway_basic_explorer-0.9.dev5}/COPYING +0 -0
- {brightway_basic_explorer-0.9.dev3 → brightway_basic_explorer-0.9.dev5}/README.md +0 -0
- {brightway_basic_explorer-0.9.dev3 → brightway_basic_explorer-0.9.dev5}/brightway_basic_explorer/icons/emission.png +0 -0
- {brightway_basic_explorer-0.9.dev3 → brightway_basic_explorer-0.9.dev5}/brightway_basic_explorer/icons/natural_resource.png +0 -0
- {brightway_basic_explorer-0.9.dev3 → brightway_basic_explorer-0.9.dev5}/brightway_basic_explorer/icons/process.png +0 -0
- {brightway_basic_explorer-0.9.dev3 → brightway_basic_explorer-0.9.dev5}/brightway_basic_explorer/icons/unknown.png +0 -0
- {brightway_basic_explorer-0.9.dev3 → brightway_basic_explorer-0.9.dev5}/brightway_basic_explorer.egg-info/SOURCES.txt +0 -0
- {brightway_basic_explorer-0.9.dev3 → brightway_basic_explorer-0.9.dev5}/brightway_basic_explorer.egg-info/dependency_links.txt +0 -0
- {brightway_basic_explorer-0.9.dev3 → brightway_basic_explorer-0.9.dev5}/brightway_basic_explorer.egg-info/requires.txt +0 -0
- {brightway_basic_explorer-0.9.dev3 → brightway_basic_explorer-0.9.dev5}/brightway_basic_explorer.egg-info/top_level.txt +0 -0
- {brightway_basic_explorer-0.9.dev3 → 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>
|
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
|
|
3
|
+
from IPython.external.qt_for_kernel import QtGui, QtCore
|
|
4
|
+
from IPython.lib.guisupport import get_app_qt4, is_event_loop_running_qt4
|
|
5
|
+
|
|
6
|
+
import re
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
import bw2data
|
|
10
|
+
|
|
11
|
+
def activity_to_json(act):
|
|
12
|
+
a = dict(act)
|
|
13
|
+
exs = list()
|
|
14
|
+
for e in act.exchanges():
|
|
15
|
+
de = dict(bw2data.get_activity(e["input"]))
|
|
16
|
+
de["amount"] = e["amount"]
|
|
17
|
+
de["formula"] = e.get("formula", None)
|
|
18
|
+
exs.append(de)
|
|
19
|
+
a["exchanges"] = exs
|
|
20
|
+
return a
|
|
21
|
+
|
|
22
|
+
def activity_to_json_with_params(act, params):
|
|
23
|
+
try:
|
|
24
|
+
from lca_algebraic.params import (
|
|
25
|
+
all_params,
|
|
26
|
+
_complete_and_expand_params,
|
|
27
|
+
_getAmountOrFormula,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
from sympy import Basic
|
|
31
|
+
except:
|
|
32
|
+
raise Exception("lca_algebraic not found, please install it before using show_activity_with_params")
|
|
33
|
+
|
|
34
|
+
a = dict(act)
|
|
35
|
+
exs = list()
|
|
36
|
+
for e in act.exchanges():
|
|
37
|
+
de = dict(bw2data.get_activity(e["input"]))
|
|
38
|
+
|
|
39
|
+
amount = _getAmountOrFormula(e)
|
|
40
|
+
|
|
41
|
+
# Params provided ? Evaluate formulas
|
|
42
|
+
if isinstance(amount, Basic):
|
|
43
|
+
new_params = list(_complete_and_expand_params(params, list(all_params().keys())).items())
|
|
44
|
+
amount = amount.subs(new_params)
|
|
45
|
+
if amount.is_number:
|
|
46
|
+
amount = float(amount.evalf())
|
|
47
|
+
de["computed_amount"] = True
|
|
48
|
+
|
|
49
|
+
de["amount"] = amount
|
|
50
|
+
de["formula"] = e.get("formula", None)
|
|
51
|
+
exs.append(de)
|
|
52
|
+
a["exchanges"] = exs
|
|
53
|
+
return a
|
|
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
|
+
|
|
69
|
+
class QStandardItemRO(QtGui.QStandardItem):
|
|
70
|
+
def __init__(self, *args, data=None, **kwargs):
|
|
71
|
+
super().__init__(*args, **kwargs)
|
|
72
|
+
self.setEditable(False)
|
|
73
|
+
self.setSelectable(True)
|
|
74
|
+
self.setData(data)
|
|
75
|
+
|
|
76
|
+
class ExchangeModel(QtGui.QStandardItemModel):
|
|
77
|
+
def __init__(self, parent=None):
|
|
78
|
+
super().__init__(parent)
|
|
79
|
+
self.setColumnCount(6)
|
|
80
|
+
self.setHeaderData(0, QtCore.Qt.Orientation.Horizontal, "Name")
|
|
81
|
+
self.setHeaderData(1, QtCore.Qt.Orientation.Horizontal, "Unit")
|
|
82
|
+
self.setHeaderData(2, QtCore.Qt.Orientation.Horizontal, "Category")
|
|
83
|
+
self.setHeaderData(3, QtCore.Qt.Orientation.Horizontal, "Location")
|
|
84
|
+
self.setHeaderData(4, QtCore.Qt.Orientation.Horizontal, "Amount")
|
|
85
|
+
self.setHeaderData(5, QtCore.Qt.Orientation.Horizontal, "Formula")
|
|
86
|
+
|
|
87
|
+
def load(self, data):
|
|
88
|
+
self.exchanges = [e for e in data]
|
|
89
|
+
self.root = self.invisibleRootItem()
|
|
90
|
+
for e in data:
|
|
91
|
+
row = [
|
|
92
|
+
QStandardItemRO(str(e.get(k, "-")), data=e)
|
|
93
|
+
for k in ["name", "unit", "categories", "location", "amount", "formula"]
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
if "computed_amount" in e:
|
|
97
|
+
row[-2].setForeground(QtGui.QBrush(QtCore.Qt.GlobalColor.red))
|
|
98
|
+
|
|
99
|
+
etype = e.get("type", "unknown")
|
|
100
|
+
if etype == "emission":
|
|
101
|
+
path = os.path.join(os.path.dirname(__file__), "icons", "emission.png")
|
|
102
|
+
elif etype in {"process", "processwithreferenceproduct"}:
|
|
103
|
+
path = os.path.join(os.path.dirname(__file__), "icons", "process.png")
|
|
104
|
+
elif etype == "natural resource":
|
|
105
|
+
path = os.path.join(os.path.dirname(__file__), "icons", "natural_resource.png")
|
|
106
|
+
else:
|
|
107
|
+
path = os.path.join(os.path.dirname(__file__), "icons", "unknown.png")
|
|
108
|
+
|
|
109
|
+
row[0].setIcon(QtGui.QIcon(path))
|
|
110
|
+
self.root.appendRow(row)
|
|
111
|
+
|
|
112
|
+
class ActionMenu(QtGui.QMenu):
|
|
113
|
+
def __init__(self, parent, index):
|
|
114
|
+
super().__init__(parent)
|
|
115
|
+
self.index = index
|
|
116
|
+
|
|
117
|
+
self.action_copy = self.addAction("Copy")
|
|
118
|
+
self.action_copy.triggered.connect(self.copy_triggered)
|
|
119
|
+
|
|
120
|
+
self.action_copy = self.addAction("Copy Full Reference")
|
|
121
|
+
self.action_copy.triggered.connect(self.copy_full_reference_triggered)
|
|
122
|
+
|
|
123
|
+
if hasattr(self.parentWidget(), "explore"):
|
|
124
|
+
self.action_explore = self.addAction("Explore")
|
|
125
|
+
self.action_explore.triggered.connect(self.explore_triggered)
|
|
126
|
+
|
|
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)
|
|
130
|
+
|
|
131
|
+
def copy_triggered(self):
|
|
132
|
+
QtGui.QGuiApplication.clipboard().setText(self.index.data())
|
|
133
|
+
|
|
134
|
+
def copy_full_reference_triggered(self):
|
|
135
|
+
parent = self.parentWidget()
|
|
136
|
+
v = parent.model.root.child(self.index.row(), 0).data()
|
|
137
|
+
text = (v["database"], v["name"], v.get("location", None), v.get("categories", tuple()), v.get("unit", None))
|
|
138
|
+
QtGui.QGuiApplication.clipboard().setText(str(text))
|
|
139
|
+
|
|
140
|
+
def explore_triggered(self):
|
|
141
|
+
self.parentWidget().explore(self.index)
|
|
142
|
+
|
|
143
|
+
def explore_in_new_window_triggered(self):
|
|
144
|
+
self.parentWidget().explore_in_new_window(self.index)
|
|
145
|
+
|
|
146
|
+
class TabBar(QtGui.QTabBar):
|
|
147
|
+
def __init__(self):
|
|
148
|
+
super().__init__()
|
|
149
|
+
|
|
150
|
+
def tabSizeHint(self, index):
|
|
151
|
+
value = super().tabSizeHint(index)
|
|
152
|
+
return QtCore.QSize(200, value.height())
|
|
153
|
+
|
|
154
|
+
def minimumTabSizeHint(self, index):
|
|
155
|
+
value = super().minimumTabSizeHint(index)
|
|
156
|
+
return QtCore.QSize(100, value.height())
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class ActivityWindow(QtGui.QMainWindow):
|
|
160
|
+
keep = list()
|
|
161
|
+
|
|
162
|
+
def __init__(self, activity_json, params=None):
|
|
163
|
+
super().__init__()
|
|
164
|
+
self.tabs = dict()
|
|
165
|
+
self.xcount = 0
|
|
166
|
+
self.params = params
|
|
167
|
+
|
|
168
|
+
self.setWindowTitle("Activity Viewer")
|
|
169
|
+
self.setMinimumSize(QtCore.QSize(800, 600))
|
|
170
|
+
|
|
171
|
+
self.tabs_widget = QtGui.QTabWidget()
|
|
172
|
+
self.tabs_widget.setTabBar(TabBar())
|
|
173
|
+
self.tabs_widget.setTabsClosable(True)
|
|
174
|
+
self.tabs_widget.setMovable(True)
|
|
175
|
+
self.tabs_widget.tabCloseRequested.connect(self.close_tab)
|
|
176
|
+
self.tabs_widget.setElideMode(QtCore.Qt.TextElideMode.ElideRight)
|
|
177
|
+
self.setCentralWidget(self.tabs_widget)
|
|
178
|
+
self.add_activity(activity_json)
|
|
179
|
+
|
|
180
|
+
def add_activity(self, activity_json):
|
|
181
|
+
key = (activity_json["database"], activity_json["code"])
|
|
182
|
+
if key in self.tabs:
|
|
183
|
+
activity_widget = self.tabs[key]
|
|
184
|
+
self.tabs_widget.setCurrentWidget(activity_widget)
|
|
185
|
+
return
|
|
186
|
+
|
|
187
|
+
activity_widget = ActivityTab(self, activity_json)
|
|
188
|
+
self.tabs[activity_widget.activity_key] = activity_widget
|
|
189
|
+
self.tabs_widget.addTab(activity_widget, f"#{self.xcount} "+activity_json["name"])
|
|
190
|
+
self.tabs_widget.setCurrentWidget(activity_widget)
|
|
191
|
+
activity_widget.tree_view.setColumnWidth(0, 400)
|
|
192
|
+
self.xcount += 1
|
|
193
|
+
|
|
194
|
+
def close_tab(self, index):
|
|
195
|
+
w = self.tabs_widget.widget(index)
|
|
196
|
+
del self.tabs[w.activity_key]
|
|
197
|
+
self.tabs_widget.removeTab(index)
|
|
198
|
+
|
|
199
|
+
def closeEvent(self, ev):
|
|
200
|
+
ActivityWindow.keep.remove(self)
|
|
201
|
+
super().closeEvent(ev)
|
|
202
|
+
|
|
203
|
+
class ActivityTab(QtGui.QWidget):
|
|
204
|
+
def __init__(self, xparent, activity_json):
|
|
205
|
+
super().__init__()
|
|
206
|
+
|
|
207
|
+
self.xparent = xparent
|
|
208
|
+
self.activity_key = (activity_json["database"], activity_json["code"])
|
|
209
|
+
|
|
210
|
+
layout = QtGui.QVBoxLayout()
|
|
211
|
+
self.setLayout(layout)
|
|
212
|
+
|
|
213
|
+
grid = QtGui.QGridLayout()
|
|
214
|
+
layout.addLayout(grid)
|
|
215
|
+
|
|
216
|
+
for i, k in enumerate(["database", "name", "location", "unit", "categories", "type"]):
|
|
217
|
+
grid.addWidget(QtGui.QLabel(f"{k}:"), i, 0)
|
|
218
|
+
x = QtGui.QLabel(f"{str(activity_json.get(k, '-'))}")
|
|
219
|
+
x.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.TextSelectableByMouse)
|
|
220
|
+
grid.addWidget(x, i, 1)
|
|
221
|
+
next_row = grid.rowCount()
|
|
222
|
+
grid.addWidget(QtGui.QLabel("filter:"), next_row, 0)
|
|
223
|
+
self.filter_edit = QtGui.QLineEdit("")
|
|
224
|
+
self.filter_edit.textChanged.connect(self.update_filter)
|
|
225
|
+
grid.addWidget(self.filter_edit, next_row, 1)
|
|
226
|
+
grid.setColumnStretch(0, 0)
|
|
227
|
+
grid.setColumnStretch(1, 1)
|
|
228
|
+
self.ignore_case = QtGui.QCheckBox("Ignore Case")
|
|
229
|
+
self.ignore_case.setChecked(True)
|
|
230
|
+
if hasattr(self.ignore_case, "checkStateChanged"):
|
|
231
|
+
self.ignore_case.checkStateChanged.connect(self.update_filter)
|
|
232
|
+
else:
|
|
233
|
+
self.ignore_case.stateChanged.connect(self.update_filter)
|
|
234
|
+
next_row = grid.rowCount()
|
|
235
|
+
grid.addWidget(self.ignore_case, next_row, 1)
|
|
236
|
+
# Create tree view
|
|
237
|
+
self.tree_view = QtGui.QTreeView()
|
|
238
|
+
layout.addWidget(self.tree_view)
|
|
239
|
+
self.tree_view.setSortingEnabled(True)
|
|
240
|
+
self.tree_view.setAlternatingRowColors(True)
|
|
241
|
+
|
|
242
|
+
self.model = ExchangeModel()
|
|
243
|
+
self.model.load(activity_json["exchanges"])
|
|
244
|
+
self.tree_view.setModel(self.model)
|
|
245
|
+
self.tree_view.doubleClicked.connect(self.doubleCliked)
|
|
246
|
+
self.tree_view.setExpandsOnDoubleClick(False)
|
|
247
|
+
self.tree_view.setSelectionBehavior(QtGui.QAbstractItemView.SelectionBehavior.SelectItems)
|
|
248
|
+
#self.tree_view.rightClick.connect(self.rightClick)
|
|
249
|
+
self.tree_view.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.CustomContextMenu)
|
|
250
|
+
self.tree_view.customContextMenuRequested.connect(self.context_menu)
|
|
251
|
+
|
|
252
|
+
def context_menu(self, point):
|
|
253
|
+
index = self.tree_view.indexAt(point)
|
|
254
|
+
self.action_menu = ActionMenu(self, index)
|
|
255
|
+
self.action_menu.exec(self.tree_view.viewport().mapToGlobal(point))
|
|
256
|
+
|
|
257
|
+
def update_filter(self, *args):
|
|
258
|
+
text = self.filter_edit.text()
|
|
259
|
+
if self.ignore_case.isChecked():
|
|
260
|
+
text = text.lower()
|
|
261
|
+
index = self.model.root.index()
|
|
262
|
+
for i in range(self.model.rowCount()):
|
|
263
|
+
v = self.model.root.child(i, 0).data()
|
|
264
|
+
self.tree_view.setRowHidden(i, index, text not in v["name"].lower())
|
|
265
|
+
else:
|
|
266
|
+
index = self.model.root.index()
|
|
267
|
+
for i in range(self.model.rowCount()):
|
|
268
|
+
v = self.model.root.child(i, 0).data()
|
|
269
|
+
self.tree_view.setRowHidden(i, index, text not in v["name"])
|
|
270
|
+
|
|
271
|
+
def doubleCliked(self, index):
|
|
272
|
+
self.explore(index)
|
|
273
|
+
|
|
274
|
+
def explore(self, index):
|
|
275
|
+
r = index.row()
|
|
276
|
+
v = self.model.root.child(r, 0).data()
|
|
277
|
+
act = bw2data.get_activity((v["database"], v["code"]))
|
|
278
|
+
if self.xparent.params is None:
|
|
279
|
+
activity_json = activity_to_json(act)
|
|
280
|
+
else:
|
|
281
|
+
activity_json = activity_to_json_with_params(act, self.xparent.params)
|
|
282
|
+
self.xparent.add_activity(activity_json)
|
|
283
|
+
|
|
284
|
+
def explore_in_new_window(self, index):
|
|
285
|
+
r = index.row()
|
|
286
|
+
v = self.model.root.child(r, 0).data()
|
|
287
|
+
act = bw2data.get_activity((v["database"], v["code"]))
|
|
288
|
+
show_activity(act, self.xparent.params)
|
|
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
|
+
|
|
440
|
+
|
|
441
|
+
# Replace IPython version to one compatible with Qt6
|
|
442
|
+
def start_event_loop_qt4(app=None):
|
|
443
|
+
"""Start the qt event loop in a consistent manner."""
|
|
444
|
+
if app is None:
|
|
445
|
+
app = get_app_qt4([""])
|
|
446
|
+
if not is_event_loop_running_qt4(app):
|
|
447
|
+
app._in_event_loop = True
|
|
448
|
+
if hasattr(app, "exec_"):
|
|
449
|
+
app.exec_()
|
|
450
|
+
else:
|
|
451
|
+
app.exec()
|
|
452
|
+
app._in_event_loop = False
|
|
453
|
+
else:
|
|
454
|
+
app._in_event_loop = True
|
|
455
|
+
|
|
456
|
+
def show_activity(act, params=None):
|
|
457
|
+
|
|
458
|
+
if 'matplotlib' in sys.modules:
|
|
459
|
+
import matplotlib
|
|
460
|
+
if hasattr(matplotlib.backends, "backend"):
|
|
461
|
+
if 'qt' not in matplotlib.backends.backend:
|
|
462
|
+
print("WARNING: ActivityGUI will block, use `%matplotlib qt` to avoid blocking")
|
|
463
|
+
|
|
464
|
+
app = get_app_qt4()
|
|
465
|
+
|
|
466
|
+
if params is None:
|
|
467
|
+
activity_json = activity_to_json(act)
|
|
468
|
+
else:
|
|
469
|
+
activity_json = activity_to_json_with_params(act, params)
|
|
470
|
+
|
|
471
|
+
window = ActivityWindow(activity_json, params)
|
|
472
|
+
ActivityWindow.keep.append(window)
|
|
473
|
+
window.show()
|
|
474
|
+
|
|
475
|
+
if not is_event_loop_running_qt4(app):
|
|
476
|
+
start_event_loop_qt4(app)
|
|
477
|
+
|
|
478
|
+
window.activateWindow()
|
|
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
|
+
|
|
507
|
+
def close_all():
|
|
508
|
+
for w in list(ActivityWindow.keep):
|
|
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"]
|
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
# coding=utf-8
|
|
2
|
-
|
|
3
|
-
from IPython.external.qt_for_kernel import QtGui, QtCore
|
|
4
|
-
from IPython.lib.guisupport import start_event_loop_qt4, get_app_qt4, is_event_loop_running_qt4
|
|
5
|
-
|
|
6
|
-
import os
|
|
7
|
-
import sys
|
|
8
|
-
import bw2data
|
|
9
|
-
|
|
10
|
-
def activity_to_json(act):
|
|
11
|
-
a = dict(act)
|
|
12
|
-
exs = list()
|
|
13
|
-
for e in act.exchanges():
|
|
14
|
-
de = dict(bw2data.get_activity(e["input"]))
|
|
15
|
-
de["amount"] = e["amount"]
|
|
16
|
-
de["formula"] = e.get("formula", None)
|
|
17
|
-
exs.append(de)
|
|
18
|
-
a["exchanges"] = exs
|
|
19
|
-
return a
|
|
20
|
-
|
|
21
|
-
def activity_to_json_with_params(act, params):
|
|
22
|
-
try:
|
|
23
|
-
from lca_algebraic.params import (
|
|
24
|
-
all_params,
|
|
25
|
-
_complete_and_expand_params,
|
|
26
|
-
_getAmountOrFormula,
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
from sympy import Basic, Symbol
|
|
30
|
-
except:
|
|
31
|
-
raise Exception("lca_algebraic not found, please install it before using show_activity_with_params")
|
|
32
|
-
|
|
33
|
-
a = dict(act)
|
|
34
|
-
exs = list()
|
|
35
|
-
for e in act.exchanges():
|
|
36
|
-
de = dict(bw2data.get_activity(e["input"]))
|
|
37
|
-
|
|
38
|
-
amount = _getAmountOrFormula(e)
|
|
39
|
-
|
|
40
|
-
# Params provided ? Evaluate formulas
|
|
41
|
-
if isinstance(amount, Basic):
|
|
42
|
-
print(amount)
|
|
43
|
-
new_params = [(name, value) for name, value in _complete_and_expand_params(params, list(all_params().keys())).items()]
|
|
44
|
-
print(new_params)
|
|
45
|
-
amount = amount.subs(new_params).evalf()
|
|
46
|
-
print(amount)
|
|
47
|
-
de["computed_amount"] = True
|
|
48
|
-
|
|
49
|
-
de["amount"] = amount
|
|
50
|
-
de["formula"] = e.get("formula", None)
|
|
51
|
-
exs.append(de)
|
|
52
|
-
a["exchanges"] = exs
|
|
53
|
-
return a
|
|
54
|
-
|
|
55
|
-
class QStandardItemRO(QtGui.QStandardItem):
|
|
56
|
-
def __init__(self, *args, data=None, **kwargs):
|
|
57
|
-
super().__init__(*args, **kwargs)
|
|
58
|
-
self.setEditable(False)
|
|
59
|
-
self.setSelectable(True)
|
|
60
|
-
self.setData(data)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
class TableModel(QtGui.QStandardItemModel):
|
|
64
|
-
def __init__(self, parent=None):
|
|
65
|
-
super().__init__(parent)
|
|
66
|
-
self.setColumnCount(6)
|
|
67
|
-
self.setHeaderData(0, QtCore.Qt.Orientation.Horizontal, "Name")
|
|
68
|
-
self.setHeaderData(1, QtCore.Qt.Orientation.Horizontal, "Unit")
|
|
69
|
-
self.setHeaderData(2, QtCore.Qt.Orientation.Horizontal, "Category")
|
|
70
|
-
self.setHeaderData(3, QtCore.Qt.Orientation.Horizontal, "Location")
|
|
71
|
-
self.setHeaderData(4, QtCore.Qt.Orientation.Horizontal, "Amount")
|
|
72
|
-
self.setHeaderData(5, QtCore.Qt.Orientation.Horizontal, "Formula")
|
|
73
|
-
|
|
74
|
-
def load(self, data):
|
|
75
|
-
self.exchanges = [e for e in data]
|
|
76
|
-
self.root = self.invisibleRootItem()
|
|
77
|
-
for e in data:
|
|
78
|
-
row = [
|
|
79
|
-
QStandardItemRO(str(e.get(k, "-")), data=e)
|
|
80
|
-
for k in ["name", "unit", "categories", "location", "amount", "formula"]
|
|
81
|
-
]
|
|
82
|
-
|
|
83
|
-
if "computed_amount" in e:
|
|
84
|
-
row[-2].setForeground(QtGui.QBrush(QtCore.Qt.GlobalColor.red))
|
|
85
|
-
|
|
86
|
-
etype = e.get("type", "unknown")
|
|
87
|
-
if etype == "emission":
|
|
88
|
-
path = os.path.join(os.path.dirname(__file__), "icons", "emission.png")
|
|
89
|
-
elif etype in {"process", "processwithreferenceproduct"}:
|
|
90
|
-
path = os.path.join(os.path.dirname(__file__), "icons", "process.png")
|
|
91
|
-
elif etype == "natural resource":
|
|
92
|
-
path = os.path.join(os.path.dirname(__file__), "icons", "natural_resource.png")
|
|
93
|
-
else:
|
|
94
|
-
path = os.path.join(os.path.dirname(__file__), "icons", "unknown.png")
|
|
95
|
-
|
|
96
|
-
row[0].setIcon(QtGui.QIcon(path))
|
|
97
|
-
self.root.appendRow(row)
|
|
98
|
-
|
|
99
|
-
class ActionMenu(QtGui.QMenu):
|
|
100
|
-
def __init__(self, parent, index):
|
|
101
|
-
super().__init__(parent)
|
|
102
|
-
self.index = index
|
|
103
|
-
|
|
104
|
-
self.action_copy = self.addAction("Copy")
|
|
105
|
-
self.action_copy.triggered.connect(self.copy_triggered)
|
|
106
|
-
|
|
107
|
-
self.action_copy = self.addAction("Copy Full Reference")
|
|
108
|
-
self.action_copy.triggered.connect(self.copy_full_reference_triggered)
|
|
109
|
-
|
|
110
|
-
self.action_explore = self.addAction("Explore")
|
|
111
|
-
self.action_explore.triggered.connect(self.explore_triggered)
|
|
112
|
-
|
|
113
|
-
def copy_triggered(self):
|
|
114
|
-
QtGui.QGuiApplication.clipboard().setText(self.index.data())
|
|
115
|
-
|
|
116
|
-
def copy_full_reference_triggered(self):
|
|
117
|
-
parent = self.parentWidget()
|
|
118
|
-
v = parent.model.root.child(self.index.row(), 0).data()
|
|
119
|
-
text = (v["database"], v["name"], v.get("location", None), v.get("categories", tuple()), v.get("unit", None))
|
|
120
|
-
QtGui.QGuiApplication.clipboard().setText(str(text))
|
|
121
|
-
|
|
122
|
-
def explore_triggered(self):
|
|
123
|
-
self.parentWidget().explore(self.index)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
class ActivityWindow(QtGui.QMainWindow):
|
|
127
|
-
keep = dict()
|
|
128
|
-
|
|
129
|
-
def __init__(self, activity_json):
|
|
130
|
-
super().__init__()
|
|
131
|
-
self.activity_key = (activity_json["database"], activity_json["code"])
|
|
132
|
-
|
|
133
|
-
self.setWindowTitle("Activity Viewer")
|
|
134
|
-
self.setGeometry(100, 100, 800, 600)
|
|
135
|
-
|
|
136
|
-
# Create central widget and layout
|
|
137
|
-
central_widget = QtGui.QWidget()
|
|
138
|
-
self.setCentralWidget(central_widget)
|
|
139
|
-
layout = QtGui.QVBoxLayout()
|
|
140
|
-
central_widget.setLayout(layout)
|
|
141
|
-
|
|
142
|
-
grid = QtGui.QGridLayout()
|
|
143
|
-
layout.addLayout(grid)
|
|
144
|
-
|
|
145
|
-
for i, k in enumerate(["database", "name", "location", "unit", "categories", "type"]):
|
|
146
|
-
grid.addWidget(QtGui.QLabel(f"{k}:"), i, 0)
|
|
147
|
-
x = QtGui.QLabel(f"{str(activity_json.get(k, '-'))}")
|
|
148
|
-
x.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.TextSelectableByMouse)
|
|
149
|
-
grid.addWidget(x, i, 1)
|
|
150
|
-
next_row = grid.rowCount()
|
|
151
|
-
grid.addWidget(QtGui.QLabel("filter:"), next_row, 0)
|
|
152
|
-
self.filter_edit = QtGui.QLineEdit("")
|
|
153
|
-
self.filter_edit.textChanged.connect(self.update_filter)
|
|
154
|
-
grid.addWidget(self.filter_edit, next_row, 1)
|
|
155
|
-
grid.setColumnStretch(0, 0)
|
|
156
|
-
grid.setColumnStretch(1, 1)
|
|
157
|
-
self.ignore_case = QtGui.QCheckBox("Ignore Case")
|
|
158
|
-
self.ignore_case.setChecked(True)
|
|
159
|
-
if hasattr(self.ignore_case, "checkStateChanged"):
|
|
160
|
-
self.ignore_case.checkStateChanged.connect(self.update_filter)
|
|
161
|
-
else:
|
|
162
|
-
self.ignore_case.stateChanged.connect(self.update_filter)
|
|
163
|
-
next_row = grid.rowCount()
|
|
164
|
-
grid.addWidget(self.ignore_case, next_row, 1)
|
|
165
|
-
# Create tree view
|
|
166
|
-
self.tree_view = QtGui.QTreeView()
|
|
167
|
-
layout.addWidget(self.tree_view)
|
|
168
|
-
self.tree_view.setSortingEnabled(True)
|
|
169
|
-
self.tree_view.setAlternatingRowColors(True)
|
|
170
|
-
|
|
171
|
-
self.model = TableModel()
|
|
172
|
-
self.model.load(activity_json["exchanges"])
|
|
173
|
-
self.tree_view.setModel(self.model)
|
|
174
|
-
self.tree_view.doubleClicked.connect(self.doubleCliked)
|
|
175
|
-
self.tree_view.setExpandsOnDoubleClick(False)
|
|
176
|
-
self.tree_view.setSelectionBehavior(QtGui.QAbstractItemView.SelectionBehavior.SelectItems)
|
|
177
|
-
#self.tree_view.rightClick.connect(self.rightClick)
|
|
178
|
-
self.tree_view.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.CustomContextMenu)
|
|
179
|
-
self.tree_view.customContextMenuRequested.connect(self.context_menu)
|
|
180
|
-
|
|
181
|
-
def context_menu(self, point):
|
|
182
|
-
index = self.tree_view.indexAt(point)
|
|
183
|
-
self.action_menu = ActionMenu(self, index)
|
|
184
|
-
self.action_menu.exec(self.tree_view.viewport().mapToGlobal(point))
|
|
185
|
-
|
|
186
|
-
def closeEvent(self, ev):
|
|
187
|
-
if self.activity_key in ActivityWindow.keep:
|
|
188
|
-
del ActivityWindow.keep[self.activity_key]
|
|
189
|
-
super().closeEvent(ev)
|
|
190
|
-
|
|
191
|
-
def update_filter(self, *args):
|
|
192
|
-
text = self.filter_edit.text()
|
|
193
|
-
if self.ignore_case.isChecked():
|
|
194
|
-
text = text.lower()
|
|
195
|
-
index = self.model.root.index()
|
|
196
|
-
for i in range(self.model.rowCount()):
|
|
197
|
-
v = self.model.root.child(i, 0).data()
|
|
198
|
-
self.tree_view.setRowHidden(i, index, text not in v["name"].lower())
|
|
199
|
-
else:
|
|
200
|
-
index = self.model.root.index()
|
|
201
|
-
for i in range(self.model.rowCount()):
|
|
202
|
-
v = self.model.root.child(i, 0).data()
|
|
203
|
-
self.tree_view.setRowHidden(i, index, text not in v["name"])
|
|
204
|
-
|
|
205
|
-
def doubleCliked(self, index):
|
|
206
|
-
self.explore(index)
|
|
207
|
-
|
|
208
|
-
def explore(self, index):
|
|
209
|
-
r = index.row()
|
|
210
|
-
v = self.model.root.child(r, 0).data()
|
|
211
|
-
show_activity(bw2data.get_activity((v["database"], v["code"])), self.params)
|
|
212
|
-
|
|
213
|
-
def show(self, *args, **kwargs):
|
|
214
|
-
super().show(*args, **kwargs)
|
|
215
|
-
self.tree_view.setColumnWidth(0, 400)
|
|
216
|
-
|
|
217
|
-
class ActivityWindowWithParams(ActivityWindow):
|
|
218
|
-
keep = dict()
|
|
219
|
-
|
|
220
|
-
def __init__(self, activity_json, params):
|
|
221
|
-
super().__init__(activity_json)
|
|
222
|
-
self.params = params
|
|
223
|
-
|
|
224
|
-
def closeEvent(self, ev):
|
|
225
|
-
if self.activity_key in ActivityWindow.keep:
|
|
226
|
-
del ActivityWindowWithParams.keep[self.activity_key]
|
|
227
|
-
super().closeEvent(ev)
|
|
228
|
-
|
|
229
|
-
def explore(self, index):
|
|
230
|
-
r = index.row()
|
|
231
|
-
v = self.model.root.child(r, 0).data()
|
|
232
|
-
show_activity_with_params(bw2data.get_activity((v["database"], v["code"])), self.params)
|
|
233
|
-
|
|
234
|
-
# Replace IPython version to one compatible with Qt6
|
|
235
|
-
def start_event_loop_qt4(app=None):
|
|
236
|
-
"""Start the qt event loop in a consistent manner."""
|
|
237
|
-
if app is None:
|
|
238
|
-
app = get_app_qt4([""])
|
|
239
|
-
if not is_event_loop_running_qt4(app):
|
|
240
|
-
app._in_event_loop = True
|
|
241
|
-
if hasattr(app, "exec_"):
|
|
242
|
-
app.exec_()
|
|
243
|
-
else:
|
|
244
|
-
app.exec()
|
|
245
|
-
app._in_event_loop = False
|
|
246
|
-
else:
|
|
247
|
-
app._in_event_loop = True
|
|
248
|
-
|
|
249
|
-
def _show_activity(cls, activity_json, *args):
|
|
250
|
-
|
|
251
|
-
if 'matplotlib' in sys.modules:
|
|
252
|
-
import matplotlib
|
|
253
|
-
if hasattr(matplotlib.backends, "backend"):
|
|
254
|
-
if 'qt' not in matplotlib.backends.backend:
|
|
255
|
-
print("WARNING: ActivityGUI will block, use `%matplotlib qt` to avoid blocking")
|
|
256
|
-
|
|
257
|
-
app = get_app_qt4()
|
|
258
|
-
|
|
259
|
-
if (window := cls.keep.get((activity_json["database"], activity_json["code"]), None)) is None:
|
|
260
|
-
window = cls(activity_json, *args)
|
|
261
|
-
cls.keep[(activity_json["database"], activity_json["code"])] = window
|
|
262
|
-
window.show()
|
|
263
|
-
|
|
264
|
-
if not is_event_loop_running_qt4(app):
|
|
265
|
-
start_event_loop_qt4(app)
|
|
266
|
-
|
|
267
|
-
window.activateWindow()
|
|
268
|
-
|
|
269
|
-
def show_activity(act):
|
|
270
|
-
activity_json = activity_to_json(act)
|
|
271
|
-
_show_activity(ActivityWindow, activity_json)
|
|
272
|
-
|
|
273
|
-
def show_activity_with_params(act, params):
|
|
274
|
-
activity_json = activity_to_json_with_params(act, params)
|
|
275
|
-
_show_activity(ActivityWindowWithParams, activity_json, params)
|
|
276
|
-
|
|
277
|
-
def close_all():
|
|
278
|
-
for w in list(ActivityWindow.keep.values()):
|
|
279
|
-
w.close()
|
|
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
|