QTimer execute function faster by one second when I pause then resume - python-3.x

Trying to build a stopwatch GUI and I am unable to make the pause function to work. Every time when I pause the timer and then resume, the timer runs faster by a second.
import sys
import datetime
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QGridLayout
from PyQt5.QtCore import QTimer, Qt
from PyQt5.QtGui import QFont, QPalette
class StopWatch(QWidget):
def __init__(self):
super().__init__()
self.isRunning = False
self.counter = 0
self.setWindowTitle('Stop Watch')
self.create_layout()
def create_layout(self):
self.timer = QTimer()
self.timer.setInterval(1000)
labelFormating = QPalette()
labelFormating.setColor(QPalette.WindowText, Qt.darkBlue)
self.startButton = QPushButton('Start')
self.pauseButton = QPushButton('Pause')
self.stopButton = QPushButton('Stop')
self.startButton.setEnabled(True)
self.pauseButton.setEnabled(False)
self.stopButton.setEnabled(False)
self.startButton.clicked.connect(self.start_timer)
self.pauseButton.clicked.connect(self.pause)
self.stopButton.clicked.connect(self.stop_timer)
self.displayLabel = QLabel(str(datetime.timedelta(seconds=0)))
self.displayLabel.setFont(QFont('Open Sans', 24))
self.displayLabel.setPalette(labelFormating)
gridLayout = QGridLayout()
gridLayout.addWidget(self.displayLabel, 0, 0, 1, 3) # row span | color span (extend)
gridLayout.addWidget(self.startButton, 1, 0, 1, 1)
gridLayout.addWidget(self.pauseButton, 1, 1, 1, 1)
gridLayout.addWidget(self.stopButton, 1, 2, 1, 1)
gridLayout.setAlignment(self.displayLabel, Qt.AlignCenter)
self.setLayout(gridLayout)
def start_timer(self, slot):
print('start clicked')
self.startButton.setEnabled(False)
self.pauseButton.setEnabled(True)
self.stopButton.setEnabled(True)
self.timer.start()
self.timer.timeout.connect(self.run) #This signal is emitted when the timer times out.
def stop_timer(self):
print('Stop clicked')
self.startButton.setEnabled(True)
self.pauseButton.setEnabled(False)
self.stopButton.setEnabled(False)
self.isRunning = False
self.timer.stop()
self.displayLabel.setText(str(datetime.timedelta(seconds=0)))
def run(self):
# print('Counter value', str(self.counter))
self.counter += 1
self.display()
def pause(self):
print('pause clicked')
self.startButton.setEnabled(True)
self.pauseButton.setEnabled(False)
self.stopButton.setEnabled(True)
self.timer.stop()
self.displayLabel.setText(str(datetime.timedelta(seconds=self.counter)))
def display(self):
# print(str(datetime.timedelta(seconds=self.counter)))
self.displayLabel.setText(str(datetime.timedelta(seconds=self.counter)))
def main():
app = QApplication(sys.argv)
window = StopWatch()
window.show()
sys.exit(app.exec_())
main()
What I am looking for is to perform the pause function. For example, when I click Start, then Pause, then Start, I wish the counter to run normally. Any help is appreciated.

Try it:
import sys
import datetime
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QGridLayout
from PyQt5.QtCore import QTimer, Qt
from PyQt5.QtGui import QFont, QPalette
class StopWatch(QWidget):
def __init__(self):
super().__init__()
self.isRunning = False
self.counter = 0
self.setWindowTitle('Stop Watch')
self.create_layout()
def create_layout(self):
self.timer = QTimer()
self.timer.setInterval(1000)
self.timer.timeout.connect(self.run) # +++
labelFormating = QPalette()
labelFormating.setColor(QPalette.WindowText, Qt.darkBlue)
self.startButton = QPushButton('Start')
self.pauseButton = QPushButton('Pause')
self.stopButton = QPushButton('Stop')
self.startButton.setEnabled(True)
self.pauseButton.setEnabled(False)
self.stopButton.setEnabled(False)
self.startButton.clicked.connect(self.start_timer)
self.pauseButton.clicked.connect(self.pause)
self.stopButton.clicked.connect(self.stop_timer)
self.displayLabel = QLabel(str(datetime.timedelta(seconds=0)))
self.displayLabel.setFont(QFont('Open Sans', 24))
self.displayLabel.setPalette(labelFormating)
gridLayout = QGridLayout()
gridLayout.addWidget(self.displayLabel, 0, 0, 1, 3)
gridLayout.addWidget(self.startButton, 1, 0, 1, 1)
gridLayout.addWidget(self.pauseButton, 1, 1, 1, 1)
gridLayout.addWidget(self.stopButton, 1, 2, 1, 1)
gridLayout.setAlignment(self.displayLabel, Qt.AlignCenter)
self.setLayout(gridLayout)
def start_timer(self, slot):
self.startButton.setEnabled(False)
self.pauseButton.setEnabled(True)
self.stopButton.setEnabled(True)
self.timer.start()
# self.timer.timeout.connect(self.run) # ----
def stop_timer(self):
self.startButton.setEnabled(True)
self.pauseButton.setEnabled(False)
self.stopButton.setEnabled(False)
self.isRunning = False
self.timer.stop()
# ? self.displayLabel.setText(str(datetime.timedelta(seconds=0)))
self.counter = 0 # +++
def run(self):
self.counter += 1
self.display()
def pause(self):
self.startButton.setEnabled(True)
self.pauseButton.setEnabled(False)
self.stopButton.setEnabled(True)
self.timer.stop()
# self.displayLabel.setText(str(datetime.timedelta(seconds=self.counter)))
def display(self):
self.displayLabel.setText(str(datetime.timedelta(seconds=self.counter)))
def main():
app = QApplication(sys.argv)
window = StopWatch()
window.show()
sys.exit(app.exec_())
main()

Related

Updating/clearing the PyQt form using confirmation window

I would like that by typing anything in LineEdit in the first window and by pressing 'Confirm clearing' button in the second window, the first window would be updated so that the LineEdit field is emptied and focused.
import sys
from PyQt6.QtWidgets import (
QApplication, QWidget, QPushButton, QLabel, QLineEdit, QGridLayout
)
class Vw(QWidget):
def __init__(self):
super().__init__()
self.resize(1000, 400)
self.setContentsMargins(30, 30, 30, 30)
self.grid = QGridLayout(self)
self.setLayout(self.grid)
self.t1 = QLineEdit(self)
self.grid.addWidget(self.t1, 0, 1)
self.bOk = QPushButton("Clear the line edit", self)
self.bOk.setFixedSize(120, 30)
self.grid.addWidget(self.bOk, 5, 0)
self.bOk.clicked.connect(self.implementing)
self.bOk.setAutoDefault(True)
def implementing(self):
# to be added a code
done()
def clearing(self):
self.t1.clear()
self.t1.setFocus()
def done():
Vw.wda = WdA()
Vw.wda.show()
class WdA(QWidget):
def __init__(self):
super().__init__()
self.resize(300, 50)
self.setContentsMargins(0, 0, 0, 0)
self.grid = QGridLayout(self)
self.setLayout(self.grid)
self.el1 = QLabel("About to clear!", self)
self.grid.addWidget(self.el1, 0, 0)
self.wOk = QPushButton("Confirm clearing", self)
self.wOk.setFixedSize(120, 30)
self.grid.addWidget(self.wOk, 1, 0)
self.wOk.setFocus()
self.wOk.clicked.connect(self.end_of_entry)
self.wOk.setAutoDefault(True)
def end_of_entry(self):
self.close()
# Vw.clearing() # <--- This line needs to be implemented
def appl():
app_ = QApplication(sys.argv)
wnd = Vw()
wnd.show()
sys.exit(app_.exec())
if __name__ == '__main__':
appl()
In other words, I would like 'clearing' method would trigger on clicking on 'wOk' button.
In other words, I would like 'clearing' method would trigger on clicking on 'wOk' button.
As an option using Signals & Slots.
Support for Signals and Slots
import sys
'''
from PyQt6.QtWidgets import (
QApplication, QWidget, QPushButton, QLabel, QLineEdit, QGridLayout
)
'''
from PyQt5.QtWidgets import (
QApplication, QWidget, QPushButton, QLabel, QLineEdit, QGridLayout
)
from PyQt5.QtCore import pyqtSignal # <---
class WdA(QWidget):
signal = pyqtSignal() # <---
def __init__(self):
super().__init__()
self.resize(300, 50)
self.setContentsMargins(0, 0, 0, 0)
self.grid = QGridLayout(self)
self.setLayout(self.grid)
self.el1 = QLabel("About to clear!", self)
self.grid.addWidget(self.el1, 0, 0)
self.wOk = QPushButton("Confirm clearing", self)
self.wOk.setFixedSize(120, 30)
self.grid.addWidget(self.wOk, 1, 0)
self.wOk.setFocus()
self.wOk.clicked.connect(self.end_of_entry)
self.wOk.setAutoDefault(True)
def end_of_entry(self):
self.signal.emit() # <---
self.close()
class Vw(QWidget):
def __init__(self):
super().__init__()
self.resize(1000, 400)
self.setContentsMargins(30, 30, 30, 30)
self.grid = QGridLayout(self)
self.setLayout(self.grid)
self.t1 = QLineEdit(self)
self.grid.addWidget(self.t1, 0, 1)
self.bOk = QPushButton("Clear the line edit", self)
self.bOk.setFixedSize(120, 30)
self.grid.addWidget(self.bOk, 5, 0)
self.bOk.clicked.connect(self.implementing)
self.bOk.setAutoDefault(True)
self.wda = WdA() # +++
self.wda.signal.connect(self.clearing) # <---
def implementing(self):
self.wda.show()
def clearing(self): # <---
self.t1.clear()
self.t1.setFocus()
def closeEvent(self, event):
self.wda.close()
def appl():
app_ = QApplication(sys.argv)
wnd = Vw()
wnd.show()
sys.exit(app_.exec())
if __name__ == '__main__':
appl()

PyQt Progress Bar Update using Thread

I've been writing a program that used a MyWindow(QTableWidget) with a thread. A progress bar is displayed above the sub-window(self.win) displayed as a pop-up.
I want a green bar on the status bar to be displayed consecutively, however after resetting the Spyder-kernel, the green bar does not output continuously. And I want to run the 'stop'/'continue' alternately every time I click the push button. This hasn't been resolved for almost three days.
import sys, time
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtCore import QThread, pyqtSignal, pyqtSlot, QRect
from PyQt5.QtWidgets import *
progVal = 0
class thread(QThread):
signalTh = pyqtSignal(int)
def __init__(self, *args):
super().__init__()
self.flag = True
def run(self):
global progVal
if self.flag:
self.signalTh.emit(progVal)
time.sleep(0.1)
def stop(self):
self.flag = False
self.quit()
self.wait(2)
class MyWindow(QTableWidget):
def __init__(self):
global progVal
super().__init__()
self.setupUi()
self.show()
self.test = thread(None)
self.test.signalTh.connect(self.signal_function)
self.test.run()
self.saveData()
def saveData(self):
global progVal
counts = range(1, 51)
for row in counts:
progVal = int(row/len(counts)*100)
self.test.signalTh.emit(progVal)
time.sleep(0.1)
def click1_function(self):
if self.test.flag:
self.test.stop()
self.pb_start.setText('Start!')
else:
self.test.flag = True
self.test.run()
self.pb_start.setText('Stop!')
#pyqtSlot(int)
def signal_function(self, val):
self.progress.setValue(val)
self.progress.update()
self.win.update()
self.update()
def setupUi(self):
self.resize(500, 400)
self.pb_start = QPushButton(self)
self.pb_start.setGeometry(QRect(80, 20, 100, 50))
self.pb_start.setText("Start")
self.pb_start.clicked.connect(self.click1_function)
self.win = QDialog(self)
self.win.resize(330, 100)
self.progress = QProgressBar(self.win)
self.progress.setGeometry(10, 10, 300, 30)
self.progress.setMaximum(100)
self.win.show()
def closeEvent(self, event):
quit_msg = "Are you sure you want to exit the program?"
reply = QMessageBox.question(self, 'Message', quit_msg, QMessageBox.Yes, QMessageBox.No)
if reply == QMessageBox.Yes:
self.test.stop()
event.accept()
else:
event.ignore()
if __name__ == "__main__":
app = QApplication(sys.argv)
myApp = MyWindow()
myApp.show()
app.exec_()

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 trying to use QGridLayout to organise my QLabel, QLineEdit, QPushButton, and "Pop-up" QLabel

I am trying to get the "Game Name:" (QLabel), input box (QLineEdit), and QPushButton on one line and the "Pop-up" QLabel) to appear on the bottom
but am having difficulties with get QGridLayout to work
With this code:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout, QPushButton, QLabel, QLineEdit, QGridLayout, QGroupBox, QDialog
from PyQt5.QtCore import pyqtSlot
class Window(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle("Project PiBu!!")
self.createGridLayout()
self.windowLayout = QVBoxLayout()
self.windowLayout.addWidget(self.horizontalGroupBox)
self.setLayout(self.windowLayout)
self.game_name = QLabel("Game Name:", self)
self.game_line_edit = QLineEdit(self)
self.search_button = QPushButton("Search", self)
self.search_button.clicked.connect(self.on_click)
self.game = QLabel(self)
self.show()
def createGridLayout(self):
self.horizontalGroupBox = QGroupBox()
self.layout = QGridLayout()
self.layout.setColumnStretch(1, 4)
self.layout.setColumnStretch(2, 4)
self.layout.addWidget(self.game_name, 0, 0)
self.layout.addWidget(self.game_line_edit, 0, 1)
self.layout.addWidget(self.search_button, 0, 2)
self.layout.addWidget(self.game, 1, 0)
self.horizontalGroupBox.setLayout(layout)
#pyqtSlot()
def on_click(self):
self.game.setText(self.game_line_edit.text())
if __name__ == '__main__':
app = QApplication(sys.argv)
win = Window()
sys.exit(app.exec_())
I am getting this error:
AttributeError: 'Window' object has no attribute 'game_name'
Why?
have a feeling its something simple rather than something more complicated but maybe I'm wrong
Please help!!!
Thank you!
You call the createGridLayout method earlier than you define the variables used in it.
import sys
from PyQt5.QtWidgets import (QApplication, QWidget, QHBoxLayout,
QVBoxLayout, QPushButton, QLabel, QLineEdit,
QGridLayout, QGroupBox, QDialog)
from PyQt5.QtCore import pyqtSlot
class Window(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle("Project PiBu!!")
# self.createGridLayout()
# self.windowLayout = QVBoxLayout()
# self.windowLayout.addWidget(self.horizontalGroupBox)
# self.setLayout(self.windowLayout)
self.game_name = QLabel("Game Name:", self)
self.game_line_edit = QLineEdit(self)
self.search_button = QPushButton("Search", self)
self.search_button.clicked.connect(self.on_click)
self.game = QLabel(self)
self.createGridLayout() # < --
self.windowLayout = QVBoxLayout() # < --
self.windowLayout.addWidget(self.horizontalGroupBox) # < --
self.setLayout(self.windowLayout) # < --
self.show()
def createGridLayout(self):
self.horizontalGroupBox = QGroupBox()
self.layout = QGridLayout()
self.layout.setColumnStretch(1, 4)
self.layout.setColumnStretch(2, 4)
# AttributeError: 'Window' object has no attribute 'game_name'
self.layout.addWidget(self.game_name, 0, 0)
self.layout.addWidget(self.game_line_edit, 0, 1)
self.layout.addWidget(self.search_button, 0, 2)
self.layout.addWidget(self.game, 1, 0)
self.horizontalGroupBox.setLayout(self.layout) # +++ self.
#pyqtSlot()
def on_click(self):
self.game.setText(self.game_line_edit.text())
if __name__ == '__main__':
app = QApplication(sys.argv)
win = Window()
sys.exit(app.exec_())

Reset QProgressBar for another run

I have a ProgressBar for downloading data.
Sometimes the task can timeout and if so I want to be able to restart the process.
Task is started
Timer is started
if the timer finishes, end the task
press the button to restart the task
All of the above works fine except for the progressbar does not start again.
I can reset it to 0 but it will not move (before that it was stuck at 100%!)
If the task finishes before the timer runs out, the progress bar works fine again.
How can I restart the progressbar if the timer runs out before the task?
import sys
from PyQt5.QtWidgets import QWidget, QApplication, QProgressBar, QPushButton
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class DownloadThread(QThread):
taskFinished = pyqtSignal()
def __init__(self, parent=None):
super(DownloadThread, self).__init__(parent)
def run(self):
for i in range(20000):
print(i)
self.taskFinished.emit()
class Example(QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
# Set Progress Bard
self.progressBar = QProgressBar(self)
self.progressBar.setGeometry(30, 40, 200, 25)
# Button
self.btn = QPushButton('Start', self)
self.btn.move(40, 80)
self.btn.clicked.connect(self.start_download_thread)
# Set Timer
self.timer = QBasicTimer()
self.step = 0
# Display
self.setGeometry(300, 300, 280, 170)
self.show()
def start_download_thread(self):
# Set Progress bar to 0%
self.progressBar.setValue(0)
#Start Thread
self.Download = DownloadThread()
self.Download.taskFinished.connect(self.onDownloaded)
self.onDownloading()
self.Download.start()
def onDownloading(self):
#start the timer
self.progressBar.show()
self.timerSwitch()
def timerSwitch(self):
# Turn timer off or on
if self.timer.isActive():
self.timer.stop()
else:
self.timer.start(2, self)
def onDownloaded(self):
# Stop the timer
self.timerSwitch()
# progress bar 100%
self.progressBar.setValue(1)
self.progressBar.setRange(0, 1)
def timerEvent(self, e):
if self.step >= 100:
self.timer.stop()
# self.Download.quit()
return
self.step = self.step + 1
self.progressBar.setValue(self.step)
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I solved this by moving the Qtimer() step into the start_download_thread
def start_download_thread(self):
self.step = 0 # reset the steps to 0
This means it resets the steps and the progress bar starts again.
import sys
from PyQt5.QtWidgets import QWidget, QApplication, QProgressBar, QPushButton
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class DownloadThread(QThread):
taskFinished = pyqtSignal()
def __init__(self, parent=None):
super(DownloadThread, self).__init__(parent)
def run(self):
for i in range(20000):
print(i)
self.taskFinished.emit()
class Example(QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
# Set Progress Bard
self.progressBar = QProgressBar(self)
self.progressBar.setGeometry(30, 40, 200, 25)
# Button
self.btn = QPushButton('Start', self)
self.btn.move(40, 80)
self.btn.clicked.connect(self.start_download_thread)
# Set Timer
self.timer = QBasicTimer()
# Display
self.setGeometry(300, 300, 280, 170)
self.show()
def start_download_thread(self):
self.step = 0
# Set Progress bar to 0%
self.progressBar.setValue(0)
#Start Thread
self.Download = DownloadThread()
self.Download.taskFinished.connect(self.onDownloaded)
self.onDownloading()
self.Download.start()
def onDownloading(self):
#start the timer
self.progressBar.show()
self.timerSwitch()
def timerSwitch(self):
# Turn timer off or on
if self.timer.isActive():
self.timer.stop()
else:
self.timer.start(2, self)
def onDownloaded(self):
# Stop the timer
self.timerSwitch()
# progress bar 100%
self.progressBar.setValue(100)
self.progressBar.setRange(0, 100)
def timerEvent(self, e):
if self.step >= 100:
self.timer.stop()
return
self.step = self.step + 1
self.progressBar.setValue(self.step)
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Resources