Skip to content

Commit e18f179

Browse files
panosathadladrichemmaartenvanormondtfrederique-hubSantonia27
authored
Env fix (#65)
* initial changes to choropleth layer * updates in choropleth for efficient loading and legend * changes based on review * Add multiselect choices for tables * Format black * Revert "Format black" This reverts commit de1732d. * Fix example * Fix cht_tiling * small fixes in choropleth mapbox layer * updated loading per zoom level in point and choro layers * small correction to avoid error messase for noselection tables * added favicon.ico in the served folder * fixed most legend issues (#42) * added second option for choropleth coloring (#44) * small change in units in choropleths legend (#45) * Env fix mvo (#43) * fixed most legend issues still need more generic way * major changes in mapbox layers and tableview allow for same layer js function in both main.js and mapbox_compare.js table renamed to tableview * more guitares updates added mapbox_simple (will probably remove that later) popup windows can get optional id made layers a bit more robust * allow value to be a string (format: yyyymmdd HHMMSS) in datetimeedit elements * added icons for cyclone track layer * just some formatting changes * fixed visibility issue for choropleth layer * finished cyclone_track_layer * removed mapbox simple also stopped mapbox from being loaded if it is made invisible by a dependency * updated marker layer and changed names of nearly all layer modules and js files * removed svg files * now using resource file for arrows * fixed popup width and height * correct choropleth redrawing --------- Co-authored-by: Panos <[email protected]> * added coloring configuration for choropleths (#48) * add functionality to have option as variable in radiobuttons (#50) * add functionality to have option as variable in radiobuttons * small correction * Add column selection * Add class callbacks * Add variable deleting * Add okay method checking * Fix header selection in column selection * Change hardcoded bins to loop * Fix tiling cht in pyproject.toml * Add new icons * added server icons paths in pyproject.toml * Added more custom coloring of layers (#60) * start of custom coloring of points * Implemented custom coloring of points * first implementation of custom polygon layers * speed up determining the column width * Small bug * test pushadd additional_attribute layer * add polygon_layer_additional_attr * Custom coloring of circle layer also possible for big_data = True * add display circle layer * remove display circle layer custom * Fixed small bug not displaying points * For now removing the legend in the if-statements, still to be implemented * add units to hovering proprties * made width of columns in table view flexible --------- Co-authored-by: sarah.rautenbach <[email protected]> Co-authored-by: Panos Athanasiou <[email protected]> --------- Co-authored-by: Daley Adrichem <[email protected]> Co-authored-by: dladrichem <[email protected]> Co-authored-by: Maarten van Ormondt <[email protected]> Co-authored-by: frederique-hub <[email protected]> Co-authored-by: sarah.rautenbach <[email protected]>
1 parent bec2897 commit e18f179

File tree

103 files changed

+4374
-1497
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

103 files changed

+4374
-1497
lines changed

examples/table/table.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ window:
33
width: 200
44
height: 200
55
module: callbacks
6-
variable_group: table
6+
variable_group: table
77
element:
88
- style: table
99
position:
@@ -17,6 +17,7 @@ element:
1717
option_value:
1818
variable: dataframe
1919
method: select
20+
selection_type: 'SingleSelection'
2021
- style: text
2122
position:
2223
x: 20

pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,18 @@ dependencies = [
2525
"pyyaml",
2626
"pyqt5",
2727
"pyqtwebengine",
28+
"cht_tiling @ git+https://github.com/Deltares-research/cht_tiling.git",
29+
"datashader",
30+
"spatialpandas",
31+
"pyogrio"
2832
]
2933

3034
[tool.setuptools.package-data]
3135
"guitares.pyqt5.mapbox.server" = ["*.js", "*.html"]
3236
"guitares.pyqt5.mapbox.server.js" = ["*.js"]
3337
"guitares.pyqt5.mapbox.server.js.img" = ["*.png"]
3438
"guitares.pyqt5.mapbox.server.css" = ["*.css"]
39+
"guitares.pyqt5.mapbox.server.icons" = ["*.png"]
3540

3641
[project.urls]
3742
"Homepage" = "https://github.com/Deltares/Guitares"

src/guitares/element.py

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ def __init__(self, dct, parent, window):
7575
self.fraction_collapsed = 1.0
7676
self.fraction_expanded = 0.5
7777
self.multiselection = False
78+
self.sortable = True
79+
self.selection_type = "single"
80+
self.ready = False
7881

7982
# Now update element attributes based on dict
8083

@@ -107,10 +110,28 @@ def __init__(self, dct, parent, window):
107110
self.callback = self.method
108111
else:
109112
if self.module and self.method:
110-
if hasattr(self.module, self.method):
111-
self.callback = getattr(self.module, self.method)
112-
else:
113-
print("Error! Could not find method " + self.method)
113+
try:
114+
# Start with the base module
115+
module = self.module
116+
# If the method contains a dot, it means that it is a method of a class
117+
method_path = self.method.split(".")
118+
# Loop through the method path
119+
for idx, method in enumerate(method_path):
120+
# The last element is the method itself
121+
if idx == len(method_path) - 1:
122+
if hasattr(module, method):
123+
self.callback = getattr(module, method)
124+
else:
125+
raise Exception("Error! Could not find method " + self.method + " in module " + self.module.__name__)
126+
else:
127+
if hasattr(module, method):
128+
class_ = getattr(module, method)
129+
# Initialize the class object
130+
module = class_()
131+
else:
132+
raise Exception("Error! Could not find method " + self.method + " in module " + self.module.__name__)
133+
except Exception as e:
134+
print(e)
114135
if self.variable_group in self.gui.variables:
115136
if self.variable in self.gui.variables[self.variable_group]:
116137
self.type = type(self.gui.variables[self.variable_group][self.variable]["value"])
@@ -188,7 +209,13 @@ def __init__(self, dct, parent, window):
188209
self.multiselection = dct["multiselection"]
189210
if "enable" in dct:
190211
self.enable = dct["enable"]
191-
212+
if "selection_type" in dct:
213+
self.selection_type = dct["selection_type"]
214+
if "selection_direction" in dct:
215+
self.selection_direction = dct["selection_direction"]
216+
if "sortable" in dct:
217+
self.sortable = dct["sortable"]
218+
192219
if "dependency" in dct:
193220
for dep in dct["dependency"]:
194221
dependency = Dependency()
@@ -227,8 +254,9 @@ def __init__(self, dct, parent, window):
227254
try:
228255
if tab_dct["module"]:
229256
tab.module = importlib.import_module(tab_dct["module"])
230-
except:
257+
except Exception as e:
231258
print("Error! Module " + tab_dct["module"] + " could not be imported!")
259+
print(e)
232260
self.tabs.append(tab)
233261

234262
def add(self):
@@ -272,9 +300,9 @@ def add(self):
272300
from .pyqt5.listbox import ListBox
273301
self.widget = ListBox(self)
274302

275-
elif self.style == "table":
276-
from .pyqt5.table import Table
277-
self.widget = Table(self)
303+
elif self.style == "tableview":
304+
from .pyqt5.tableview import TableView
305+
self.widget = TableView(self)
278306

279307
elif self.style == "checkbox":
280308
from .pyqt5.checkbox import CheckBox
@@ -297,12 +325,30 @@ def add(self):
297325
self.widget = PushSaveFile(self)
298326

299327
elif self.style == "mapbox":
300-
from .pyqt5.mapbox.mapbox import MapBox
301-
self.widget = MapBox(self)
328+
# We don't want to add the mapbox widget if set to invisible, because it takes a long time to load.
329+
# This means that widgets that are originally set to invisible will not be added to the GUI!
330+
okay = True
331+
if self.dependencies:
332+
for dep in self.dependencies:
333+
if dep.action == "visible":
334+
if not dep.get():
335+
okay = False
336+
if okay:
337+
from .pyqt5.mapbox.mapbox import MapBox
338+
self.widget = MapBox(self)
302339

303340
elif self.style == "mapbox_compare":
304-
from .pyqt5.mapbox.mapbox_compare import MapBoxCompare
305-
self.widget = MapBoxCompare(self)
341+
# We don't want to add the mapbox widget if set to invisible, because it takes a long time to load.
342+
# This means that widgets that are originally set to invisible will not be added to the GUI!
343+
okay = True
344+
if self.dependencies:
345+
for dep in self.dependencies:
346+
if dep.action == "visible":
347+
if not dep.get():
348+
okay = False
349+
if okay:
350+
from .pyqt5.mapbox.mapbox_compare import MapBoxCompare
351+
self.widget = MapBoxCompare(self)
306352

307353
elif self.style == "webpage":
308354
from .pyqt5.webpage import WebPage

src/guitares/gui.py

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
from guitares.window import Window
1313
from guitares.server import start_server
1414

15+
import guitares.icons_rc
16+
1517
class GUI:
1618
def __init__(self, module,
1719
framework="pyqt5",
@@ -25,6 +27,7 @@ def __init__(self, module,
2527
server_nodejs=False,
2628
js_messages=True,
2729
copy_mapbox_server_folder=True,
30+
icon_path=None,
2831
mapbox_token_file="mapbox_token.txt"):
2932

3033
self.module = module
@@ -43,7 +46,8 @@ def __init__(self, module,
4346
self.server_thread = None
4447
self.server_nodejs = server_nodejs
4548
self.js_messages = js_messages
46-
self.popup_data = None
49+
self.popup_window = {}
50+
self.popup_data = {}
4751
self.resize_factor = 1.0
4852

4953
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts)
@@ -70,6 +74,7 @@ def __init__(self, module,
7074
shutil.rmtree(server_path)
7175
# Now copy over folder from mapbox
7276
shutil.copytree(mpboxpth, server_path)
77+
7378
# Read mapbox token and store in js file in server path
7479
if os.path.exists(os.path.join(self.config_path, mapbox_token_file)):
7580
fid = open(os.path.join(self.config_path, mapbox_token_file), "r")
@@ -78,7 +83,16 @@ def __init__(self, module,
7883
fid = open(os.path.join(server_path, "mapbox_token.js"), "w")
7984
fid.write("mapbox_token = '" + mapbox_token[0].strip() + "';")
8085
fid.close()
81-
start_server(server_path, port=server_port, node=self.server_nodejs)
86+
87+
if icon_path:
88+
# Copy all files in icon_path to server_path/icons
89+
if not os.path.exists(os.path.join(server_path, "icons")):
90+
os.mkdir(os.path.join(server_path, "icons"))
91+
for file in os.listdir(icon_path):
92+
if file.endswith(".png"):
93+
shutil.copy(os.path.join(icon_path, file), os.path.join(server_path, "icons", file))
94+
95+
start_server(server_path, port=server_port, node=self.server_nodejs)
8296

8397
def show_splash(self):
8498
if self.framework == "pyqt5" and self.splash_file:
@@ -124,14 +138,23 @@ def setvar(self, group, name, value):
124138

125139
def getvar(self, group, name):
126140
if group not in self.variables:
127-
print("Error! GUI variable group '" + group + "' not defined !")
141+
print("Error! Cannot get variable! GUI variable group '" + group + "' not defined!")
128142
return None
129143
elif name not in self.variables[group]:
130-
print("Error! GUI variable '" + name + "' not defined in group '" + group + "'!")
144+
print("Error! Cannot get variable! GUI variable '" + name + "' not defined in group '" + group + "'!")
131145
return None
132146
return self.variables[group][name]["value"]
147+
148+
def delvar(self, group, name):
149+
if group not in self.variables:
150+
print("Error! Cannot delete variable! GUI variable group '" + group + "' not defined!")
151+
return None
152+
elif name not in self.variables[group]:
153+
print("Error! Cannot delete variable! GUI variable '" + name + "' not defined in group '" + group + "'!")
154+
return None
155+
del self.variables[group][name]
133156

134-
def popup(self, config, data=None):
157+
def popup(self, config, id="popup", data=None):
135158
# Make pop-up window
136159
# config needs to be file name of yml file, or configuration dict
137160
# Data is optional and can have any shape (e.g. dict, str, object, etc.)
@@ -141,15 +164,16 @@ def popup(self, config, data=None):
141164
file_name = os.path.basename(config)
142165
config = self.read_gui_config(path, file_name)
143166
if data:
144-
self.popup_data = copy.copy(data)
167+
self.popup_data[id] = copy.copy(data)
145168
else:
146-
self.popup_data = None
147-
self.popup_window = Window(config, self, type="popup")
148-
p = self.popup_window.build()
169+
self.popup_data[id] = None
170+
self.popup_window[id] = Window(config, self, type="popup")
171+
p = self.popup_window[id].build()
149172
okay = False
150173
if p.result() == 1:
151174
okay = True
152-
data = self.popup_data
175+
data = self.popup_data[id]
176+
# Remove popup window and data
153177
return okay, data
154178

155179
def read_gui_config(self, path, file_name):

src/guitares/icons.qrc

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<!DOCTYPE RCC>
2+
<!-- Build with: pyrcc5 icons.qrc -o icons_rc.py -->
3+
<RCC version="1.0">
4+
<qresource>
5+
<file>img/category_1_hurricane_icon_24x48.png</file>
6+
<file>img/category_2_hurricane_icon_24x48.png</file>
7+
<file>img/category_3_hurricane_icon_24x48.png</file>
8+
<file>img/category_4_hurricane_icon_24x48.png</file>
9+
<file>img/category_5_hurricane_icon_24x48.png</file>
10+
<file>img/icons8-triangle-arrow-24_white_right.png</file>
11+
<file>img/icons8-triangle-arrow-24_white_left.png</file>
12+
<file>img/icons8-triangle-arrow-24_black_right.png</file>
13+
<file>img/icons8-triangle-arrow-24_black_left.png</file>
14+
<file>img/icons8-triangle-arrow-24_black_up.png</file>
15+
<file>img/icons8-triangle-arrow-24_black_down.png</file>
16+
<file>img/icons8-triangle-arrow-16_white_right.png</file>
17+
<file>img/icons8-triangle-arrow-16_white_left.png</file>
18+
<file>img/icons8-triangle-arrow-16_white_up.png</file>
19+
<file>img/icons8-triangle-arrow-16_white_down.png</file>
20+
<file>img/white_up_48x48.png</file>
21+
<file>img/white_right_48x48.png</file>
22+
<file>img/white_down_48x48.png</file>
23+
<file>img/white_left_48x48.png</file>
24+
<file>img/black_up_48x48.png</file>
25+
<file>img/black_right_48x48.png</file>
26+
<file>img/black_down_48x48.png</file>
27+
<file>img/black_left_48x48.png</file>
28+
<file>img/mapbox-marker-icon-20px-blue.png</file>
29+
<file>img/mapbox-marker-icon-20px-gray.png</file>
30+
<file>img/mapbox-marker-icon-20px-green.png</file>
31+
<file>img/mapbox-marker-icon-20px-orange.png</file>
32+
<file>img/mapbox-marker-icon-20px-pink.png</file>
33+
<file>img/mapbox-marker-icon-20px-purple.png</file>
34+
<file>img/mapbox-marker-icon-20px-red.png</file>
35+
<file>img/mapbox-marker-icon-20px-yellow.png</file>
36+
<file>img/tide_icon_48x48.png</file>
37+
<file>img/tropical_storm_icon_24x48.png</file>
38+
</qresource>
39+
</RCC>

0 commit comments

Comments
 (0)