问题
I've created a set of pixmap buttons (PicButton) using QAbstractButton based on this https://stackoverflow.com/a/2714554/6859682 and want to add them in a scroll area such that the user can scroll horizontally. However, I need
- Aspect ratio of the pixmap buttons to be constant
- The pixmap buttons should always occupy the full height of the window upto 200 px.
The issue with my current code is that the pixmap buttons get squeezed when the height becomes too much.
I was able to get the aspect ratio to be constant when the height is small enough for all the buttons to fit in the window. I am attaching my PicButton class below.
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtGui import QPixmap, QPainter, QPen
def testPixmap(r = 255,g = 0,b = 0, a = 255,size =(200,200)):
px = QtGui.QPixmap(size[0],size[1])
color = QtGui.QColor(r,g,b,a)
px.fill(color)
return px
class PicButton(QtWidgets.QAbstractButton):
checked = QtCore.pyqtSignal(object, QtCore.QRect)
def __init__(self, name, parent=None, w = 200, h = 200):
self.w = w; self.h = h; self.name = name
super(PicButton, self).__init__(parent)
pixmap = testPixmap(255,0,0)
self.resetPixmaps(pixmap); self.pixmap = pixmap
self.setCheckable(True); self.setChecked(False)
self.pressed.connect(self.update)
self.released.connect(self.blank)
def resetPixmaps(self, pixmap):
self.pixmap_hover = testPixmap(20,125,200,128)
self.pixmap_pressed = testPixmap(30,180,200,128)
def blank(self):
self.setChecked(True)
def paintEvent(self, event):
pix = self.pixmap_hover if self.underMouse() else self.pixmap
if self.isChecked():
self.checked.emit( self.name, event.rect())
pix = self.pixmap_pressed
size = self.size()
scaledPix = pix.scaledToHeight(size.height(), Qt.SmoothTransformation)
# start painting the label from left upper corner
point = QtCore.QPoint(0,0)
point.setX((size.width() - scaledPix.width())/2)
point.setY((size.height() - scaledPix.height())/2)
painter = QPainter(self)
painter.drawPixmap(point, scaledPix)
def otherBoxChecked(self, func, rect):
if self.isChecked():
pix = self.pixmap; painter = QPainter(self); painter.drawPixmap(rect, pix)
self.setChecked(False)
def enterEvent(self, event):
self.update()
def leaveEvent(self, event):
self.update()
def sizeHint(self):
return QtCore.QSize(self.w, self.h)
In the scenario when the height is too much for all the buttons to fit scenario, I want to activate the scroll bar instead and keep the aspect ratio of the buttons. Any ideas on how to do this? I'm attaching the window code below for completeness
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
buttons = ['str(i)' for i in range(10)]
HB2layout = QtWidgets.QHBoxLayout()
self.maskButtons = [PicButton(button) for button in buttons]
for maskButton, mb in zip(self.maskButtons, range(len(self.maskButtons))):
for maskConnect, mc in zip(self.maskButtons, range(len(self.maskButtons))):
if mb!=mc:
maskButton.checked.connect(maskConnect.otherBoxChecked)
for button in self.maskButtons:
HB2layout.addWidget(button)
self.scrollArea = QtWidgets.QScrollArea(self)
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.scrollArea.setMaximumHeight(200)
self.scrollArea.setLayout(HB2layout)
self.scrollArea.show()
Vlayout = QtWidgets.QVBoxLayout(self)
Vlayout.addWidget(self.scrollArea)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 800, 200)
window.show()
sys.exit(app.exec_())
回答1:
Added
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtGui import QPixmap, QPainter, QPen
def testPixmap(r = 255,g = 0,b = 0, a = 255,size =(200,200)):
px = QtGui.QPixmap(size[0],size[1])
color = QtGui.QColor(r,g,b,a)
px.fill(color)
return px
class PicButton(QtWidgets.QAbstractButton):
checked = QtCore.pyqtSignal(object, QtCore.QRect)
def __init__(self, name, parent=None, w = 200, h = 200):
self.w = w; self.h = h; self.name = name
super(PicButton, self).__init__(parent)
pixmap = testPixmap(255,0,0)
self.resetPixmaps(pixmap); self.pixmap = pixmap
self.setCheckable(True); self.setChecked(False)
self.pressed.connect(self.update)
self.released.connect(self.blank)
def resetPixmaps(self, pixmap):
self.pixmap_hover = testPixmap(20,125,200,128)
self.pixmap_pressed = testPixmap(30,180,200,128)
def blank(self):
self.setChecked(True)
def paintEvent(self, event):
pix = self.pixmap_hover if self.underMouse() else self.pixmap
if self.isChecked():
self.checked.emit( self.name, event.rect())
pix = self.pixmap_pressed
size = self.size()
scaledPix = pix.scaledToHeight(size.height(), Qt.SmoothTransformation)
# start painting the label from left upper corner
point = QtCore.QPoint(0,0)
point.setX((size.width() - scaledPix.width())/2)
point.setY((size.height() - scaledPix.height())/2)
painter = QPainter(self)
painter.drawPixmap(point, scaledPix)
def otherBoxChecked(self, func, rect):
if self.isChecked():
pix = self.pixmap; painter = QPainter(self); painter.drawPixmap(rect, pix)
self.setChecked(False)
def enterEvent(self, event):
self.update()
def leaveEvent(self, event):
self.update()
def sizeHint(self):
return QtCore.QSize(self.w, self.h)
class View(QtWidgets.QGraphicsView):
def __init__(self):
super(View,self).__init__()
self.pic_scene = QtWidgets.QGraphicsScene()
self.neighborhood = 200
buttons = ['str(i)' for i in range(10)]
self.maskButtons = [PicButton(button) for button in buttons]
for maskButton, mb in zip(self.maskButtons , range(len(self.maskButtons ))):
for maskConnect, mc in zip(self.maskButtons , range(len(self.maskButtons ))):
if mb!=mc:
maskButton.checked.connect(maskConnect.otherBoxChecked)
self.pic_scene.setSceneRect(0,0,10000,200)
for i,item in enumerate(self.maskButtons ):
item.setGeometry(self.neighborhood*i,item.geometry().y(),item.geometry().width(),item.geometry().height())
self.pic_scene.addWidget(item)
self.setScene(self.pic_scene)
self.setMaximumHeight(200)
self.setGeometry(500,500,5000,200)
def paintEvent(self,event):
for k,i in enumerate(self.maskButtons ):
rect = i.geometry()
#eventually,width = height
rect.setSize(QtCore.QSize(self.height(),self.height()))
self.neighborhood = self.height()
rect.setX(self.height()*k)
rect.setY(rect.y())
i.setGeometry(rect)
self.pic_scene.addWidget(i)
return QtWidgets.QGraphicsView.paintEvent(self,event)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = View()
window.setGeometry(500, 300, 800, 200)
window.show()
sys.exit(app.exec_())
I'm sorry for not enbedding answer you want at one time.
QAbstractButton ver.
Yes, I didn't use QAbstractButton.This has being hung on my head. Here is the QAbstractButton version.You will be able to customize the buttons you want.
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtGui import QPixmap, QPainter, QPen
class PicButton(QtWidgets.QAbstractButton):
def __init__(self,x=0,y=0,width=200,height=200):
super(PicButton,self).__init__()
self.setGeometry(x,y,width,height)
def paintEvent(self,event):
painter = QtGui.QPainter()
if not painter.isActive():
painter.begin(self)
brush = QtGui.QBrush()
brush.setColor(QtGui.QColor(Qt.red))
brush.setStyle(Qt.SolidPattern)
painter.setBrush(brush)
painter.drawRect(QtCore.QRect(0,0,200,200))
painter.end()
class View(QtWidgets.QGraphicsView):
def __init__(self):
super(View,self).__init__()
self.pic_scene = QtWidgets.QGraphicsScene()
self.neighborhood = 200
self.rect_items = [PicButton() for i in range(10)]
self.pic_scene.setSceneRect(0,0,10000,200)
for i,item in enumerate(self.rect_items):
item.setGeometry(self.neighborhood*i,item.geometry().y(),item.geometry().width(),item.geometry().height())
self.pic_scene.addWidget(item)
self.setScene(self.pic_scene)
self.setMaximumHeight(200)
self.setGeometry(500,500,5000,200)
def paintEvent(self,event):
for k,i in enumerate(self.rect_items):
rect = i.geometry()
#eventually,width = height
rect.setSize(QtCore.QSize(self.height(),self.height()))
self.neighborhood = self.height()
rect.setX(self.height()*k)
rect.setY(rect.y())
i.setGeometry(rect)
self.pic_scene.addWidget(i)
return QtWidgets.QGraphicsView.paintEvent(self,event)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = View()
window.setGeometry(500, 300, 800, 200)
window.show()
sys.exit(app.exec_())
New
Because I accepted the comments, so I tried to elaborate the occasion of QGraphicsView & QGraphicsScene.
But this is only the result.This code may not have any popularity. At any rate, I want to you to execute this code. I hope you like it.
If you want to know the detail,please write comments.
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtGui import QPixmap, QPainter, QPen
class PicRectItem(QtWidgets.QGraphicsRectItem):
def __init__(self,x=0,y=0,width=200,height=200):
super(PicRectItem,self).__init__()
self.setRect(x,y,width,height)
brush = QtGui.QBrush()
brush.setColor(QtGui.QColor(Qt.red))
brush.setStyle(Qt.SolidPattern)
self.setBrush(brush)
class View(QtWidgets.QGraphicsView):
def __init__(self):
super(View,self).__init__()
self.pic_scene = QtWidgets.QGraphicsScene()
self.neighborhood = 200
self.rect_items = [PicRectItem() for i in range(10)]
for i,item in enumerate(self.rect_items):
item.setRect(self.neighborhood*i,item.y(),item.rect().width(),item.rect().height())
for i in self.rect_items:
self.pic_scene.addItem(i)
self.setScene(self.pic_scene)
self.setMaximumHeight(200)
def paintEvent(self,event):
for k,i in enumerate(self.rect_items):
rect = i.rect()
#eventually,width = height
rect.setSize(QtCore.QSizeF(self.height(),self.height()))
self.neighborhood = self.height()
i.setRect(rect)
self.pic_scene.addItem(i)
rect = i.rect()
rect.setX(self.height()*k)
rect.setY(rect.y())
i.setRect(rect)
return QtWidgets.QGraphicsView.paintEvent(self,event)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = View()
window.setGeometry(500, 300, 800, 200)
window.show()
sys.exit(app.exec_())
Previous
I'm not sure about what you want,do you want to do this? If it is not,I will delete this answer or rewrite.
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtGui import QPixmap, QPainter, QPen
def testPixmap(r = 255,g = 0,b = 0, a = 255,size =(200,200)):
px = QtGui.QPixmap(size[0],size[1])
color = QtGui.QColor(r,g,b,a)
px.fill(color)
return px
class PicButton(QtWidgets.QAbstractButton):
checked = QtCore.pyqtSignal(object, QtCore.QRect)
def __init__(self, name, parent=None, w = 200, h = 200):
self.w = w; self.h = h; self.name = name
super(PicButton, self).__init__(parent)
pixmap = testPixmap(255,0,0)
self.resetPixmaps(pixmap); self.pixmap = pixmap
self.setCheckable(True); self.setChecked(False)
self.pressed.connect(self.update)
self.released.connect(self.blank)
def resetPixmaps(self, pixmap):
self.pixmap_hover = testPixmap(20,125,200,128)
self.pixmap_pressed = testPixmap(30,180,200,128)
def blank(self):
self.setChecked(True)
def paintEvent(self, event):
pix = self.pixmap_hover if self.underMouse() else self.pixmap
if self.isChecked():
self.checked.emit( self.name, event.rect())
pix = self.pixmap_pressed
size = self.size()
scaledPix = pix.scaledToHeight(size.height(), Qt.SmoothTransformation)
# start painting the label from left upper corner
point = QtCore.QPoint(0,0)
point.setX((size.width() - scaledPix.width())/2)
point.setY((size.height() - scaledPix.height())/2)
painter = QPainter(self)
painter.drawPixmap(point, scaledPix)
def otherBoxChecked(self, func, rect):
if self.isChecked():
pix = self.pixmap; painter = QPainter(self); painter.drawPixmap(rect, pix)
self.setChecked(False)
def enterEvent(self, event):
self.update()
def leaveEvent(self, event):
self.update()
def sizeHint(self):
return QtCore.QSize(self.w, self.h)
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
buttons = ['str(i)' for i in range(10)]
HB2layout = QtWidgets.QHBoxLayout()
self.maskButtons = [PicButton(button) for button in buttons]
for maskButton, mb in zip(self.maskButtons, range(len(self.maskButtons))):
for maskConnect, mc in zip(self.maskButtons, range(len(self.maskButtons))):
if mb!=mc:
maskButton.checked.connect(maskConnect.otherBoxChecked)
for button in self.maskButtons:
HB2layout.addWidget(button)
self.scrollChildArea = QtWidgets.QWidget()
self.scrollChildArea.setLayout(HB2layout)
self.scrollArea = QtWidgets.QScrollArea()
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.scrollArea.setMaximumHeight(200)
self.scrollArea.setWidget(self.scrollChildArea)
self.scrollArea.show()
Vlayout = QtWidgets.QVBoxLayout()
Vlayout.addWidget(self.scrollArea)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 800, 200)
sys.exit(app.exec_())
来源:https://stackoverflow.com/questions/53914267/pyqt5-qscrollarea-keep-pixmap-aspect-ratio