I am using Python and PyQt5.QtWidgets to simulate a card game. I have created a card table using QTdesigner and am dynamically creating QLabel widgets then using QPixmap to set them to one of 52 card images.
I can successfully drag and drop them anywhere on the card table.
However, when I drop them so they overlap each other, the widget which was created later, in time, is always in front of the one created earlier.
I still want to see both widgets even if one is partially overlapping the other.
Is there any way, in PyQt5, to control the z-axis, i.e. so I can decide at run-time which widget is to be on top?
I have included the python code and qtdesigner .ui xml below. I don't know how to include the 52 card images, but they can be simulated by creating simple .png images with a width of 66 and height of 100 pixels.
python code:
# cardTableQT.py
# imports
from PyQt5.QtWidgets import *
from PyQt5 import uic
from PyQt5.Qt import QStandardItemModel, QStandardItem
from PyQt5.QtGui import QPixmap, QDrag, QPainter
from PyQt5.QtCore import Qt, QMimeData
# classes
class DraggableLabel(QLabel):
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.drag_start_position = event.pos()
if len(label_being_dragged) > 0:
label_being_dragged[0] = self
else:
label_being_dragged.append(self)
def mouseMoveEvent(self, event):
if not (event.buttons() & Qt.LeftButton):
return
if (event.pos() - self.drag_start_position).manhattanLength() < QApplication.startDragDistance():
return
self.setVisible(False)
drag = QDrag(self)
mimedata = QMimeData()
mimedata.setText(self.text())
drag.setMimeData(mimedata)
pixmap = QPixmap(self.size())
painter = QPainter(pixmap)
painter.drawPixmap(self.rect(), self.grab())
painter.end()
drag.setPixmap(pixmap)
drag.setHotSpot(event.pos())
drag.exec_(Qt.CopyAction | Qt.MoveAction)
if not self.isVisible(): self.setVisible(True) # if dragged off window, make visible again
class cardTableQTGUI(QMainWindow):
def __init__(self):
global gui
super(cardTableQTGUI, self).__init__()
uic.loadUi('cardTableQT.ui', self)
self.setAcceptDrops(True)
gui = self
self.setWindowTitle('cardTableQT V1.0')
green_baise = '#008800' # colour the card table
self.stackedWidget.setStyleSheet(f"background-color: {green_baise};")
# this folder contains 52 card images (0.png - 51.png)
cards_path = 'Y:/Downloads/p/PlayingCards/cards_png_zip/deck/renamed'
# create a deck of 52 cards
self.card_labels = []
for n in range(52):
self.card_labels.append(DraggableLabel("",self))
self.card_labels[-1].setGeometry(10 + n * 22, 10, 66, 100)
self.card_labels[-1].setScaledContents(True)
self.card_labels[-1].setPixmap(QPixmap(cards_path + '/' + str(n) + '.png'))
self.show()
def dragEnterEvent(self, event):
if event.mimeData().hasText():
event.acceptProposedAction()
def dropEvent(self, event):
pos = event.pos()
x = pos.x() - 33 # centre the point on image
y = pos.y() - 50
label_dragged = label_being_dragged[0]
label_dragged.setGeometry(x, y, 66, 100)
label_dragged.setVisible(True)
label_being_dragged.pop()
event.acceptProposedAction()
# global variables
label_being_dragged = []
# main program gui
app = QApplication([])
gui = cardTableQTGUI()
# start app
app.exec_()
QTDesigner "cardTableQT.ui" xml:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1255</width>
<height>672</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QStackedWidget" name="stackedWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>9</y>
<width>1231</width>
<height>651</height>
</rect>
</property>
<widget class="QWidget" name="page"/>
<widget class="QWidget" name="page_2"/>
</widget>
</widget>
</widget>
<resources/>
<connections/>
</ui>
code supplied, as suggested by: @Сергей Кох
Thanks to @JonB at https://forum.qt.io/topic/141865 He pointed me to
QWidget.raise_()which solved the problem, using it in thedef dropEvent