Source code for kf.QtWaves
"""PyQt5 widgets to render wave generator systems.
"""
################################################################
# 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('QtConcentricLoops')
# filter out most logging; the default is NOTSET which passes along everything
# log.setLevel(logging.WARNING)
################################################################
[docs]class QtWaves1D(QtWidgets.QWidget):
"""Custom widget representing a 1D wave system generator."""
def __init__(self, channels=1):
super().__init__()
self.setMinimumSize(QtCore.QSize(100, 100))
self.setAutoFillBackground(True)
# graphical state variables
self.W = channels # the number of waves to show superimposed
# q is a (W, N) matrix representing W waves of N sample points
# the values are vertical position ranging over [-1,1]
N = 20
self.q = np.zeros((self.W, N))
# finish initialization
self.show()
return
[docs] def update_positions(self, positions):
"""Update the wave display with a (W, N) numpy matrix of position values."""
self.q = positions
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 centered in the visible area.
scene_width = 22 # define minimum visible area
scene_height = 2
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 wave as a set of circular masses
pen = QtGui.QPen(QtCore.Qt.black)
pen.setWidthF(0.01)
qp.setPen(pen)
W = len(self.q)
for i, wave in enumerate(self.q[:]):
color = QtGui.QColor()
hue = i / W
color.setHsvF(hue, 1.0, 1.0, 1.0)
brush = QtGui.QBrush(color)
qp.setBrush(brush)
N = len(wave)
x_delta = 21 / N
for i, pos in enumerate(wave):
x = -10 + i*x_delta
qp.drawEllipse(QtCore.QPointF(x, -pos), 0.2, 0.2)
# restore the initial unscaled coordinates
qp.restore()
qp.end()
################################################################