ncca-ngl 0.3.0__tar.gz → 0.3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/PKG-INFO +1 -1
  2. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/pyproject.toml +1 -1
  3. ncca_ngl-0.3.2/src/ncca/ngl/widgets/NGLDebug.log +0 -0
  4. ncca_ngl-0.3.2/src/ncca/ngl/widgets/__init__.py +12 -0
  5. ncca_ngl-0.3.2/src/ncca/ngl/widgets/__main__.py +77 -0
  6. ncca_ngl-0.3.2/src/ncca/ngl/widgets/glsl/phong.frag +27 -0
  7. ncca_ngl-0.3.2/src/ncca/ngl/widgets/glsl/phong.vert +14 -0
  8. ncca_ngl-0.3.2/src/ncca/ngl/widgets/glsl/picking.frag +7 -0
  9. ncca_ngl-0.3.2/src/ncca/ngl/widgets/glsl/picking.vert +7 -0
  10. ncca_ngl-0.3.2/src/ncca/ngl/widgets/lookatwidget.py +77 -0
  11. ncca_ngl-0.3.2/src/ncca/ngl/widgets/mat4widget.py +7 -0
  12. ncca_ngl-0.3.2/src/ncca/ngl/widgets/rgbacolourwidget.py +165 -0
  13. ncca_ngl-0.3.2/src/ncca/ngl/widgets/rgbcolourwidget.py +145 -0
  14. ncca_ngl-0.3.2/src/ncca/ngl/widgets/transformation_widget.py +299 -0
  15. ncca_ngl-0.3.2/src/ncca/ngl/widgets/transformwidget.py +81 -0
  16. ncca_ngl-0.3.2/src/ncca/ngl/widgets/vec2widget.py +141 -0
  17. ncca_ngl-0.3.2/src/ncca/ngl/widgets/vec3widget.py +157 -0
  18. ncca_ngl-0.3.2/src/ncca/ngl/widgets/vec4widget.py +178 -0
  19. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/LICENSE.txt +0 -0
  20. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/.ruff_cache/.gitignore +0 -0
  21. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/.ruff_cache/0.13.0/10564494386971134025 +0 -0
  22. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/.ruff_cache/0.13.0/7783445477288392980 +0 -0
  23. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/.ruff_cache/CACHEDIR.TAG +0 -0
  24. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/PrimData/NGLDebug.log +0 -0
  25. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/PrimData/Primitives.npz +0 -0
  26. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/PrimData/buddah.npy +0 -0
  27. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/PrimData/bunny.npy +0 -0
  28. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/PrimData/cube.npy +0 -0
  29. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/PrimData/dodecahedron.npy +0 -0
  30. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/PrimData/dragon.npy +0 -0
  31. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/PrimData/football.npy +0 -0
  32. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/PrimData/icosahedron.npy +0 -0
  33. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/PrimData/octahedron.npy +0 -0
  34. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/PrimData/pack_arrays.py +0 -0
  35. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/PrimData/teapot.npy +0 -0
  36. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/PrimData/tetrahedron.npy +0 -0
  37. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/PrimData/troll.npy +0 -0
  38. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/__init__.py +0 -0
  39. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/abstract_vao.py +0 -0
  40. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/base_mesh.py +0 -0
  41. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/base_mesh.pyi +0 -0
  42. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/bbox.py +0 -0
  43. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/bezier_curve.py +0 -0
  44. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/first_person_camera.py +0 -0
  45. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/image.py +0 -0
  46. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/log.py +0 -0
  47. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/mat2.py +0 -0
  48. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/mat3.py +0 -0
  49. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/mat4.py +0 -0
  50. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/multi_buffer_vao.py +0 -0
  51. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/obj.py +0 -0
  52. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/plane.py +0 -0
  53. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/prim_data.py +0 -0
  54. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/primitives.py +0 -0
  55. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/pyside_event_handling_mixin.py +0 -0
  56. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/quaternion.py +0 -0
  57. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/random.py +0 -0
  58. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/shader.py +0 -0
  59. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/shader_lib.py +0 -0
  60. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/shader_program.py +0 -0
  61. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/shaders/checker_fragment.glsl +0 -0
  62. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/shaders/checker_vertex.glsl +0 -0
  63. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/shaders/colour_fragment.glsl +0 -0
  64. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/shaders/colour_vertex.glsl +0 -0
  65. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/shaders/diffuse_fragment.glsl +0 -0
  66. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/shaders/diffuse_vertex.glsl +0 -0
  67. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/shaders/text_fragment.glsl +0 -0
  68. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/shaders/text_geometry.glsl +0 -0
  69. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/shaders/text_vertex.glsl +0 -0
  70. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/simple_index_vao.py +0 -0
  71. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/simple_vao.py +0 -0
  72. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/text.py +0 -0
  73. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/texture.py +0 -0
  74. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/transform.py +0 -0
  75. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/util.py +0 -0
  76. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/vao_factory.py +0 -0
  77. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/vec2.py +0 -0
  78. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/vec2_array.py +0 -0
  79. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/vec3.py +0 -0
  80. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/vec3_array.py +0 -0
  81. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/vec4.py +0 -0
  82. {ncca_ngl-0.3.0 → ncca_ngl-0.3.2}/src/ncca/ngl/vec4_array.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ncca-ngl
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: A Python version of the NGL graphics library.
5
5
  Author: Jon Macey
6
6
  Author-email: Jon Macey <jmacey@bournemouth.ac.uk>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ncca-ngl"
3
- version = "0.3.0"
3
+ version = "0.3.2"
4
4
  description = "A Python version of the NGL graphics library."
5
5
  authors = [{ name = "Jon Macey", email = "jmacey@bournemouth.ac.uk" }]
6
6
  requires-python = ">=3.13"
File without changes
@@ -0,0 +1,12 @@
1
+ __version__ = "0.1.0"
2
+
3
+
4
+ from .lookatwidget import LookAtWidget
5
+ from .rgbacolourwidget import RGBAColourWidget
6
+ from .rgbcolourwidget import RGBColourWidget
7
+ from .transformwidget import TransformWidget
8
+ from .vec2widget import Vec2Widget
9
+ from .vec3widget import Vec3Widget
10
+ from .vec4widget import Vec4Widget
11
+
12
+ __all__ = ["Vec2Widget", "Vec3Widget", "Vec4Widget", "TransformWidget", "LookAtWidget", "RGBColourWidget"]
@@ -0,0 +1,77 @@
1
+ import sys
2
+
3
+ from PySide6.QtWidgets import QApplication, QDialog, QGridLayout, QLabel
4
+
5
+ from ncca.ngl import Vec2, Vec3, Vec4
6
+ from ncca.ngl.widgets import (
7
+ LookAtWidget,
8
+ RGBAColourWidget,
9
+ RGBColourWidget,
10
+ TransformWidget,
11
+ Vec2Widget,
12
+ Vec3Widget,
13
+ Vec4Widget,
14
+ )
15
+
16
+
17
+ class SimpleDialog(QDialog):
18
+ def __init__(self, parent=None):
19
+ super(SimpleDialog, self).__init__(parent)
20
+ self.setWindowTitle("PyNGL ncca.widgets library ")
21
+ self.setMinimumWidth(200)
22
+ layout = QGridLayout()
23
+
24
+ self.vec2_widget = Vec2Widget(self, "Vec2 Widget", Vec2(1, 2))
25
+ self.vec2_widget.set_x_range(-1, 1)
26
+ self.vec2_widget.set_y_range(-2, 2)
27
+ layout.addWidget(self.vec2_widget, 0, 0)
28
+ self.vec2_label = QLabel("[0.0,0.0]")
29
+ layout.addWidget(self.vec2_label, 0, 1)
30
+ self.vec2_widget.valueChanged.connect(self._update_vec2)
31
+
32
+ self.vec3_widget = Vec3Widget(self, "Vec3 Widget", Vec3(1, 2, 3))
33
+ self.vec3_widget.set_y_range(-2, 2)
34
+ self.vec3_widget.set_z_range(-3, 3)
35
+ layout.addWidget(self.vec3_widget, 1, 0)
36
+ self.vec3_label = QLabel("[0.0,0.0,0.0]")
37
+ layout.addWidget(self.vec3_label, 1, 1)
38
+ self.vec3_widget.valueChanged.connect(self._update_vec3)
39
+
40
+ self.vec4_widget = Vec4Widget(self, "Vec4 Widget", Vec4(1, 2, 3, 1.0))
41
+ self.vec4_widget.set_y_range(-2, 2)
42
+ self.vec4_widget.set_z_range(-3, 3)
43
+ layout.addWidget(self.vec4_widget, 2, 0)
44
+ self.vec4_label = QLabel("[0.0,0.0,0.0,1.0]")
45
+ layout.addWidget(self.vec4_label, 2, 1)
46
+ self.vec4_widget.valueChanged.connect(self._update_vec4)
47
+
48
+ self.transform_widget = TransformWidget(self, "Transform Widget")
49
+ layout.addWidget(self.transform_widget, 3, 0)
50
+
51
+ self.lookat = LookAtWidget(self, "Look At")
52
+ layout.addWidget(self.lookat, 4, 0)
53
+
54
+ self.rgb_colour_widget = RGBColourWidget(self, "RGB Colour Widget", 1.0, 0.0, 0.0)
55
+ layout.addWidget(self.rgb_colour_widget, 5, 0)
56
+ self.rgba_colour_widget = RGBAColourWidget(self, "RGB Colour Widget", 1.0, 0.0, 0.0, 1.0)
57
+ layout.addWidget(self.rgba_colour_widget, 6, 0)
58
+
59
+ self.setLayout(layout)
60
+
61
+ def _update_vec3(self, value):
62
+ self.vec3_label.setText(f"[{value.x:0.2f}, {value.y:0.2f}, {value.z:0.2f}]")
63
+
64
+ def _update_vec2(self, value):
65
+ self.vec2_label.setText(f"[{value.x:0.2f}, {value.y:0.2f}]")
66
+
67
+ def _update_vec4(self, value):
68
+ self.vec4_label.setText(f"[{value.x:0.2f}, {value.y:0.2f}, {value.z:0.2f}, {value.w:0.2f}]")
69
+
70
+
71
+ if __name__ == "__main__":
72
+ app = QApplication(sys.argv)
73
+ print("NCCA Widgets")
74
+
75
+ dialog = SimpleDialog()
76
+ dialog.show()
77
+ app.exec()
@@ -0,0 +1,27 @@
1
+ #version 330 core
2
+ out vec4 FragColor;
3
+ in vec3 FragPos;
4
+ in vec3 Normal;
5
+ uniform vec3 light_pos;
6
+ uniform vec3 view_pos;
7
+ uniform vec3 light_color;
8
+ uniform vec3 object_color;
9
+ void main()
10
+ {
11
+ // Ambient
12
+ float ambient_strength = 0.1;
13
+ vec3 ambient = ambient_strength * light_color;
14
+ // Diffuse
15
+ vec3 norm = normalize(Normal);
16
+ vec3 light_dir = normalize(light_pos - FragPos);
17
+ float diff = max(dot(norm, light_dir), 0.0);
18
+ vec3 diffuse = diff * light_color;
19
+ // Specular
20
+ float specular_strength = 0.5;
21
+ vec3 view_dir = normalize(view_pos - FragPos);
22
+ vec3 reflect_dir = reflect(-light_dir, norm);
23
+ float spec = pow(max(dot(view_dir, reflect_dir), 0.0), 32);
24
+ vec3 specular = specular_strength * spec * light_color;
25
+ vec3 result = (ambient + diffuse + specular) * object_color;
26
+ FragColor = vec4(result, 1.0);
27
+ }
@@ -0,0 +1,14 @@
1
+ #version 330 core
2
+ layout (location = 0) in vec3 aPos;
3
+ layout (location = 1) in vec3 aNormal;
4
+ out vec3 FragPos;
5
+ out vec3 Normal;
6
+ uniform mat4 model;
7
+ uniform mat4 MVP;
8
+ uniform mat3 normal_matrix;
9
+ void main()
10
+ {
11
+ FragPos = vec3(model * vec4(aPos, 1.0));
12
+ Normal = normal_matrix * aNormal;
13
+ gl_Position = MVP * vec4(aPos, 1.0);
14
+ }
@@ -0,0 +1,7 @@
1
+ #version 330 core
2
+ out vec3 FragColor;
3
+ uniform vec3 face_id;
4
+ void main()
5
+ {
6
+ FragColor = face_id;
7
+ }
@@ -0,0 +1,7 @@
1
+ #version 330 core
2
+ layout (location = 0) in vec3 aPos;
3
+ uniform mat4 MVP;
4
+ void main()
5
+ {
6
+ gl_Position = MVP * vec4(aPos, 1.0);
7
+ }
@@ -0,0 +1,77 @@
1
+ from PySide6.QtCore import Property, QSignalBlocker, Qt, Signal, Slot
2
+ from PySide6.QtWidgets import QComboBox, QFrame, QLabel, QToolButton, QVBoxLayout, QWidget
3
+
4
+ from ncca.ngl import Mat4, Vec3, look_at
5
+
6
+ from .vec3widget import Vec3Widget
7
+
8
+
9
+ class LookAtWidget(QFrame):
10
+ """A widget for displaying and editing a Transform object, with foldable sections."""
11
+
12
+ valueChanged = Signal(Mat4)
13
+
14
+ def __init__(self, parent: QWidget | None = None, name: str = "", eye=Vec3(2, 2, 2), look=Vec3(0, 0, 0)) -> None:
15
+ """
16
+ Args:
17
+ name: The name of the widget.
18
+ parent: The parent widget.
19
+ """
20
+ super().__init__(parent)
21
+ self.setFrameShape(QFrame.Shape.StyledPanel)
22
+ self._name = name
23
+ self._view = Mat4()
24
+ main_layout = QVBoxLayout(self)
25
+ main_layout.setContentsMargins(2, 2, 2, 2)
26
+ main_layout.setSpacing(0)
27
+
28
+ self._toggle_button = QToolButton(self)
29
+ self._toggle_button.setText(self._name)
30
+ self._toggle_button.setCheckable(True)
31
+ self._toggle_button.setChecked(True)
32
+ self._toggle_button.setStyleSheet("QToolButton { border: none; }")
33
+ self._toggle_button.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
34
+ self._toggle_button.setArrowType(Qt.ArrowType.DownArrow)
35
+ self._toggle_button.clicked.connect(self.toggle_collapsed)
36
+
37
+ self._content_widget = QWidget(self)
38
+ content_layout = QVBoxLayout(self._content_widget)
39
+ content_layout.setContentsMargins(0, 0, 0, 0)
40
+
41
+ self._eye = Vec3Widget(self, "Eye", eye)
42
+ self._look = Vec3Widget(self, "Look", look)
43
+ self._up = QComboBox(self)
44
+ for v in ["y-up", "x-up", "z-up"]:
45
+ self._up.addItem(v)
46
+ self._eye.valueChanged.connect(self._update_matrix)
47
+ self._look.valueChanged.connect(self._update_matrix)
48
+ self._up.currentIndexChanged.connect(self._update_matrix)
49
+ content_layout.addWidget(self._eye)
50
+ content_layout.addWidget(self._look)
51
+ content_layout.addWidget(QLabel("World Up"))
52
+ content_layout.addWidget(self._up)
53
+ main_layout.addWidget(self._toggle_button)
54
+ main_layout.addWidget(self._content_widget)
55
+
56
+ def toggle_collapsed(self, checked: bool) -> None:
57
+ """Toggles the visibility of the content widget."""
58
+ if checked:
59
+ self._toggle_button.setArrowType(Qt.ArrowType.DownArrow)
60
+ self._content_widget.setVisible(True)
61
+ else:
62
+ self._toggle_button.setArrowType(Qt.ArrowType.RightArrow)
63
+ self._content_widget.setVisible(False)
64
+
65
+ def _update_matrix(self) -> None:
66
+ """Updates the view matrix based on the widget values."""
67
+ eye = self._eye.value
68
+ look = self._look.value
69
+ world_up = [Vec3(0, 1, 0), Vec3(1, 0, 0), Vec3(0, 0, 1)]
70
+ up = world_up[self._up.currentIndex()]
71
+
72
+ self._view = look_at(eye, look, up)
73
+ self.valueChanged.emit(self._view)
74
+
75
+ def view(self) -> Mat4:
76
+ """Returns the current view matrix."""
77
+ return self._view
@@ -0,0 +1,7 @@
1
+ from PySide6.QtCore import Property, QSignalBlocker, Signal
2
+ from PySide6.QtWidgets import QDoubleSpinBox, QFrame, QHBoxLayout, QLabel, QWidget
3
+
4
+ from ncca.ngl import Mat4
5
+
6
+
7
+ class Mat4ViewWidget(QFrame):
@@ -0,0 +1,165 @@
1
+ from PySide6.QtCore import Property, QSignalBlocker, Signal
2
+ from PySide6.QtGui import QColor
3
+ from PySide6.QtWidgets import (
4
+ QColorDialog,
5
+ QDoubleSpinBox,
6
+ QFrame,
7
+ QHBoxLayout,
8
+ QLabel,
9
+ QPushButton,
10
+ QWidget,
11
+ )
12
+
13
+ from ncca.ngl import Vec4
14
+
15
+
16
+ class RGBAColourWidget(QFrame):
17
+ """A widget for displaying and editing a Vec4 object."""
18
+
19
+ colourChanged = Signal(Vec4)
20
+ rValueChanged = Signal(float)
21
+ gValueChanged = Signal(float)
22
+ bValueChanged = Signal(float)
23
+ aValueChanged = Signal(float)
24
+
25
+ def __init__(
26
+ self,
27
+ parent: QWidget | None = None,
28
+ name: str = "",
29
+ r: float = 1.0,
30
+ g: float = 1.0,
31
+ b: float = 1.0,
32
+ a: float = 1.0,
33
+ ) -> None:
34
+ """
35
+ Args:
36
+ name: The name of the widget.
37
+ r: The initial red component of the colour.
38
+ g: The initial green component of the colour.
39
+ b: The initial blue component of the colour.
40
+ a: The initial alpha component of the colour.
41
+ parent: The parent widget.
42
+ """
43
+ super().__init__(parent)
44
+ self.setFrameShape(QFrame.Shape.StyledPanel)
45
+ self._colour = Vec4(r, g, b, a)
46
+ self._name = name
47
+ layout = QHBoxLayout()
48
+
49
+ self.r_spinbox = self._create_spinbox(self._colour.x)
50
+ self.g_spinbox = self._create_spinbox(self._colour.y)
51
+ self.b_spinbox = self._create_spinbox(self._colour.z)
52
+ self.a_spinbox = self._create_spinbox(self._colour.w)
53
+ self._label = QLabel(self._name)
54
+ self._color_button = QPushButton()
55
+ self._color_button.setFixedSize(20, 20)
56
+ self._color_button.clicked.connect(self._show_color_dialog)
57
+ self._update_button_color()
58
+
59
+ layout.addWidget(self._label)
60
+ layout.addWidget(self.r_spinbox)
61
+ layout.addWidget(self.g_spinbox)
62
+ layout.addWidget(self.b_spinbox)
63
+ layout.addWidget(self.a_spinbox)
64
+ layout.addWidget(self._color_button)
65
+ self.setLayout(layout)
66
+
67
+ def _create_spinbox(self, value: float) -> QDoubleSpinBox:
68
+ """Helper method to create and configure a QDoubleSpinBox.
69
+
70
+ Args:
71
+ value: The initial value of the spinbox.
72
+
73
+ Returns:
74
+ A configured QDoubleSpinBox.
75
+ """
76
+ spinbox = QDoubleSpinBox()
77
+ spinbox.setValue(value)
78
+ spinbox.setRange(0.0, 1.0)
79
+ spinbox.setSingleStep(0.01)
80
+ spinbox.valueChanged.connect(self._on_value_changed)
81
+ return spinbox
82
+
83
+ def colour(self) -> Vec4:
84
+ """
85
+ Returns:
86
+ The current value of the widget.
87
+ """
88
+ return self._colour
89
+
90
+ def _on_value_changed(self, value: float) -> None:
91
+ """This slot is called when the value of a spinbox changes.
92
+
93
+ Args:
94
+ value: The new value of the spinbox.
95
+ """
96
+ sender = self.sender()
97
+ if sender == self.r_spinbox:
98
+ self._colour.x = value
99
+ self.rValueChanged.emit(value)
100
+ elif sender == self.g_spinbox:
101
+ self._colour.y = value
102
+ self.gValueChanged.emit(value)
103
+ elif sender == self.b_spinbox:
104
+ self._colour.z = value
105
+ self.bValueChanged.emit(value)
106
+ elif sender == self.a_spinbox:
107
+ self._colour.w = value
108
+ self.aValueChanged.emit(value)
109
+ # emit the Vec4 value changed signal
110
+ self.colourChanged.emit(self._colour)
111
+ self._update_button_color()
112
+
113
+ def set_colour(self, value: Vec4) -> None:
114
+ """Sets the value of the widget.
115
+
116
+ Args:
117
+ value: The new value of the widget.
118
+ """
119
+ with (
120
+ QSignalBlocker(self.r_spinbox),
121
+ QSignalBlocker(self.g_spinbox),
122
+ QSignalBlocker(self.b_spinbox),
123
+ QSignalBlocker(self.a_spinbox),
124
+ ):
125
+ self.r_spinbox.setValue(value.x)
126
+ self.g_spinbox.setValue(value.y)
127
+ self.b_spinbox.setValue(value.z)
128
+ self.a_spinbox.setValue(value.w)
129
+ self._colour = value
130
+ self.colourChanged.emit(self._colour)
131
+ self._update_button_color()
132
+
133
+ def _update_button_color(self) -> None:
134
+ """Updates the background color of the color button."""
135
+ color = QColor.fromRgbF(self._colour.x, self._colour.y, self._colour.z, self._colour.w)
136
+ self._color_button.setStyleSheet(f"background-color: {color.name(QColor.NameFormat.HexArgb)}")
137
+
138
+ def _show_color_dialog(self) -> None:
139
+ """Shows a QColorDialog to select a new color."""
140
+ current_color = QColor.fromRgbF(self._colour.x, self._colour.y, self._colour.z, self._colour.w)
141
+ color = QColorDialog.getColor(
142
+ current_color, self, "Select Color", options=QColorDialog.ColorDialogOption.ShowAlphaChannel
143
+ )
144
+ if color.isValid():
145
+ new_colour = Vec4(color.redF(), color.greenF(), color.blueF(), color.alphaF())
146
+ self.set_colour(new_colour)
147
+
148
+ def name(self) -> str:
149
+ """
150
+ Returns:
151
+ The name of the widget.
152
+ """
153
+ return self._name
154
+
155
+ def set_name(self, name: str) -> None:
156
+ """Sets the name of the widget.
157
+
158
+ Args:
159
+ name: The new name of the widget.
160
+ """
161
+ self._name = name
162
+ self._label.setText(name)
163
+
164
+ value = Property(Vec4, colour, set_colour)
165
+ name = Property(str, name, set_name)
@@ -0,0 +1,145 @@
1
+ from PySide6.QtCore import Property, QSignalBlocker, Signal
2
+ from PySide6.QtGui import QColor
3
+ from PySide6.QtWidgets import (
4
+ QColorDialog,
5
+ QDoubleSpinBox,
6
+ QFrame,
7
+ QHBoxLayout,
8
+ QLabel,
9
+ QPushButton,
10
+ QWidget,
11
+ )
12
+
13
+ from ncca.ngl import Vec3
14
+
15
+
16
+ class RGBColourWidget(QFrame):
17
+ """A widget for displaying and editing a Vec3 object."""
18
+
19
+ colourChanged = Signal(Vec3)
20
+ rValueChanged = Signal(float)
21
+ gValueChanged = Signal(float)
22
+ bValueChanged = Signal(float)
23
+
24
+ def __init__(
25
+ self, parent: QWidget | None = None, name: str = "", r: float = 1.0, g: float = 1.0, b: float = 1.0
26
+ ) -> None:
27
+ """
28
+ Args:
29
+ name: The name of the widget.
30
+ r: The initial red component of the colour.
31
+ g: The initial green component of the colour.
32
+ b: The initial blue component of the colour.
33
+ parent: The parent widget.
34
+ """
35
+ super().__init__(parent)
36
+ self.setFrameShape(QFrame.Shape.StyledPanel)
37
+ self._colour = Vec3(r, g, b)
38
+ self._name = name
39
+ layout = QHBoxLayout()
40
+
41
+ self.r_spinbox = self._create_spinbox(self._colour.x)
42
+ self.g_spinbox = self._create_spinbox(self._colour.y)
43
+ self.b_spinbox = self._create_spinbox(self._colour.z)
44
+
45
+ self._label = QLabel(self._name)
46
+ self._color_button = QPushButton()
47
+ self._color_button.setFixedSize(20, 20)
48
+ self._color_button.clicked.connect(self._show_color_dialog)
49
+ self._update_button_color()
50
+
51
+ layout.addWidget(self._label)
52
+ layout.addWidget(self.r_spinbox)
53
+ layout.addWidget(self.g_spinbox)
54
+ layout.addWidget(self.b_spinbox)
55
+ layout.addWidget(self._color_button)
56
+ self.setLayout(layout)
57
+
58
+ def _create_spinbox(self, value: float) -> QDoubleSpinBox:
59
+ """Helper method to create and configure a QDoubleSpinBox.
60
+
61
+ Args:
62
+ value: The initial value of the spinbox.
63
+
64
+ Returns:
65
+ A configured QDoubleSpinBox.
66
+ """
67
+ spinbox = QDoubleSpinBox()
68
+ spinbox.setValue(value)
69
+ spinbox.setRange(0.0, 1.0)
70
+ spinbox.setSingleStep(0.01)
71
+ spinbox.valueChanged.connect(self._on_value_changed)
72
+ return spinbox
73
+
74
+ def colour(self) -> Vec3:
75
+ """
76
+ Returns:
77
+ The current value of the widget.
78
+ """
79
+ return self._colour
80
+
81
+ def _on_value_changed(self, value: float) -> None:
82
+ """This slot is called when the value of a spinbox changes.
83
+
84
+ Args:
85
+ value: The new value of the spinbox.
86
+ """
87
+ sender = self.sender()
88
+ if sender == self.r_spinbox:
89
+ self._colour.x = value
90
+ self.rValueChanged.emit(value)
91
+ elif sender == self.g_spinbox:
92
+ self._colour.y = value
93
+ self.gValueChanged.emit(value)
94
+ elif sender == self.b_spinbox:
95
+ self._colour.z = value
96
+ self.bValueChanged.emit(value)
97
+ # emit the Vec3 value changed signal
98
+ self.colourChanged.emit(self._colour)
99
+ self._update_button_color()
100
+
101
+ def set_colour(self, value: Vec3) -> None:
102
+ """Sets the value of the widget.
103
+
104
+ Args:
105
+ value: The new value of the widget.
106
+ """
107
+ with QSignalBlocker(self.r_spinbox), QSignalBlocker(self.g_spinbox), QSignalBlocker(self.b_spinbox):
108
+ self.r_spinbox.setValue(value.x)
109
+ self.g_spinbox.setValue(value.y)
110
+ self.b_spinbox.setValue(value.z)
111
+ self._colour = value
112
+ self.colourChanged.emit(self._colour)
113
+ self._update_button_color()
114
+
115
+ def _update_button_color(self) -> None:
116
+ """Updates the background color of the color button."""
117
+ color = QColor.fromRgbF(self._colour.x, self._colour.y, self._colour.z)
118
+ self._color_button.setStyleSheet(f"background-color: {color.name()}")
119
+
120
+ def _show_color_dialog(self) -> None:
121
+ """Shows a QColorDialog to select a new color."""
122
+ current_color = QColor.fromRgbF(self._colour.x, self._colour.y, self._colour.z)
123
+ color = QColorDialog.getColor(current_color, self, "Select Color")
124
+ if color.isValid():
125
+ new_colour = Vec3(color.redF(), color.greenF(), color.blueF())
126
+ self.set_colour(new_colour)
127
+
128
+ def name(self) -> str:
129
+ """
130
+ Returns:
131
+ The name of the widget.
132
+ """
133
+ return self._name
134
+
135
+ def set_name(self, name: str) -> None:
136
+ """Sets the name of the widget.
137
+
138
+ Args:
139
+ name: The new name of the widget.
140
+ """
141
+ self._name = name
142
+ self._label.setText(name)
143
+
144
+ value = Property(Vec3, colour, set_colour)
145
+ name = Property(str, name, set_name)