I am unable to get a QFrame to completely surround a QPushButton Like a Border. It only frames the top and left side of the button. I was wondering what I'm doing wrong with the frame.
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class main(QWidget):
def __init__(self):
super().__init__()
layout1 = QVBoxLayout()
btn1 = QPushButton("Test")
frame = QFrame(btn1)
frame.setGeometry(btn1.geometry())
frame.setFrameShape(QFrame.Box)
frame.setFrameShadow(QFrame.Plain)
frame.setLineWidth(4)
layout1.addWidget(btn1)
self.setLayout(layout1)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = main()
window.show()
sys.exit(app.exec_())
The problem is caused because the QFrame does not change the size, instead QPushButton does. In my solution I have resized every time the size is changed in the QPushButton
class FrameButton(QPushButton):
def __init__(self, *args, **kwargs):
QPushButton.__init__(self, *args, **kwargs)
self.frame = QFrame(self)
self.frame.setFrameShape(QFrame.Box)
self.frame.setFrameShadow(QFrame.Plain)
self.frame.setLineWidth(4)
def resizeEvent(self, event):
self.frame.resize(self.size())
QWidget.resizeEvent(self, event)
class main(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout(self)
layout.addWidget(FrameButton("Test"))
Related
In this example, as the main window, I use a QWidget that contains a QStackedLayout and a QPushButton to change the current widget to a QStackedLayout.
from PySide6.QtWidgets import QFrame, QWidget, QApplication, QVBoxLayout, QStackedLayout, QPushButton
from PySide6.QtCore import Qt
class ColorWidget(QFrame):
def __init__(self, color):
super(ColorWidget, self).__init__()
self.setFixedSize(200, 200)
self.setStyleSheet(f"background-color: {color}; border-radius: 6px;")
# Some widget. In this case, just a colored background.
class MainWidget(QWidget):
def __init__(self):
super(MainWidget, self).__init__()
self.current_widget = False
layout = QStackedLayout()
layout.addWidget(ColorWidget("red"))
layout.addWidget(ColorWidget("yellow"))
layout.setCurrentIndex(0)
self.setLayout(layout)
# Main widget. Contains 2 colored widgets.
def change_visible_widget(self):
self.current_widget = not self.current_widget
self.layout().setCurrentIndex(int(self.current_widget))
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.setWindowFlag(Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground)
# no frame, no background
layout = QVBoxLayout()
main_widget = MainWidget()
button = QPushButton("change")
button.clicked.connect(main_widget.change_visible_widget)
# button to change QStackedLayout index in Main Widget
layout.addWidget(main_widget)
layout.addWidget(button)
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication()
win = MainWindow()
win.show()
app.exec()
The problem is that when the program starts, an empty window appears for a few moments.
By trial and error, I realized that this is because of the QStackedLayout and the number of windows that appear is equal to the number of created QStackedLayout (in this case it is 1).
How can this be fixed?
Just add self to layout = QStackedLayout():
from PySide6.QtWidgets import QFrame, QWidget, QApplication, QVBoxLayout, QStackedLayout, QPushButton
from PySide6.QtCore import Qt
class ColorWidget(QFrame):
def __init__(self, color):
super(ColorWidget, self).__init__()
self.setFixedSize(200, 200)
self.setStyleSheet(f"background-color: {color}; border-radius: 6px;")
# Some widget. In this case, just a colored background.
class MainWidget(QWidget):
def __init__(self):
super(MainWidget, self).__init__()
self.current_widget = False
layout = QStackedLayout(self)
layout.addWidget(ColorWidget("red"))
layout.addWidget(ColorWidget("yellow"))
layout.setCurrentIndex(0)
# Main widget. Contains 2 colored widgets.
def change_visible_widget(self):
self.current_widget = not self.current_widget
self.layout().setCurrentIndex(int(self.current_widget))
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.setWindowFlag(Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground)
# no frame, no background
layout = QVBoxLayout()
main_widget = MainWidget()
button = QPushButton("change")
button.clicked.connect(main_widget.change_visible_widget)
# button to change QStackedLayout index in Main Widget
layout.addWidget(main_widget)
layout.addWidget(button)
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication()
win = MainWindow()
win.show()
app.exec()
I have opened a image in a QHBoxLayout. I need to crop the opened image and save the cropped image. How I can do this in PySide?
import sys
from PySide import QtGui, QtCore
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
hbox = QtGui.QHBoxLayout(self)
pixmap = QtGui.QPixmap("re.png")
lbl = QtGui.QLabel(self)
lbl.setPixmap(pixmap)
self.rect = QtCore.QRect()
hbox.addWidget(lbl)
self.setLayout(hbox)
self.setGeometry(300, 300, 280, 170)
self.setWindowTitle('Open Image')
self.show()
# Tried here to implement Qpen
#self.painter = QtGui.QPainter(self)
#self.painter.setPen(QtGui.QPen(QtCore.Qt.black, 1, QtCore.Qt.SolidLine));
#self.painter.drawRect(self.rect);
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I suggest use class QtGui.QRubberBand to select area of image to crop. (PySide also implements the same functionality as PyQt)
First, implement method mouseMoveEvent (self, QMouseEvent), mouseReleaseEvent (self, QMouseEvent) and mousePressEvent (self, QMouseEvent) (More infomation read in QtGui.QRubberBand class reference).
Next, get last geometry of QtGui.QRubberBand to crop image by use QRect QWidget.geometry (self).
Last, Use QPixmap QPixmap.copy (self, QRect rect = QRect()) to crop image by put geometry from crop area. And save image it by use bool QPixmap.save (self, QString fileName, str format = None, int quality = -1).
Example;
import sys
from PyQt4 import QtGui, QtCore
class QExampleLabel (QtGui.QLabel):
def __init__(self, parentQWidget = None):
super(QExampleLabel, self).__init__(parentQWidget)
self.initUI()
def initUI (self):
self.setPixmap(QtGui.QPixmap('input.png'))
def mousePressEvent (self, eventQMouseEvent):
self.originQPoint = eventQMouseEvent.pos()
self.currentQRubberBand = QtGui.QRubberBand(QtGui.QRubberBand.Rectangle, self)
self.currentQRubberBand.setGeometry(QtCore.QRect(self.originQPoint, QtCore.QSize()))
self.currentQRubberBand.show()
def mouseMoveEvent (self, eventQMouseEvent):
self.currentQRubberBand.setGeometry(QtCore.QRect(self.originQPoint, eventQMouseEvent.pos()).normalized())
def mouseReleaseEvent (self, eventQMouseEvent):
self.currentQRubberBand.hide()
currentQRect = self.currentQRubberBand.geometry()
self.currentQRubberBand.deleteLater()
cropQPixmap = self.pixmap().copy(currentQRect)
cropQPixmap.save('output.png')
if __name__ == '__main__':
myQApplication = QtGui.QApplication(sys.argv)
myQExampleLabel = QExampleLabel()
myQExampleLabel.show()
sys.exit(myQApplication.exec_())
I would use QImage's copy method:
im2 = im.copy(self.rect)
im2.save(...)
import sys
from PyQt5 import QtGui, QtCore,QtWidgets
from PyQt5.QtWidgets import QRubberBand, QLabel, QApplication, QWidget
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import QRect
class QExampleLabel (QLabel):
def __init__(self, parentQWidget = None):
super(QExampleLabel, self).__init__(parentQWidget)
self.initUI()
def initUI (self):
self.setPixmap(QPixmap('input.png'))
def mousePressEvent (self, eventQMouseEvent):
self.originQPoint = eventQMouseEvent.pos()
self.currentQRubberBand = QRubberBand(QRubberBand.Rectangle, self)
self.currentQRubberBand.setGeometry(QRect(self.originQPoint, QtCore.QSize()))
self.currentQRubberBand.show()
def mouseMoveEvent (self, eventQMouseEvent):
self.currentQRubberBand.setGeometry(QRect(self.originQPoint, eventQMouseEvent.pos()).normalized())
def mouseReleaseEvent (self, eventQMouseEvent):
self.currentQRubberBand.hide()
currentQRect = self.currentQRubberBand.geometry()
self.currentQRubberBand.deleteLater()
cropQPixmap = self.pixmap().copy(currentQRect)
cropQPixmap.save('output.png')
if __name__ == '__main__':
myQApplication = QApplication(sys.argv)
myQExampleLabel = QExampleLabel()
myQExampleLabel.show()
sys.exit(myQApplication.exec_())
I am new to the world of pyqt5 I have created a window using this code:
class Ui_menu(QtWidgets.QMainWindow):
def __init__(self):
super(Ui_menu, self).__init__() # Call the inherited classes __init__ method
uic.loadUi('Windows/menu.ui', self) # Load the .ui file
self.show()
app = QtWidgets.QApplication(sys.argv)
window = Ui_menu()
app.exec_()
I want to know if you can create an animation when you click a button at the top and open a floating window as in the attached image.
look this code
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QSize, QPoint, Qt
from PyQt5.QtGui import QIcon
class MyProxyStyle(QProxyStyle):
pass
def pixelMetric(self, QStyle_PixelMetric, option=None, widget=None):
if QStyle_PixelMetric == QStyle.PM_SmallIconSize:
return 100
else:
return QProxyStyle.pixelMetric(self, QStyle_PixelMetric, option, widget)
class main(QMainWindow):
def __init__(self):
super().__init__()
self.central = QWidget()
self.toolbar = toolbar()
self.button1 = toolbutton("your_icon")
self.button2 = toolbutton("your_icon")
self.toolbar.addWidget(self.button1)
self.toolbar.addWidget(self.button2)
self.button1.clicked.connect(lambda: self.open_menu(self.button1))
self.button2.clicked.connect(lambda: self.open_menu(self.button2))
self.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.toolbar)
self.setCentralWidget(self.central)
self.resize(600,400)
self.show()
def open_menu(self, obj):
self.menu = QMenu()
self.menu.setStyle(MyProxyStyle())
self.menu.addAction(QIcon("your_icon"), "Person")
self.menu.addMenu("Configuration")
self.menu.addSeparator()
self.menu.addMenu("Profile")
pos = self.mapToGlobal(QPoint(obj.x(), obj.y()+obj.height()))
self.menu.exec(pos)
class toolbar(QToolBar):
def __init__(self):
super().__init__()
self.setIconSize(QSize(80,80))
self.setMinimumHeight(80)
self.setMovable(False)
class toolbutton(QToolButton):
def __init__(self, icon):
super().__init__()
self.setIcon(QIcon(icon))
self.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
app = QApplication([])
window = main()
app.exec()
I'm trying to add custom animation to QPushbutton without making a custom QPushbutton and overriding its enterEvent() and leaveEvent().
So far I've tried this,
#staticmethod
def addButtonHoverAnimation(button:QPushButton,currentPos:QPoint):
'''
Method to:
=> Add hover animation for provided button
'''
enterShift = QPropertyAnimation(button,b'pos',button)
exitShift = QPropertyAnimation(button,b'pos',button)
def enterEvent(e):
pos=button.pos()
enterShift.setStartValue(pos)
enterShift.setEndValue(QPoint(pos.x()+3,pos.y()+3))
enterShift.setDuration(100)
enterShift.start()
Effects.dropShadow(button,1,2)
def leaveEvent(e):
pos=button.pos()
exitShift.setStartValue(pos)
exitShift.setEndValue(QPoint(pos.x()-3,pos.y()-3))
exitShift.setDuration(100)
exitShift.start()
Effects.dropShadow(button)
button.enterEvent=enterEvent
button.leaveEvent=leaveEvent
But when I move the mouse very quickly in and out of the button before the animation finishes, The button starts to move wierdly towards the North-West direction.
Button Animation Using Dynamic Positions
I figured out this was due to the leaveEvent() being triggered before enterEvent() even finishes and also because the start and end values are dynamic. So, I tried providing currentPos as a static position and using it instead,
#staticmethod
def addButtonHoverAnimation(button:QPushButton,currentPos:QPoint):
'''
Method to:
=> Add hover animation for provided button
'''
enterShift = QPropertyAnimation(button,b'pos',button)
enterShift.setStartValue(currentPos)
enterShift.setEndValue(QPoint(currentPos.x()+3,currentPos.y()+3))
enterShift.setDuration(100)
exitShift = QPropertyAnimation(button,b'pos',button)
exitShift.setStartValue(QPoint(currentPos.x()-3,currentPos.y()-3))
exitShift.setEndValue(currentPos)
exitShift.setDuration(100)
def enterEvent(e):
button.setProperty(b'pos',exitShift.endValue())
enterShift.start()
Effects.dropShadow(button,1,2)
def leaveEvent(e):
exitShift.start()
Effects.dropShadow(button)
button.enterEvent=enterEvent
button.leaveEvent=leaveEvent
On running, as soon as the mouse enters the QPushbutton, it moves to the top-left of its parent widget and the animation starts working fine. I can't figure out why this is happening. But I was able to get that, it only happened when I used any static value in the animation.
Button Animation with Static Position:
Here is an example:
import sys
from PyQt5.QtCore import QEvent, QPoint, QObject, QPropertyAnimation
from PyQt5.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget
# This is the same method mentioned above
from styling import addButtonHoverAnimation
class Widget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
layout=QVBoxLayout()
button1 = QPushButton("Proceed1", self)
layout.addWidget(button1)
button2 = QPushButton("Proceed2", self)
layout.addWidget(button2)
self.setLayout(layout)
self.resize(640, 480)
addButtonHoverAnimation(button1)
addButtonHoverAnimation(button2)
def main():
app = QApplication(sys.argv)
view = Widget()
view.show()
ret = app.exec_()
sys.exit(ret)
if __name__ == "__main__":
main()
The problem is that probably when the state is changed from enter to leave (or vice versa) the previous animation still does not end so the position of the widget is not the initial or final position, so when starting the new animation there is a deviation that accumulates. One possible solution is to initialize the position and keep it as a reference.
On the other hand you should not do x.fooMethod = foo_callable since many can fail, in this case it is better to use an eventfilter.
import sys
from dataclasses import dataclass
from functools import cached_property
from PyQt5.QtCore import QEvent, QPoint, QObject, QPropertyAnimation
from PyQt5.QtWidgets import QApplication, QPushButton, QWidget
#dataclass
class AnimationManager(QObject):
widget: QWidget
delta: QPoint = QPoint(3, 3)
duration: int = 100
def __post_init__(self):
super().__init__(self.widget)
self._start_value = QPoint()
self._end_value = QPoint()
self.widget.installEventFilter(self)
self.animation.setTargetObject(self.widget)
self.animation.setPropertyName(b"pos")
self.reset()
def reset(self):
self._start_value = self.widget.pos()
self._end_value = self._start_value + self.delta
self.animation.setDuration(self.duration)
#cached_property
def animation(self):
return QPropertyAnimation(self)
def eventFilter(self, obj, event):
if obj is self.widget:
if event.type() == QEvent.Enter:
self.start_enter_animation()
elif event.type() == QEvent.Leave:
self.start_leave_animation()
return super().eventFilter(obj, event)
def start_enter_animation(self):
self.animation.stop()
self.animation.setStartValue(self.widget.pos())
self.animation.setEndValue(self._end_value)
self.animation.start()
def start_leave_animation(self):
self.animation.stop()
self.animation.setStartValue(self.widget.pos())
self.animation.setEndValue(self._start_value)
self.animation.start()
class Widget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
button1 = QPushButton("Proceed1", self)
button1.move(100, 100)
button2 = QPushButton("Proceed2", self)
button2.move(200, 200)
self.resize(640, 480)
animation_manager1 = AnimationManager(widget=button1)
animation_manager2 = AnimationManager(widget=button2)
def main():
app = QApplication(sys.argv)
view = Widget()
view.show()
ret = app.exec_()
sys.exit(ret)
if __name__ == "__main__":
main()
184 / 5000
Resultados de traducción
If you are using a layout then you must reset the position since the layout does not apply the position change immediately but only when the parent widget applies the changes.
class Widget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
button1 = QPushButton("Proceed1")
button2 = QPushButton("Proceed2")
lay = QVBoxLayout(self)
lay.addWidget(button1)
lay.addWidget(button2)
self.resize(640, 480)
self.animation_manager1 = AnimationManager(widget=button1)
self.animation_manager2 = AnimationManager(widget=button2)
def resizeEvent(self, event):
super().resizeEvent(event)
self.animation_manager1.reset()
self.animation_manager2.reset()
In the App there are a QButton and a QLabel. In the QLabel I put a QMovie in, to show a GIF. By clicking the QButton I want to change the GIF, which path is defined in a list.
The problem: the App shows only the first GIF. The Button seems not working. What have I done wrong?
But: Please dont change the structure of the code. E.g. I want to have the QLabel defined in the sub-function and return from there the QLabel.
The code:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import random
list = ['F:\\test1.gif', 'F:\\test2.gif', 'F:\\test3.gif', 'F:\\test4.gif']
class Window(QWidget):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.resize(600, 600)
self.initUI()
def initUI(self):
self.btn = QPushButton("change", self)
self.btn.clicked.connect(self.changeGIF)
self.grid = QVBoxLayout()
self.grid.addWidget(self.btn)
self.grid.addWidget(self.changeGIF())
self.grid.addStretch(1)
self.setLayout(self.grid)
def changeGIF(self):
randomValue = list[random.randint(1, len(list)-1)]
print(randomValue)
self.lbl = QLabel()
self.gif = QMovie(randomValue)
self.lbl.setMovie(self.gif)
self.gif.start()
return self.lbl
if __name__ == '__main__':
app = QApplication(sys.argv)
MyApp = Window()
MyApp.show()
sys.exit(app.exec_())
Thanks for the help!
since the QLabel will be responsible for showing GIFs in a random way, it is advisable to create a class that only takes care of that task, in this widget you must have a method that changes the QMovie of the QLabel.
list_of_gifs = ['F:\\test1.gif', 'F:\\test2.gif', 'F:\\test3.gif', 'F:\\test4.gif']
class GIFLabel(QLabel):
def __init__(self, gifs, *args, **kwargs):
QLabel.__init__(self, *args, **kwargs)
self.mGifs = gifs
self.changeGIF()
def changeGIF(self):
gif = random.choice(self.mGifs)
movie = QMovie(gif)
self.setMovie(movie)
movie.start()
class Window(QWidget):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.resize(600, 600)
self.initUI()
def initUI(self):
self.btn = QPushButton("change", self)
self.label = GIFLabel(list_of_gifs, self)
self.btn.clicked.connect(self.label.changeGIF)
self.grid = QVBoxLayout(self)
self.grid.addWidget(self.btn)
self.grid.addWidget(self.label)
self.grid.addStretch(1)
if __name__ == '__main__':
app = QApplication(sys.argv)
MyApp = Window()
MyApp.show()
sys.exit(app.exec_())