Skip to content
Snippets Groups Projects
Commit 6eb682a7 authored by Petter Sagvold's avatar Petter Sagvold
Browse files

Merge branch 'oppdatere-inline-edit-view-etter-oppsett-endres' into 'master'

#81 SOI listeners for prop oppdateringer & InlineEditableSOIView endres når oppsett lagres

See merge request !48
parents 835faa42 8377d3cb
No related branches found
No related tags found
1 merge request!48#81 SOI listeners for prop oppdateringer & InlineEditableSOIView endres når oppsett lagres
Pipeline #77916 passed
......@@ -78,6 +78,7 @@ class InlineEditableSOIView(QScrollArea):
# add listeners to react properly to SOI changes
self.soi.add_reorganization_listener(self.update_pages)
self.soi.add_new_module_listener(self.ensure_proxies)
self.soi.add_update_property_listener(self.update_pages)
# self.launch_auto_zoom()
......
......@@ -16,6 +16,11 @@ from PySide2.QtWidgets import (
QComboBox,
QGroupBox,
)
from soitool.soi import (
STRING_TO_ALGORITHM_RECTPACK_BIN,
STRING_TO_ALGORITHM_RECTPACK_PACK,
STRING_TO_ALGORITHM_INITIAL_SORT,
)
class Setup(QDialog): # pylint: disable = R0902
......@@ -120,28 +125,25 @@ class Setup(QDialog): # pylint: disable = R0902
# placement algorithm
self.layout_setup.addWidget(self.label_algorithm)
self.rbutton_bin1 = QRadioButton("BFF")
self.rbutton_bin2 = QRadioButton("BBF")
self.rbutton_pack1 = QRadioButton("MaxRectsBI")
self.rbutton_pack2 = QRadioButton("SkylinBl")
self.rbutton_pack3 = QRadioButton("GuillotineBssfSas")
self.rbutton_sort1 = QRadioButton("none")
self.rbutton_sort2 = QRadioButton("area")
self.rbutton_sort3 = QRadioButton("width")
self.rbutton_sort4 = QRadioButton("height")
self.rbuttons_option_bin = [
QRadioButton(name) for name in STRING_TO_ALGORITHM_RECTPACK_BIN
]
self.rbuttons_option_pack = [
QRadioButton(name) for name in STRING_TO_ALGORITHM_RECTPACK_PACK
]
self.rbuttons_option_sort = [
QRadioButton(name) for name in STRING_TO_ALGORITHM_INITIAL_SORT
]
self.layout_alg_button_bin.addWidget(QLabel("Bin"))
self.layout_alg_button_bin.addWidget(self.rbutton_bin1)
self.layout_alg_button_bin.addWidget(self.rbutton_bin2)
for rbutton in self.rbuttons_option_bin:
self.layout_alg_button_bin.addWidget(rbutton)
self.layout_alg_button_pack.addWidget(QLabel("Pack"))
self.layout_alg_button_pack.addWidget(self.rbutton_pack1)
self.layout_alg_button_pack.addWidget(self.rbutton_pack2)
self.layout_alg_button_pack.addWidget(self.rbutton_pack3)
for rbutton in self.rbuttons_option_pack:
self.layout_alg_button_pack.addWidget(rbutton)
self.layout_alg_button_sort.addWidget(QLabel("Sort"))
self.layout_alg_button_sort.addWidget(self.rbutton_sort1)
self.layout_alg_button_sort.addWidget(self.rbutton_sort2)
self.layout_alg_button_sort.addWidget(self.rbutton_sort3)
self.layout_alg_button_sort.addWidget(self.rbutton_sort4)
for rbutton in self.rbuttons_option_sort:
self.layout_alg_button_sort.addWidget(rbutton)
self.group_algorithm_bin.setLayout(self.layout_alg_button_bin)
self.group_algorithm_pack.setLayout(self.layout_alg_button_pack)
......@@ -169,43 +171,48 @@ class Setup(QDialog): # pylint: disable = R0902
def save(self):
"""Save and update the SOI with the given changes."""
self.soi.title = self.edit_title.text()
self.soi.description = self.edit_description.text()
self.soi.version = self.edit_version.text()
self.soi.date = self.edit_date.text()
self.soi.valid_from = self.edit_valid_from.text()
self.soi.valid_to = self.edit_valid_to.text()
self.soi.classification = (
self.edit_classification.currentText().lower()
)
# find which radiobutton that is checked
# Dict will contain all changes to make
property_changes = {}
property_changes["title"] = self.edit_title.text()
property_changes["description"] = self.edit_description.text()
property_changes["version"] = self.edit_version.text()
property_changes["date"] = self.edit_date.text()
property_changes["valid_from"] = self.edit_valid_from.text()
property_changes["valid_to"] = self.edit_valid_to.text()
property_changes[
"classification"
] = self.edit_classification.currentText()
# Find which radiobutton that is checked
if self.rbutton_landscape.isChecked():
self.soi.orientation = "landscape"
property_changes["orientation"] = "landscape"
else:
self.soi.orientation = "portrait"
property_changes["orientation"] = "portrait"
if self.rbutton_auto.isChecked():
self.soi.placement_strategy = "auto"
property_changes["placement_strategy"] = "auto"
else:
self.soi.placement_strategy = "manual"
property_changes["placement_strategy"] = "manual"
# loop through groupbox to find checked radiobuttons
# Loop through groupbox to find checked radiobuttons
for i in self.group_algorithm_bin.findChildren(QRadioButton):
if i.isChecked():
self.soi.algorithm_bin = i.text()
property_changes["algorithm_bin"] = i.text()
break
for i in self.group_algorithm_pack.findChildren(QRadioButton):
if i.isChecked():
self.soi.algorithm_pack = i.text()
property_changes["algorithm_pack"] = i.text()
break
for i in self.group_algorithm_sort.findChildren(QRadioButton):
if i.isChecked():
self.soi.algorithm_sort = i.text()
property_changes["algorithm_sort"] = i.text()
break
# Pass changes as unpacked variable list
self.soi.update_properties(**property_changes)
self.accept()
def get_from_soi(self):
......@@ -216,11 +223,9 @@ class Setup(QDialog): # pylint: disable = R0902
self.edit_date.setText(self.soi.date)
self.edit_valid_from.setText(self.soi.valid_from)
self.edit_valid_to.setText(self.soi.valid_to)
self.edit_classification.setCurrentText(
self.soi.classification.upper()
)
self.edit_classification.setCurrentText(self.soi.classification)
# check radiobuttons according to current SOI settings
# Check radiobuttons according to current SOI settings
if self.soi.orientation == "landscape":
self.rbutton_landscape.setChecked(True)
else:
......
......@@ -188,6 +188,12 @@ class SOI:
updating the widget positions based on the "meta" positions, using the
formulas above.
## Note about properties
To ensure other users of SOI are properly notified, all property updates
should happen through member functions. `update_properties` for general
properties, and `add_module` for modules and attachments.
Parameters
----------
title : string
......@@ -307,21 +313,22 @@ class SOI:
self.modules = modules
self.attachments = attachments
# the following properties are relevant when self.placement_strategy
# The following properties are relevant when self.placement_strategy
# is "auto". They are used by rectpack
self.algorithm_bin = algorithm_bin
self.algorithm_pack = algorithm_pack
self.algorithm_sort = algorithm_sort
# prepare listener lists: list of functions to call after certain
# Prepare listener lists: list of functions to call after certain
# events happen
self.reorganization_listeners = []
self.new_module_listeners = []
self.update_property_listeners = []
self.reorganize()
def add_reorganization_listener(self, function):
"""Add function to list of functions to be called after reorganization.
"""Add to list of functions to be called after reorganization.
This is useful for users of this class to handle changes to the SOI.
As an example a class displaying an SOI can be updated after changes.
......@@ -329,13 +336,21 @@ class SOI:
self.reorganization_listeners.append(function)
def add_new_module_listener(self, function):
"""Add function to list of functions to be called after added module.
"""Add to list of functions to be called after added module.
This is useful for users of this class to handle changes to the SOI.
As an example a class displaying an SOI can be updated after changes.
"""
self.new_module_listeners.append(function)
def add_update_property_listener(self, function):
"""Add to list of functions to be called after properties change.
This is useful for users of this class to handle changes to the SOI.
As an example a class displaying an SOI can be updated after changes.
"""
self.update_property_listeners.append(function)
def update_module_widget_position(self, module):
"""Update position of module widget based on meta position.
......@@ -524,3 +539,80 @@ class SOI:
# call listener functions
for listener in self.new_module_listeners:
listener()
# Ignoring pylint "Too many branches" error as this function is a special
# case where the if-elif-elif-...-else makes sense
def update_properties(self, **kwargs): # pylint: disable=R0912
"""Update properties given as input, and call listeners.
This function exists solely because there are listeners on the
properties. A "cleaner" way to achieve the same goal would be to
create a "setter" for each property and call the listeners there, but
for bulk changes this would get unwieldy. Another way to achieve the
goal of calling listeners after changes to properties would be to
create a separate function that the user is expected to call after
changing the properties directly, but this would put unnecessary
responsibility on the user.
All properties except "modules" and "attachements" can be updated
using this function.
If a change is made that affects placement of modules this function
will call `reorganize`
Parameters
----------
**kwargs : key, value pairs of properties to update
Accepts both a normal dict, and passing arguments as normal:
`update_properties({'title': 'my title'})` and
update_properties(title='my title')`. Accepted keys are properties
of the SOI class, except 'modules' and 'attachements'. Explanation
of **kwargs:
https://stackoverflow.com/a/1769475/3545896
"""
# For every key, value pair passed in kwargs, update corresponding
# property
for key, value in kwargs.items():
if key == "title":
self.title = value
elif key == "description":
self.description = value
elif key == "version":
self.version = value
elif key == "date":
self.date = value
elif key == "valid_from":
self.valid_from = value
elif key == "valid_to":
self.valid_to = value
elif key == "icon":
self.icon = value
elif key == "classification":
self.classification = value
elif key == "orientation":
self.orientation = value
elif key == "placement_strategy":
self.placement_strategy = value
elif key == "algorithm_bin":
self.algorithm_bin = value
elif key == "algorithm_pack":
self.algorithm_pack = value
elif key == "algorithm_sort":
self.algorithm_sort = value
else:
raise ValueError(
f"Unknown property name {key} passed with value {value}"
)
for listener in self.update_property_listeners:
listener()
# If any properties relating to module placement were touched,
# reorganize
if (
"placement_strategy" in kwargs
or "algorithm_bin" in kwargs
or "algorithm_pack" in kwargs
or "algorithm_sort" in kwargs
):
self.reorganize()
......@@ -361,3 +361,28 @@ class TestSOI(unittest.TestCase):
)
except ModuleNameTaken:
self.fail("Exception should not be raised when name is unique")
def test_update_properties(self):
"""Test updating properties and corresponding listener."""
test_title = "a test title"
listener_called = False
def listener_update_properties():
# nonlocal is required to access variable in nesting function's
# scope
nonlocal listener_called
listener_called = True
self.soi.add_update_property_listener(listener_update_properties)
self.soi.update_properties(title=test_title)
self.assertTrue(listener_called)
self.assertEqual(self.soi.title, test_title)
def test_update_properties_invalid_key(self):
"""Test update_properties properly throws exception if invalid key."""
self.assertRaises(
ValueError,
lambda: self.soi.update_properties(invalid_key="garbage"),
)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment