Source code for kf.QtOrbits
"""PyQt5 widgets to render a particle orbit system.
"""
################################################################
# Written in 2019 by Garth Zeglin <garthz@cmu.edu>
# To the extent possible under law, the author has dedicated all copyright
# and related and neighboring rights to this software to the public domain
# worldwide. This software is distributed without any warranty.
# You should have received a copy of the CC0 Public Domain Dedication along with this software.
# If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
################################################################
import math, logging
import numpy as np
# for documentation on the PyQt5 API, see http://pyqt.sourceforge.net/Docs/PyQt5/index.html
from PyQt5 import QtCore, QtGui, QtWidgets
# set up logger for module
log = logging.getLogger('QtOrbits')
# filter out most logging; the default is NOTSET which passes along everything
# log.setLevel(logging.WARNING)
################################################################
[docs]class QtOrbits(QtWidgets.QWidget):
"""Custom widget representing a particle orbit system generator."""
def __init__(self, channels=4):
super().__init__()
self.setMinimumSize(QtCore.QSize(100, 100))
self.setAutoFillBackground(True)
# graphical state variables
self.channels = channels
self.positions = [[0,0,0]]*self.channels # spatial positions, unit scaling [-1,1]
self.attractors = [[0,0,0]]*4
# finish initialization
self.show()
return
[docs] def update_positions(self, positions):
self.positions = positions
self.repaint()
[docs] def update_attractors(self, attractors):
self.attractors = attractors
self.repaint()
# === element drawing methods ============================================================
# === Qt API methods ============================================================
[docs] def paintEvent(self, e):
"""Subclass implementation of parent QWidget class callback to repaint the graphics."""
geometry = self.geometry()
view_width = geometry.width()
view_height = geometry.height()
# clear the background
qp = QtGui.QPainter()
qp.begin(self)
qp.fillRect(QtCore.QRectF(0, 0, view_width, view_height), QtCore.Qt.white)
# qp.setRenderHint(QtGui.QPainter.Antialiasing)
# Set up a coordinate system scaled to real-world millimeters, centered
# in the visible area, keeping the minimum visible area in view.
# define minimum visible area
scene_width = 3.5
scene_height = scene_width
scene_aspect = scene_width / scene_height
view_aspect = view_width / view_height
if scene_aspect > view_aspect:
scaling = view_width / scene_width
else:
scaling = view_height/scene_height
qp.save()
qp.translate(QtCore.QPointF(view_width/2, view_height/2))
qp.scale(scaling, scaling)
# draw the bounds
pen = QtGui.QPen(QtCore.Qt.black)
pen.setWidthF(0.01)
qp.setPen(pen)
qp.drawEllipse(QtCore.QPointF(0.0,0.0),math.sqrt(3),math.sqrt(3))
# draw the attractors in black
brush = QtGui.QBrush(QtGui.QColor(240, 240, 240, 255))
qp.setBrush(brush)
for pos in self.attractors:
qp.drawEllipse(QtCore.QPointF(pos[0], pos[1]), 0.2, 0.2)
# draw the particles in a color derived from their coordinates
color = QtGui.QColor()
for pos in self.positions:
color.setRgbF(min(1.0, abs(pos[0])), min(1.0, abs(pos[1])), min(1.0, abs(pos[2])), 1.0)
brush = QtGui.QBrush(color)
qp.setBrush(brush)
qp.drawEllipse(QtCore.QPointF(pos[0], pos[1]), 0.05, 0.05)
# restore the initial unscaled coordinates
qp.restore()
qp.end()
################################################################