为啥不更新重绘场景?
Posted
技术标签:
【中文标题】为啥不更新重绘场景?【英文标题】:Why doesn't update repaint a scene?为什么不更新重绘场景? 【发布时间】:2020-02-19 01:55:49 【问题描述】:我正在使用 PyQt5 和 PyOpenGL 在 python 中制作一个行进立方体项目。我试图隐藏穿过屏幕的线框立方体,引用为mainWindow.marchingCube
,以在循环后消失。我设法让消失周期发生,但立方体实际上并没有消失。我调用了QOpenGLWidget
的更新函数,但立方体仍然没有消失。
import sys
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QSlider,
QOpenGLWidget, QLabel, QPushButton
)
from PyQt5.QtCore import Qt
from OpenGL.GL import (
glLoadIdentity, glTranslatef, glRotatef,
glClear, glBegin, glEnd,
glColor3fv, glVertex3fv,
GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT,
GL_QUADS, GL_LINES
)
from OpenGL.GLU import gluPerspective
from numerics import sin, cos, tan, avg, rnd #Numerics is my custom math library.
import random, time
class mainWindow(QMainWindow): #Main class.
shapes = [] #this will hold instances of the following classes: cube
dataPoints = []
zoomLevel = -10
rotateDegreeV = -90
rotateDegreeH = 0
marchActive = False
limit = -1
meshPoints = []
class cube():
render = True
solid = False
color = (1, 1, 1)
def config(self, x, y, z, size = 0.1, solid = False, color = (1, 1, 1)):
self.solid = solid
self.color = color
self.size = size / 2
s = self.size
self.vertices = [
(-s + x, s + y, -s + z),
(s + x, s + y, -s + z),
(s + x, -s + y, -s + z),
(-s + x, -s + y, -s + z),
(-s + x, s + y, s + z),
(s + x, s + y, s + z),
(s + x, -s + y, s + z),
(-s + x, -s + y, s + z)
]
self.edges = [
(0,1), (0,3), (0,4), (2,1),
(2,3), (2,6), (7,3), (7,4),
(7,6), (5,1), (5,4), (5,6)
]
self.facets = [
(0, 1, 2, 3), (0, 1, 6, 5),
(0, 3, 7, 4), (6, 5, 1, 2),
(6, 7, 4, 5), (6, 7, 3, 2)
]
def show(self):
self.render = True
def hide(self):
self.render = False
class dataPoint():
location = (0, 0, 0)
value = 0
shape = None
def place(self, x, y, z):
self.location = (x, y, z)
def set(self, val):
self.value = val
def setShape(self, shape):
self.shape = shape
class meshPoint():
location = (0, 0, 0)
shape = None
def place(self, x, y, z):
self.location = (x, y, z)
def setShape(self, shape):
self.shape = shape
def keyPressEvent(self, event): #This is the keypress detector. I use this to determine input to edit grids.
try:
key = event.key()
#print(key)
if key == 87:
self.rotateV(5)
elif key == 65:
self.rotateH(5)
elif key == 83:
self.rotateV(-5)
elif key == 68:
self.rotateH(-5)
elif key == 67:
self.zoom(1)
elif key == 88:
self.zoom(-1)
elif key == 77:
self.marchStep()
except:
pass
def __init__(self):
super(mainWindow, self).__init__()
self.currentStep = 0
self.width = 700 #Variables used for the setting of the size of everything
self.height = 600
self.setGeometry(0, 0, self.width + 50, self.height) #Set the window size
self.initData(3, 3, 3)
def setupUI(self):
self.openGLWidget = QOpenGLWidget(self) #Create the GLWidget
self.openGLWidget.setGeometry(0, 0, self.width, self.height)
self.openGLWidget.initializeGL()
self.openGLWidget.resizeGL(self.width, self.height) #Resize GL's knowledge of the window to match the physical size?
self.openGLWidget.paintGL = self.paintGL #override the default function with my own?
self.filterSlider = QSlider(Qt.Vertical, self)
self.filterSlider.setGeometry(self.width + 10, int(self.height / 2) - 100, 30, 200)
self.filterSlider.valueChanged[int].connect(self.filter)
self.limitDisplay = QLabel(self)
self.limitDisplay.setGeometry(self.width, int(self.height / 2) - 130, 50, 30)
self.limitDisplay.setAlignment(Qt.AlignCenter)
self.limitDisplay.setText('-1')
self.marchButton = QPushButton(self)
self.marchButton.setGeometry(self.width, int(self.height / 2) - 160, 50, 30)
self.marchButton.setText('March!')
self.marchButton.clicked.connect(self.marchStep)
def marchStep(self):
if not self.marchActive:
marchAddr = len(self.shapes)
self.shapes.append(self.cube())
self.marchingCube = self.shapes[marchAddr]
self.marchActive = True
self.currentStep = 0
if self.currentStep == len(self.marchPoints):
self.currentStep = 0
#print('meshPoints: '.format(self.meshPoints))
for mp in self.meshPoints:
#print(mp.shape)
self.shapes.remove(mp.shape)
self.meshPoints.clear()
self.marchingCube.hide()
return
if self.currentStep == 0:
self.marchingCube.show()
p = self.marchPoints[self.currentStep]
x, y, z = p
self.marchingCube.config(x, y, z, size = 1)
points = []
for i in range(8):
point = self.getDataPointByLocation(self.marchingCube.vertices[i])
points.append(point)
self.openGLWidget.update()
#print('step: x: y: z: '.format(self.currentStep, x, y, z))
#for point in points:
# print(point.location, end = ' ')
#print()
for pair in self.marchingCube.edges:
pointA = points[pair[0]]
pointB = points[pair[1]]
#print('pointA.value: pointB.value: limit: '.formatpointA.value, pointB.value, self.limit)
if (pointA.value < self.limit and pointB.value > self.limit) or (pointA.value > self.limit and pointB.value < self.limit):
xA, yA, zA = pointA.location
xB, yB, zB = pointB.location
valA = (pointA.value + 1) / 2
valB = (pointB.value + 1) / 2
xC = float(avg([xA, xB]))
yC = float(avg([yA, yB]))
zC = float(avg([zA, zB]))
mp = self.meshPoint()
mp.place(xC, yC, zC)
mp.setShape(self.cube())
mp.shape.config(xC, yC, zC, size = 0.05, solid = True, color = (1, 0, 0))
self.shapes.append(mp.shape)
self.meshPoints.append(mp)
self.currentStep += 1
self.openGLWidget.update()
def zoom(self, value):
self.zoomLevel += value
self.openGLWidget.update()
def rotateV(self, value):
self.rotateDegreeV += value
self.openGLWidget.update()
def rotateH(self, value):
self.rotateDegreeH += value
self.openGLWidget.update()
def filter(self, value):
self.limit = rnd((value / 49.5) -1, -2)
for d in self.dataPoints:
if d.value < self.limit:
d.shape.hide()
else:
d.shape.show()
self.limitDisplay.setText(str(self.limit))
self.openGLWidget.update()
def getDataPointByLocation(self, coord):
x, y, z = coord
for dp in self.dataPoints:
if dp.location == (x, y, z):
return dp
return False
def paintGL(self):
glLoadIdentity()
gluPerspective(45, self.width / self.height, 0.1, 110.0) #set perspective?
glTranslatef(0, 0, self.zoomLevel) #I used -10 instead of -2 in the PyGame version.
glRotatef(self.rotateDegreeV, 1, 0, 0) #I used 2 instead of 1 in the PyGame version.
glRotatef(self.rotateDegreeH, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
if len(self.shapes) != 0:
glBegin(GL_LINES)
for s in self.shapes:
glColor3fv(s.color)
if s.render and not s.solid:
for e in s.edges:
for v in e:
glVertex3fv(s.vertices[v])
glEnd()
glBegin(GL_QUADS)
for s in self.shapes:
glColor3fv(s.color)
if s.render and s.solid:
for f in s.facets:
for v in f:
glVertex3fv(s.vertices[v])
glEnd()
def initData(self, sizeX, sizeY, sizeZ):
marchSizeX = sizeX - 1
marchSizeY = sizeY - 1
marchSizeZ = sizeZ - 1
xOff = -(sizeX / 2) + 0.5
yOff = -(sizeY / 2) + 0.5
zOff = -(sizeZ / 2) + 0.5
xMarchOff = -(marchSizeX / 2) + 0.5
yMarchOff = -(marchSizeY / 2) + 0.5
zMarchOff = -(marchSizeZ / 2) + 0.5
vals = []
self.marchPoints = []
for z in range(marchSizeZ):
for y in range(marchSizeY):
for x in range(marchSizeX):
self.marchPoints.append((x + xMarchOff, y + yMarchOff ,z + zMarchOff))
for z in range(sizeZ):
for y in range(sizeY):
for x in range(sizeX):
loc = len(self.dataPoints)
val = self.generate(x + xOff, y + yOff, z + zOff)
self.dataPoints.append(self.dataPoint())
self.dataPoints[loc].place(x + xOff, y + yOff, z + zOff)
self.dataPoints[loc].set(val)
loc2 = len(self.shapes)
self.shapes.append(self.cube())
self.shapes[loc2].config(x + xOff, y + yOff, z + zOff, solid = True, color = (0, (val + 1) / 2, (val + 1) / -2 + 1))
self.dataPoints[loc].setShape(self.shapes[loc2])
vals.append(val)
print(avg(vals))
def generate(self, xIn, yIn, zIn): #Function which produces semi-random values based on the supplied coordinates.
i = -(xIn * yIn * (10 + zIn))
j = xIn * yIn * (10 + zIn)
if i < j:
mixer = random.randint(i, j)
else:
mixer = random.randint(j, i + 1)
a = avg([sin(cos(xIn)), tan(tan(yIn)), cos(tan(zIn))])
out = mixer * a
while out > 10:
out -= 5
while out < -10:
out += 5
return float(out / 10)
app = QApplication([])
window = mainWindow()
window.setupUI()
window.show()
sys.exit(app.exec_())
为什么立方体没有消失?在我的网络搜索过程中,我发现更新并不总是按预期工作。直接调用self.openGLWidget.paintGL()
也不起作用。我必须怎么做才能让立方体消失?
编辑: 如果我调用旋转、旋转或缩放,屏幕会刷新,网格点以及行进立方体都会消失。我想我最终可能会通过调用其中一个零值来解决问题。
要进行测试,请将以下代码保存在与其余代码相同的目录中名为 numerics.py
的文件中。
from decimal import Decimal as dec
degrad = 'deg'
pi = 3.14159265358979323846
terms = dec(9) #number of terms used for the trig calculations
def version():
print('numerics.py version 1.0.0')
print('Packaged with the cubes project')
def mode(modeinput = ''): #switch between degrees and radians or check the current mode
global degrad
if modeinput == 'deg':
degrad = 'deg'
return 'deg'
if modeinput == 'rad':
degrad = 'rad'
return 'rad'
if modeinput == '':
return degrad
else:
return False
def accuracy(accinput = ''):
global terms
global pi
if accinput == '':
return terms
terms = dec(accinput)
PI = calculatePi(accinput)
print('Pi is: '.format(PI))
return terms
def calculatePi(placeIn = terms):
if placeIn > 15:
if input("Warning: You have chosen to calculate more than 20 digits of pi. This may take a LONG TIME and may be inacurate. Enter 'yes' if you wish to proceed. If you enter anything else, this function will revert to 10 digits.") == 'yes':
place = placeIn
else:
place = 10
else:
place = placeIn
print('Calculating Pi...\nPlease wait, as this may take a while.')
PI = dec(3)
addSub = True
for i in range(2, 2 * (int(place) ** 6) + 1, 2):
if addSub:
PI += dec(4) / (dec(i) * dec(i + 1) * dec(i + 2))
elif not addSub:
PI -= dec(4) / (dec(i) * dec(i + 1) * dec(i + 2))
addSub = not addSub
return rnd(PI, -(place), mode = 'cutoff')
def radToDeg(radin):
return (dec(radin) * dec(180 / pi))
def degToRad(degin):
return (dec(degin) * dec(pi / 180))
def avg(numsIn): #return the average of two numbers, specified as an integer or float
num1 = dec(0)
for i in numsIn:
num1 += dec(i)
return rnd(dec(num1 / dec(len(numsIn))))
def sin(anglein, dr = degrad): #return sine of the supplied angle using the predetermined mode or the supplied mode
if dr == 'deg':
while anglein > 180:
anglein -= 360
while anglein < -180:
anglein += 360
angle = degToRad(anglein)
if dr == 'rad':
while anglein > pi:
anglein -= (2 * pi)
while anglein < -pi:
anglein += (2 * pi)
angle = anglein
return rnd(rawsin(dec(angle)), -terms)
def arcsin(ratioin, dr = degrad): #return arcsine of the supplied ratio using the predetermined mode or the supplied mode
if ratioin > 1 or ratioin < -1: #if the input is illegal
return False
attempt = dec(0) #start at 0
target = rnd(dec(ratioin), -terms) #identify the target value
#print('target is: '.format(target))
for i in range(-1, int(terms) + 1): #for each place from 10s to terms decimal place (use -i, not i)
#print('Editing place 0'.format(10 ** -i)) #debugging
for j in range(10): #for 10 steps
#print('current attempt: '.format(attempt), end = ' ')
if rnd(sin(attempt, dr), -terms) == target:
if attempt < 0:
final = (attempt * dec(-1))
else:
final = attempt
#print('attempt: 0 final: 1'.format(attempt, final))
return final
if rnd(sin(attempt, dr), -terms) < target:
#add some
attempt += (dec(10) ** -i)
#print('attempt: '.format(attempt), end = ' ')
if rnd(sin(attempt, dr), -terms) > target:
#subtract some
attempt -= (dec(10) ** -i)
#print('attempt: '.format(attempt), end = ' ')
#print('')
if attempt < 0:
final = (attempt * dec(-1))
else:
final = attempt
#print('attempt: 0 final: 1'.format(attempt, final))
return (final)
def cos(anglein, dr = degrad): #return cosine of the supplied angle
if dr == 'deg':
return rawsin(degToRad(90 - anglein))
else:
angle = anglein
return rnd(rawsin(90 - angle), -terms)
def arccos(ratioin, dr = degrad): #return arccosine of the supplied ratio
if ratioin > 1 or ratioin < -1:
return False
attempt = dec(0) #start at 0
target = rnd(dec(ratioin), -terms) #identify the target value
#print('target is: '.format(target))
for i in range(-1, int(terms) + 1): #for each place from 10s to terms decimal place (use -i, not i)
#print('Editing place 0'.format(10 ** -i)) #debugging
for j in range(10): #for 10 steps
#print('current attempt: '.format(attempt), end = ' ')
if rnd(cos(attempt, dr), -terms) == target:
if attempt < 0:
final = (attempt * dec(-1))
else:
final = attempt
#print('attempt: 0 final: 1'.format(attempt, final))
return final
if rnd(cos(attempt, dr), -terms) < target:
#add some
attempt += (dec(10) ** -i)
#print('attempt: '.format(attempt), end = ' ')
if rnd(cos(attempt, dr), -terms) > target:
#subtract some
attempt -= (dec(10) ** -i)
#print('attempt: '.format(attempt), end = ' ')
#print('')
if attempt < 0:
final = (attempt * dec(-1))
else:
final = attempt
#print('attempt: 0 final: 1'.format(attempt, final))
return (final)
def tan(anglein, dr = degrad): #return tangent of the supplied angle
a = sin(anglein, dr)
b = cos(anglein, dr)
if (not a == 0) and (not b == 0):
return rnd((a / b), -terms)
else:
return False
def arctan(ratioin, dr = degrad): #return arctangent of the supplied ratio
if ratioin > 1 or ratioin < -1:
return False
attempt = dec(0) #start at 0
target = rnd(dec(ratioin), -terms) #identify the target value
#print('target is: '.format(target))
for i in range(-1, int(terms) + 1): #for each place from 10s to terms decimal place (use -i, not i)
#print('Editing place 0'.format(10 ** -i)) #debugging
for j in range(10): #for 10 steps
#print('current attempt: '.format(attempt), end = ' ')
if rnd(tan(attempt, dr), -terms) == target:
if attempt < 0:
final = (attempt * dec(-1))
else:
final = attempt
#print('attempt: 0 final: 1'.format(attempt, final))
return final
if rnd(tan(attempt, dr), -terms) < target:
#add some
attempt += (dec(10) ** -i)
#print('attempt: '.format(attempt), end = ' ')
if rnd(tan(attempt, dr), -terms) > target:
#subtract some
attempt -= (dec(10) ** -i)
#print('attempt: '.format(attempt), end = ' ')
#print('')
if attempt < 0:
final = (attempt * dec(-1))
else:
final = attempt
#print('attempt: 0 final: 1'.format(attempt, final))
return (final)
def rawsin(anglein): #return the result of sine of the supplied angle, using radians
#This is the taylor series used.
#final = x - (x^3 / 3!) + (x^5 / 5!) - (x^7 / 7!) + (x^9 / 9!) - (x^11 / 11!)...
angle = dec(anglein)
final = angle
add = False
for i in range(3, int(terms) * 3, 2):
if add:
final += dec(angle ** i) / fact(i)
elif not add:
final -= dec(angle ** i) / fact(i)
add = not add
return final
def fact(intin): #return the factorial of the given integer, return False if not given an int
if intin == int(intin):
intout = 1
for i in range(1, intin + 1):
intout *= i
return intout
else:
return False
def rnd(numIn, decPlcIn = -terms, mode = 'fiveHigher'): #return the given number, rounded to the given decimal place.
#use 1 to indicate 10s, 0 to indicate 1s, -2 to indicate 100ths, etc.
num1 = dec(numIn)
decPlc = dec(decPlcIn)
if mode == 'fiveHigher':
return dec(str(dec(round(num1 * (dec(10) ** -decPlc))) * (dec(10) ** decPlc)).rstrip('0'))
elif mode == 'cutoff':
return dec(str(dec(int(num1 * (dec(10) ** -decPlc))) * (dec(10) ** decPlc)).rstrip('0'))
def root(numIn, rootVal):
num = dec(numIn)
rt = dec(dec(1) / rootVal)
num1 = num ** rt
return rnd(num1, -terms)
def quad(aIn, bIn, cIn): #Plugin for the quadratic formula. Provide a, b, and c.
a = dec(aIn)
b = dec(bIn)
c = dec(cIn)
try:
posResult = (-b + root((b ** dec(2)) - (dec(4) * a * c), 2)) / (dec(2) * a)
except:
posResult = False
try:
negResult = (-b - root((b ** dec(2)) - (dec(4) * a * c), 2)) / (dec(2) * a)
except:
negResult = False
return (posResult, negResult)
【问题讨论】:
什么是数字? @eyllanesc numerics 是一个基于十进制库的自定义数学库。它执行基本的数学函数。我使用它主要是因为我深入了解它,部分是因为我为它感到自豪。 分享一下你用来轻松测试MRE的函数代码会很好。 @eyllanesc 感谢您的提示。我已经添加了。 @Rabbid76 我终于有机会测试它了。是的,你的修复成功了! 【参考方案1】:您错过了 1 个对 self.openGLWidget.update()
的呼叫。在if
的指令块中有return
语句。函数此时终止,代码末尾的self.openGLWidget.update()
指令永远不会执行。
在return
之前添加self.openGLWidget.update()
,解决问题:
class mainWindow(QMainWindow):
# [...]
def marchStep(self):
if not self.marchActive:
# [...]
self.currentStep = 0
if self.currentStep == len(self.marchPoints):
# [...]
self.meshPoints.clear()
self.marchingCube.hide()
self.openGLWidget.update() # <--------- ADD
return
if self.currentStep == 0:
self.marchingCube.show()
# [...]
【讨论】:
以上是关于为啥不更新重绘场景?的主要内容,如果未能解决你的问题,请参考以下文章
如何仅刷新/重绘 uicollectionview 中的补充视图?