I'm making a Solitaire card game to practice OOP and PyQt5, and I'm having trouble adding a card, which inherits QAbstractButton
, to a layout (QGridLayout
, QHBoxLayout
, or QVBoxLayout
). Here is part of the Card
object:
class Card(QAbstractButton):
def __init__(self, rank=None, suit=None, parent=None):
super().__init__(parent)
self.rank = rank
self.suit = suit
self.visibility = False
def paintEvent(self, e):
painter = QPainter()
painter.begin(self)
if self.visibility == True:
self.draw_card_front(painter)
else:
self.draw_card_back(painter)
painter.end()
def draw_card_back(self, painter):
painter.setPen(COLOR_OUTLINE)
painter.setBrush(COLOR_BACK)
painter.drawRoundedRect(0, 0, CARD_WIDTH-1, CARD_HEIGHT-1, 10, 10)
def draw_card_front(self, painter):
painter.setPen(COLOR_OUTLINE)
painter.setBrush(COLOR_FRONT)
painter.drawRoundedRect(0, 0, CARD_WIDTH-1, CARD_HEIGHT-1, 10, 10)
self.draw_rank(painter)
self.draw_suit(painter)
...
And here is the game's class:
class Solitaire(QWidget):
def __init__(self):
super().__init__()
self.score = 0
self.initUI()
def initUI(self):
grid = QGridLayout()
self.setLayout(grid)
self.card1 = Card(rank=1, suit=2, parent=self)
self.card2 = Card(rank=1, suit=2, parent=self)
grid.addWidget(self.card1, 0, 0)
grid.addWidget(self.card2, 1, 0)
self.setWindowTitle('Yay')
self.setGeometry(300, 300, 400, 400)
self.show()
...
if __name__ == '__main__':
app = QApplication(sys.argv)
game = Solitaire()
app.exec_()
When I run the program, the Card
does not show up. But if I don't use a layout, the Card
shows up normally. And if I try adding a QPushButton
to a layout it works fine, too. I feel like I'm missing something with the parent
property, or perhaps I'm not overloading a function from QAbstractButton
in the Card
class. Can anyone advise?
According to the docs:
To subclass QAbstractButton, you must reimplement at least paintEvent() to draw the button's outline and its text or pixmap. It is generally advisable to reimplement sizeHint() as well, and sometimes hitButton() (to determine whether a button press is within the button). For buttons with more than two states (like tri-state buttons), you will also have to reimplement checkStateSet() and nextCheckState().
From the above we conclude that you must implement the paintEvent()
method that is responsible for drawing the button, this depends on what you want to draw, and the sizeHint()
method which is the size used by the layouts.
For example:
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class Card(QAbstractButton):
def __init__(self, rank=None, suit=None, parent=None):
super().__init__(parent)
self.rank = rank
self.suit = suit
self.visibility = False
def sizeHint(self):
return QSize(100, 100)
def paintEvent(self, e):
painter = QPainter(self)
if self.visibility:
self.draw_card_front(painter)
else:
self.draw_card_back(painter)
...
class Solitaire(QWidget):
def __init__(self):
super().__init__()
self.score = 0
self.initUI()
def initUI(self):
grid = QGridLayout()
self.setLayout(grid)
self.card1 = Card(rank=1, suit=2, parent=self)
self.card2 = Card(rank=1, suit=2, parent=self)
grid.addWidget(self.card1, 0, 0)
grid.addWidget(self.card2, 1, 0)
self.setWindowTitle('Yay')
self.setGeometry(300, 300, 400, 400)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
game = Solitaire()
app.exec_()