window.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. """Create window and widgets, the starting point for executing Shapeshift."""
  2. # Standard library imports
  3. import random
  4. import sys
  5. # Third-party imports
  6. from PySide6 import QtCore, QtGui, QtWidgets
  7. # Local imports
  8. import figures
  9. from graphics import ModernGLWidget
  10. from polytope import Polyhedron
  11. class AxisSlider(QtWidgets.QSlider):
  12. def __init__(self):
  13. super().__init__(QtCore.Qt.Horizontal)
  14. self.setValue(random.randint(0, 100))
  15. class OperationButton(QtWidgets.QPushButton):
  16. def __init__(self, name):
  17. super().__init__(name)
  18. class MainWindow(QtWidgets.QMainWindow):
  19. def __init__(self):
  20. super(MainWindow, self).__init__()
  21. self.setWindowTitle("Shapeshift")
  22. self.setGeometry(100, 100, 830, 550)
  23. self.polytope_models = [Model(figures.octahedron)]
  24. self.setStyleSheet("""
  25. background-color: #262626;
  26. color: #FFFFFF;
  27. """)
  28. @property
  29. def polytope(self):
  30. return self.polytope_models[-1].polytope
  31. def setupUI(self):
  32. """Set up layout of widgets."""
  33. # Toolbar
  34. menubar = QtWidgets.QMenuBar()
  35. file_menu = menubar.addMenu("File")
  36. file_menu.addAction(open_action := QtGui.QAction("Open", self))
  37. file_menu.addAction(export_action := QtGui.QAction("Export", self))
  38. edit_menu = menubar.addMenu("Edit")
  39. edit_menu.addAction(undo_action := QtGui.QAction("Undo", self))
  40. edit_menu.addAction(redo_action := QtGui.QAction("Redo", self))
  41. view_menu = menubar.addMenu("View")
  42. view_menu.addAction(tetrahedron_action := QtGui.QAction("Tetrahedron", self))
  43. view_menu.addAction(cube_action := QtGui.QAction("Cube", self))
  44. view_menu.addAction(octahedron_action := QtGui.QAction("Octahedron", self))
  45. view_menu.addAction(dodecahedron_action := QtGui.QAction("Dodecahedron", self))
  46. view_menu.addAction(icosahedron_action := QtGui.QAction("Icosahedron", self))
  47. modify_menu = menubar.addMenu("Modify")
  48. modify_menu.addAction(clear_polyhedra_action := QtGui.QAction("Clear polyhedra", self))
  49. details_menu = menubar.addMenu("Details")
  50. details_menu.addAction(element_values_action := QtGui.QAction("Element values", self))
  51. details_menu.addAction(element_counts_action := QtGui.QAction("Element counts", self))
  52. details_menu.addAction(face_types_action := QtGui.QAction("Face types", self))
  53. self.setMenuBar(menubar)
  54. # Operations tab
  55. self.tab_1 = QtWidgets.QWidget()
  56. self.tab_1_layout = QtWidgets.QGridLayout()
  57. self.tab_1_layout.addWidget(truncate := OperationButton("Truncate"), 1, 1)
  58. self.tab_1_layout.addWidget(rectify := OperationButton("Rectify"), 2, 1)
  59. self.tab_1_layout.addWidget(facet := OperationButton("Facet"), 3, 1)
  60. self.tab_1_layout.addWidget(dual := OperationButton("Dual"), 2, 2)
  61. self.tab_1_layout.addWidget(cap := OperationButton("Cap"), 1, 3)
  62. self.tab_1_layout.addWidget(bridge := OperationButton("Bridge"), 2, 3)
  63. self.tab_1_layout.addWidget(stellate := OperationButton("Stellate"), 3, 3)
  64. self.tab_1.setLayout(self.tab_1_layout)
  65. self.tab_2 = QtWidgets.QWidget()
  66. self.tab_2_layout = QtWidgets.QGridLayout()
  67. self.tab_2_layout.addWidget(decompose := OperationButton("Decompose"), 1, 1)
  68. self.tab_2_layout.addWidget(uncouple := OperationButton("Uncouple"), 1, 2)
  69. self.tab_2.setLayout(self.tab_2_layout)
  70. self.tab_3 = QtWidgets.QWidget()
  71. self.tab_3_layout = QtWidgets.QGridLayout()
  72. self.tab_3_layout.addWidget(prismate := OperationButton("Prismate"), 1, 1)
  73. self.tab_3.setLayout(self.tab_3_layout)
  74. # Create tab widget
  75. self.tab_widget = QtWidgets.QTabWidget()
  76. self.tab_widget.addTab(self.tab_1, "Truncate")
  77. self.tab_widget.addTab(self.tab_2, "Separate")
  78. self.tab_widget.addTab(self.tab_3, "Hypermutate")
  79. # Create descriptive text box
  80. self.descriptive_textbox = QtWidgets.QLabel("")
  81. self.descriptive_textbox.setWordWrap(True)
  82. # Polyhedra rendering options
  83. self.show_edges_only = QtWidgets.QCheckBox("Show edges only", self)
  84. self.show_prior_polyhedra_checkbox = QtWidgets.QCheckBox("Show prior polyhedra", self)
  85. # Create left vertical box
  86. self.left_vbox = QtWidgets.QVBoxLayout()
  87. self.left_vbox.addWidget(self.tab_widget)
  88. self.left_vbox.addWidget(self.descriptive_textbox)
  89. self.left_vbox.addWidget(self.show_edges_only)
  90. self.left_vbox.addWidget(self.show_prior_polyhedra_checkbox)
  91. self.left_widget = QtWidgets.QWidget()
  92. self.left_widget.setLayout(self.left_vbox)
  93. self.left_widget.setMaximumWidth(330)
  94. # Create horizontal box layout
  95. self.hbox = QtWidgets.QHBoxLayout()
  96. self.hbox.addWidget(self.left_widget)
  97. # Create vertical box for QOpenGLWidget and QSliders
  98. self.vbox = QtWidgets.QVBoxLayout()
  99. self.vbox.addWidget(moderngl_widget := ModernGLWidget(self))
  100. self.x_slider = AxisSlider()
  101. self.vbox.addWidget(self.x_slider)
  102. self.y_slider = AxisSlider()
  103. self.vbox.addWidget(self.y_slider)
  104. self.z_slider = AxisSlider()
  105. self.vbox.addWidget(self.z_slider)
  106. self.moderngl_and_sliders = QtWidgets.QWidget()
  107. self.moderngl_and_sliders.setLayout(self.vbox)
  108. self.hbox.addWidget(self.moderngl_and_sliders)
  109. # Add zoom slider
  110. self.zoom_slider = QtWidgets.QSlider()
  111. self.hbox.addWidget(self.zoom_slider)
  112. self.zoom_slider.setValue(10)
  113. # Create list of created polyhedra
  114. self.right_vbox = QtWidgets.QVBoxLayout()
  115. self.right_widget = QtWidgets.QWidget()
  116. self.right_widget.setLayout(self.right_vbox)
  117. self.hbox.addWidget(self.right_widget)
  118. # Create central widget
  119. self.central_widget = QtWidgets.QWidget()
  120. self.central_widget.setLayout(self.hbox)
  121. self.setCentralWidget(self.central_widget)
  122. # Connect operations to buttons/actions
  123. truncate.clicked.connect(lambda: self.operations(Polyhedron.truncate))
  124. rectify.clicked.connect(lambda: self.operations(Polyhedron.rectify))
  125. facet.clicked.connect(lambda: self.operations(Polyhedron.facet))
  126. dual.clicked.connect(lambda: self.operations(Polyhedron.reciprocate))
  127. cap.clicked.connect(lambda: self.operations(Polyhedron.cap))
  128. bridge.clicked.connect(lambda: self.operations(Polyhedron.bridge))
  129. stellate.clicked.connect(lambda: self.operations(Polyhedron.stellate))
  130. decompose.clicked.connect(lambda: self.operations(Polyhedron.decompose))
  131. uncouple.clicked.connect(lambda: self.operations(Polyhedron.uncouple))
  132. tetrahedron_action.triggered.connect(lambda: self.set_current_polyhedron(figures.tetrahedron))
  133. cube_action.triggered.connect(lambda: self.set_current_polyhedron(figures.cube))
  134. octahedron_action.triggered.connect(lambda: self.set_current_polyhedron(figures.octahedron))
  135. dodecahedron_action.triggered.connect(lambda: self.set_current_polyhedron(figures.dodecahedron))
  136. icosahedron_action.triggered.connect(lambda: self.set_current_polyhedron(figures.icosahedron))
  137. undo_action.triggered.connect(self.undo)
  138. redo_action.triggered.connect(self.redo)
  139. clear_polyhedra_action.triggered.connect(self.clear_polyhedra)
  140. element_values_action.triggered.connect(self.element_values)
  141. element_counts_action.triggered.connect(self.element_count)
  142. face_types_action.triggered.connect(self.face_types)
  143. timer = QtCore.QTimer(self)
  144. timer.timeout.connect(moderngl_widget.update)
  145. timer.start(30)
  146. #def add_list_item(self, text):
  147. # if self.current_polyhedron:
  148. # item = QtWidgets.QListWidgetItem(text)
  149. # #self.listWidget.addItem(item)
  150. def set_current_polyhedron(self, polyhedron):
  151. """Set current_polyhedron to a seed polyhedron."""
  152. self.polytope_models.append(Model(polyhedron))
  153. #self.current_polyhedron = polyhedron
  154. #self.add_list_item(polyhedron.__class__.__name__.capitalize())
  155. def operations(self, operation):
  156. """Perform a polyhedral operation on current_polyhedron."""
  157. current_polyhedron = operation(self.polytope)
  158. self.polytope_models.append(Model(current_polyhedron))
  159. #self.add_list_item(operation.__name__.capitalize())
  160. def clear_polyhedra(self):
  161. self.polytope_models.clear()
  162. def element_values(self):
  163. self.polytope.full_stats()
  164. def element_count(self):
  165. self.polytope.stats()
  166. def face_types(self):
  167. self.polytope.face_types()
  168. def undo(self):
  169. if len(self.polytope_models) == 0:
  170. print("No previous polyhedron")
  171. else:
  172. self.polytope_models.pop()
  173. def redo(self):
  174. print("No function yet")
  175. class Model:
  176. def __init__(self, polytope):
  177. # Determine color of the polyhedron
  178. self.polytope = polytope
  179. self.color = [self.randomize_color() for face in self.polytope.faces]
  180. def randomize_color(self):
  181. """Change RGB color of polyhedron."""
  182. return (random.randint(2,10)/10,
  183. random.randint(2,10)/10,
  184. random.randint(2,10)/10)
  185. app = QtWidgets.QApplication(sys.argv)
  186. app_window = MainWindow()
  187. app_window.setupUI()
  188. app_window.show()
  189. sys.exit(app.exec())