PyQt5: Progress Indicator as Overlay - multithreading

I am again playing around with PyQt5 and this time I am stuck with how to add something like a "circular progress indicator" to my gui when the execution of a method takes a bit longer.
In order to do this, I have a (super) simple gui with just one button. When the button gets clicked, the application prints for 15 seconds "hello world!" to the console (this is just to see that it's actually doing something).
My question now is: How can I "overlay" a progress indicator to my gui when the execution of a method takes more than e.g. 4 seconds (until the method finishes, then the progress indicator should disappear again)? I have a sample script for a progress indicator from here. But unfortunately, I am lost when it comes to adding it to my toy example. Any help here would be very much appreciated.
I'm using: PyQt5.6.0, Python3.5.2
My code:
design.py (the gui file)
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'design.ui'
#
# Created by: PyQt5 UI code generator 5.6
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(300, 300)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(105, 140, 113, 32))
self.pushButton.setObjectName("pushButton")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "PushButton"))
main.py: (the main script):
from PyQt5 import QtWidgets
import time
from design import Ui_MainWindow
class ToyEx(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.pushButton.clicked.connect(self.gorepeat)
def gorepeat(self):
"""
print sth for 15 seconds
"""
end_time = time.time() + 15 * 1
print(end_time)
while time.time() < end_time:
print('hello world!')
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
form = ToyEx()
form.show()
app.exec_()
progress_indicator.py: (the code for the progress indicator, adapted from here)
"""
adapted to PyQt5 from:
Author: Jared P. Sutton <jpsutton#gmail.com>
License: LGPL
Note: I've licensed this code as LGPL because it was a complete translation of the code found here...
https://github.com/mojocorp/QProgressIndicator
sourcecode from here: https://github.com/mojocorp/QProgressIndicator
"""
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import time
class QProgressIndicator(QWidget):
m_angle = None
m_timerId = None
m_delay = None
m_displayedWhenStopped = None
m_color = None
def __init__(self, parent):
# Call parent class constructor first
super(QProgressIndicator, self).__init__(parent)
# Initialize Qt Properties
self.setProperties()
# Intialize instance variables
self.m_angle = 0
self.m_timerId = -1
self.m_delay = 40
self.m_displayedWhenStopped = False
self.m_color = Qt.black
# Set size and focus policy
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
self.setFocusPolicy(Qt.NoFocus)
# Show the widget
self.show()
def animationDelay(self):
return self.delay
def isAnimated(self):
return (self.m_timerId != -1)
def isDisplayedWhenStopped(self):
return self.displayedWhenStopped
def getColor(self):
return self.color
def sizeHint(self):
return QSize(20, 20)
def startAnimation(self):
self.m_angle = 0
if self.m_timerId == -1:
self.m_timerId = self.startTimer(self.m_delay)
def stopAnimation(self):
if self.m_timerId != -1:
self.killTimer(self.m_timerId)
self.m_timerId = -1
self.update()
def setAnimationDelay(self, delay):
if self.m_timerId != -1:
self.killTimer(self.m_timerId)
self.m_delay = delay
if self.m_timerId != -1:
self.m_timerId = self.startTimer(self.m_delay)
def setDisplayedWhenStopped(self, state):
self.displayedWhenStopped = state
self.update()
def setColor(self, color):
self.m_color = color
self.update()
def timerEvent(self, event):
self.m_angle = (self.m_angle + 30) % 360
self.update()
def paintEvent(self, event):
if (not self.m_displayedWhenStopped) and (not self.isAnimated()):
return
width = min(self.width(), self.height())
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
outerRadius = (width - 1) * 0.5
innerRadius = (width - 1) * 0.5 * 0.48
capsuleHeight = outerRadius - innerRadius
capsuleWidth = capsuleHeight * .23 if (width > 32) else capsuleHeight * .35
capsuleRadius = capsuleWidth / 2
for i in range(0, 12):
color = QColor(self.m_color)
if self.isAnimated():
color.setAlphaF(1.0 - (i / 12.0))
else:
color.setAlphaF(0.2)
painter.setPen(Qt.NoPen)
painter.setBrush(color)
painter.save()
painter.translate(self.rect().center())
painter.rotate(self.m_angle - (i * 30.0))
painter.drawRoundedRect(capsuleWidth * -0.5, (innerRadius + capsuleHeight) * -1, capsuleWidth,
capsuleHeight, capsuleRadius, capsuleRadius)
painter.restore()
def setProperties(self):
self.delay = pyqtProperty(int, self.animationDelay, self.setAnimationDelay)
self.displayedWhenStopped = pyqtProperty(bool, self.isDisplayedWhenStopped, self.setDisplayedWhenStopped)
self.color = pyqtProperty(QColor, self.getColor, self.setColor)
def TestProgressIndicator():
app = QApplication(sys.argv)
t_end = time.time() + 10 *1
progress = QProgressIndicator(None)
progress.setAnimationDelay(70)
progress.startAnimation()
# Execute the application
sys.exit(app.exec_())
if __name__ == "__main__":
TestProgressIndicator()
EDIT:
I tried something again by using the threading module when including the progress indicator. Below is the updated code:
design.py (gui file, includes progress indicator):
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'design.ui'
#
# Created by: PyQt5 UI code generator 5.6
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(300, 300)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(105, 140, 113, 32))
self.pushButton.setObjectName("pushButton")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "PushButton"))
class QProgressIndicator(QtWidgets.QWidget):
m_angle = None
m_timerId = None
m_delay = None
m_displayedWhenStopped = None
m_color = None
def __init__(self, parent):
# Call parent class constructor first
super(QProgressIndicator, self).__init__(parent)
# palette = QPalette(self.palette())
# palette.setColor(palette.Background, Qt.transparent)
# self.setPalette(palette)
# Initialize Qt Properties
self.setProperties()
# Intialize instance variables
self.m_angle = 0
self.m_timerId = -1
self.m_delay = 40
self.m_displayedWhenStopped = False
#self.m_color = Qt.black
self.m_color = QtCore.Qt.transparent
# Set size and focus policy
self.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
self.setFocusPolicy(QtCore.Qt.NoFocus)
# Show the widget
self.show()
def animationDelay(self):
return self.delay
def isAnimated(self):
return (self.m_timerId != -1)
def isDisplayedWhenStopped(self):
return self.displayedWhenStopped
def getColor(self):
return self.color
def sizeHint(self):
return QtCore.QSize(50, 50)
def startAnimation(self):
self.m_angle = 0
if self.m_timerId == -1:
self.m_timerId = self.startTimer(self.m_delay)
def stopAnimation(self):
if self.m_timerId != -1:
self.killTimer(self.m_timerId)
self.m_timerId = -1
self.update()
def setAnimationDelay(self, delay):
if self.m_timerId != -1:
self.killTimer(self.m_timerId)
self.m_delay = delay
if self.m_timerId != -1:
self.m_timerId = self.startTimer(self.m_delay)
def setDisplayedWhenStopped(self, state):
self.displayedWhenStopped = state
self.update()
def setColor(self, color):
self.m_color = color
self.update()
def timerEvent(self, event):
self.m_angle = (self.m_angle + 30) % 360
self.update()
def paintEvent(self, event):
if (not self.m_displayedWhenStopped) and (not self.isAnimated()):
return
width = min(self.width(), self.height())
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
outerRadius = (width - 1) * 0.5
innerRadius = (width - 1) * 0.5 * 0.48
capsuleHeight = outerRadius - innerRadius
capsuleWidth = capsuleHeight * .23 if (width > 32) else capsuleHeight * .35
capsuleRadius = capsuleWidth / 2
for i in range(0, 12):
color = QtGui.QColor(self.m_color)
if self.isAnimated():
color.setAlphaF(1.0 - (i / 12.0))
else:
color.setAlphaF(0.2)
painter.setPen(QtCore.Qt.NoPen)
painter.setBrush(color)
painter.save()
painter.translate(self.rect().center())
painter.rotate(self.m_angle - (i * 30.0))
painter.drawRoundedRect(capsuleWidth * -0.5, (innerRadius + capsuleHeight) * -1, capsuleWidth,
capsuleHeight, capsuleRadius, capsuleRadius)
painter.restore()
def setProperties(self):
self.delay = QtCore.pyqtProperty(int, self.animationDelay, self.setAnimationDelay)
self.displayedWhenStopped = QtCore.pyqtProperty(bool, self.isDisplayedWhenStopped, self.setDisplayedWhenStopped)
self.color = QtCore.pyqtProperty(QtGui.QColor, self.getColor, self.setColor)
main.py (updated):
from PyQt5 import QtWidgets
from threading import Thread, Timer
from design import Ui_MainWindow, QProgressIndicator
import time
class ToyEx(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.pushButton.clicked.connect(self.threadfuncts)
def threadfuncts(self):
Thread(target=gorepeat).start()
Timer(4, Thread(target=test_progressindicator).start()) # <-- THIS DOES NOT WORK!
def gorepeat():
"""
print sth for 15 seconds
"""
end_time = time.time() + 15 * 1
while time.time() < end_time:
print('hello world!')
def test_progressindicator():
progress = QProgressIndicator(None)
progress.setAnimationDelay(70)
progress.startAnimation()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
form = ToyEx()
form.show()
app.exec_()
But when I run this code, it crashes after one or two seconds already. Shouldn't the method threadfuncts() handle the threading in a way that the first function ( gorepeat()) gets called and 4 seconds later the second function (test_progressindicator()) gets called (and then they run in parallel)? If I define test_progressindicator() as another function that simply prints sth to the console, then the threading "kind of" works (the delay of 4 seconds doesn't work -- which is another point here that I do not understand why it does not work).
Again, if anyone could help me with this here, I'd appreciate it very much...

Related

How to let pyqt update the ui in the thread without blocking the interface?

I am trying to apply the load icon to the button, but I find that when I process time-consuming operations, it will cause a deadlock. I don't want to execute time. sleep (), which will affect the efficiency of the program. How should I optimize this code.
This is the original project:
https://github.com/prasanna892/PyQt5_loading_screen
To reduce wastage, I removed some code
loding_Screen.py
import os, inspect, sys
from PyQt5.QtWidgets import QWidget, QLabel, QGridLayout, QGraphicsOpacityEffect
from PyQt5.QtCore import QSize, Qt, QThread, QTimer
from PyQt5.QtGui import QMovie, QPalette, QColor
class LoadingTranslucentScreen(QWidget):
def __init__(self, parent: QWidget, description_text: str = '', dot_animation: bool = False):
super().__init__(parent)
self.__parent = parent
self.__parent.installEventFilter(self)
self.__parent.resizeEvent = self.resizeEvent
self.__dot_animation_flag = dot_animation
self.__descriptionLbl_original_text = description_text
self.__initUi(description_text)
def __initUi(self, description_text):
self.setAttribute(Qt.WA_TransparentForMouseEvents, True)
self.__movieLbl = QLabel(self.__parent)
caller_path = os.path.dirname(inspect.getframeinfo(sys._getframe(1)).filename)
loading_screen_ico_filename = os.path.join(caller_path, 'ico/Loading.gif')
self.__loading_mv = QMovie(loading_screen_ico_filename)
self.__loading_mv.setScaledSize(QSize(25, 25))
self.__movieLbl.setMovie(self.__loading_mv)
# self.__movieLbl.setStyleSheet('QLabel { background: transparent; }')
self.__movieLbl.setAlignment(Qt.AlignVCenter | Qt.AlignCenter)
self.__descriptionLbl = QLabel()
if description_text.strip() != '':
self.__descriptionLbl.setText(description_text)
self.__descriptionLbl.setVisible(False)
self.__descriptionLbl.setAlignment(Qt.AlignVCenter | Qt.AlignCenter)
lay = QGridLayout()
lay.setContentsMargins(0, 0, 0, 0)
lay.setAlignment(Qt.AlignVCenter | Qt.AlignCenter)
self.setLayout(lay)
self.setDescriptionLabelDirection('Bottom') # default description label direction
self.setMinimumSize(self.__parent.width(), self.__parent.height())
self.setVisible(False)
# self.__timerInit()
# def __timerInit(self):
# if self.__dot_animation_flag:
# self.__timer = QTimer(self)
# self.__timer.timeout.connect(self.__ticking)
# self.__timer.singleShot(0, self.__ticking)
# self.__timer.start(500)
#
# def __ticking(self):
# dot = '.'
# cur_text = self.__descriptionLbl.text()
# cnt = cur_text.count(dot)
# if cnt % 3 == 0 and cnt != 0:
# self.__descriptionLbl.setText(self.__descriptionLbl_original_text + dot)
# else:
# self.__descriptionLbl.setText(cur_text + dot)
def setParentThread(self, parent_thread: QThread):
self.__thread = parent_thread
def setDescriptionLabelDirection(self, direction: str):
lay = self.layout()
if direction == 'Left':
lay.addWidget(self.__descriptionLbl, 0, 0, 1, 1)
lay.addWidget(self.__movieLbl, 0, 1, 1, 1)
elif direction == 'Top':
lay.addWidget(self.__descriptionLbl, 0, 0, 1, 1)
lay.addWidget(self.__movieLbl, 1, 0, 1, 1)
elif direction == 'Right':
lay.addWidget(self.__movieLbl, 0, 0, 1, 1)
lay.addWidget(self.__descriptionLbl, 0, 1, 1, 1)
elif direction == 'Bottom':
lay.addWidget(self.__movieLbl, 0, 0, 1, 1)
lay.addWidget(self.__descriptionLbl, 1, 0, 1, 1)
else:
raise BaseException('Invalid direction.')
def start(self):
self.__loading_mv.start()
self.__descriptionLbl.setVisible(True)
self.raise_()
self.setVisible(True)
# opacity_effect = QGraphicsOpacityEffect(opacity=0.75)
# self.setGraphicsEffect(opacity_effect)
def stop(self):
self.__loading_mv.stop()
self.__descriptionLbl.setVisible(False)
self.lower()
self.setVisible(False)
def makeParentDisabledDuringLoading(self):
if self.__thread.isRunning():
self.__parent.setEnabled(False)
else:
self.__parent.setEnabled(True)
def paintEvent(self, e):
palette = QPalette()
palette.setColor(QPalette.Window, QColor(255, 255, 255))
self.setAutoFillBackground(True)
self.setPalette(palette)
return super().paintEvent(e)
def eventFilter(self, obj, e):
if isinstance(obj, QWidget):
if e.type() == 14:
self.setFixedSize(e.size())
return super(LoadingTranslucentScreen, self).eventFilter(obj, e)
To facilitate understanding, this is a small example I wrote:
main.py
`
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtCore import QWaitCondition, QMutex
from PyQt5.QtWidgets import QMainWindow, QMessageBox, QApplication, QDesktopWidget
from PyQt5.QtCore import pyqtSignal
import sys
from PyQt5.QtCore import QThread
from pyqt_translucent_full_loading_screen_thread import LoadingThread, LoadingTranslucentScreen
class MySearch_Loading_Thread(LoadingThread):
loadingSignal = pyqtSignal()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.cond = QWaitCondition()
self.mutex = QMutex()
def resume(self):
self.cond.wakeAll()
def run(self):
self.mutex.lock()
self.cond.wait(self.mutex)
self.mutex.unlock()
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setObjectName("pushButton")
self.horizontalLayout.addWidget(self.pushButton)
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setObjectName("label")
self.horizontalLayout.addWidget(self.label)
self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_2.setObjectName("pushButton_2")
self.horizontalLayout.addWidget(self.pushButton_2)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 23))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "PushButton"))
self.label.setText(_translate("MainWindow", "TextLabel"))
self.pushButton_2.setText(_translate("MainWindow", "stop"))
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.__init2__()
def __init2__(self):
self.search_running = False
self.ui.pushButton.clicked.connect(self.search)
self.ui.pushButton_2.clicked.connect(self.stop_search)
def stop_search(self):
if self.search_running:
reply = QMessageBox.information(self, 'notice', 'Do you want to stop searching?',
QMessageBox.Yes | QMessageBox.Cancel)
if reply == QMessageBox.Yes:
self.search_thread.terminate()
self.__thread.resume()
self.search_running = False
else:
QMessageBox.information(self, 'tip', 'no search!', QMessageBox.Ok)
def search(self):
if not self.search_running:
self.__loadingTranslucentScreen = LoadingTranslucentScreen(parent=self.ui.pushButton,
description_text='搜索中')
self.__loadingTranslucentScreen.setDescriptionLabelDirection('Right')
self.__thread = MySearch_Loading_Thread(loading_screen=self.__loadingTranslucentScreen)
self.__thread.start()
self.search_running = True
self.search_thread = Search_Thread()
self.search_thread.trigger.connect(lambda i: self.ui.label.setText(str(i)))
self.search_thread.complete.connect(self.search_complete)
self.search_thread.start()
def search_complete(self):
self.__thread.resume()
self.search_running = False
class Search_Thread(QThread):
trigger = pyqtSignal(int)
complete = pyqtSignal()
def __init__(self):
super().__init__()
def run(self):
for i in range(10000):
for j in range(10000):
for k in range(10000):
# time.sleep(0.1)
self.trigger.emit(i + j + k)
self.complete.emit()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
screen = QDesktopWidget().screenGeometry()
height = 100
width = 300
window.setGeometry(int((screen.width() - width) / 2), int((screen.height() - height) / 2), width, height)
window.setWindowTitle("test")
window.show()
sys.exit(app.exec())
`

Turtle screen gets closed when .onclick() or onscreenclick() is used

I have a global variable, 'is_game_on' set to 'False' to start with. I have a turtle which responds to .ondrag() function. My program works perfectly fine if I change the 'is_game_on' variable to 'True' (The main program runs in a while loop when 'is_game_on' is 'True').
In the same Screen I have created turtle (a text- 'Click to start') in the top right of the screen, which I want to return 'is_game_on' = 'True' when the mouse is clicked on it so that the rest of my program starts working there after. However, my screen gets closed when I click the mouse. I think this is because of the command screen.exitonclick() at the end. Appreciate any suggestions how to avoid this problem.
Below is my code. I want to start with 'is_game_on == False' and with the output a static display. Then when I click the mouse on 'Click to Start', a mechanism to trigger 'is_game_on" as True and then the ball starts bouncing up and down.
from turtle import Screen, Turtle
import time
# is_game_on = False
is_game_on = True
def click(i, j):
global is_game_on
if i >= 250 and j >= 300:
is_game_on = True
print(is_game_on)
return is_game_on
class Ball(Turtle):
def __init__(self):
super().__init__()
self.shape('circle')
self.color('black')
self.shapesize(stretch_wid=2, stretch_len=2)
self.penup()
self.speed(6)
self.goto(0, -355)
self.x_move = 0
self.y_move = 1
self.move_speed = 10
def move(self):
xcor_new = self.xcor() + self.x_move
ycor_new = self.ycor() + self.y_move
self.goto(xcor_new, ycor_new)
def bounce_y(self):
self.y_move *= -1
class Paddle(Turtle):
def __init__(self):
super().__init__()
self.shape('square')
self.penup()
self.goto(0, -380)
self.color('blue')
self.shapesize(stretch_wid=.5, stretch_len=10)
def move(self,i, j):
self.goto(i, -380)
class Start(Turtle):
def __init__(self):
super().__init__()
self.penup()
self.goto(250, 300)
self.color('blue')
self.shapesize(stretch_wid=4, stretch_len=10)
self.hideturtle()
self.write('Click to Start', font=('Arial', 35, 'bold'))
screen = Screen()
screen.colormode(255)
screen.bgcolor('white')
screen.setup(1200, 800)
screen.tracer(0)
paddle = Paddle()
ball = Ball()
screen.listen()
paddle.ondrag(paddle.move)
screen.onclick(click)
start = Start()
while is_game_on:
time.sleep(0)
screen.update()
ball.move()
if ball.ycor() >= 375:
ball.bounce_y()
if (abs(ball.xcor() - paddle.xcor()) < 120) and ball.ycor() == -355:
ball.bounce_y()
screen.update()
screen.exitonclick()
After lot of trial and errors and lot of web searches, I found the solution.
The screen closing problem can be simply avoided by importing turtle and adding turtle.done() command just before screen.exitonclick() command. The complete code will be.
from turtle import Screen, Turtle
import time
import turtle
is_game_on = False
# is_game_on = True
def click(i, j):
global is_game_on
if i >= 250 and j >= 300:
is_game_on = True
print(is_game_on)
return is_game_on
class Ball(Turtle):
def __init__(self):
super().__init__()
self.shape('circle')
self.color('black')
self.shapesize(stretch_wid=2, stretch_len=2)
self.penup()
self.speed(6)
self.goto(0, -355)
self.x_move = 0
self.y_move = 1
self.move_speed = 10
def move(self):
xcor_new = self.xcor() + self.x_move
ycor_new = self.ycor() + self.y_move
self.goto(xcor_new, ycor_new)
def bounce_y(self):
self.y_move *= -1
class Paddle(Turtle):
def __init__(self):
super().__init__()
self.shape('square')
self.penup()
self.goto(0, -380)
self.color('blue')
self.shapesize(stretch_wid=.5, stretch_len=10)
def move(self,i, j):
self.goto(i, -380)
class Start(Turtle):
def __init__(self):
super().__init__()
self.penup()
self.goto(250, 300)
self.color('blue')
self.shapesize(stretch_wid=4, stretch_len=10)
self.hideturtle()
self.write('Click to Start', font=('Arial', 35, 'bold'))
screen = Screen()
screen.colormode(255)
screen.bgcolor('white')
screen.setup(1200, 800)
screen.tracer(0)
paddle = Paddle()
ball = Ball()
screen.listen()
paddle.ondrag(paddle.move)
screen.onclick(click)
start = Start()
while is_game_on:
time.sleep(0)
screen.update()
ball.move()
if ball.ycor() >= 375:
ball.bounce_y()
if (abs(ball.xcor() - paddle.xcor()) < 120) and ball.ycor() == -355:
ball.bounce_y()
screen.update()
turtle.done()
screen.exitonclick()
The animation can work by moving the while loop into the click() function. The code will be as follows.
from turtle import Screen, Turtle
import time
import turtle
is_game_on = False
# is_game_on = True
def click(i, j):
global is_game_on
if i >= 250 and j >= 300:
is_game_on = True
print(is_game_on)
while is_game_on:
time.sleep(0)
screen.update()
ball.move()
if ball.ycor() >= 375:
ball.bounce_y()
if (abs(ball.xcor() - paddle.xcor()) < 120) and ball.ycor() == -355:
ball.bounce_y()
return is_game_on
class Ball(Turtle):
def __init__(self):
super().__init__()
self.shape('circle')
self.color('black')
self.shapesize(stretch_wid=2, stretch_len=2)
self.penup()
self.speed(6)
self.goto(0, -355)
self.x_move = 0
self.y_move = 1
self.move_speed = 10
def move(self):
xcor_new = self.xcor() + self.x_move
ycor_new = self.ycor() + self.y_move
self.goto(xcor_new, ycor_new)
def bounce_y(self):
self.y_move *= -1
class Paddle(Turtle):
def __init__(self):
super().__init__()
self.shape('square')
self.penup()
self.goto(0, -380)
self.color('blue')
self.shapesize(stretch_wid=.5, stretch_len=10)
def move(self,i, j):
self.goto(i, -380)
class Start(Turtle):
def __init__(self):
super().__init__()
self.penup()
self.goto(250, 300)
self.color('blue')
self.shapesize(stretch_wid=4, stretch_len=10)
self.hideturtle()
self.write('Click to Start', font=('Arial', 35, 'bold'))
screen = Screen()
screen.colormode(255)
screen.bgcolor('white')
screen.setup(1200, 800)
screen.tracer(0)
paddle = Paddle()
ball = Ball()
screen.listen()
paddle.ondrag(paddle.move)
screen.onclick(click)
start = Start()
screen.update()
turtle.done()
screen.exitonclick()

How can I make a circle bounce in a window?

I just made this program as a practice I want to make the circle bounce from end to end which I came up in this source codes can you make if happen.
import PyQt5,sys
from PyQt5 import QtCore,QtGui,QtWidgets
from PyQt5.QtCore import Qt,QTimer,QPoint
from PyQt5.QtWidgets import QWidget,QApplication,QMainWindow
from PyQt5.QtGui import QPainter
class Circle(QWidget):
def __init__(self):
QMainWindow.__init__(self)
self.resize(250,500)
self.setWindowTitle("Bounce-Man")
self.color = Qt.red
self.fixedplace = 125
self.mover = 450
def paintEvent(self,event):
bouncer.setPen(Qt.black)
bouncer.setBrush(self.color)
bouncer.drawEllipse(QPoint(self.fixedplace,self.mover),50,50)
#QtCore.pyqtSlot()
def timer(self):
timer = QTimer()
while self.mover >=50:
timer.start(1000)
if self.mover == 50:
self.mover = 450
self.mover -= 1
self.update()
if __name__ == "__main__":
window = QApplication(sys.argv)
app = Circle()
app.show()
sys.exit( window.exec_() )
You must use a QTimer to call a function that updates the position, do not use a while loop:
from PyQt5 import QtCore, QtGui, QtWidgets
class Circle(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Circle, self).__init__(parent)
self.resize(250,500)
self.setWindowTitle("Bounce-Man")
self.color = QtGui.QColor(QtCore.Qt.red)
self.rect_circle = QtCore.QRect(0, 0, 50, 50)
self.rect_circle.moveCenter(QtCore.QPoint(self.width()/2, self.rect_circle.height()/2))
self.step = QtCore.QPoint(0, 5)
self.y_direction = 1
timer = QtCore.QTimer(self, interval=30)
timer.timeout.connect(self.update_position)
timer.start()
def paintEvent(self,event):
bouncer = QtGui.QPainter(self)
bouncer.setPen(QtCore.Qt.black)
bouncer.setBrush(self.color)
bouncer.drawEllipse(self.rect_circle)
#QtCore.pyqtSlot()
def update_position(self):
if self.rect_circle.bottom() > self.height() and self.y_direction == 1:
self.y_direction = -1
if self.rect_circle.top() < 0 and self.y_direction == -1:
self.y_direction = 1
self.rect_circle.translate(self.step * self.y_direction)
self.update()
if __name__ == "__main__":
import sys
window = QtWidgets.QApplication(sys.argv)
app = Circle()
app.show()
sys.exit( window.exec_() )

PyQt5 crashing with threading and progress bar

I have a progress bar that is controlled by a start stop button. The progress bar fills up using a thread. However, whenever I run the program it crashes at random times (different time each time program is run).
class Window(QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.fill_thread = None
Thread for filling progress bar. I have tried using both time.sleep and QThread.msleep.
def fill_bar(self):
while not self.stop_fill and self.completed < 100:
self.completed += 0.5
QThread.msleep(100)
self.chargeprog.setValue(self.completed)
Buttons that alternate between start and stop that call the thread
def charging(self):
if self.chargebtn.text() == 'Start':
self.chargebtn.setText('Stop')
self.chargebtn.setStyleSheet('background-color:red')
self.fill_thread = threading.Thread(target=self.fill_bar)
self.stop_fill = False
self.fill_thread.start()
else:
self.stop_fill = True
self.fill_thread.join()
self.chargebtn.setText('Start')
self.chargebtn.setStyleSheet('background-color:limegreen')
window.py
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'window.ui'
#
# Created by: PyQt5 UI code generator 5.6
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
self.progressBar.setGeometry(QtCore.QRect(180, 150, 118, 23))
self.progressBar.setProperty("value", 24)
self.progressBar.setObjectName("progressBar")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(330, 150, 75, 23))
self.pushButton.setObjectName("pushButton")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 18))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "PushButton"))
test.py
import sys
from PyQt5.QtWidgets import QApplication,QMainWindow
from PyQt5.QtCore import QThread,pyqtSignal
from window import Ui_MainWindow
class CThread(QThread):
valueChanged = pyqtSignal([int])
def __init__(self,value):
super().__init__()
self.stop_fill = False
self.completed = value
def run(self):
while not self.stop_fill and self.completed < 100:
self.completed += 0.5
self.sleep(1)
self.valueChanged.emit(self.completed)
class Window(QMainWindow,Ui_MainWindow):
def __init__(self):
super().__init__()
super().setupUi(self)
self.fill_thread = None
self.pushButton.clicked.connect(self.charging)
def charging(self):
if self.pushButton.text() == 'Start':
self.pushButton.setText('Stop')
self.pushButton.setStyleSheet('background-color:red')
print(self.progressBar.value())
self.fill_thread = CThread(self.progressBar.value())
self.fill_thread.valueChanged.connect(self.progressBar.setValue)
self.fill_thread.start()
else:
if self.fill_thread != None:
self.fill_thread.disconnect() # event unbind
self.fill_thread.stop_fill = True
self.fill_thread.wait()
self.fill_thread.quit()
print(self.fill_thread.isRunning())
self.pushButton.setText('Start')
self.pushButton.setStyleSheet('background-color:limegreen')
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
If you want communicate between multi-threads, you can try signal/slots.

Python 3.5, pyqt5 progress bar gui in seperate window

I am new to PyQt5 and pretty new to Python. I am trying to create a graphical user interface using PyQt5 in Python 3.5, where I click a button to launch a separate window in which the progress bar iterates up to 100 and then closing the window at the end of iteration to generate a message "it worked".
The problem is the progress bar is created but doesn't update and after reaching the end it doesn't display the message that it worked. When I try to debug it crashes completely with no warning as to why. I don't know how else to debug the code
My progress bar code is shown below:
from PyQt5 import QtCore, QtWidgets
import sys
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(1075, 84)
self.progressBar = QtWidgets.QProgressBar(Form)
self.progressBar.setGeometry(QtCore.QRect(30, 30, 1000, 35))
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.progressBar.sizePolicy().hasHeightForWidth())
self.progressBar.setSizePolicy(sizePolicy)
self.progressBar.setMinimumSize(QtCore.QSize(1000, 35))
self.progressBar.setMaximumSize(QtCore.QSize(1000, 35))
self.progressBar.setProperty("value", 0)
self.progressBar.setObjectName("progressBar")
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def setValue(self, val):
self.progressBar.setProperty("value", val)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Progress bar"))
The main program is given below
from PyQt5.QtWidgets import QApplication, QDialog, QWidget, QPushButton, QMessageBox
import ProgressBar
import sys
class App(QWidget):
def __init__(self):
super().__init__()
self.title = 'PyQt5 button - pythonspot.com'
self.left = 200
self.top = 200
self.width = 320
self.height = 200
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
button = QPushButton('PyQt5 button', self)
button.setToolTip('This is an example button')
button.move(100, 70)
button.clicked.connect(self.on_click)
self.show()
def on_click(self):
print('PyQt5 button click')
app1 = QApplication(sys.argv)
window = QDialog()
ui = ProgressBar.Ui_Form()
ui.setupUi(window)
window.show()
for i in range(0, 100):
ui.setValue(((i + 1) / 100) * 100)
app1.quit()
QMessageBox.information(self, "Message", "Data Loaded")
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
Any help would be greatly appreciated.
Here is my final code. I have tried to include some good practices with using pyqt designer and not editing the created file directly but run it from another file and call into it. This has made things much easier when I wanted to change things as suggested. I have also included a time.sleep(0.1) to the code to slow it down so you can see it working. Hope it helps.
ProgressBar_ui.py - file generated by converted in python from ProgressBar.ui
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'Base.ui'
#
# Created by: PyQt5 UI code generator 5.6
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(1075, 84)
self.progressBar = QtWidgets.QProgressBar(Form)
self.progressBar.setGeometry(QtCore.QRect(30, 30, 1000, 35))
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.progressBar.sizePolicy().hasHeightForWidth())
self.progressBar.setSizePolicy(sizePolicy)
self.progressBar.setMinimumSize(QtCore.QSize(1000, 35))
self.progressBar.setMaximumSize(QtCore.QSize(1000, 35))
self.progressBar.setProperty("value", 0)
self.progressBar.setObjectName("progressBar")
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Progress bar"))
ProgressBar.py - Calls ProgrssBar.ui
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
from ProgressBar_ui import Ui_Form
class ProgressBar(QtWidgets.QDialog, Ui_Form):
def __init__(self, desc = None, parent=None):
super(ProgressBar, self).__init__(parent)
self.setupUi(self)
self.show()
if desc != None:
self.setDescription(desc)
def setValue(self, val): # Sets value
self.progressBar.setProperty("value", val)
def setDescription(self, desc): # Sets Pbar window title
self.setWindowTitle(desc)
def main():
app = QtWidgets.QApplication(sys.argv) # A new instance of QApplication
form = ProgressBar('pbar') # We set the form to be our MainWindow (design)
app.exec_() # and execute the app
if __name__ == '__main__': # if we're running file directly and not importing it
main() # run the main function
Main_program.py - Run from here
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QMessageBox
from ProgressBar import ProgressBar
import sys, time
class App(QWidget):
def __init__(self):
super().__init__()
self.title = 'PyQt5 button - pythonspot.com'
self.left = 200
self.top = 200
self.width = 320
self.height = 200
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
button = QPushButton('PyQt5 button', self)
button.setToolTip('This is an example button')
button.move(100, 70)
button.clicked.connect(self.on_click)
self.show()
def on_click(self):
pb = ProgressBar()
for i in range(0, 100):
time.sleep(0.05)
pb.setValue(((i + 1) / 100) * 100)
QApplication.processEvents()
pb.close()
QMessageBox.information(self, "Message", "Data Loaded")
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
Figured out solution eventually. 3 problems:
1) Should only call QApplication once in the main application an not again
2) Add QApplication.processEvents() to the forloop so it updates the progressbar continuously
3) Add window.close() after the forloop so it closes

Resources