Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 57 additions & 81 deletions freecad/gridfinity_workbench/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
# ruff: noqa: D101, D107

import re
from collections import OrderedDict
from pathlib import Path
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any

import FreeCAD as fc # noqa: N813
import FreeCADGui as fcg # noqa: N813
Expand Down Expand Up @@ -150,44 +151,6 @@ def Activated(self) -> None: # noqa: N802
fcg.SendMsgToActiveView("ViewFit")


class DrawCommand(BaseCommand):
"""Base for gridfinity workbench command.

Used for commands where an object is drawn.

"""

def __init__(
self,
*,
name: str,
gridfinity_function: type[FoundationGridfinity],
pixmap: Path,
) -> None:
super().__init__(
name=name,
pixmap=pixmap,
menu_text=f"Gridfinity {PASCAL_CASE_REGEX.sub(' ', name)}",
tooltip=f"Draw a Gridfinty {PASCAL_CASE_REGEX.sub(' ', name)}.",
)
self.gridfinity_function = gridfinity_function

def Activated(self) -> None: # noqa: N802, D102
layout = custom_shape.get_layout()
if layout is None:
return

obj = utils.new_object(self.name)
if fc.GuiUp:
view_object: fcg.ViewProviderDocumentObject = obj.ViewObject
ViewProviderGridfinity(view_object, str(self.pixmap))

self.gridfinity_function(obj, layout) # type: ignore [call-arg]

fc.ActiveDocument.recompute()
fcg.SendMsgToActiveView("ViewFit")


class CreateBinBlank(CreateCommand):
def __init__(self) -> None:
super().__init__(
Expand Down Expand Up @@ -269,66 +232,79 @@ def __init__(self) -> None:
)


class CreateCustomBlankBin(DrawCommand):
def __init__(self) -> None:
super().__init__(
name="CustomBlankBin",
gridfinity_function=CustomBlankBin,
pixmap=ICONDIR / "CustomBlankBin.svg",
)

class DrawCommand(BaseCommand):
"""Base for gridfinity workbench command.

class CreateCustomBinBase(DrawCommand):
def __init__(self) -> None:
super().__init__(
name="CustomBinBase",
gridfinity_function=CustomBinBase,
pixmap=ICONDIR / "CustomBinBase.svg",
)
Used for commands where an object is drawn.

"""

class CreateCustomEcoBin(DrawCommand):
def __init__(self) -> None:
def __init__(
self,
*,
name: str,
pixmap: Path,
menu_text: str,
tooltip: str,
gridfinity_functions: OrderedDict[str, Any],
) -> None:
super().__init__(
name="CustomEcoBin",
gridfinity_function=CustomEcoBin,
pixmap=ICONDIR / "CustomEcoBin.svg",
name=name,
pixmap=pixmap,
menu_text=menu_text,
tooltip=tooltip,
)
self.gridfinity_functions = gridfinity_functions

def Activated(self) -> None: # noqa: N802, D102
dialog_data = custom_shape.custom_bin_dialog(list(self.gridfinity_functions.keys()))
if dialog_data is None:
return
assert dialog_data.bin_type in self.gridfinity_functions

class CreateCustomStorageBin(DrawCommand):
def __init__(self) -> None:
super().__init__(
name="CustomStorageBin",
gridfinity_function=CustomStorageBin,
pixmap=ICONDIR / "CustomStorageBin.svg",
)
obj = utils.new_object(self.name)
if fc.GuiUp:
view_object: fcg.ViewProviderDocumentObject = obj.ViewObject
ViewProviderGridfinity(view_object, str(self.pixmap))

self.gridfinity_functions[dialog_data.bin_type](obj, dialog_data.layout)

class CreateCustomBaseplate(DrawCommand):
def __init__(self) -> None:
super().__init__(
name="CustomBaseplate",
gridfinity_function=CustomBaseplate,
pixmap=ICONDIR / "CustomBaseplate.svg",
)
fc.ActiveDocument.recompute()
fcg.SendMsgToActiveView("ViewFit")


class CreateCustomMagnetBaseplate(DrawCommand):
class DrawBin(DrawCommand):
def __init__(self) -> None:
super().__init__(
name="CustomMagnetBaseplate",
gridfinity_function=CustomMagnetBaseplate,
pixmap=ICONDIR / "CustomMagnetBaseplate.svg",
name="CustomBin",
pixmap=ICONDIR / "CustomBin.svg",
menu_text="Gridfinity Custom Bin",
tooltip="Draw a custom gridfinity bin of any type.",
gridfinity_functions=OrderedDict(
[
("Blank Bin", CustomBlankBin),
("Bin Base", CustomBinBase),
("Storage Bin", CustomStorageBin),
("Eco Bin", CustomEcoBin),
],
),
)


class CreateCustomScrewTogetherBaseplate(DrawCommand):
class DrawBaseplate(DrawCommand):
def __init__(self) -> None:
super().__init__(
name="CustomScrewTogetherBaseplate",
gridfinity_function=CustomScrewTogetherBaseplate,
pixmap=ICONDIR / "CustomScrewTogetherBaseplate.svg",
name="CustomBaseplate",
pixmap=ICONDIR / "CustomBaseplate.svg",
menu_text="Gridfinity Custom Baseplate",
tooltip="Draw a custom gridfinity baseplate of any type.",
gridfinity_functions=OrderedDict(
[
("Simple Baseplate", CustomBaseplate),
("Magnet Baseplate", CustomMagnetBaseplate),
("Screw Together Baseplate", CustomScrewTogetherBaseplate),
],
),
)


Expand Down
25 changes: 21 additions & 4 deletions freecad/gridfinity_workbench/custom_shape.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations

import math
from dataclasses import dataclass

# The PySide library is provided by FreeCAD and it's path is platform dependent,
# so it cannot be analyzed by mypy.
Expand All @@ -22,6 +23,7 @@
QShowEvent,
)
from PySide.QtWidgets import (
QComboBox,
QDialog,
QDialogButtonBox,
QLabel,
Expand All @@ -32,7 +34,7 @@
class GridDialog(QDialog):
"""A dialog with togglable grid cells."""

def __init__(self, x: int, y: int, offset: int, spacing: int) -> None:
def __init__(self, types: list[str], x: int, y: int, offset: int, spacing: int) -> None:
"""Create the dialog object."""
super().__init__()
self.x = x
Expand All @@ -44,11 +46,15 @@ def __init__(self, x: int, y: int, offset: int, spacing: int) -> None:
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)

self.comboBox = QComboBox()
self.comboBox.addItems(types)

self.label = QLabel()
self.pixmap = QPixmap(QSize(2 * offset + x * spacing, 2 * offset + y * spacing))

layout = QVBoxLayout()
layout.addWidget(self.label)
layout.addWidget(self.comboBox)
layout.addWidget(self.buttonBox)
self.setLayout(layout)

Expand Down Expand Up @@ -139,13 +145,24 @@ def mousePressEvent(self, event: QMouseEvent) -> None: # noqa: D102, N802
self._recompute()


def get_layout() -> list[list[bool]] | None:
@dataclass
class GridDialogData:
"""A result of a successful GridDialog."""

layout: list[list[bool]]
bin_type: str


def custom_bin_dialog(types: list[str]) -> GridDialogData | None:
"""Get a custom layout from the user.

Returns None if the user aborted the operation.

"""
dialog = GridDialog(10, 10, 40, 50)
dialog = GridDialog(types, 10, 10, 40, 50)
if not dialog.exec():
return None
return dialog.grid_layout
return GridDialogData(
layout=dialog.grid_layout,
bin_type=dialog.comboBox.currentText(),
)
49 changes: 30 additions & 19 deletions freecad/gridfinity_workbench/icons/CustomBaseplate.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading