secv_guis package¶
Subpackages¶
Submodules¶
secv_guis.base_widgets module¶
This module is a library of reusable, extendable widgets.
-
class
secv_guis.base_widgets.CheckBoxGroup(parent=None, horizontal=False)[source]¶ Bases:
PySide2.QtWidgets.QWidgetA group of
CheckBoxes-
add_box(name, tristate=False, initial_val=True)[source]¶ Parameters: tristate (bool) – If true, the added check box will have 3 states.
-
remove_box(idx)[source]¶ Parameters: idx (int) – Boxes are added in increasing index order, so this the lower this index the ‘older’ the box that is being removed.
-
staticMetaObject= <PySide2.QtCore.QMetaObject object>¶
-
-
class
secv_guis.base_widgets.FileList(label, parent=None, default_path=None, extensions=None, sort=True)[source]¶ Bases:
PySide2.QtWidgets.QWidgetA file dialog button followed by a list that shows the files in the selected folder.
-
staticMetaObject= <PySide2.QtCore.QMetaObject object>¶
-
-
class
secv_guis.base_widgets.MaskPaintForm(brush_names, max_brush_size=100, parent=None, thresh_min=0, thresh_max=1, thresh_num_steps=100, min_alpha=1, max_alpha=255)[source]¶ Bases:
PySide2.QtWidgets.QWidgetThis widget contains one section for the masks and one for the painter. The mask section contains a set of elements, one per mask. A radio button selects the currently active mask, and each mask features an RGBA box and a thershold slider. The painter section contains a ComboBox to select the painter type, and a slider for the painter size.
To use it in specific applications override
button_pressed, combo_box_changed, rgba_box_changed...-
add_item(name, rgba, slider_visible=True, activate=False)[source]¶ Add an element to the ‘mask’ section, with the given name and color.
Parameters: - slider_visible – If false, the slider will be still there but hidden.
- activate – Once created, select this item in the radio buttons.
-
brush_type_changed(idx)[source]¶ Override me!
Parameters: idx (int) – Starts with 0 and respects ordering given at construction. So when overriding this method, you can assume that 0 will correspond to the firstly added element, and so on.
Override me!
Parameters: idx (int) – Starts with 0 and respects ordering given at construction. So when overriding this method, you can assume that 0 will correspond to the firstly added element, and so on. Implementation example:
i = self._buttons.index(but) print("button pressed: >>>", i, but.text())
-
remove_item(idx)[source]¶ Remove an element from the ‘mask’ section by index. Indexes are in increasing order, so lowest is oldest.
-
slider_to_p_val(sl_val)[source]¶ Since the slider goes from 0 to
thresh_num_steps, this function linearly interpolates the, so that 0 maps tothresh_minandthresh_num_stepsmaps tothresh_max. Note that min does not neccesarily have to be smaller than max.Parameters: sl_val (int) – The actual slider value from 0 to num_steps. Returns: The converted and interpolated value.
-
staticMetaObject= <PySide2.QtCore.QMetaObject object>¶
-
-
class
secv_guis.base_widgets.RGBASpinbox(initial_rgba=(0, 255, 0, 100), parent=None, min_alpha=1, max_alpha=255)[source]¶ Bases:
PySide2.QtWidgets.QWidgetA cluster of 4 [0-255] spin boxes, representing (and having) an RGBA color. Use
self.connectto wire this widget to any method.-
connect(fn)[source]¶ Parameters: fn – A function to connect this widget to. It must have the following signature``fn(idx, r, g, b, a)``. When calling
self.connect(f), any value changes in R, G, B or A will triggerf(r, g, b, a)with the changed values
-
staticMetaObject= <PySide2.QtCore.QMetaObject object>¶
-
-
class
secv_guis.base_widgets.SaveForm(parent=None, default_path=None)[source]¶ Bases:
PySide2.QtWidgets.QWidgetA formulary providing functionality for selecting what to save, where to save, the output suffix and overwriting policy.
-
DIALOG_TEXT= 'Output\nfolder'¶
-
OVERWRITE_TEXT= 'Overwrite\nsaved'¶
-
SAVE_TEXT= 'Save\nselected'¶
-
add_checkbox(checkbox_name, initial_val=True, initial_txt=None)[source]¶ Adds an element that can be selected to be saved.
Parameters: - checkbox_name – The element identifier
- initial_txt – The initial suffix to be appended to the files. If
none is given, the
checkbox_nameis picked as default. The user can change this from the GUI.
-
save_masks(states, suffixes, overwrite)[source]¶ Parameters: - states – A list with booleans, representing the checkbox states for the contained elements.
- suffixes – A list with the corresponding suffixes
- overwrite – A boolean determining whether the ‘overwrite’ checkbox has been activated.
Override me!
-
staticMetaObject= <PySide2.QtCore.QMetaObject object>¶
-
secv_guis.commands module¶
This module contains all the ‘undoable’ actions. They must implement a way to undo and redo them.
Composite commands deserve a special mention: they are trains of actions that only track, store and report the initial and final state. They are particularly useful when performing interactive editings on big datastructures like pixmaps, to prevent memory bloating.
-
class
secv_guis.commands.CompositeCommand(parent=None)[source]¶ Bases:
PySide2.QtWidgets.QUndoCommandIn some cases like painting a stroke into a pixmap, it doesn’t make sense to store every single update: rather, the prior and finished states only. This class provides a structure for such cases:
- Instantiate the command with the parameters that belong to the whole
- composite action.
- Call
actionfor every desired update of the finished state - Call
finishto crystalize the final state. No furtheractions - will be allowed, and (optionally) the action will be added to a Qt
UndoStack.
- Call
The following is required to extend the class: 1. Define a
COMMAND_NAME2. Extend the__init__,action,undoandredomethods.-
COMMAND_NAME= NotImplemented¶
-
class
secv_guis.commands.DrawCommand(pmi, rgba, diameter, comp_mode=PySide2.QtGui.QPainter.CompositionMode.CompositionMode_Source, parent=None)[source]¶ Bases:
secv_guis.commands.CompositeCommandA composite command to draw a stroke of circles into a
PixmapIten.-
COMMAND_NAME= 'Draw'¶
-
action(x_pos, y_pos)[source]¶ Once the object has been constructed and ``finish()`` hasn’t been called yet, Call this function to paint a circle at given position. Check constructor for further variables.
-
-
class
secv_guis.commands.DrawOverlappingCommand(pmi, ref_pmi, rgba, diameter, comp_mode=PySide2.QtGui.QPainter.CompositionMode.CompositionMode_Source, parent=None)[source]¶ Bases:
secv_guis.commands.DrawCommandLike
DrawCommand, but accepts 2 PixmapItems instead of one, so that the drawing onto the first is only allowed if the same pixel is active in the second.-
COMMAND_NAME= 'Draw Overlapping'¶
-
-
class
secv_guis.commands.EraseCommand(pmi, diameter, comp_mode=PySide2.QtGui.QPainter.CompositionMode.CompositionMode_Source, parent=None)[source]¶ Bases:
secv_guis.commands.DrawCommandA composite command to erase a stroke of circles into a
PixmapIten. SeeDrawCommanddocstrings for more info.-
COMMAND_NAME= 'Erase'¶
-
-
class
secv_guis.commands.UndoableLambda(command_name, undo_fn, redo_fn, parent=None)[source]¶ Bases:
PySide2.QtWidgets.QUndoCommandThis kind of functor can be used to create them on the spot and send them to the UndoStack. Useful to split down a composite action in arbitrary undo-able subactions. Usage example:
# in action ... do something ... cmd = UndoableLambda("My partial action", lambda: print("undo"), lambda: print("redo")) undo_stack.push(cmd)
secv_guis.dialogs module¶
This module defines several reusable dialog types.
-
class
secv_guis.dialogs.ExceptionDialog(error_msg, timeout_ms=None, parent=None)[source]¶ Bases:
secv_guis.dialogs.InfoDialogThis class is intended to be used at the main loop level, to catch any exceptions that the app may have and show them in a Dialog. To do that, it suffices to put the following line anywhere before
app.exec_():sys.excepthook = ExceptionDialog.excepthook
Source: https://stackoverflow.com/a/55819545/4511978
-
DEACTIVATE= False¶
-
ERROR_TXT= '\nERROR!\nIf you think this is an app error consider reporting the following\nto the developers (see Help->About):'¶
-
classmethod
excepthook(exc_type, exc_value, exc_tb)[source]¶ Set this method as
sys.excepthook = <THIS_CLASS>.excepthooksomewhere beforeapp.exec_()to wrap all Python exceptions with this dialog.
-
on_reject()[source]¶ If the user presses on don’t show errors again, the whole class gets deactivated, so further created instances won’t pop up.
-
staticMetaObject= <PySide2.QtCore.QMetaObject object>¶
-
-
class
secv_guis.dialogs.FlexibleDialog(accept_button_name=None, reject_button_name=None, timeout_ms=None, parent=None)[source]¶ Bases:
PySide2.QtWidgets.QDialogDialog class that allows for OK, Yes/No, and timeout interactions. To extend this dialog class, override
setup_ui_body,on_acceptandon_reject, store the instance and call it withshoworexec_.Note that
setup_ui_bodyis being called IN the constructor, so any variables that it may need when extending the class need to be set beforesuper().__init__is called.As it can be seen here, https://stackoverflow.com/questions/56449605/pyside2-qdialog-possible-bug
implementing a Dialog in PySide2 is a little tricky. These are some things to consider:
- Do not implement
accept, rejectdirectly. Rather, connect the buttons toaccept, reject, and then connect theaccepted, rejectedsignals to custom methods (in this caseon_accept, on_reject). - When calling the Dialog from the main window, the dialog must be
persistently stored as a field of the main window
i.e. self.d = .... Otherwise it will not show up. Then it can be called in modal or modeless way, as follows:XXX.connect(self.d.show), ...(self.d.exec_).
-
TIMEOUT_LBL_TXT= 'Closing in {} seconds...'¶
-
setup_ui_body(widget)[source]¶ Populate the widget with your desired contents. The widget will be above the buttons.
-
staticMetaObject= <PySide2.QtCore.QMetaObject object>¶
- Do not implement
-
class
secv_guis.dialogs.InfoDialog(header, message, accept_button_name=None, reject_button_name=None, timeout_ms=None, parent=None, print_msg=True, header_style='font-weight: bold; color: black')[source]¶ Bases:
secv_guis.dialogs.FlexibleDialogA type of dialog that shows a header and body strings.
-
INTERACT_BODY= True¶
-
INTERACT_HEADER= False¶
-
staticMetaObject= <PySide2.QtCore.QMetaObject object>¶
-
secv_guis.masked_scene module¶
This module contains the QGraphicsScene+QGraphicsView binomial, typically
used in Qt apps to display and navigate images, together with some specific
functionality to annotate.
-
class
secv_guis.masked_scene.DisplayView(scene=None, parent=None, scale_percent=15)[source]¶ Bases:
secv_guis.mouse_event_manager.MouseEventManager,PySide2.QtWidgets.QGraphicsViewIn Qt applications, it is usual to wrap a scene with a view. This allows to dynamically and easily change the perspective on the scene.
-
staticMetaObject= <PySide2.QtCore.QMetaObject object>¶
-
zoom(pos_x, pos_y, zoom_out=False)[source]¶ Source for wheel zoom: https://stackoverflow.com/a/29026916/4511978
-
-
class
secv_guis.masked_scene.MaskedImageScene(img_arr=None, parent=None)[source]¶ Bases:
PySide2.QtWidgets.QGraphicsScene,secv_guis.objects.ObjectContainerBasic area that allows to display a color image, together with a set of binary masks on top of it.
-
DEFAULT_MASK_ALPHA= 100¶
-
add_mask(mask_arr, rgba=None, item_on_top=None)[source]¶ Parameters: - mask_arr – A
np.bool(h, w)array. - item_on_top – If given, mask will be added underneath that item. Otherwise will be added on top of item stack.
Returns: The added
PixmapItem.- mask_arr – A
-
items(ascending=True)[source]¶ Returns: This scene’s items. Parameters: ascending – If true, items are given from background to foreground.
-
mask_as_bool_arr(pmi)[source]¶ Asserts that the given
pmiis inself.mask_pmis, and returns the map asnp.bool(h, w)array, in which all non-zero values are true.
-
remove_mask(pmi)[source]¶ Parameters: pmi – The PixmapItem to remove. It has to be a mask added via add_mask
-
replace_mask_pmi(pmi, new_mask_arr, new_rgba=None)[source]¶ If we call
remove_maskand thenadd_maskfails, we will lose the removed mask forever. This method updates the mask in an atomary way: either succeeds or does nothing.Warning
The input
pmigets removed from the scene and the reference becomes invalid. Use the reference returned by this function instead.
-
staticMetaObject= <PySide2.QtCore.QMetaObject object>¶
-
secv_guis.mouse_event_manager module¶
This module contains a convenience mixin that provides the following functionality for mouse tracking:
- Record previous mouse states
- Demultiplex mouse events and preprocess relevant informations
To make a widget responsive to mouse events, simply this class there and override the desired methods.
-
class
secv_guis.mouse_event_manager.MouseEventManager(track=True)[source]¶ Bases:
objectExtend this class and then instantiate it once as a property in your desired widget.
Warning
The Mixin is compatible with multiple inheritance, but not all initializations work. The following does:
- class A(MouseEventManager, QtWidgets.XXX):
- def __init__(self, …):
- QtWidgets.XXX.__init__(self, …) MouseEventManager.__init__(self, …)
In this example, class
Awill respond to the overridenon_move, etc… methods.
secv_guis.objects module¶
Object composite commands are intended for composite objects (like e.g.
a train of points). The main difference with the regular commands is that
they implement a state method (which e.g. for a train of points returns
a list of (x, y) tuples), and a clear method, which allows to ‘remove’
the object without having to roll back or break the undo queue.
-
class
secv_guis.objects.ObjectContainer[source]¶ Bases:
objectThis class is a Mixin. When a
QGraphicsSceneinherits from it, it acquires functionality to add multiple composite objects from this module.-
close_current_object_action(undo_stack=None)[source]¶ If there is an open object action, closes it and optionally adds it to the undo stack
-
object_action(obj_class, action_args, obj_instantiation_args, undo_stack=None)[source]¶ This function implements a protocol to add composite objects to the scene.
- If a different composite action was running, it closes it and starts
- this one, adding it to
self.objects.
- If no composite action was running, starts this one and adds it.
- If
obj_classis already running, does nothing here.
In all cases, including if
obj_classwas already running, performs the actionobj.action(*action_args).Note
The scene simply calls the object’s action. The object is responsible for keeping track of the
scene itemsit generates, and also removing/adding them to the scene when needed.Parameters: - obj_instantiation_args – If this action needs to be started, it
will be called via
cmd = action_class(*instantiation_args) - action_args – The action will be called with this args.
Usage example:
# adds a point to the existing cloud or starts one otherwise scene.object_action(ExamplePointCloud, [x, y], [cloud_color, points_size...]) # check the state of the last added point cloud (this one): scene.objects[ExamplePointCloud][-1].state()
-
-
class
secv_guis.objects.PointList(scene, diameter, fill_rgba=(100, 100, 100, 100), contour_rgba=(0, 0, 0, 255), draw_lines_between_dots=False, comp_mode=PySide2.QtGui.QPainter.CompositionMode.CompositionMode_SourceOver, parent=None)[source]¶ Bases:
secv_guis.commands.CompositeCommandThis class provides functionality to add circles to a scene (optionally connected by lines), and to return their centers as a list of
(x, y)positions. It also-
COMMAND_NAME= 'Draw point list'¶
-
action(x, y, undo_stack=None)[source]¶ Add a new point at given position. :param undo_stack: If given, this action is added to the undo stack.
-
finish(undo_stack=NotImplemented)[source]¶ Simply sets
self.finishedto true, because the undo stack gets the separateactionsinstead of the whole composite one.
-
secv_guis.utils module¶
This module contains helper functions and utilities that may be used anywhere else in the project.
-
class
secv_guis.utils.RandomColorGenerator(seed=None)[source]¶ Bases:
randomcolor.RandomColorFlexible generator for nice random colors. For more details check https://pypi.org/project/randomcolor/
- Usage example::
- r, g, b = next(RandomColorGenerator().generate(form=”rgbArray”))
-
generate(hue=None, luminosity=None, count=1, form='rgbArray')[source]¶ Parameters: form – Popular ones: rgbArray, rgba, hex, rgbReturns: A generator with countrandom colors.- Overriden to return a generator instead of a list. Source:
- https://github.com/kevinwuhoo/randomcolor-py
-
secv_guis.utils.bool_arr_to_rgba_pixmap(arr, rgba=(255, 0, 0, 255))[source]¶ Parameters: - arr – Expects a
np.bool(h, w)array. - rgba – 4 values between 0 and 255. Alpha=255 means full opacity.
Returns: A
QtGui.QPixmapin formatRGBA8888(w, h), where thefalsevalues are all zeros and thetruevalues have the specifiedrgbacolor.- arr – Expects a
-
secv_guis.utils.load_exif(img_path)[source]¶ Returns: A dictionary with the EXIF data contained at img_path.
-
secv_guis.utils.load_img_and_exif(img_path: str, as_np_array=True, ignore_alpha=True)[source]¶ Loads the image at given path using PIL, and its EXIF data. If the EXIF data contains extra info about orientation, also rotates the image accordingly.
Parameters: - as_np_array – If true, the image will be converted from PIL format
to np via
np.asarray(image) - ignore_alpha – If the type of the image is
RGBA, it will be converted toRGB.
Returns: A tuple
(image, exif_dict).Inspired in https://stackoverflow.com/a/26928142
- as_np_array – If true, the image will be converted from PIL format
to np via
-
secv_guis.utils.pixmap_to_arr(pm, img_format=PySide2.QtGui.QImage.Format.Format_RGBA8888)[source]¶ Parameters: - pm – A Pixmap to be converted
- img_format – The
QtGui.QImageformat thatpmcorresponds to. https://doc.qt.io/qtforpython/PySide2/QtGui/QImage.html#image-formats
Returns: A
np.uint8(h, w, C)array, where the number of channelsCdepends on the image format.- ..note::
- Pixmaps are in format (w, h, …) but arrays are returned
in
(h, w, ...), as usual for numpy