Hallo Zusammen,
untenstehendes Programm macht jetzt das, was es soll und ich habe verrsucht, alles etwas zu trennen. Allerdings denke ich, dass es an manchen Stellen zu umständlich ist. In main.py erstelle ich GUI und controller.Im conroller selbst die Instanzen des models. Den controller gebe ich der GUI mit auf den Weg, damit diese wiederum über den controller an das model heran kommt und auch seinerseits nach Button Click die funktion im controller aufrufen kann.
Da es in der View anfänglich noch keine Bounding_Box gibt (da sie ja erst nach der Berechnung existiert)
Code: Alles auswählen
class DrawingArea(QWidget):
def __init__(self, controller):
super().__init__()
self.setMinimumSize(QSize(400, 350))
self._controller = controller
self._int_points = []
self._bounding_box = BoundingBox()
weise ich hier eine leere Instanz zu, die ich später ersetze. Das führt aber dazu, dass es hier:
Code: Alles auswählen
scale_quotient = g.height() / self._bounding_box.get_height()
zu
division by zero führt, was logisch ist, da es hier noch keine Werte gibt, der paintEvent aber von Beginn an los läuft.
Wenn ich hier @property verwende:
Code: Alles auswählen
class IntPointModel():
def __init__(self):
self._int_points = []
@property
def get_int_points(self):
return self._int_points
kommt der Fehler
Wer könnte Tipps geben, wie das besser geht, z.B. mit Signals & Slots, bzw ist das überhaupt eingermaßen idiomatisch?
Code: Alles auswählen
# main.py
#!/usr/bin/env python
import sys
from PySide6.QtCore import QSize, Qt, QPoint
from PySide6.QtGui import QGradient, QPainter, QPen
from PySide6.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget
from view import Window
from controller import Controller
def main():
controller = Controller()
app = QApplication(sys.argv)
window = Window(controller)
window.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()
Code: Alles auswählen
#controller.py
#!/usr/bin/env python
import sys
from PySide6.QtCore import QSize, Qt, QPoint, Slot, Signal
from PySide6.QtGui import QGradient, QPainter, QPen, QBrush
from PySide6.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget, QLabel, QLineEdit
from GearWheel import GearWheel
from model import FloatingPointModel, IntPointModel, BoundingBox
class Controller():
def __init__(self):
self._floating_point_model = FloatingPointModel()
self._int_point_model = IntPointModel()
self._bounding_box = BoundingBox()
# @property
def get_int_point_model(self):
return self._int_point_model
# @property
def get_bounding_box(self):
return self._bounding_box
def do_all_the_work(self, gear_modul, teeth_count, drawing_area):
self._floating_point_model.clear_floating_points()
self._int_point_model.clear_int_points()
gw = GearWheel(gear_modul, teeth_count, self._floating_point_model)
gw.calculate_basics()
gw.calculate_complete_teeth()
self._int_point_model.create_int_points(self._floating_point_model.get_floating_points())
self._bounding_box.create(self._int_point_model.get_int_points())
drawing_area.get_and_update()
Code: Alles auswählen
#model.py
#!/usr/bin/env python
from PySide6.QtCore import QPoint, Slot, Signal
import sys
class FloatingPointModel():
def __init__(self):
self._floating_points = []
def get_floating_points(self):
return self._floating_points
def append_floating_point(self, x):
self._floating_points.append(x)
def clear_floating_points(self):
self._floating_points = []
class IntPointModel():
def __init__(self):
self._int_points = []
# @property
def get_int_points(self):
return self._int_points
def append_int_point(self, x, y):
self._int_points.append(QPoint(x, y))
def clear_int_points(self):
self._int_points = []
def create_int_points(self, floating_points):
int_scale_factor = 100.0
for evol in floating_points:
x, y = evol
self._int_points.append(QPoint(x * int_scale_factor, y * int_scale_factor))
class BoundingBox():
def __init__(self):
self._bb_left_x = 0
self._bb_width_x = 0
self._bb_top_y = 0
self._bb_height_y = 0
# @property
def get_left(self):
return self._bb_left_x
# @property
def get_width(self):
return self._bb_width_x
# @property
def get_top(self):
return self._bb_top_y
# @property
def get_height(self):
return self._bb_height_y
def create(self, int_points):
x_min = 10000
x_max = -10000
y_min = 10000
y_max = -10000
boundary = 10
for evol in int_points:
x = evol.x()
y = evol.y()
x_min = min(x_min, x)
x_max = max(x_max, x)
y_min = min(y_min, y)
y_max = max(y_max, y)
x_min -= boundary
x_max += boundary
y_min -= boundary
y_max += boundary
self._bb_left_x = x_min
self._bb_top_y = y_max
self._bb_width_x = x_max - x_min
self._bb_height_y = y_max - y_min
Code: Alles auswählen
#view.py
#!/usr/bin/env python
import sys
from PySide6.QtCore import QSize, Qt, QPoint, Slot, Signal
from PySide6.QtGui import QGradient, QPainter, QPen, QBrush
from PySide6.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget, QLabel, QLineEdit
from controller import Controller
from model import IntPointModel, BoundingBox
class DrawingArea(QWidget):
def __init__(self, controller):
super().__init__()
self.setMinimumSize(QSize(400, 350))
self._controller = controller
self._int_points = []
self._bounding_box = BoundingBox()
def get_and_update(self):
int_model = self._controller.get_int_point_model()
self._int_points = int_model.get_int_points()
self._bounding_box = self._controller.get_bounding_box()
self.update()
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
g = self.geometry()
scale_quotient = g.height() / self._bounding_box.get_height()
center_x = self._bounding_box.get_left() * scale_quotient *(-1)
center_y = self._bounding_box.get_top() * scale_quotient
painter.translate(QPoint(center_x, center_y))
painter.scale(scale_quotient, scale_quotient)
painter.setPen(QPen(Qt.GlobalColor.red, 5 / scale_quotient))
for evol_point in self._int_points:
painter.drawPoint(QPoint(evol_point.x(), evol_point.y() *(-1) ))
painter.end()
return super().paintEvent(event)
class Window(QWidget):
def __init__(self, controller):
super().__init__()
self._controller = controller
layout = QVBoxLayout(self)
button = QPushButton("Start")
label_m = QLabel("Modul")
label_z = QLabel("Zähnezahl")
self. edit_m = QLineEdit()
self. edit_z = QLineEdit()
self.drawing_area = DrawingArea(self._controller)
layout.addWidget(button, 0, Qt.AlignTop)
layout.addWidget(label_m, 0, Qt.AlignTop)
layout.addWidget(self.edit_m, 0, Qt.AlignTop)
layout.addWidget(label_z, 0, Qt.AlignTop)
layout.addWidget(self.edit_z, 0, Qt.AlignTop)
layout.addWidget(self.drawing_area, 1)
button.clicked.connect(self.start_calculations)
# Testwerte
self.edit_m.setText("1.0")
self.edit_z.setText("23")
@Slot()
def start_calculations(self):
gear_modul = float(self.edit_m.text())
teeth_count = float(self.edit_z.text())
self._controller.do_all_the_work(gear_modul, teeth_count, self.drawing_area)
def main():
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()
Code: Alles auswählen
#GearWheel.py
#!/usr/bin/env python
from math import pi,sin, cos, tan, acos
from PySide6.QtCore import QPoint
from model import FloatingPointModel
ALPHA_0 = 20.0 * pi / 180.0 # Normaleingriffswinkel rad (= 20° deg)
class GearWheel():
def __init__(self, gear_modul, teeth_count, floating_point_model):
self.gear_modul = gear_modul
self.teeth_count = teeth_count
self.floating_point_model = floating_point_model
def calculate_basics(self):
# self.gear_modul # Modul
# self.teeth_count # Zähnezahl
self.partial_diameter = self.teeth_count * self.gear_modul # Teilkreis Durchmesser
self.circumferential_division = self.gear_modul * pi # Umfangsteilung
self.circumferential_angle_rad = self.circumferential_division / (self.partial_diameter / 2.0) # Umfangswinkel Radian
self.circumferential_angle_deg = self.circumferential_angle_rad / pi * 180.0 # Umfangswinkel Degree
self.tooth_head_gap = self.gear_modul * 0.167 # Zahnkopfspiel
self.foot_circle_diameter = self.partial_diameter - 2.0 * (self.gear_modul + self.tooth_head_gap) # Fusskreis Durchmesser
self.head_circle_diameter = self.partial_diameter + 2.0 * self.gear_modul # Kopfkreis Durchmesser
self.head_circle_diameter_with_gap = self.partial_diameter + 2.0 * (self.gear_modul + self.tooth_head_gap) # Kopfkreis Durchmesser + Spiel
self.base_circle_diameter = self.partial_diameter * cos(ALPHA_0) # Grundkreis Durchmesser
self.tooth_head_height = self.gear_modul # Zahnkopf Höhe
self.tooth_foot_height = self.gear_modul + self.tooth_head_gap # Zahnfuss Höhe
self.tooth_height_over_all = 2.0 * self.gear_modul + self.tooth_head_gap # Zahn Höhe insgesamt
print(f'Modul={self.gear_modul}\nAnzahl Zähne={self.teeth_count}\nTeilkreis_Durchmesser={self.partial_diameter}')
print(f'Umfangsteilung={self.circumferential_division}\nUmfangswinkel_Radian={self.circumferential_angle_rad}')
print(f'Zahnkopfspiel={self.tooth_head_gap}\nUmfangswinkel_Degree={self.circumferential_angle_deg}')
print(f'Fusskreis_durchmesser={self.foot_circle_diameter}\nKopfkreis_Durchmesser={self.head_circle_diameter}')
print(f'Kopfkreis_Durchmesser + Spiel={self.head_circle_diameter_with_gap}')
print(f'Grundkreis_Durchmesser={self.base_circle_diameter}\nZahnkopf_Höhe={self.tooth_head_height}')
print(f'Zahnfuss_Höhe={self.tooth_foot_height}\nZahn_Höhe={self.tooth_height_over_all}')
print(f'Minimaler Section Wert={self.foot_circle_diameter}\nMaximaler Section Wert={self.head_circle_diameter}')
def calculate_evolvente_thickness(self, section):
if section < self.base_circle_diameter or section > self.head_circle_diameter: return
s_0 = self.gear_modul * pi / 2.0
d_0 = self.partial_diameter
gamma_0 = tan(ALPHA_0) - ALPHA_0
alpha = acos(d_0 / section * cos(ALPHA_0))
gamma = tan(alpha) - alpha
s = section * (s_0 / d_0 + gamma_0 - gamma)
return s
def calculate_complete_teeth(self):
step = 0.1
section = self.base_circle_diameter
while(section <= self.head_circle_diameter):
evol_point = self.calculate_evolvente_thickness(section)
self.floating_point_model.append_floating_point((evol_point / 2.0, section / 2.0))
self.floating_point_model.append_floating_point((evol_point / 2.0 *(-1) , section / 2.0))
section += step
def main():
pass
if __name__ == '__main__':
main()