code it's clean from bugs but when is running program will freezing
the program its countdown using button to start the countdown
the picture below discripe the layout
enter image description here
the probelm is im using loop to changing label text and that make program freezed
from time import sleep
import PyQt5.QtWidgets as Qtw
class MainWindow(Qtw.QWidget):
def __init__(self):
super().__init__()
self.setLayout(Qtw.QVBoxLayout())
pushButton = Qtw.QPushButton("start",clicked = lambda: setting_label())
self.layout().addWidget(pushButton)
my_label = Qtw.QLabel("00:00:00")
self.layout().addWidget(my_label)
self.show()
def setting_label():
t = 1200
while t:
h = t // 3600
m = t // 60
s = t % 60
timer = "{:02d}:{:02d}:{:02d}".format(h,m,s)
my_label.setText(timer)
sleep(1)
t -= 1
app = Qtw.QApplication([])
window = MainWindow()
app.exec_()
The way the code is written in the OP, it doesn't really get stuck or frozen. But rather, the display fails to update. You can get around this by trying to force Qt to update the GUI with app.processEvents(). Put it in your setting_label function after the setText():
self.my_label.setText(timer)
app.processEvents()
The Preferred Way
Using app.processEvents() a lot is usually discouraged. Another way to make a countdown timer is to use a QTimer. There is a little more overhead in setting up all of the signals and slots. But it can be very powerful. Here is an example
from time import sleep
import PyQt5.QtWidgets as Qtw
from PyQt5.QtCore import QTimer
class MainWindow(Qtw.QWidget):
def __init__(self):
super().__init__()
self.setLayout(Qtw.QVBoxLayout())
pushButton = Qtw.QPushButton("start",clicked = self.start_countdown)
self.layout().addWidget(pushButton)
self.my_label = Qtw.QLabel("00:00:00")
self.layout().addWidget(self.my_label)
self.timer = QTimer() # create a new QTimer instance
self.timer.setInterval(1000) # make it fire every 1000 msec
self.t = 1200
self.timer.timeout.connect(self.setting_label) # connect the timeout signal to self.setting_label
self.show()
def start_countdown(self):
self.t = 1200
self.timer.start()
def setting_label(self):
if self.t == 0:
self.timer.stop()
print('timer stopped')
h = self.t // 3600
m = self.t // 60
s = self.t % 60
timer = "{:02d}:{:02d}:{:02d}".format(h,m,s)
self.my_label.setText(timer)
self.t -= 1
app = Qtw.QApplication([])
window = MainWindow()
app.exec_()
Edit: This solution works for the OP, but others have pointed out that doing this using threading can cause unexpected behavior, crashes, etc. so it's better to do this using other methods (for example: this answer)
The interface freezes because the while loop you use to change the label blocks the event loop of the app (i.e. receiving inputs). You can fix this by moving the function to a thread like this:
import threading
from time import sleep
import PyQt5.QtWidgets as Qtw
class MainWindow(Qtw.QWidget):
def __init__(self):
super().__init__()
self.setLayout(Qtw.QVBoxLayout())
def setting_label():
t = 1200
while t:
h = t // 3600
m = t // 60
s = t % 60
timer = "{:02d}:{:02d}:{:02d}".format(h,m,s)
my_label.setText(timer)
sleep(1)
t -= 1
pushButton = Qtw.QPushButton(
"start",
clicked=lambda: thread.Thread(target=setting_label).start()
)
self.layout().addWidget(pushButton)
my_label = Qtw.QLabel("00:00:00")
self.layout().addWidget(my_label)
self.show()
app = Qtw.QApplication([])
window = MainWindow()
app.exec_()
Related
I want my program to show another window where it says that it's the break time when the determined requirements are met (in this case the exact same hour) but when I run it, the first window (not the login one) crashes or simply doesn't show up until it is the hour.
(horatime and min can be changed for testing it)
main and main2 are windows programed in PyCharm they are clocks.
I want to show main window until the time set in the code ("timecheck") is reached then "timecheck" will hide main and show main2
this is my code:
'''
from PyQt5 import QtWidgets, uic
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import QTimer, QTime
from PyQt5.uic import loadUi
import datetime
app = QtWidgets.QApplication([])
login = uic.loadUi("ventana1.ui")
time = uic.loadUi("ventana2.ui")
error = uic.loadUi("ventana3.ui")
def gui_login():
name = login.lineEdit.text()
password = login.lineEdit_2.text()
if len(name)==0 or len(password)==0:
login.label_5.setText("Ingrese todos los datos")
elif name == "Pedro" and password == "1234":
gui_clock()
timecheck()
else:
gui_error()
def gui_clock():
login.hide()
main.show()
def timecheck():
horatime = int(19)
min = int(21)
while True:
if horatime == datetime.datetime.now().hour and min == datetime.datetime.now().minute:
main.hide()
main2.show()
break
#this is the clock1, it shows the time in red
class VentanaPrincipal(QMainWindow):
def __init__(self):
super(VentanaPrincipal, self).__init__()
loadUi('ventana2.ui', self)
timer = QTimer(self)
timer.timeout.connect(self.displayTime)
timer.start(1000)
def displayTime(self):
currentTime = QTime.currentTime()
displayText = currentTime.toString('hh:mm:ss')
self.reloj.setText(displayText)
self.lcdNumber.display(displayText)
#this is the clock2, it shows the time in green
class VentanaPrincipal2(QMainWindow):
def __init__(self2):
super(VentanaPrincipal2, self2).__init__()
loadUi('ventana4.ui', self2)
timer2 = QTimer(self2)
timer2.timeout.connect(self2.displayTime)
timer2.start(1000)
def displayTime(self):
currentTime2 = QTime.currentTime()
displayText2 = currentTime2.toString('hh:mm:ss')
self.reloj.setText(displayText2)
self.lcdNumber.display(displayText2)
def gui_error():
login.hide()
error.show()
def back_error():
error.hide()
login.show()
login.pushButton.clicked.connect(gui_login)
error.pushButton.clicked.connect(regresar_error)
login.show()
main = VentanaPrincipal()
main2 = VentanaPrincipal2()
app.exec()
i tried a lot of things to change the loop to show what i want, but im just an amateur on programming so i cant find a way to make my programm to do what i want
I am running into the issue that creating a PySide window takes quite a bit of time (talking about several seconds here). I have a window and it contains lots of sub widgets. Each sub widget itself is build of a QWidget and contains several widgets. When running the following code and logging the time it takes to generate the widget, it turns out that this is quite time consuming. As an example, building a window with 100 sub widgets takes around 0.6 seconds and 1000 takes around 6 seconds. So it feels that the number of subwidgets to create increases the time to build the window linearly.
Is there any tip to hopefully reduce the time to create the window heavily? Or at least a bit?
Would it be possible to pickle the widgets so that they need to be created once and can be used the next time the window gets created?
Here is the code:
import sys
import time
from PySide2 import QtWidgets
from PySide2 import QtCore
# Adjust this number to increase/ decrease the number of sub widgets being
# created.
NUMBER_ELEMENTS = 1000
class Main(QtWidgets.QWidget):
def __init__(self):
super(Main, self).__init__()
self.setMinimumSize(QtCore.QSize(1000, 700))
self.table = QtWidgets.QTableWidget()
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.table)
self.setLayout(self.layout)
self.table.setColumnCount(1)
self.table.setRowCount(NUMBER_ELEMENTS)
for row in range(NUMBER_ELEMENTS):
widget = SubWidget()
self.table.setCellWidget(row, 0, widget)
self.table.resizeColumnsToContents()
class SubWidget(QtWidgets.QWidget):
def __init__(self):
super(SubWidget, self).__init__()
self.label_name = QtWidgets.QLabel("My name")
self.label_format = QtWidgets.QLabel("my format")
self.label_frames = QtWidgets.QLabel("XXX")
self.label_type = QtWidgets.QLabel("some type")
self.label_size = QtWidgets.QLabel("some size")
self.push_favorite = QtWidgets.QPushButton("hello")
self.label_comment = QtWidgets.QTextEdit("something")
self.label_comment.setMaximumHeight(80)
self.label_comment.setProperty("style", "comment")
self.video = QtWidgets.QLabel("my video")
self.layout_main = QtWidgets.QHBoxLayout()
self.layout_main.addWidget(self.video, 17)
self.layout_main.addWidget(self.label_name, 20)
self.layout_main.addWidget(self.label_format, 10)
self.layout_main.addWidget(self.label_frames, 10)
self.layout_main.addWidget(self.label_type, 10)
self.layout_main.addWidget(self.label_size, 10)
self.layout_main.addWidget(self.push_favorite, 15)
self.layout_main.addWidget(self.label_comment, 50)
self.setLayout(self.layout_main)
if __name__ == "__main__":
start = time.time()
app = QtWidgets.QApplication(sys.argv)
main_window = Main()
main_window.raise_()
main_window.show()
print "Duration to build window: ", time.time() - start
app.exec_()
The following code draw single random lines every merry one second. What I would like to do is to keep each line already drawn. What is the best way to do that ?
I know that I need to use a QTimer to do a responsive user interface but first I need to know how to draw more and more lines...
Maybe one way would be to draw all lines hidden and to show more and more lines... Or must I use a QGraphicsView ?
from random import random
import sys
from time import sleep
from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5.QtGui import QPainter
from PyQt5.QtCore import QTimer
LINES = [
(500*random(), 500*random(), 500*random(), 500*random())
for _ in range(50)
]
class Interface(QWidget):
def __init__(self):
super().__init__()
self.max = len(LINES)
self.cursor = 0
self.painter = QPainter()
self.setFixedSize(500, 500)
self.show()
def paintEvent(self, e):
self.painter.begin(self)
self.drawsetpbystep()
self.painter.end()
def drawsetpbystep(self):
if self.cursor < self.max:
self.painter.drawLine(*LINES[self.cursor])
self.update()
sleep(0.25)
self.cursor += 1
if __name__ == '__main__':
app = QApplication(sys.argv)
interface = Interface()
sys.exit(app.exec_())
It is not recommended to use sleep in a GUI, and in the case of PyQt it is very dangerous, because Qt offers alternatives to create the same effect as QTimer, QEventLoop, etc.
Another error is that the QPainter has a very large life cycle, it should only be created and called in paintEvent.
And the last mistake is wanting to pause the task of paintEvent since you're doing it through the drawsetpbystep method. the paintEvent method not only will you use it but actually uses the application whenever you need it, the right thing to do is use a flag to indicate when you should paint as shown below:
LINES = [
(500*random(), 500*random(), 500*random(), 500*random())
for _ in range(50)
]
class Interface(QWidget):
def __init__(self):
super().__init__()
self.max = len(LINES)
self.cursor = 0
self.show()
self.paint = False
timer = QTimer(self)
timer.timeout.connect(self.onTimeout)
timer.start(250)
def paintEvent(self, e):
painter = QPainter(self)
if self.paint:
self.drawsetpbystep(painter)
def onTimeout(self):
self.paint = True
self.update()
def drawsetpbystep(self, painter):
if self.cursor < self.max:
painter.drawLine(*LINES[self.cursor])
self.cursor += 1
self.paint = False
if __name__ == '__main__':
app = QApplication(sys.argv)
interface = Interface()
sys.exit(app.exec_())
Using time.sleep in PyQt applications is not recommended because it blocks execution of the Qt event loop which is responsible for handling user input (via keyboard and mouse) and actually drawing the application window.
Instead, you should use QTimer to schedule execution of a specified method at the times you want. In this case, you probably want to use multiple QTimer.singleShot calls. Likely the first method called by the timer will draw one point/line and then set up a timer to call another method which will draw one point/line and set up a timer to call another method...etc. etc.
I have the following PySide application, where the intended functionality is to have the text of the number_button be updated every 5 seconds, counting from 0 to 9 once the start_button has been pressed.
import sys
from PySide import QtGui, QtCore
class Example(QtGui.QWidget):
def __init__(self, parent=None):
super(Example, self).__init__(parent)
self.app_layout = QtGui.QVBoxLayout()
self.setLayout(self.app_layout)
self.setGeometry(300, 300, 50, 50)
self.count_to = 10
self.delay = 5000
self.timer = QtCore.QTimer(self)
self.timer.setSingleShot(True)
# start button
start_button = QtGui.QPushButton()
start_button.setText('START')
start_button.clicked.connect(self.startCount)
self.app_layout.addWidget(start_button)
# number button
self.number_button = QtGui.QPushButton()
self.number_button.setText('0')
self.app_layout.addWidget(self.number_button)
def startCount(self):
def updateButtonCount():
self.number_button.setText("%s" % count)
for count in range(0, self.count_to):
self.timer.singleShot(self.delay, updateButtonCount)
def main():
app = QtGui.QApplication(sys.argv)
example = Example()
example.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
However, this results in a 9 appearing after 6 seconds without the intermediate numbers showing at all. I'm pretty sure that the problem is that while the .singleShot is running, count has already been incremented to its maximum value (9).
I can think of a few hack to make this work as intended, but I'd like to fix it in the most effective and appropriate way.
As mentioned in the QTimer PySide documentation, what you need is a QTimer that will repeatedly time out (every 5 seconds in your case) and call the function updateButtonCount once for every timeout - as mentioned by aruisdante. Take a look at this:
timer = QTimer() # set up your QTimer
timer.timeout.connect(self.updateButtonCount) # connect it to your update function
timer.start(5000) # set it to timeout in 5000 ms
With some modifications, the previous code should help you achieve the functionality you want. Keep in mind, timer.start(5000) only sets up one timeout to occur in 5000 ms, or 5 seconds, and your updateButtonCount function should include this line at the end if the QTimer is to timeout again.
I hope that helps. Feel free to comment if something is not clear.
the following code counts a bunch of PyQt4 progressbar's up tp 99%, I would like for my GUI NOT to freeze as they count up to 99%. I would LOVE to do this without custom classes or functions if it is possible.
I know it is good to use classes but for this tiny snippet of code I don't want to create a class.
From what I've read there may be a update() function that could accomplish this ... please advise If I'm on the right track
import sys
import time
from PyQt4 import QtGui
from PyQt4 import QtCore
app = QtGui.QApplication(sys.argv)
widget = QtGui.QWidget()
widget.resize(400, 200)
widget.setWindowTitle('simple')
widget.show()
shift = 0
cntControl = 5
barra = [0] * cntControl
for i in range(cntControl):
shift = shift + 10
barra[i] = QtGui.QProgressBar(widget)
barra[i].show()
barra[i].setMinimum(0)
barra[i].setMaximum(10000)
barra[i].setGeometry(QtCore.QRect(10, shift, 200, 10))
for a in range(10000):
for i in range(cntControl):
barra[i].setValue(a)
sys.exit(app.exec_())
try changing your for loop with:
while True:
for a in range(10000):
time.sleep(0.0001)
for i in range(cntControl):
barra[i].setValue(a)
if works for me.
The while loop continues endlessly moving the bar. If you are looking only to clean the bar after it reaches the end you should use reset:
PySide.QtGui.QProgressBar.reset()
Reset the progress bar. The progress bar “rewinds” and shows no
progress
Update after OP comments: If you want your gui to be responsive when entering a long loop or other operation you should use either python thread module or QThreads.
I really could not get threads to work at all ... I could post my thread attempt which looks flawless to my (now tired) eyes ...
I have however been able to tweek http://zetcode.com/tutorials/pyqt4/widgets/ 's progressbar example and came out with the following code ... which solves the problem of freezing in the GUI:
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.setWindowTitle('ProgressBar')
self.setGeometry(300, 300, 250, 150)
self.pbar = [0] * 3
self.timer = [0] * 3
self.step = [0] * 3
self.shift = 0
for i in range(3):
self.shift = self.shift + 30
self.pbar[i] = QtGui.QProgressBar(self)
self.pbar[i].setGeometry(30, self.shift, 200, 25)
self.timer[i] = QtCore.QBasicTimer()
self.step[i] = 0
self.timer[i].start(100, self)
def timerEvent(self, event):
for i in range(3):
if self.step[i] >= 100:
self.timer[i].stop()
return
self.step[i] = self.step[i] + 1
self.pbar[i].setValue(self.step[i])
app = QtGui.QApplication(sys.argv) ex
= Example() ex.show() app.exec_()
I have NO IDEA why it works which is probably not a good thing. I'm guessing it might have something to do with super(Example, self).__init__() and the custom timer pyqt4 uses. I was really hoping to do this without functions or classes, but not sure that is possible. If you think it is, feel free to post!