How to make a constant and smooth animation? - python

So I want to make an animation as the arrow from the Waze app, I want that it moves smoothly and constant. I don't know how to do it inside the QtGraphicsView. My object(arrow) moves by an QVariantAnimation, Which interpolates from the current position until the next position. End it slightly stops. I don't wanna this feature (stops), I want that my animation runs continuous and smoothly. Does anyone know how?
Here is a minimum reproducible example:
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5 import QtGui
import sys
import random
random.seed(0)
class Example(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.button = QtWidgets.QPushButton("Start", self)
self.button.clicked.connect(self.doAnim)
self.button.move(10, 10)
self.scene = QtWidgets.QGraphicsScene()
self.view = QtWidgets.QGraphicsView(self)
self.view.setScene(self.scene)
self.view.setGeometry(150, 30, 500, 800)
self.setGeometry(300, 300, 380, 300)
self.setWindowTitle('Animation')
pen = QtGui.QPen()
pen.setBrush(QtGui.QBrush(QtCore.Qt.darkBlue))
pen.setWidth(5)
self.scene.addEllipse(0,0,10,10, pen)
self.show()
self.ellipse = self.scene.items()[0]
def doAnim(self):
# Every time that I click on the start buttom this animation runs
# and stops,
pos = self.ellipse.pos()
new_pos = QtCore.QPointF(pos.x()+ random.randint(-10, 10), pos.y() +random.randint(-10, 10))
self.anim = QtCore.QVariantAnimation()
self.anim.setDuration(1000)
self.anim.setStartValue(pos)
self.anim.setEndValue(new_pos)
self.anim.setLoopCount(-1)
self.anim.valueChanged.connect(self.ellipse.setPos)
self.anim.start(QtCore.QVariantAnimation.DeleteWhenStopped)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ex = Example()
ex.show()
app.exec_()

The simple solution is to connect to the finished signal of the animation, and set again the start/end values if a new target position is available.
from random import randrange
from PyQt5 import QtCore, QtGui, QtWidgets
class Example(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
layout = QtWidgets.QGridLayout(self)
self.startButton = QtWidgets.QPushButton("Start")
layout.addWidget(self.startButton)
self.addPointButton = QtWidgets.QPushButton("Add target point")
layout.addWidget(self.addPointButton, 0, 1)
self.view = QtWidgets.QGraphicsView()
layout.addWidget(self.view, 1, 0, 1, 2)
self.scene = QtWidgets.QGraphicsScene()
self.view.setScene(self.scene)
self.scene.setSceneRect(-10, -10, 640, 480)
pen = QtGui.QPen(QtCore.Qt.darkBlue, 5)
self.ellipse = self.scene.addEllipse(0, 0, 10, 10, pen)
self.queue = []
self.startButton.clicked.connect(self.begin)
self.addPointButton.clicked.connect(self.addPoint)
self.anim = QtCore.QVariantAnimation()
self.anim.setDuration(1000)
self.anim.valueChanged.connect(self.ellipse.setPos)
self.anim.setStartValue(self.ellipse.pos())
self.anim.finished.connect(self.checkPoint)
def begin(self):
self.startButton.setEnabled(False)
self.addPoint()
def addPoint(self):
self.queue.append(QtCore.QPointF(randrange(600), randrange(400)))
self.checkPoint()
def checkPoint(self):
if not self.anim.state() and self.queue:
if self.anim.currentValue():
# a valid currentValue is only returned when the animation has
# been started at least once
self.anim.setStartValue(self.anim.currentValue())
self.anim.setEndValue(self.queue.pop(0))
self.anim.start()

from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5 import QtGui
import sys
import random
import queue
import time
class Position(queue.Queue):
def __init__(self, *args, **kwargs):
super(Position, self).__init__(*args, **kwargs)
class Worker(QtCore.QThread):
pos_signal = QtCore.pyqtSignal(QtCore.QPointF)
def __init__(self, rect, parent=None, *args, **kwargs):
super().__init__(parent)
random.seed(0)
self.pos = QtCore.QPointF(0, 0)
self.rect: QtCore.QRectF = rect
def run(self) -> None:
new_pos = QtCore.QPointF(self.pos.x()+ random.randint(-100, 100), self.pos.y() +random.randint(-100, 100))
if not self.rect.contains(new_pos):
self.run()
else:
self.pos = new_pos
self.pos_signal.emit(new_pos)
time.sleep(0.1)
self.run()
class Example(QtWidgets.QWidget):
next_signal = QtCore.pyqtSignal()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.button = QtWidgets.QPushButton("Start", self)
self.button.clicked.connect(self.doAnim)
self.scene = QtWidgets.QGraphicsScene()
self.view = QtWidgets.QGraphicsView(self)
self.view.setScene(self.scene)
self.view.setGeometry(100, 100, 500, 800)
self.setGeometry(300, 300, 800, 800)
pen = QtGui.QPen()
pen.setBrush(QtGui.QBrush(QtCore.Qt.darkBlue))
pen.setWidth(5)
self.scene.addEllipse(0,0,20,10, pen)
self.ellipse = self.scene.items()[0]
self.rect = QtCore.QRectF(0,0, 200, 200)
self.scene.addRect(self.rect, pen)
self.positions = Position()
self.producer = Worker(rect=self.rect)
self.producer.pos_signal.connect(self.positions.put)
self.producer.start()
self.old = QtCore.QPointF(0, 0)
self.new = None
def ready(self):
self.next_signal.emit()
self.old = self.new
self.doAnim()
def doAnim(self):
# Every time that I click on the start buttom this animation runs
# and stops,
self.sequential_animation = QtCore.QSequentialAnimationGroup(self)
self.new = self.positions.get()
self.anim = QtCore.QVariantAnimation()
self.anim.setDuration(1000)
self.anim.setStartValue(self.old)
self.anim.setEndValue(self.new)
self.anim.valueChanged.connect(self.ellipse.setPos)
self.anim.finished.connect(self.ready)
self.sequential_animation.addAnimation(self.anim)
self.sequential_animation.start(QtCore.QSequentialAnimationGroup.DeleteWhenStopped)
# self.anim.start(QtCore.QVariantAnimation.DeleteWhenStopped)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ex = Example()
ex.show()
app.exec_()

Related

Updating slider value constantly crashes program

Maybe I'm doing this completely wrong here, I'm trying to make the "timeline" for an mp3 player. Everything works and I've tried to set the slider to one value and it was fine. My problem is when I try updating the timeline as it keeps on freezing the program and it doesn't unfreeze. Here's my code, I commented out where to start looking at in terms of where I'm having trouble in:
import sys
from PyQt5.QtCore import QCoreApplication, Qt
from PyQt5.QtGui import *
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QPushButton,
QSlider
import vlc
player = vlc.MediaPlayer("/songs/Immigrant Song.mp3")
class window(QMainWindow):
def __init__(self):
super(window, self).__init__()
self.setGeometry(50, 50, 400, 200)
self.setWindowTitle('SlugPlayer')
#self.setWindowIcon(QIcon('pic.png'))
self.home()
def home(self):
playing = 0
btn = QPushButton('quit', self)
btn.clicked.connect(self.close_application)
btn.resize(btn.sizeHint()) #set to acceptable size automatic
btn.move(0, 0)
playbtn = QPushButton('Play', self)
# playbtn.clicked.connect(self.update_bar, playing)
playbtn.clicked.connect(self.play_music, playing)
playbtn.resize(playbtn.sizeHint())
playbtn.move(100, 0)
psebtn = QPushButton('Pause', self)
psebtn.clicked.connect(self.pause_music, playing)
psebtn.resize(psebtn.sizeHint())
psebtn.move(200, 0)
stopbtn = QPushButton('Stop', self)
stopbtn.clicked.connect(self.stop_music, playing)
stopbtn.resize(stopbtn.sizeHint())
stopbtn.move(300, 0)
self.sl = QSlider(Qt.Horizontal, self)
self.sl.setFocusPolicy(Qt.NoFocus)
self.sl.setGeometry(30, 50, 300, 20)
self.sl.setMinimum(0)
self.sl.setMaximum(100)
self.sl.setValue(0)
self.sl.move(50, 100)
# self.sl.valueChanged[int].connect(self.print_value)
self.show()
def close_application(self):
sys.exit()
#here is where I'm trying to update the bar
def play_music(self, playing):
player.play()
playing = 1
while playing == 1:
self.update_bar(playing)
def pause_music(self, playing):
player.pause()
playing = 0
def stop_music(self, playing):
player.stop()
playing = 0
def print_value(self, value):
print(value)
#function to update the bar (converts to an int)
def update_bar(self, playing):
self.sl.setValue(int(player.get_position() * 100))
def run():
app = QApplication(sys.argv)
Gui = window()
sys.exit(app.exec_())
run()
An infinite loop like while playing == 1 freezes the GUI since it does not allow you to do other tasks, in these cases it is better to use a QTimer. Going a little further to the bottom of the problem I have implemented a class that is in charge of managing the player by sending signals when necessary.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
import vlc
class VLCManager(QtCore.QObject):
durationChanged = QtCore.pyqtSignal(int)
positionChanged = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
super(VLCManager, self).__init__(parent)
self._player = vlc.MediaPlayer()
self._timer = QtCore.QTimer(self, interval=100, timeout=self.update_values)
def setFileName(self, filename):
self._player = vlc.MediaPlayer(filename)
def position(self):
self.durationChanged.emit(self.duration())
return self._player.get_time()
def setPosition(self, time):
if self.position() != time:
self._player.set_time(time)
#QtCore.pyqtSlot()
def update_values(self):
self.positionChanged.emit(self.position())
def duration(self):
return self._player.get_length()
#QtCore.pyqtSlot()
def play(self):
self._player.play()
self.update_values()
self._timer.start()
#QtCore.pyqtSlot()
def pause(self):
self._player.pause()
self.update_values()
self._timer.stop()
#QtCore.pyqtSlot()
def stop(self):
self._player.stop()
self.update_values()
self._timer.stop()
class window(QtWidgets.QMainWindow):
def __init__(self):
super(window, self).__init__()
self._manager = VLCManager(self)
self._manager.setFileName("/songs/Immigrant Song.mp3")
self.setWindowTitle('SlugPlayer')
self.home()
def home(self):
quitbtn = QtWidgets.QPushButton('quit')
quitbtn.clicked.connect(self.close)
playbtn = QtWidgets.QPushButton('Play')
playbtn.clicked.connect(self._manager.play)
psebtn = QtWidgets.QPushButton('Pause', self)
psebtn.clicked.connect(self._manager.pause)
stopbtn = QtWidgets.QPushButton('Stop', self)
stopbtn.clicked.connect(self._manager.stop)
self.sl = QtWidgets.QSlider(orientation=QtCore.Qt.Horizontal)
self.sl.setFocusPolicy(QtCore.Qt.NoFocus)
self._manager.durationChanged.connect(self.sl.setMaximum)
self._manager.positionChanged.connect(self.sl.setValue)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QVBoxLayout(central_widget)
hlay = QtWidgets.QHBoxLayout()
for b in (quitbtn, playbtn, psebtn, stopbtn, ):
hlay.addWidget(b)
lay.addLayout(hlay)
lay.addWidget(self.sl)
self.sl.valueChanged[int].connect(self._manager.setPosition)
self.show()
def run():
app = QtWidgets.QApplication(sys.argv)
Gui = window()
sys.exit(app.exec_())
run()

Emit signal from QAction passing custom widget value

I have created a simple menu using a custom widget. How can i make the menu QAction emit the color value of the swatch clicked? Each color swatch contains a property called 'color'. If the user clicks 'Reset' i would like the value emitted to be 'None'
I tried to overload the triggered signal to pass the color of the swatch clicked but it didn't work either.
import sys
from PySide import QtGui, QtCore
class QColorSwatch(QtGui.QPushButton):
colorClicked = QtCore.Signal(object)
colorChanged = QtCore.Signal(object)
def __init__(self, *args, **kwargs):
super(QColorSwatch, self).__init__(*args, **kwargs)
self.setFixedWidth(18)
self.setFixedHeight(18)
self.setAutoFillBackground(True)
self._color = None
self.color = QtGui.QColor(0,0,0)
self.pressed.connect(self.color_clicked)
#property
def color(self):
return self._color
#color.setter
def color(self, value):
self._color = value
self.setIconSize(self.size())
pixmap = QtGui.QPixmap(self.size())
pixmap.fill(QtCore.Qt.black)
painter = QtGui.QPainter(pixmap)
painter.setBrush(QtGui.QColor(self.color))
painter.setPen(QtGui.QPen(QtCore.Qt.white, 1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin))
painter.drawRect(1,1,self.size().width()-3,self.size().height()-3)
painter.end()
self.setIcon(pixmap)
self.colorChanged.emit(value)
def color_clicked(self):
self.colorClicked.emit(self.color)
class ColorFilters(QtGui.QWidget):
def __init__(self, action):
super(ColorFilters,self).__init__()
self.action = action
self.ui_any_color = QtGui.QLabel('Reset')
self.ui_swatch_01 = QColorSwatch()
self.ui_swatch_01.color = QtGui.QColor(255,0,0)
self.ui_swatch_02 = QColorSwatch()
self.ui_swatch_02.color = QtGui.QColor(0,255,0)
self.ui_swatch_03 = QColorSwatch()
self.ui_swatch_03.color = QtGui.QColor(0,0,255)
lay_main = QtGui.QGridLayout()
lay_main.setSpacing(5)
lay_main.setContentsMargins(5,5,5,5)
lay_main.addWidget(self.ui_any_color,0,0,1,4)
lay_main.addWidget(self.ui_swatch_01,1,0)
lay_main.addWidget(self.ui_swatch_02,1,1)
lay_main.addWidget(self.ui_swatch_03,1,2)
self.setLayout(lay_main)
# connections
self.ui_swatch_01.colorClicked.connect(self.clicked_swatch)
self.ui_swatch_02.colorClicked.connect(self.clicked_swatch)
self.ui_swatch_03.colorClicked.connect(self.clicked_swatch)
def mouseReleaseEvent(self,e):
self.action.trigger()
def clicked_swatch(self, col):
col = self.sender().color
self.action.trigger()
class Example(QtGui.QMainWindow):
def __init__(self):
super(Example, self).__init__()
colAction = QtGui.QWidgetAction(self)
ql = ColorFilters(colAction)
colAction.setDefaultWidget(ql)
colAction.triggered.connect(self.clicked_color)
menubar = self.menuBar()
fileMenu = menubar.addMenu('&File')
fileMenu.addAction(colAction)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Menubar')
self.show()
def clicked_color(self):
print 'Clicked'
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
Instead of trying to use the triggered signal it creates a new signal that sends that information. Also you should know that a signal can also connect to another signal as I show below.
import sys
from PySide import QtGui, QtCore
class QColorSwatch(QtGui.QPushButton):
colorClicked = QtCore.Signal(QtGui.QColor)
colorChanged = QtCore.Signal(object)
def __init__(self, *args, **kwargs):
super(QColorSwatch, self).__init__(*args, **kwargs)
self.setFixedWidth(18)
self.setFixedHeight(18)
self.setAutoFillBackground(True)
self._color = None
self.color = QtGui.QColor(0,0,0)
self.pressed.connect(self.color_clicked)
#property
def color(self):
return self._color
#color.setter
def color(self, value):
self._color = value
self.setIconSize(self.size())
pixmap = QtGui.QPixmap(self.size())
pixmap.fill(QtCore.Qt.black)
painter = QtGui.QPainter(pixmap)
painter.setBrush(QtGui.QColor(self.color))
painter.setPen(QtGui.QPen(QtCore.Qt.white, 1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin))
painter.drawRect(1,1,self.size().width()-3,self.size().height()-3)
painter.end()
self.setIcon(pixmap)
self.colorChanged.emit(value)
def color_clicked(self):
self.colorClicked.emit(self.color)
class ColorFilters(QtGui.QWidget):
colorSelected = QtCore.Signal(QtGui.QColor)
def __init__(self, parent=None):
super(ColorFilters, self).__init__(parent)
lay_main = QtGui.QGridLayout(self)
lay_main.setSpacing(5)
lay_main.setContentsMargins(5,5,5,5)
self.ui_any_color = QtGui.QLabel('Reset')
lay_main.addWidget(self.ui_any_color,0,0,1,4)
self.ui_any_color.installEventFilter(self)
for i, color in enumerate((QtGui.QColor(255,0,0), QtGui.QColor(0,255,0), QtGui.QColor(0,0,255))):
ui_swatch = QColorSwatch()
ui_swatch.color = color
lay_main.addWidget(ui_swatch,1,i+1)
ui_swatch.colorClicked.connect(self.colorSelected)
def eventFilter(self, obj, event):
if obj == self.ui_any_color and event.type() == QtCore.QEvent.Type.MouseButtonPress:
self.colorSelected.emit(QtGui.QColor)
return super(ColorFilters, self).eventFilter(obj, event)
class Example(QtGui.QMainWindow):
def __init__(self):
super(Example, self).__init__()
colAction = QtGui.QWidgetAction(self)
ql = ColorFilters(self)
colAction.setDefaultWidget(ql)
ql.colorSelected.connect(self.clicked_color)
menubar = self.menuBar()
fileMenu = menubar.addMenu('&File')
fileMenu.addAction(colAction)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Menubar')
self.show()
def clicked_color(self, color):
if not color.isValid():
print("reset")
else:
print('Clicked', color)
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

PySide/PyQT5: How to emit signals from a QGraphicsItem?

I want to emit a signal from a QGraphicsItem when it is doubled-clicked, in order to change a widget in the main window. The graphics-scene/-item does not provide an emit() method, but I was just wondering if there is an alternate way to do this. The code below has a function within a QGraphicsView class that will print to the terminal when an item is double-clicked. How can I make that into a slot/signal instead (if QGraphicsItem does not support signal/slots)?
import sys
from PySide.QtCore import *
from PySide.QtGui import *
class MyFrame(QGraphicsView):
def __init__( self, parent = None ):
super(MyFrame, self).__init__(parent)
scene = QGraphicsScene()
self.setScene(scene)
self.setFixedSize(500, 500)
pen = QPen(QColor(Qt.green))
brush = QBrush(pen.color().darker(150))
item = scene.addEllipse(0, 0, 45, 45, pen, brush)
item.setPos(0,0)
def mouseDoubleClickEvent(self, event):
print("Circle Clicked!")
# this double click event prints to terminal but how to setup
# signal/slot to update the QWidget QLabel text instead?
class Example(QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
hbox = QHBoxLayout(self)
top = QLabel("Double Click Green Circle (Howto change this QWidget Label with signals?)")
bottom = MyFrame()
splitter = QSplitter(Qt.Vertical)
splitter.addWidget(top)
splitter.addWidget(bottom)
hbox.addWidget(splitter)
self.setLayout(hbox)
self.setGeometry(0, 0, 500, 600)
self.show()
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Below is a simple example showing one way to emit signals from a graphics-item. This defines a custom signal on a subclass of QGraphicsScene and then uses the scene() method of graphics-items to emit it:
import sys
from PySide import QtCore, QtGui
class GraphicsScene(QtGui.QGraphicsScene):
itemDoubleClicked = QtCore.Signal(object)
class GraphicsRectangle(QtGui.QGraphicsRectItem):
def mouseDoubleClickEvent(self, event):
self.scene().itemDoubleClicked.emit(self)
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.view = QtGui.QGraphicsView()
self.scene = GraphicsScene(self)
self.view.setScene(self.scene)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.view)
for i in range(1, 4):
self.scene.addItem(GraphicsRectangle(50 * i, 50 * i, 20, 20))
self.scene.itemDoubleClicked.connect(self.handleItemDoubleClicked)
def handleItemDoubleClicked(self, item):
print(item.boundingRect())
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 100, 300, 200)
window.show()
sys.exit(app.exec_())
UPDATE:
Below is a an example based on the code in your question. The basic idea is the same: define a custom signal on an available QObject (the graphics-view in this case), and use that to emit the double-click notification.
import sys
from PySide.QtCore import *
from PySide.QtGui import *
class MyFrame(QGraphicsView):
itemDoubleClicked = Signal(object)
def __init__(self, parent=None):
super(MyFrame, self).__init__(parent)
scene = QGraphicsScene()
self.setScene(scene)
self.setFixedSize(500, 500)
for i, color in enumerate('red blue green'.split()):
pen = QPen(QColor(color))
brush = QBrush(pen.color().darker(150))
item = scene.addEllipse(i * 50, i * 50, 45, 45, pen, brush)
item.setData(0, color.upper())
def mouseDoubleClickEvent(self, event):
item = self.itemAt(event.pos())
if item is not None:
self.itemDoubleClicked.emit(item)
class Example(QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
hbox = QHBoxLayout(self)
top = QLabel('Double Click a Circle')
bottom = MyFrame()
bottom.itemDoubleClicked.connect(
lambda item, top=top:
top.setText('Double Clicked: %s' % item.data(0)))
splitter = QSplitter(Qt.Vertical)
splitter.addWidget(top)
splitter.addWidget(bottom)
hbox.addWidget(splitter)
self.setLayout(hbox)
self.setGeometry(0, 0, 500, 600)
self.show()
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Make a rectangle blink in PyQt

The purpose of this code is to have a square that appears in a canvas for some time and is then erased. I understand that all painting events must be handled by the overloaded function paintEvent.
However, first, the squares are not being drawn and I believe, that the times at which the squares are supposed to be drawn and erased are not being respected either. My guess is this happens due to the frequency at which the event appears.
I already tried to call QPaintEvent under the functions drawApple and eraseApple. What am I missing?
import sys, random
import numpy as np
import math
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import QTimer
from PyQt4.QtCore import QRect
from PyQt4.QtGui import QPaintEvent
class Game(QtGui.QMainWindow):
def __init__(self):
super(Game, self).__init__()
self.initUI()
def initUI(self):
palette = QtGui.QPalette()
palette.setColor(QtGui.QPalette.Background, QtCore.Qt.white)
self.setPalette(palette)
self.tboard = Board(self)
self.setCentralWidget(self.tboard)
self.resize(400, 400)
self.center()
self.setWindowTitle('Game')
self.show()
def center(self):
screen = QtGui.QDesktopWidget().screenGeometry()
size = self.geometry()
self.move((screen.width()-size.width())/2, (screen.height()-size.height())/2)
class Board(QtGui.QFrame):
BoardWidth = 400
BoardHeight = 400
SquareWidth = 15
SquareHeight = 15
Speed = 10000
def __init__(self, parent):
super(Board, self).__init__(parent)
#self.setAutoFillBackground(True)
self.setFocusPolicy(QtCore.Qt.StrongFocus)
self.timer_draw = QtCore.QTimer()
self.timer_draw.timeout.connect(self.drawApple)
self.timer_draw.start(self.Speed)
self.timer_draw.setInterval(self.Speed)
self.timer_erase = QtCore.QTimer()
self.timer_erase.timeout.connect(self.eraseApple)
self.timer_erase.start(self.Speed + self.Speed/2)
self.timer_erase.setInterval(self.Speed)
self.apple_color = QtCore.Qt.red
self.bkg_color = QtCore.Qt.white
self.draw_apple = False
self.x_apple = 0
self.y_apple = 0
self.rect = QRect(self.x_apple, self.y_apple, self.SquareWidth, self.SquareHeight)
def paintEvent(self, event):
painter = QtGui.QPainter(self)
print "Paint Event?"
if self.draw_apple == True:
print "Draw"
self.apple_color = QtCore.Qt.red
else:
print "Do not draw"
self.apple_color = self.bkg_color
painter.setPen(self.apple_color)
painter.drawRect(self.rect)
def drawApple(self):
print "Enters drawApple"
self.x_apple = np.random.randint(0, math.floor(self.BoardWidth/self.SquareWidth)) * self.SquareWidth
self.y_apple = np.random.randint(0, math.floor(self.BoardHeight/self.SquareHeight)) * self.SquareHeight
self.draw_apple == True
def eraseApple(self):
print "Enters eraseApple"
self.draw_apple == True
def main():
app = QtGui.QApplication([])
game = Game()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You should call the update() function as it calls paintEvent(). Also I recommend using a single timer for that task. You only have to deny the variable draw_apple to change state.
import sys, random
import numpy as np
import math
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import QTimer, QRect
from PyQt4.QtGui import QPaintEvent
class Game(QtGui.QMainWindow):
def __init__(self):
super(Game, self).__init__()
self.initUI()
def initUI(self):
palette = QtGui.QPalette()
palette.setColor(QtGui.QPalette.Background, QtCore.Qt.white)
self.setPalette(palette)
self.tboard = Board(self)
self.setCentralWidget(self.tboard)
self.resize(400, 400)
self.center()
self.setWindowTitle('Game')
self.show()
def center(self):
screen = QtGui.QDesktopWidget().screenGeometry()
size = self.geometry()
self.move((screen.width()-size.width())/2, (screen.height()-size.height())/2)
class Board(QtGui.QFrame):
BoardWidth = 400
BoardHeight = 400
SquareWidth = 15
SquareHeight = 15
Speed = 10000
def __init__(self, parent):
super(Board, self).__init__(parent)
#self.setAutoFillBackground(True)
self.setFocusPolicy(QtCore.Qt.StrongFocus)
self.timer_draw = QtCore.QTimer(self)
self.timer_draw.timeout.connect(self.drawApple)
self.timer_draw.start(self.Speed)
self.apple_color = QtCore.Qt.red
self.draw_apple = False
self.x_apple = 0
self.y_apple = 0
self.drawApple()
def paintEvent(self, event):
painter = QtGui.QPainter(self)
print "Paint Event?"
if self.draw_apple == True:
print "Draw"
self.apple_color = QtCore.Qt.red
else:
print "Do not draw"
self.apple_color = QtCore.Qt.white
painter.setPen(self.apple_color)
painter.drawRect(self.rect)
def drawApple(self):
self.draw_apple = not self.draw_apple
self.x_apple = np.random.randint(0, math.floor(self.BoardWidth/self.SquareWidth)) * self.SquareWidth
self.y_apple = np.random.randint(0, math.floor(self.BoardHeight/self.SquareHeight)) * self.SquareHeight
self.rect = QRect(self.x_apple, self.y_apple, self.SquareWidth, self.SquareHeight)
self.update()
def main():
app = QtGui.QApplication([])
game = Game()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

pyqt4 drawing arc from other py file

Trying to draw arc and problem is I want to cal draw function form another py file and no luck so far (if draw function in main py file it is ok). I imported another py file but nothing happens. here is the code:
main.py
from PyQt4 import QtGui, Qt, QtCore
import sys
from src.cPrg import cPrg
from PyQt4.Qt import QPen
class mainWindow(QtGui.QWidget):
def __init__(self):
super(mainWindow, self).__init__()
self.otherFile = cPrg()
self.initUI()
def initUI(self):
#self.exitBtn = QtGui.QPushButton('Exit', self)
#self.exitBtn.setGeometry(100,100,60,40)
#self.exitBtn.clicked.connect(self.close_app)
self.label = QtGui.QLabel(self)
self.label.setText(self.otherFile.textas)
self.label.setGeometry(100,140, 60, 40)
self.otherFile.setGeometry(20,20, 20,20)
self.otherFile.startA = 270
self.otherFile.endA = -270
#self.showFullScreen()
self.setGeometry(100, 100, 800, 480)
self.setWindowTitle('Window Title')
self.show()
def close_app(self):
sys.exit()
def main():
app = QtGui.QApplication(sys.argv)
gui = mainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
and anotherfile.py
from PyQt4 import QtGui, QtCore, Qt
from PyQt4.Qt import QPen
class cPrg(QtGui.QWidget):
def __init__(self):
super(cPrg, self).__init__()
self.startA = 0
self.endA = 0
self.textas = 'bandom'
def paintEvent(self, e):
painter = QtGui.QPainter(self)
painter.setRenderHint(painter.Antialiasing)
rect = e.rect
r = QtCore.QRect(200,200,20,20) #<-- create rectangle
size = r.size() #<-- get rectangle size
r.setSize(size*10) #<-- set size
startAngle = self.startA*16 #<-- set start angle to draw arc
endAngle = self.endA*16 #<-- set end arc angle
painter.setPen(QPen(QtGui.QColor('#000000'))) #<-- arc color
#painter.setBrush(QtCore.Qt.HorPattern)
painter.drawArc(r, startAngle, endAngle) #<-- draw arc
painter.end()
super(cPrg,self).paintEvent(e)
What I doing wrong and how can I change line width?
Thank you
EDIT: all painting I made in main py file, here is the code:
from PyQt4 import QtGui, Qt, QtCore
import sys
from src.cprg import cPrg
from src.cprogress import GaugeWidget
from PyQt4.Qt import QPen
class mainWindow(QtGui.QWidget):
def __init__(self):
self.otherFile = cPrg()
self.gauge = GaugeWidget()
self.i = 0
self.lineWidth = 3
self._value = 0
self.completed = 0
super(mainWindow, self).__init__()
self.initUI()
def initUI(self):
self.setValue(.5)
#self.showFullScreen()
self.setGeometry(100, 100, 800, 480)
self.setWindowTitle('Window Title')
self.show()
def close_app(self):
sys.exit()
def setValue(self, val):
val = float(min(max(val, 0), 1))
self._value = -270 * val
self.update()
def setLineWidth(self, lineWidth):
self.lineWidth = lineWidth
def paintEvent(self, e):
painter = QtGui.QPainter(self)
painter.setRenderHint(painter.Antialiasing)
rect = e.rect
outerRadius = min(self.width(),self.height())
#arc line
r = QtCore.QRect(20,20,outerRadius-10,outerRadius-10) #<-- create rectangle
size = r.size() #<-- get rectangle size
r.setSize(size*.4) #<-- set size
startAngle = 270*16 #<-- set start angle to draw arc
endAngle = -270*16 #<-- set end arc angle
painter.setPen(QPen(QtGui.QColor('#000000'), self.lineWidth)) #<-- arc color
#painter.setBrush(QtCore.Qt.HorPattern)
painter.drawArc(r, startAngle, endAngle) #<-- draw arc
#arc prg
painter.save()
painter.setPen(QPen(QtGui.QColor('#ffffff'), 20))
painter.drawArc(r, startAngle, self._value*16)
painter.restore()
painter.end()
super(mainWindow,self).paintEvent(e)
def main():
app = QtGui.QApplication(sys.argv)
gui = mainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
this is my simple circular progress bar, now question is how can I place setValue, setlineWidth and paintEvent functions into separate py file and then just call by importing this file and class with these functions? I tried this:
from PyQt4 import QtGui, Qt, QtCore
import sys
from src.cprg import cPrg #<import progressbar
from src.cprogress import GaugeWidget
from PyQt4.Qt import QPen
class mainWindow(QtGui.QWidget):
def __init__(self):
self.otherFile = cPrg() #< imported progress bar
self.gauge = GaugeWidget()
self.i = 0
self.lineWidth = 3
self._value = 0
self.completed = 0
super(mainWindow, self).__init__()
self.initUI()
def initUI(self):
self.otherFile.setGeometry(10,10,100,100) #<<<< progress bar size
self.otherFile.setValue(0.5) #< progress bar value
and this is not working.
Change line width with QPen(color, line_width), update() redraw with paintEvent.
try with this:
from PyQt4 import QtGui, QtCore
class cPrg:
def __init__(self):
self.linewidth = 0
def setLineWidth(self, linewidth):
self.linewidth = linewidth
def drawArc(self, painter):
painter.setRenderHint(painter.Antialiasing)
r = QtCore.QRect(200,200,20,20) #<-- create rectangle
size = r.size() #<-- get rectangle size
r.setSize(size*10) #<-- set size
startAngle = self.startA*16 #<-- set start angle to draw arc
endAngle = self.endA*16 #<-- set end arc angle
painter.setPen(QtGui.QPen(QtGui.QColor('#000000'), self.linewidth)) #<-- arc color
painter.drawArc(r, startAngle, endAngle) #<-- draw arc
class mainWindow(QtGui.QWidget):
def __init__(self):
super(mainWindow, self).__init__()
self.otherFile = cPrg()
self.initUI()
self.i = 0
def initUI(self):
self.label = QtGui.QLabel(self)
self.label.setText(self.otherFile.textas)
self.label.setGeometry(100,140, 60, 40)
self.otherFile.startA = 270
self.otherFile.endA = -270
self.setGeometry(100, 100, 800, 480)
self.setWindowTitle('Window Title')
timer = QtCore.QTimer(self)
timer.timeout.connect(self.changeLineWidth)
timer.start(1000)
def changeLineWidth(self):
self.otherFile.setLineWidth(self.i)
self.i += 1
self.i %= 60
self.update()
def paintEvent(self, e):
painter = QtGui.QPainter(self)
self.otherFile.drawArc(painter)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
w = mainWindow()
w.show()
sys.exit(app.exec_())
If you want a circular progressbar, your must be override QProgressbar:
from math import ceil
from PyQt4 import QtGui, Qt, QtCore
import sys
class cPrg(QtGui.QProgressBar):
def __init__(self, parent=None):
super(cPrg, self).__init__(parent)
self.linewidth = 1
def factor(self, value):
a = 360 / (self.maximum() - self.minimum())
b = -a / (self.maximum() - self.minimum())
return a*value + b
def setLineWidth(self, linewidth):
self.linewidth = linewidth
self.update()
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.setRenderHint(painter.Antialiasing)
r = self.rect()
val = ceil(self.factor(self.value()))
nr = QtCore.QRect(r.topLeft() + QtCore.QPoint(self.linewidth, self.linewidth),
QtCore.QSize(r.width()-2*self.linewidth, r.height()-2*self.linewidth))
painter.setPen(QtGui.QPen(QtGui.QColor('#000000'), self.linewidth))
painter.drawArc(nr, 0*16, val*16)
class mainWindow(QtGui.QWidget):
def __init__(self):
super(mainWindow, self).__init__()
self.otherFile = cPrg(self)
self.otherFile.setMinimum(0)
self.otherFile.setMaximum(360)
self.otherFile.setValue(90)
self.initUI()
timerLW = QtCore.QTimer(self)
timerLW.timeout.connect(self.changeLW)
timerLW.start(100)
timerVal = QtCore.QTimer(self)
timerVal.timeout.connect(self.updateValue)
timerVal.start(100)
def initUI(self):
self.label = QtGui.QLabel(self)
self.label.setText("test")
self.label.setGeometry(200, 200, 60, 40)
self.otherFile.setGeometry(0, 0, 200, 200)
self.setGeometry(0, 0, 800, 480)
self.setWindowTitle('Window Title')
def changeLW(self):
lw = (self.otherFile.linewidth + 1) % 20
self.otherFile.setLineWidth(lw)
def updateValue(self):
self.otherFile.setValue(self.otherFile.value() + 1)
def main():
app = QtGui.QApplication(sys.argv)
gui = mainWindow()
gui.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Categories

Resources