I am setting the Initial Movie File like this:
self.movie = QMovie("dotGreenBlack.gif", QByteArray(), self)
At runtime, i want to change it by setting:
self.movie.setFileName("dotGreyStatic.gif")
But this leads to frame-freeze of the primarly set file "dotGreenBlack.gif"
Anything else i have to do to change the Gif-Animation on runtime?
Here is the fullcode:
from PyQt4 import QtCore, QtGui
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys
class ImagePlayer(QWidget):
def __init__(self, filename, title, parent=None):
QWidget.__init__(self, parent)
# Load the file into a QMovie
self.movie = QMovie(filename, QByteArray(), self)
size = self.movie.scaledSize()
self.setGeometry(200, 200, size.width(), size.height())
self.setWindowTitle(title)
self.movie_screen = QLabel()
# Make label fit the gif
self.movie_screen.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.movie_screen.setAlignment(Qt.AlignCenter)
# Create the layout
main_layout = QVBoxLayout()
main_layout.addWidget(self.movie_screen)
self.setLayout(main_layout)
# Add the QMovie object to the label
self.movie.setCacheMode(QMovie.CacheAll)
self.movie.setSpeed(100)
self.movie_screen.setMovie(self.movie)
self.movie.start()
self.movie.loopCount()
self.movie.setFileName("dotGreyStatic.gif")
self.movie_screen.setMovie(self.movie)
if __name__ == "__main__":
gif = "dotGreenBlack.gif"
app = QApplication(sys.argv)
app.setStyleSheet("QWidget { background-color: black }")
player = ImagePlayer(gif, "was")
player.show()
sys.exit(app.exec_())
Gif Icons used in this Example:
You just need to stop and restart the movie:
self.movie.stop()
self.movie.setFileName("dotGreyStatic.gif")
self.movie.start()
Related
I'm using QT (PySide) to view PDFs (using the PyMuPDF library) but when I resize I get a shearing artifact.
Like this:
Here is a minimal example:
import sys
import fitz
from PySide6.QtWidgets import QApplication, QLabel, QMainWindow, QScrollArea
from PySide6.QtGui import QPixmap, QImage
from PySide6.QtCore import Qt
class PDFWindow(QMainWindow):
def __init__(self, filename):
super().__init__()
# Open the PDF and get the first page
self.pdf_doc = fitz.open(filename)
self.page = self.pdf_doc[0]
# Create a QLabel to hold the rendered page
self.label = QLabel()
self.label.setAlignment(Qt.AlignCenter)
# Create a QScrollArea to hold the label
self.scroll_area = QScrollArea()
self.scroll_area.setWidget(self.label)
self.scroll_area.setWidgetResizable(True)
self.setCentralWidget(self.scroll_area)
# Render the page and set it as the pixmap for the label
self.render_page()
# Connect the windowResized signal to the render_page function
self.resizeEvent = self.render_page
def render_page(self, event=None):
zoom = 1
if event is not None:
bound = self.page.bound()
page_width, page_height = bound.width, bound.height
zoom = min(event.size().width() / page_width,
event.size().height() / page_height)
pixmap = self.page.get_pixmap(matrix=fitz.Matrix(zoom, zoom),
colorspace='rgb')
image = QImage(pixmap.samples, pixmap.width, pixmap.height,
QImage.Format_RGB888)
cur_pixmap= QPixmap.fromImage(image)
self.label.setPixmap(cur_pixmap)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = PDFWindow("example.pdf")
window.show()
sys.exit(app.exec())
I want do dynamically change the layout in Qt. For example, I want to change the QHBoxLayout to QVBoxLayout through a button. My test code is:
from PyQt5.QtWidgets import *
import sys
class SubWidget(QWidget):
def __init__(self):
super().__init__()
self.lay = QHBoxLayout()
self.label1 = QLabel('left')
self.label2 = QLabel('right')
self.lay.addWidget(self.label1)
self.lay.addWidget(self.label2)
self.setLayout(self.lay)
def change(self):
self.lay.removeWidget(self.label1)
self.lay.removeWidget(self.label2)
self.lay = QVBoxLayout()
self.setLayout(self.lay)
self.lay.addWidget(self.label2)
self.lay.addWidget(self.label1)
class Widget(QWidget):
def __init__(self):
super().__init__()
lay = QVBoxLayout()
self.btn = QPushButton('change layout')
self.btn.clicked.connect(self.btnClick)
self.subWidget = SubWidget()
lay.addWidget(self.btn)
lay.addWidget(self.subWidget)
self.setLayout(lay)
def btnClick(self, check=False):
self.subWidget.change()
if __name__ == '__main__':
app = QApplication(sys.argv)
win = Widget()
win.show()
app.exec_()
The code output GUI is:
And I hope it change to the following picture after click change layout button:
Any suggestion is appreciated~~~
The point of the solution is
Make sure old layout is deleted, included both python wrapping reference and core Qt object, that's for deleteLater() is used.
Make sure new layout is assigned strictly after old was deleted, that's for need to use destroyed()-switchlayout() signal-slot chain.
I reproduced example with PySide6 (don't forget to switch on your version of PyQt or PySide package):
# from PyQt5.QtWidgets import *
from PySide6.QtWidgets import *
import sys
class SubWidget(QWidget):
def __init__(self):
super().__init__()
self.lay = QVBoxLayout()
self.label1 = QLabel('left')
self.label2 = QLabel('right')
self.lay.addWidget(self.label1)
self.lay.addWidget(self.label2)
self.setLayout(self.lay)
def change(self):
self.lay.removeWidget(self.label1)
self.lay.removeWidget(self.label2)
self.lay.deleteLater()
self.lay.destroyed.connect(self.switchlayout)
def switchlayout(self):
# print("***destroyed")
self.lay = QHBoxLayout()
self.lay.addWidget(self.label2)
self.lay.addWidget(self.label1)
self.setLayout(self.lay)
self.adjustSize()
class Widget(QWidget):
def __init__(self):
super().__init__()
lay = QVBoxLayout()
self.btn = QPushButton('change layout')
self.btn.clicked.connect(self.btnClick)
self.subWidget = SubWidget()
lay.addWidget(self.btn)
lay.addWidget(self.subWidget)
self.setLayout(lay)
def btnClick(self, check=False):
self.subWidget.change()
if __name__ == '__main__':
app = QApplication(sys.argv)
win = Widget()
win.show()
app.exec_()
I tried using the Qt documentation example to restrict the rectangle to the area of the scene but it still fails, someone has an alternative to do this?
My code, the QGraphicsView instance was created in Qt Desginer:
# -*- coding: utf-8 -*-
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
from screen import *
class MovableItem(QGraphicsRectItem):
def __init__(self, rectang, *args, **kwargs):
QGraphicsRectItem.__init__(self, rectang, *args, **kwargs)
self.setFlags(QGraphicsItem.ItemIsMovable |
QGraphicsItem.ItemSendsGeometryChanges)
self.pen = QPen(Qt.darkMagenta)
self.pen.setWidth(4)
self.setPen(self.pen)
def itemChange(self, change, value):
if change == QGraphicsItem.ItemPositionChange and self.scene():
# value is the new position.
self.newPos = value.toPointF()
self.rect = self.scene().sceneRect()
if not(self.rect.contains(self.newPos)):
# Keep the item inside the scene rect.
self.newPos.setX(min(self.rect.right(), max(self.newPos.x(), self.rect.left())))
self.newPos.setY(min(self.rect.bottom(), max(self.newPos.y(), self.rect.top())))
return self.newPos
return QGraphicsRectItem.itemChange(self, change, value)
class Main(QWidget, Ui_Form):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
self.setupUi(self)
self.scene = QGraphicsScene()
self.cena.setScene(self.scene)
self.scene.addPixmap(QPixmap("01.png"))
self. graph = MovableItem(2, 2, 300, 150)
self.scene.addItem(self.graph)
def showEvent(self, event):
self.cena.fitInView(self.scene.sceneRect(), Qt.IgnoreAspectRatio)
app = QApplication(sys.argv)
window = Main()
window.show()
sys.exit(app.exec_())
First:
Use setSceneRect() in your main Main(), to set the size of the scene.
Second:
Actually the example of the documentation is wrong, therefore, to adjust the rectangle to the scene, delete this if and subtract, in min, the parameters right and bottom by the rectangle dimensions right and bottom in setX and setY. Replace this part of your code:
if not(self.rect.contains(self.newPos)):
# Keep the item inside the scene rect.
self.newPos.setX(min(self.rect.right(), max(self.newPos.x(), self.rect.left())))
self.newPos.setY(min(self.rect.bottom(), max(self.newPos.y(), self.rect.top())))
return self.newPos
For:
self.newPos.setX(min(self.rect.right()-self.boundingRect().right(), max(self.newPos.x(), self.rect.left())))
self.newPos.setY(min(self.rect.bottom()-self.boundingRect().bottom(), max(self.newPos.y(), self.rect.top())))
return self.newPos
Using the following code example PySide segfaults when pushing "Add", "Add", "Remove", "Add" and due to some other sequences of interaction.
Python: 2.7.6
PySide: 1.2.1
QtCore: 4.8.5
Code:
from PySide.QtGui import *
from PySide.QtCore import *
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setObjectName('MainWindow')
self.baseLayout = QWidget(self)
self.v_layout = QVBoxLayout(self.baseLayout)
self.setCentralWidget(self.baseLayout)
self.form_layout = QFormLayout(self.baseLayout)
self.v_layout.addLayout(self.form_layout)
self.button_add = QPushButton(self.baseLayout)
self.button_add.setText("Add")
self.v_layout.addWidget(self.button_add)
self.button_del = QPushButton(self.baseLayout)
self.button_del.setText("Remove")
self.v_layout.addWidget(self.button_del)
self.button_add.clicked.connect(self.add)
self.button_del.clicked.connect(self.remove)
self.fields = []
def add_item(self):
layout = QHBoxLayout(self.parent())
line = QLineEdit(self.parent())
slider = QSlider(self.parent())
layout.addWidget(line)
layout.addWidget(slider)
self.fields.append((layout, line, slider))
self.form_layout.addRow("Test", layout)
def add(self):
for i in range(15):
self.add_item()
def remove(self):
for (layout, line, slider) in self.fields:
line.deleteLater()
slider.deleteLater()
while self.form_layout.itemAt(0):
child = self.form_layout.takeAt(0)
if child.widget():
child.widget().deleteLater()
self.form_layout.update()
self.fields = []
def main():
import sys
app = QApplication(sys.argv)
frame = MainWindow()
frame.show()
app.exec_()
if __name__ == '__main__':
main()
Is this the correct way of adding compound widgets (in this case a QLineEdit and a QSlider within a QVBoxLayout) to a form layout? What am I doing wrong?
The correct way of adding "compound widgets" to a QFormLayout is to create a QWidget that will be the parent to that layout.
add_item() should rather look something like this:
def add_item(self):
widget = QWidget(self.parent())
layout = QHBoxLayout(widget)
line = QLineEdit(widget)
slider = QSlider(widget)
layout.addWidget(line)
layout.addWidget(slider)
self.fields.append((layout, widget, line, slider))
self.form_layout.addRow("Test", widget)
(And the widget also has to be removed when deleting the fields).
I think you are not creating the layouts in the right way, for example you are trying to set the layout of base_layout two times. Also you can check for count() on a QLayout to see if it has children:
from PySide.QtGui import *
from PySide.QtCore import *
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.baseLayout = QWidget(self)
self.v_layout = QVBoxLayout(self.baseLayout)
self.setCentralWidget(self.baseLayout)
self.form_layout = QFormLayout()
self.v_layout.addLayout(self.form_layout)
self.button_add = QPushButton()
self.button_add.setText("Add")
self.v_layout.addWidget(self.button_add)
self.button_del = QPushButton()
self.button_del.setText("Remove")
self.v_layout.addWidget(self.button_del)
self.button_add.clicked.connect(self.add)
self.button_del.clicked.connect(self.remove)
self.fields = []
def add_item(self):
layout = QHBoxLayout()
line = QLineEdit()
slider = QSlider()
layout.addWidget(line)
layout.addWidget(slider)
self.fields.append((layout, line, slider))
self.form_layout.addRow("Test", layout)
def add(self):
for i in range(15):
self.add_item()
def remove(self):
while self.form_layout.count() > 0:
child = self.form_layout.takeAt(0)
widget = child.widget()
if widget:
widget.deleteLater()
self.form_layout.update()
self.fields = []
def main():
import sys
app = QApplication(sys.argv)
frame = MainWindow()
frame.show()
app.exec_()
if __name__ == '__main__':
main()
I have the following code to display am image using pyQt:
app = QtGui.QApplication(sys.argv)
window = QtGui.QMainWindow()
window.setGeometry(opts.posx, opts.posy, opts.width, opts.height)
pic = QtGui.QLabel(window)
pic.setGeometry(5, 5, opts.width-10, opts.height-10)
pixmap = QtGui.QPixmap(opts.filename)
pixmap = pixmap.scaledToHeight(opts.height)
pic.setPixmap(pixmap)
window.show()
sys.exit(app.exec_())
I would like to wrap up this code possibly in the form of a class, and be able to set a different image during runtime, using signals, socket, threads I really do not know. I would imagine something like:
class MyImage(object):
def __init(self, args):
some setup code
self.pic = whatever
def set_image(self, filename):
pixmap = QtGui.QPixmap(opts.filename)
pixmap = pixmap.scaledToHeight(opts.height)
pic.setPixmap(pixmap)
With the original code I just call sys.exit(app.exec_()) which makes the code 'freeze'. But I want to send a signal (and a filename) from a different running python code. Any suggestion how this can be handled easily and straightforward? Maybe overwriting the app.exec_ method?
Something like this should work for you:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import sip
sip.setapi('QString', 2)
sip.setapi('QVariant', 2)
from PyQt4 import QtGui, QtCore
class ImageChanger(QtGui.QWidget):
def __init__(self, images, parent=None):
super(ImageChanger, self).__init__(parent)
self.comboBox = QtGui.QComboBox(self)
self.comboBox.addItems(images)
self.layout = QtGui.QVBoxLayout(self)
self.layout.addWidget(self.comboBox)
class MyWindow(QtGui.QWidget):
def __init__(self, images, parent=None):
super(MyWindow, self).__init__(parent)
self.label = QtGui.QLabel(self)
self.imageChanger = ImageChanger(images)
self.imageChanger.move(self.imageChanger.pos().y(), self.imageChanger.pos().x() + 100)
self.imageChanger.show()
self.imageChanger.comboBox.currentIndexChanged[str].connect(self.changeImage)
self.layout = QtGui.QVBoxLayout(self)
self.layout.addWidget(self.label)
#QtCore.pyqtSlot(str)
def changeImage(self, pathToImage):
pixmap = QtGui.QPixmap(pathToImage)
self.label.setPixmap(pixmap)
if __name__ == "__main__":
import sys
images = [ "/path/to/image/1",
"/path/to/image/2",
"/path/to/image/3",
]
app = QtGui.QApplication(sys.argv)
app.setApplicationName('MyWindow')
main = MyWindow(images)
main.show()
sys.exit(app.exec_())