Pyside progressbar cannot refresh - pyqt

This is the sample code using Pyside, to show a progressbar on button click. The problem is that after the button click, GUI freezes until the task is completed(here 5 seconds). Is there any way to overcome this ?
import sys
import time
try:
from PySide import QtCore
from PySide import QtGui
except ImportError:
from PyQt4 import QtCore
from PyQt4 import QtGui
class DemoBtn(QtGui.QWidget):
def __init__(self):
super(DemoBtn, self).__init__()
x, y, w, h = 500, 400, 400, 200
self.setGeometry(x, y, w, h)
x, y, w, h = 30, 10, 96, 32
btn = QtGui.QPushButton("Push button", self)
self.pbar = QtGui.QProgressBar(self)
btn.setGeometry(x, y, w, h)
btn.clicked.connect(self._btn_cb)
self.pbar.setGeometry(30, 50, 200, 25)
def _btn_cb(self):
self.pbar.setMaximum(0)
self.pbar.setMinimum(0)
time.sleep(5) # some time consuming task here
print "clicked"
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
demo = DemoBtn()
demo.show()
sys.exit(app.exec_())
Edit 1:
It seems like the problem is only with the indefinite progressbar (busy progressbar).

Related

PyQt issue with animating rotation

I experiencing some issue while trying to understand how animate a qgraphicItem in Pyqt.
I'm trying to animate a qgraphicsItem inside a scene, but when the animation starts all the objects rotate. Is there someone who can help me?
here is my code.
from PyQt6.QtGui import *
from PyQt6.QtCore import *
from PyQt6.QtWidgets import *
class GraphicsView(QGraphicsView):
def __init__(self, parent=None):
super(GraphicsView, self).__init__(parent)
self.setRenderHints(QPainter.RenderHint.Antialiasing
| QPainter.RenderHint.TextAntialiasing | QPainter.RenderHint.SmoothPixmapTransform)
self.setViewportUpdateMode(QGraphicsView.ViewportUpdateMode.FullViewportUpdate)
self.scene = QGraphicsScene(QRectF(-250, -250, 1000, 1000), self)
self.setScene(self.scene)
self.graphicItem = QGraphicsRectItem(0, 0, 100, 100)
self.graphicItem.setZValue(10)
self.graphicItem.setPos(100, 100)
self.scene.addItem(self.graphicItem)
self.scene.addRect(self.sceneRect(), brush=QBrush(Qt.GlobalColor.gray))
self.animateRotation()
def rot(self, angle: QVariant) -> None:
self.rotate(self.graphicItem.rotation() - angle)
self.graphicItem.setRotation(angle)
#pyqtSlot()
def animateRotation(self):
self.animation = QVariantAnimation(self)
self.animation.setStartValue(QVariant(0))
self.animation.setEndValue(QVariant(180))
self.animation.setDuration(10000)
self.animation.start()
self.animation.valueChanged.connect(self.rot)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = GraphicsView()
w.resize(720, 720)
w.show()
sys.exit(app.exec())

QObject: Cannot create children for a parent that is in a different thread. (Multithreading pyqt5)

I am trying to edit the text in 'QTextEdit' but I got the error which is in the title above. I know that I shouldn't edit the widgets in the main app from another thread and I searched for some answers, But I didn't get the full answer that I want
This is a sample that produce the same error that I have
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5 import QtCore
import sys
import time
import threading
class Main(QMainWindow):
def __init__(self):
super().__init__()
self.move(50, 220)
self.resize(500, 500)
self.text_edit = QTextEdit(self)
self.text_edit.resize(200, 200)
self.btn = QPushButton('start', self)
self.btn.move(10, 250)
self.btn.clicked.connect(lambda x : self.thread_creator(self.test))
self.btn2 = QPushButton('print', self)
self.btn2.move(10, 290)
self.btn2.clicked.connect(lambda x : print('dad'))
def test(self):
for i in range(99):
self.text_edit.setText(f'dad {i}')
time.sleep(0.1)
def thread_creator(self, function):
self.thread = threading.Thread(target=function)
self.thread.start()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Main()
ex.show()
sys.exit(app.exec_())

Kivy strange behavior when it updates Image Texture

I am trying to update the Kivy Image.texture with the OpenCV image, and I am using a new thread to do it. I found some discussion that "the graphics operation should be in the main thread". But still, I want to figure out why the code below works.
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.graphics.texture import Texture
import cv2
from threading import Thread
class MainApp(App):
def __init__(self):
super().__init__()
self.layout = FloatLayout()
self.image = Image()
self.layout.add_widget(self.image)
Thread(target=self.calculate).start()
def build(self):
return self.layout
def calculate(self):
img = cv2.imread("test.png")
w, h = img.shape[1], img.shape[0]
img = cv2.flip(img, flipCode=0)
buf = img.tostring()
texture = Texture(0, 0, 0).create(size=(w, h), colorfmt="bgr")
texture.blit_buffer(buf, colorfmt='bgr', bufferfmt='ubyte')
self.image.texture = texture
def main():
Image(source='test.png') # remove this line will froze the app
MainApp().run()
if __name__ == "__main__":
main()
If I remove this line:
Image(source='test.png')
the app is frozen. Can someone help me to understand why decalring an Image object outside the main loop will affect the MainApp running.
The "test.png" image can be any simple image, like below. You need to put the image in the same directory as this script.
Not sure exactly why that line affects your code, but the main problem is that not only is the GUI manipulation required to be done on the main thread, but also any blit_buffer() must also be done on the main thread. So, a working version of your code (with the Image() removed) looks like this:
from functools import partial
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.graphics.texture import Texture
import cv2
from threading import Thread
class MainApp(App):
def __init__(self):
super().__init__()
self.layout = FloatLayout()
self.image = Image()
self.layout.add_widget(self.image)
Thread(target=self.calculate).start()
def build(self):
return self.layout
def calculate(self):
img = cv2.imread("test.png")
w, h = img.shape[1], img.shape[0]
img = cv2.flip(img, flipCode=0)
buf = img.tostring()
Clock.schedule_once(partial(self.do_blit, buf, w, h))
def do_blit(self, buf, w, h, dt):
texture = Texture(0, 0, 0).create(size=(w, h), colorfmt="bgr")
texture.blit_buffer(buf, colorfmt='bgr', bufferfmt='ubyte')
self.image.texture = texture
def main():
# Image(source='test.png') # remove this line will froze the app
MainApp().run()
if __name__ == "__main__":
main()

Set scroll area initially moved

I need to create a big widget inside a scroll area and initially set both sliders in the middle of the bar. The scroll bar does not work, the widgets are not well connected I think.
MRE:
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget
import sys
class Diedrico(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
# Draws stuff
class UiVentana(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(UiVentana, self).__init__(parent)
ventana.resize(1500, 1015)
ventana.setFixedSize(1500, 1015)
self.widget_central = QtWidgets.QWidget(ventana)
scrol = QtWidgets.QScrollArea(self.widget_central)
scrol.setWidgetResizable(True)
scrol.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
scrol.setGeometry(QtCore.QRect(1010, 510, 470, 460))
self.Diedrico = Diedrico(scrol)
self.Diedrico.setGeometry(QtCore.QRect(0, 0, 1000, 1000))
# This widget should be big enough to use the scroll bar, but it does not work
ventana.setCentralWidget(self.widget_central)
ventana.show()
if __name__ == "__main__":
app = QtWidgets.QApplication([])
ventana = QtWidgets.QMainWindow()
ui = UiVentana()
sys.exit(app.exec_())
It seems that the problem comes from scrol.setWidgetResizable(True), which seems to resize the content... setting this to False worked for me.
Also, to center the scrollbar, there are a few options, like setting the value of the verticalScrollBar or using ensureVisible(x, y).
A working solution:
from PyQt5 import QtCore, QtWidgets
import sys
class UiVentana(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(UiVentana, self).__init__(parent)
self.setupUi()
self.label.setGeometry(QtCore.QRect(10, 0, 282, 331))
self.label.setText("this is a long text\n" * 100)
self.scrollArea.verticalScrollBar().setValue(300)
def setupUi(self):
self.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(self)
self.scrollArea = QtWidgets.QScrollArea(self.centralwidget)
self.scrollArea.setGeometry(QtCore.QRect(470, 330, 301, 211))
self.scrollArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.scrollArea.setWidgetResizable(False)
self.scrollAreaWidgetContents = QtWidgets.QWidget()
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 282, 10000))
self.label = QtWidgets.QLabel(self.scrollAreaWidgetContents)
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
self.setCentralWidget(self.centralwidget)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
ui = UiVentana()
ui.show()
sys.exit(app.exec_())

How to reserve space for hidden QtWaitingSpinner in QGridLayout?

I'm trying to build a QtGui application that uses the QtWaitingSpinner found here: https://github.com/z3ntu/QtWaitingSpinner. I have it in a QGridLayout. However, this means that the button next to it changes size when the spinner is unhidden and started. How do I reserve the correct amount of space for the spinner in the grid so that the button next to it stays a constant size regardless of whether the spinner is shown?
Based on this stackoverflow post How to use spacers in Qt, I suspect that the answer involves QSpacerItem. However, I can't figure out how to size the QSpacerItem based on the size the spinner will need.
Here's a minimal example of the code to show my problem:
import PyQt5.QtWidgets as QWidgets
import PyQt5.QtCore as QtCore
import PyQt5.QtGui as QtGui
from waitingspinnerwidget import QtWaitingSpinner
import sys
class Example_Window(QWidgets.QWidget):
def __init__(self):
super(QWidgets.QWidget,self).__init__()
self.initUI()
def initUI(self):
self.button=QWidgets.QPushButton("Start/Stop Spinner")
self.button.clicked.connect(self.toggle_spinner)
self.spinner = QtWaitingSpinner(self,centerOnParent=False)
self.grid = QWidgets.QGridLayout()
self.grid.addWidget(self.button,0,0)
self.grid.addWidget(self.spinner,0,1)
self.setLayout(self.grid)
self.show()
def toggle_spinner(self):
if self.spinner.isSpinning():
self.spinner.stop()
else:
self.spinner.start()
if __name__ == '__main__':
app = QWidgets.QApplication([])
main = Example_Window()
sys.exit(app.exec())
Try it:
import PyQt5.QtWidgets as QWidgets
import PyQt5.QtCore as QtCore
import PyQt5.QtGui as QtGui
from waitingspinnerwidget import QtWaitingSpinner
import sys
class Example_Window(QWidgets.QWidget):
def __init__(self):
super(QWidgets.QWidget,self).__init__()
self.initUI()
def initUI(self):
self.button = QWidgets.QPushButton("Start Spinner") # +
self.button.clicked.connect(self.toggle_spinner)
self.spinner = QtWaitingSpinner(self, centerOnParent=False)
self.grid = QWidgets.QGridLayout()
self.grid.addWidget(self.button, 0, 0)
# self.grid.addWidget(self.spinner,0,1) # ---
self.grid.addWidget(self.spinner, 0, 1, 1, 2) # +++ <---
self.setLayout(self.grid)
self.show()
def toggle_spinner(self):
if self.spinner.isSpinning():
self.spinner.stop()
self.button.setText("Start Spinner") # +
else:
self.spinner.start()
self.button.setText("Stop Spinner") # +
if __name__ == '__main__':
app = QWidgets.QApplication([])
main = Example_Window()
main.resize(170, 70) # +++
sys.exit(app.exec())

Resources