Skip to content
Snippets Groups Projects
Commit 6e5c212a authored by Thomas Holene Løkkeborg's avatar Thomas Holene Løkkeborg
Browse files

#54 Støtte for "listeners" i SOI, og ModuleList bruker dette

- økte max attributes av samme grunn den ble økt i utgangspunktet: siden vårt prosjekt er tidsbegrenset og ikke særlig stort syns vi det er greit med store klasser..
- Div. oppdateringer til errortekst
- InlineEdit view stoler nå på at SOI __init__ gjør reorganize
- Black gjorde noen stil-endringer..
- ModuleList vil nå respektere sortering i SOI om SOI er innstilt til å gjøre sortering. Om SOI ikke instilles til dette vil drag-and-drop prioritering enda fungere
- initiell sortering av moduler er nå ikke gjort av rectpack, men av oss. På den måten kan vi etterhvert støtte delvis sortering
parent f0bd8c86
No related branches found
No related tags found
1 merge request!28#54 Rectpack for reorganisering av moduler + litt black
Pipeline #73997 passed
......@@ -514,7 +514,7 @@ valid-metaclass-classmethod-first-arg=cls
max-args=5
# Maximum number of attributes for a class (see R0902).
max-attributes=15
max-attributes=20
# Maximum number of boolean expressions in an if statement.
max-bool-expr=5
......
......@@ -46,7 +46,7 @@ class InlineEditableSOIView(QScrollArea):
except ModuleLargerThanBinError:
error_message = QMessageBox()
error_message.setText(
"En av modulene er for store for å passe på én side!"
"Minst én av modulene er for store for å passe på én side!"
)
error_message.setInformativeText(
"Programmet kan desverre ikke fikse dette for deg. Se "
......@@ -76,7 +76,7 @@ class InlineEditableSOIView(QScrollArea):
self.setWidget(self.view)
self.ensure_proxies()
self.reorganize_soi_and_update_pages()
self.update_pages()
# self.launch_auto_zoom()
......
"""Includes functionality for displaying a prioritized list of modules."""
from PySide2.QtCore import Qt
from PySide2.QtWidgets import QListWidget, QListWidgetItem, QAbstractItemView
from PySide2.QtWidgets import (
QListWidget,
QListWidgetItem,
QAbstractItemView,
QMessageBox,
)
from soitool.soi import ModuleType
import soitool
......@@ -26,11 +31,14 @@ class ModuleList(QListWidget):
super().__init__()
# full import path below to avoid circular dependency
if not isinstance(parent,
soitool.soi_workspace_widget.SOIWorkspaceWidget):
raise RuntimeError('Only soitool.SOIWorkspaceWidget is '
'acceptable type for parent-variable '
'in class Module_list.')
if not isinstance(
parent, soitool.soi_workspace_widget.SOIWorkspaceWidget
):
raise RuntimeError(
"Only soitool.SOIWorkspaceWidget is "
"acceptable type for parent-variable "
"in class Module_list."
)
self.type = module_type
self.parent = parent
self.original_element_name = None
......@@ -39,6 +47,8 @@ class ModuleList(QListWidget):
self.setup_list()
self.fill_list()
self.parent.soi.add_reorganization_listener(self.update_list_order)
def setup_list(self):
"""Prepare list.
......@@ -53,20 +63,28 @@ class ModuleList(QListWidget):
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
def fill_list(self):
"""Fill list with elements."""
"""Fill list with elements in order defined in SOI."""
# Get names of modules/attachments:
if ModuleType(self.type) == ModuleType.MAIN_MODULE:
names = [module["meta"]["name"] for
module in self.parent.soi.modules]
names = [
module["meta"]["name"] for module in self.parent.soi.modules
]
elif ModuleType(self.type) == ModuleType.ATTACHMENT_MODULE:
names = [attachment["meta"]["name"] for
attachment in self.parent.soi.attachments]
names = [
attachment["meta"]["name"]
for attachment in self.parent.soi.attachments
]
for i, name in enumerate(names):
item = QListWidgetItem(name)
item.setFlags(item.flags() | Qt.ItemIsEditable)
self.insertItem(i, item)
def update_list_order(self):
"""Update order of modules in list to respect order in SOI."""
self.clear()
self.fill_list()
def currentChanged(self, current, previous):
"""Save name and index of an element when it is selected.
......@@ -126,6 +144,9 @@ class ModuleList(QListWidget):
def dropEvent(self, event):
"""Notify parent when an element is drag-and-dropped.
Note that if the SOI is not prepared for manual priorization an error
message will be displayed, and nothing else will be done.
https://doc.qt.io/qt-5/qabstractitemview.html#dropEvent.
Parameters
......@@ -133,6 +154,22 @@ class ModuleList(QListWidget):
event : QDropEvent
Is sent to super().
"""
if self.parent.soi.algorithm_sort != "none":
error_message = QMessageBox()
error_message.setText(
"SOI er ikke innstilt for automatisk plassering av moduler!"
)
error_message.setInformativeText(
"Enn så lenge vil manuell prioritering av moduler bare "
"fungere dersom SOI er innstilt til ikke å gjøre sortering av "
"moduler før pakking. I fremtiden vil det være mulig å låse "
"utvalgte moduler, men la resten optimalt pakkes."
)
error_message.setIcon(QMessageBox.Warning)
error_message.exec_()
# ignorerer dropEvent ved å returnere tidlig
return
super().dropEvent(event)
# Get origin and destination index of module/attachment
......
......@@ -5,7 +5,6 @@ from PySide2.QtCore import QPoint
from rectpack import (
newPacker,
SORT_NONE,
SORT_AREA,
PackingMode,
PackingBin,
maxrects,
......@@ -14,20 +13,98 @@ from rectpack import (
)
from soitool.modules.module_table import TableModule
RECTPACK_STRING_TO_ALGORITHM_BIN = {
# functions to sort modules by different criteria
def modules_sort_by_none(modules):
"""Don't sort. Implemented for completeness. See SOI.sort_modules.
Parameters
----------
modules : list of modules
Returns
-------
list of modules, untouched
"""
return modules
def modules_sort_by_area(modules):
"""Sort modules by area. See SOI.sort_modules.
Parameters
----------
modules : list of modules
Returns
-------
list of modules sorted by area
"""
def module_area_key(module):
width, height = module["widget"].get_size()
return width * height
return sorted(modules, key=module_area_key, reverse=True)
def modules_sort_by_width(modules):
"""Sort modules by width. See SOI.sort_modules.
Parameters
----------
modules : list of modules
Returns
-------
list of modules sorted by width
"""
def module_width_key(module):
width, _ = module["widget"].get_size()
return width
return sorted(modules, key=module_width_key, reverse=True)
def modules_sort_by_height(modules):
"""Sort modules by height. See SOI.sort_modules.
Parameters
----------
modules : list of modules
Returns
-------
list of modules sorted by height
"""
def module_height_key(module):
_, height = module["widget"].get_size()
return height
return sorted(modules, key=module_height_key, reverse=True)
# dicts that map between name of algorithm and implementation of algorithm
STRING_TO_ALGORITHM_RECTPACK_BIN = {
"BFF": PackingBin.BFF,
"BBF": PackingBin.BBF,
}
RECTPACK_STRING_TO_ALGORITHM_PACK = {
STRING_TO_ALGORITHM_RECTPACK_PACK = {
"MaxRectsBl": maxrects.MaxRectsBl,
"SkylineBl": skyline.SkylineBl,
"GuillotineBssfSas": guillotine.GuillotineBssfSas,
}
RECTPACK_STRING_TO_ALGORITHM_SORT = {
"SORT_NONE": SORT_NONE,
"SORT_AREA": SORT_AREA,
STRING_TO_ALGORITHM_INITIAL_SORT = {
"none": modules_sort_by_none,
"area": modules_sort_by_area,
"width": modules_sort_by_width,
"height": modules_sort_by_height,
}
......@@ -70,16 +147,17 @@ class SOI:
algorithm_bin : string
which bin packing algorithm should be used for rectpack. Currently the
following are implemented: 'BFF', 'BBF'. Please refer to the
RECTPACK_STRING_TO_ALGORITHM_BIN variable.
STRING_TO_ALGORITHM_RECTPACK_BIN variable.
algorithm_pack : string
which packing algorithm should be used for rectpack. Currently the
following are implemented: 'MaxRectsBl', 'SkylineBl',
'GuillotineBssfSas'. Please refer to the
RECTPACK_STRING_TO_ALGORITHM_PACK variable.
STRING_TO_ALGORITHM_RECTPACK_PACK variable.
algorithm_sort : string
which sorting method should be applied to the modules before packing.
Currently the following are implemented: 'SORT_NONE', 'SORT_AREA'.
Please refer to the RECTPACK_STRING_TO_ALGORITHM_SORT variable.
Currently the following are implemented: 'none', 'area', 'width',
'height'. Please refer to the STRING_TO_ALGORITHM_INITIAL_SORT
variable.
"""
......@@ -128,7 +206,7 @@ class SOI:
placement_strategy="auto",
algorithm_bin="BFF",
algorithm_pack="MaxRectsBl",
algorithm_sort="SORT_NONE",
algorithm_sort="area",
):
# populate date-related arguments if they are not supplied by the user
......@@ -159,6 +237,9 @@ class SOI:
self.algorithm_pack = algorithm_pack
self.algorithm_sort = algorithm_sort
# functions to call after reorganization
self.reorganization_listeners = []
# NOTE
# * test modules, just to have something show up on screen
# * not valid until reorganize has been run, as the positions must be
......@@ -189,6 +270,14 @@ class SOI:
self.reorganize()
def add_reorganization_listener(self, function):
"""Add function 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.
"""
self.reorganization_listeners.append(function)
def update_module_widget_position(self, module):
"""Update position of module widget based on meta position.
......@@ -238,7 +327,10 @@ class SOI:
return None
def reorganize(self):
"""Reorganize modules using chosen strategy."""
"""Reorganize modules using chosen strategy.
After successfull reorganization will call all listeners
"""
if self.placement_strategy == "auto":
self.reorganize_rectpack()
else:
......@@ -247,6 +339,9 @@ class SOI:
self.placement_strategy
)
)
# call listener functions
for listener in self.reorganization_listeners:
listener()
def get_rectpack_packer(self):
"""Return rectpack packer set up for this SOI.
......@@ -257,30 +352,41 @@ class SOI:
"""
# based on string stored in self.algorithm_... variables fetch real
# implementations of chosen algorithms
chosen_algorithm_bin = RECTPACK_STRING_TO_ALGORITHM_BIN[
chosen_algorithm_bin = STRING_TO_ALGORITHM_RECTPACK_BIN[
self.algorithm_bin
]
chosen_algorithm_pack = RECTPACK_STRING_TO_ALGORITHM_PACK[
chosen_algorithm_pack = STRING_TO_ALGORITHM_RECTPACK_PACK[
self.algorithm_pack
]
chosen_algorithm_sort = RECTPACK_STRING_TO_ALGORITHM_SORT[
self.algorithm_sort
]
# NOTE that initial sorting is done outside of the packer, so it is set
# to SORT_NONE here to respect our sorting
packer = newPacker(
rotation=False,
mode=PackingMode.Offline,
bin_algo=chosen_algorithm_bin,
sort_algo=chosen_algorithm_sort,
sort_algo=SORT_NONE,
pack_algo=chosen_algorithm_pack,
)
return packer
def sort_modules(self):
"""Sort modules in place using chosen sorting algorithm."""
chosen_algorithm_sort = STRING_TO_ALGORITHM_INITIAL_SORT[
self.algorithm_sort
]
modules_to_sort = chosen_algorithm_sort(self.modules)
for i, _ in enumerate(self.modules):
self.modules[i] = modules_to_sort[i]
def reorganize_rectpack(self):
"""Reorganize modules optimally using the rectpack library."""
packer = self.get_rectpack_packer()
self.sort_modules()
for module in self.modules:
module_width, module_height = module["widget"].get_size()
packer.add_rect(
......
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