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

#54 packing bruker nå bruks valg av algoritmer + rydding og linting

Merk at ignorer ny pylint error. Se beskrivelse i diff
parent 2ececf51
No related branches found
No related tags found
1 merge request!28#54 Rectpack for reorganisering av moduler + litt black
Pipeline #73903 passed
......@@ -60,6 +60,14 @@ confidence=
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use "--disable=all --enable=classes
# --disable=W".
#
# NOTE about C0330:
# Ignoring pylint's C0330 "Wrong hanging indentation before block" error
# here because it erronously reports indentation issues, and conflicts with
# the black tool. See this issue on the black gitHub
# page: https://github.com/psf/black/issues/48, and this issue on pylints
# GitHub page referenced by the first issue:
# https://github.com/PyCQA/pylint/issues/289
disable=print-statement,
parameter-unpacking,
unpacking-in-except,
......@@ -142,7 +150,8 @@ disable=print-statement,
E0611,
I1101,
E1101,
R0901
R0901,
C0330
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
......
......@@ -40,7 +40,7 @@ class InlineEditableSOIView(QScrollArea):
self.reorganize_soi_and_update_pages()
def reorganize_soi_and_update_pages(self):
"""Wrapper to reorganize SOI and update pages in one go."""
"""Reorganize SOI and update pages in one go."""
try:
self.soi.reorganize()
except ModuleLargerThanBinError:
......@@ -49,7 +49,9 @@ class InlineEditableSOIView(QScrollArea):
"En av modulene er for store for å passe på én side!"
)
error_message.setInformativeText(
"Programmet kan desverre ikke fikse dette for deg. Se igjennom modulene og sjekk at alle moduler er mindre enn én enkelt side."
"Programmet kan desverre ikke fikse dette for deg. Se "
"igjennom modulene og sjekk at alle moduler er mindre "
"enn én enkelt side."
)
error_message.setIcon(QMessageBox.Warning)
error_message.exec_()
......@@ -141,8 +143,7 @@ class InlineEditableSOIView(QScrollArea):
self.number_of_pages = required_pages
def draw_pages(self):
"""draw self.number_of_pages pages."""
"""Draw self.number_of_pages pages."""
for i in range(self.number_of_pages):
x = 0
......@@ -197,15 +198,20 @@ class InlineEditableSOIView(QScrollArea):
# classification
classification = QLabel(self.soi.classification)
classification.setStyleSheet("background-color: rgba(0,0,0,0%); "
"color: red")
classification.setStyleSheet(
"background-color: rgba(0,0,0,0%); " "color: red"
)
classification.setFont(QFont("Times New Roman", 50))
# source: https://stackoverflow.com/a/8638114/3545896
# CAUTION: does not work if font is set through stylesheet
label_width = (classification.fontMetrics()
.boundingRect(classification.text()).width())
x_pos = x + self.soi.CONTENT_WIDTH - label_width - \
self.soi.HEADER_HEIGHT
label_width = (
classification.fontMetrics()
.boundingRect(classification.text())
.width()
)
x_pos = (
x + self.soi.CONTENT_WIDTH - label_width - self.soi.HEADER_HEIGHT
)
classification.move(x_pos, y)
self.scene.addWidget(classification)
......
......@@ -5,20 +5,35 @@ from PySide2.QtCore import QPoint
from rectpack import (
newPacker,
SORT_NONE,
SORT_SSIDE,
SORT_AREA,
PackingMode,
PackingBin,
maxrects,
skyline,
guillotine,
)
from soitool.modules.module_table import TableModule
RECTPACK_STRING_TO_ALGORITHM_BIN = {
"BFF": PackingBin.BFF,
"BBF": PackingBin.BBF,
}
RECTPACK_STRING_TO_ALGORITHM_PACK = {
"MaxRectsBl": maxrects.MaxRectsBl,
"SkylineBl": skyline.SkylineBl,
"GuillotineBssfSas": guillotine.GuillotineBssfSas,
}
RECTPACK_STRING_TO_ALGORITHM_SORT = {
"SORT_NONE": SORT_NONE,
"SORT_AREA": SORT_AREA,
}
class ModuleLargerThanBinError(Exception):
"""At least one module is too large for the bin during rectpack packing."""
pass
class ModuleType(Enum):
"""Enumerate with types of modules."""
......@@ -27,11 +42,16 @@ class ModuleType(Enum):
ATTACHMENT_MODULE = 1
class SOI():
class SOI:
"""Datastructure for SOI.
Holds all info about an SOI necessary to view and edit it.
This class relies heavily on the rectpack library for optimal placement of
modules when placement_strategy is 'auto'. Refer to the rectpack
documentation for details beyond what is provided in this class:
https://github.com/secnot/rectpack
Parameters
----------
title : string
......@@ -48,11 +68,18 @@ class SOI():
placement_strategy : string
must be one of 'manual' and 'auto'
algorithm_bin : string
TODO: document when rectpack is added
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.
algorithm_pack : string
TODO: document when rectpack is added
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.
algorithm_sort : string
TODO: document when rectpack is added
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.
"""
......@@ -82,25 +109,27 @@ class SOI():
pass
raise NotImplementedError()
# ignoring pylints "Too many arguments" error here to keep this as simple
# as possible. The proper thing would probably be to put the properties in
# separate data classes (auto-placement stuff in one class, styling in
# another, etc).
# Ignoring pylint's r0913 "Too many arguments" error here to keep this as
# simple as possible. The proper thing would probably be to put the
# properties in separate data classes (auto-placement stuff in one class,
# styling in another, etc).
# pylint: disable=r0913
def __init__(self,
title="Default SOI title",
description="Default SOI description",
version="1",
date=None,
valid_from=None,
valid_to=None,
icon="soitool/media/HVlogo.png",
classification="ugradert",
orientation="landscape",
placement_strategy="auto",
algorithm_bin="BFF",
algorithm_pack="MaxRectsBI",
algorithm_sort="SORT_AREA"):
def __init__(
self,
title="Default SOI title",
description="Default SOI description",
version="1",
date=None,
valid_from=None,
valid_to=None,
icon="soitool/media/HVlogo.png",
classification="ugradert",
orientation="landscape",
placement_strategy="auto",
algorithm_bin="BFF",
algorithm_pack="MaxRectsBl",
algorithm_sort="SORT_NONE",
):
# populate date-related arguments if they are not supplied by the user
if date is None:
......@@ -191,67 +220,100 @@ class SOI():
module["widget"].set_pos(QPoint(new_x, new_y))
def get_module_with_name(self, name):
"""Return module with given name.
Parameters
----------
name : str
name of module to look for
Returns
-------
module in self.modules
"""
for module in self.modules:
if module["meta"]["name"] == name:
return module
return None
def reorganize(self):
self.reorganize_rectpack()
"""Reorganize modules using chosen strategy."""
if self.placement_strategy == "auto":
self.reorganize_rectpack()
else:
raise Exception(
"Unknown placement strategy: {}".format(
self.placement_strategy
)
)
def reorganize_rectpack(self):
def get_rectpack_packer(self):
"""Return rectpack packer set up for this SOI.
Returns
-------
packer : rectpack packer
"""
# based on string stored in self.algorithm_... variables fetch real
# implementations of chosen algorithms
chosen_algorithm_bin = RECTPACK_STRING_TO_ALGORITHM_BIN[
self.algorithm_bin
]
chosen_algorithm_pack = RECTPACK_STRING_TO_ALGORITHM_PACK[
self.algorithm_pack
]
chosen_algorithm_sort = RECTPACK_STRING_TO_ALGORITHM_SORT[
self.algorithm_sort
]
packer = newPacker(
rotation=False,
mode=PackingMode.Offline,
bin_algo=PackingBin.BBF,
sort_algo=SORT_NONE,
pack_algo=maxrects.MaxRectsBl,
bin_algo=chosen_algorithm_bin,
sort_algo=chosen_algorithm_sort,
pack_algo=chosen_algorithm_pack,
)
return packer
def reorganize_rectpack(self):
"""Reorganize modules optimally using the rectpack library."""
packer = self.get_rectpack_packer()
for module in self.modules:
module_width, module_height = module["widget"].get_size()
print("size is:", str(module_width), str(module_height))
packer.add_rect(
module_width, module_height, module["meta"]["name"]
)
# float("inf") to add infinite bins. See https://github.com/secnot/rectpack/blob/master/README.md#api
packer.add_bin(
self.CONTENT_WIDTH, self.CONTENT_HEIGHT - self.HEADER_HEIGHT
)
packer.add_bin(
self.CONTENT_WIDTH, self.CONTENT_HEIGHT - self.HEADER_HEIGHT
self.CONTENT_WIDTH,
self.CONTENT_HEIGHT - self.HEADER_HEIGHT,
float("inf"),
)
packer.pack()
packed_rects = packer.rect_list()
# explode if rectpack failed to pack all rects
if len(packed_rects) != len(self.modules):
raise ModuleLargerThanBinError()
def get_module_with_name(name):
"""Returns module with given name. """
for module in self.modules:
if module["meta"]["name"] == name:
return module
return None
# update modules based on packed rects returned from rectpack
for packed_rect in packed_rects:
print("packed rect: ", packed_rect)
packed_rect_bin = packed_rect[0]
packed_rect_x = packed_rect[1]
packed_rect_y = packed_rect[2]
packed_rect_w = packed_rect[3]
packed_rect_h = packed_rect[4]
packed_rect_id = packed_rect[5]
corresponding_module = get_module_with_name(packed_rect_id)
if not corresponding_module:
corresponding_module = self.get_module_with_name(packed_rect_id)
if corresponding_module is None:
raise Exception("Module was lost during packing!")
corresponding_module["meta"]["x"] = packed_rect_x
corresponding_module["meta"]["y"] = packed_rect_y
corresponding_module["meta"]["page"] = packed_rect_bin + 1
self.update_module_widget_position(corresponding_module)
print("---MODULES AFTER PACKING---")
for module in self.modules:
print(module)
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