Drag and Drop QLabels with PyQt5 - python

I'm trying to drag and drop a Qlabel on another Qlabel with PyQt5:
from PyQt5.QtWidgets import QApplication, QWidget, QToolTip, QPushButton, QMessageBox, QHBoxLayout, QVBoxLayout, QGridLayout,QFrame, QComboBox, QLabel, QLineEdit
from PyQt5.QtGui import QIcon, QFont, QPixmap, QImage
import sys
class my_label(QLabel):
def __init__(self,title,parent):
super().__init__(title,parent)
self.setAcceptDrops(True)
def dragEnterEvent(self,event):
if event.mimeData().hasFormat("text/plain"):
event.accept()
else:
event.ignore()
def dropEvent(self,event):
self.setText(event.mimeData().text())
class application(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
label = my_label("drop there",self)
label.resize(100,100)
label.move(190,65)
label_to_drag = QLabel("drag this",self)
#label_to_drag.setDragEnabled(True) #doesn't work with QLabel
self.show()
def closeEvent(self,event):
message = QMessageBox.question(self,"Message","Quit ?",QMessageBox.Yes | QMessageBox.No,QMessageBox.No)
if message == QMessageBox.Yes:
event.accept()
else:
event.ignore()
app = QApplication(sys.argv)
fenetre = application()
sys.exit(app.exec_())
I wanted to make my first label "draggable",by calling the setDragEnabled(True) method (as i was doing for QLineEdit),and drop it on the second label to change his text. Dropping text from another app is working fine,but I can't find how to drag the QLabel inside my own app...
What did I miss ?
EDIT : modified the code to try to Drag&Drop Images :
class DraggableLabel(QLabel):
def __init__(self,parent,image):
super(QLabel,self).__init__(parent)
self.setPixmap(QPixmap(image))
self.show()
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.drag_start_position = event.pos()
def mouseMoveEvent(self, event):
if not (event.buttons() & Qt.LeftButton):
return
if (event.pos() - self.drag_start_position).manhattanLength() < QApplication.startDragDistance():
return
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)
class my_label(QLabel):
def __init__(self,title,parent):
super().__init__(title,parent)
self.setAcceptDrops(True)
def dragEnterEvent(self,event):
if event.mimeData().hasFormat("text/plain"):
print("event accepted")
event.accept()
else:
print("event rejected")
event.ignore()
def dropEvent(self,event):
if event.mimeData().hasImage():
self.setPixmap(QPixmap.fromImage(QImage(event.mimeData().imageData())))
class application(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
label_to_drag = DraggableLabel(self,"index.jpg")
label = my_label("drop there",self)
label.resize(100,100)
label.move(190,65)
self.show()
When I drop the DraggableLabel (displaying the image) on my_label,the event is accepted, but hasImage() always returns false...Is the way I set the image up wrong ?

In the case of QLabel you must create everything from the beginning, for this you can follow the examples of the docs.
In the following example I have placed an example where one class only accepts the drop and the other the drag so that you can see each part and understand better.
import sys
from PyQt5.QtWidgets import QApplication, QLabel, QWidget
from PyQt5.QtGui import QDrag, QPixmap, QPainter, QCursor
from PyQt5.QtCore import QMimeData, Qt
class DraggableLabel(QLabel):
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.drag_start_position = event.pos()
def mouseMoveEvent(self, event):
if not (event.buttons() & Qt.LeftButton):
return
if (event.pos() - self.drag_start_position).manhattanLength() < QApplication.startDragDistance():
return
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)
class DropLabel(QLabel):
def __init__(self, *args, **kwargs):
QLabel.__init__(self, *args, **kwargs)
self.setAcceptDrops(True)
def dragEnterEvent(self, event):
if event.mimeData().hasText():
event.acceptProposedAction()
def dropEvent(self, event):
pos = event.pos()
text = event.mimeData().text()
self.setText(text)
event.acceptProposedAction()
class Widget(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
label = DropLabel("drop there",self)
label.setGeometry(190, 65, 100,100)
label_to_drag = DraggableLabel("drag this",self)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
Update:
Do not confuse the QDrag with the QMimeData, if you want to use imageData() you must set it with setImageData() as indicated by the docs:
class DraggableLabel(QLabel):
def __init__(self,parent,image):
super(QLabel,self).__init__(parent)
self.setPixmap(QPixmap(image))
self.show()
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.drag_start_position = event.pos()
def mouseMoveEvent(self, event):
if not (event.buttons() & Qt.LeftButton):
return
if (event.pos() - self.drag_start_position).manhattanLength() < QApplication.startDragDistance():
return
drag = QDrag(self)
mimedata = QMimeData()
mimedata.setText(self.text())
mimedata.setImageData(self.pixmap().toImage())
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)
class my_label(QLabel):
def __init__(self,title,parent):
super().__init__(title,parent)
self.setAcceptDrops(True)
def dragEnterEvent(self,event):
if event.mimeData().hasImage():
print("event accepted")
event.accept()
else:
print("event rejected")
event.ignore()
def dropEvent(self,event):
if event.mimeData().hasImage():
self.setPixmap(QPixmap.fromImage(QImage(event.mimeData().imageData())))

Related

draw on the lower part of the window PyQt5

I am trying to make a program the paints on the half button of the screen only, but as you can see the painting is shifted towards the bottom, if I draw in the upper part, the paint happens in the lower part.
What I want is to draw directly in the bottom part.
here is my code:
from PIL.ImageEnhance import Color
from PyQt5.QtWidgets import QMainWindow, QApplication, QMenu, QMenuBar, QAction, QFileDialog, QTextEdit, QVBoxLayout, \
QWidget, QLabel
from PyQt5.QtGui import QIcon, QImage, QPainter, QPen, QBrush, QPixmap
from PyQt5.QtCore import Qt, QPoint, QSize, QRect
import sys
import pyautogui
class Window(QMainWindow):
def __init__(self):
super().__init__()
title = "Digital Waraq"
icon = "icons/pain.png"
[x, y] = pyautogui.size()
self.setWindowTitle(title)
self.setGeometry(0, 0, x, y)
self.setWindowIcon(QIcon(icon))
self.image = QImage(pyautogui.size().width, int(pyautogui.size().height/2), QImage.Format_RGB32)
self.image.fill(Qt.gray)
self.drawing = False
self.brushSize = 2
self.brushColor = Qt.black
self.lastPoint = QPoint()
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.drawing = True
self.lastPoint = event.pos()
#print(self.lastPoint)
def mouseMoveEvent(self, event):
if(event.buttons() & Qt.LeftButton) & self.drawing:
painter = QPainter(self.image)
painter.setPen(QPen(self.brushColor, self.brushSize, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
painter.drawLine(self.lastPoint, event.pos())
self.lastPoint = event.pos()
self.update()
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.drawing = False
def paintEvent(self, event):
canvasPainter = QPainter(self)
#canvasPainter.drawImage(self.rect(), self.image, self.image.rect())
newRect = QRect(QPoint(0, int(pyautogui.size().height/2)), QSize(self.image.size()))
#canvasPainter.drawImage(newRect, self.image, self.image.rect())
canvasPainter.drawImage(newRect, self.image, self.image.rect())
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec())
Do not complicate the task dividing the painting area within the widget, a simpler solution is to create a widget where the painting is done completely and then place it at the bottom of the main window.
import sys
from PyQt5.QtCore import QPoint, Qt
from PyQt5.QtGui import QBrush, QGuiApplication, QImage, QPainter, QPen
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
class Drawer(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self._drawing = False
self.last_point = QPoint()
self._image_layer = QImage(self.size(), QImage.Format_RGB32)
self._image_layer.fill(Qt.gray)
self.brushSize = 2
self.brushColor = Qt.black
def mousePressEvent(self, event):
self._drawing = True
self.last_point = event.pos()
def mouseMoveEvent(self, event):
if self._drawing and event.buttons() & Qt.LeftButton:
painter = QPainter(self._image_layer)
painter.setPen(
QPen(
self.brushColor,
self.brushSize,
Qt.SolidLine,
Qt.RoundCap,
Qt.RoundJoin,
)
)
painter.drawLine(self.last_point, event.pos())
self.last_point = event.pos()
self.update()
def mouseReleaseEvent(self, event):
self._drawing = True
def paintEvent(self, event):
painter = QPainter(self)
painter.drawImage(QPoint(), self._image_layer)
painter.end()
def resizeEvent(self, event):
if (
self.size().width() > self._image_layer.width()
or self.size().height() > self._image_layer.height()
):
qimg = QImage(
max(self.size().width(), self._image_layer.width()),
max(self.size().height(), self._image_layer.height()),
QImage.Format_RGB32,
)
qimg.fill(Qt.gray)
painter = QPainter(qimg)
painter.drawImage(QPoint(), self._image_layer)
painter.end()
self._image_layer = qimg
self.update()
class Window(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.drawer = Drawer()
central_widget = QWidget()
self.setCentralWidget(central_widget)
vlay = QVBoxLayout(central_widget)
vlay.setContentsMargins(0, 0, 0, 0)
vlay.addStretch(1)
vlay.addWidget(self.drawer, stretch=1)
r = QGuiApplication.primaryScreen().availableGeometry()
self.setGeometry(r)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec())

How to enable button after QListWidget is no longer empty

I would like my button to be disabled until there are items actually in my QListWidget. I have tried if and while statements in my AppDemo class but I feel like it needs to change in the ListBoxWidget class, however the listBoxWidget is already a sub class of the AppDemo.
Example Code:
import sys, os
from PyQt5.QtWidgets import QApplication, QMainWindow, QListWidget, QListWidgetItem, QPushButton
from PyQt5.QtCore import Qt, QUrl
class ListBoxWidget(QListWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setAcceptDrops(True)
self.resize(600, 600)
def dragEnterEvent(self, event):
if event.mimeData().hasUrls:
event.accept()
else:
event.ignore()
def dragMoveEvent(self, event):
if event.mimeData().hasUrls():
event.setDropAction(Qt.CopyAction)
event.accept()
else:
event.ignore()
def dropEvent(self, event):
if event.mimeData().hasUrls():
event.setDropAction(Qt.CopyAction)
event.accept()
links = []
for url in event.mimeData().urls():
if url.isLocalFile():
links.append(str(url.toLocalFile()))
else:
links.append(str(url.toString()))
self.addItems(links)
else:
event.ignore()
class AppDemo(QMainWindow):
def __init__(self):
super().__init__()
self.resize(1200, 600)
self.listbox_view = ListBoxWidget(self)
self.btn = QPushButton('Get Value', self)
self.btn.setEnabled(False)
self.btn.setGeometry(850, 400, 200, 50)
self.btn.clicked.connect(lambda: print(self.getSelectedItem()))
def getSelectedItem(self):
item = QListWidgetItem(self.listbox_view.currentItem())
return item.text()
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = AppDemo()
demo.show()
sys.exit(app.exec_())
You have to use the signals that are emitted when the number of rows of the model associated with the view changes, those signals must invoke a method that updates the state of the button based on the number of items in the QListWidget:
class AppDemo(QMainWindow):
def __init__(self):
super().__init__()
self.resize(1200, 600)
self.listbox_view = ListBoxWidget(self)
self.listbox_view.model().modelReset.connect(self.handle_rows_changed)
self.listbox_view.model().rowsInserted.connect(self.handle_rows_changed)
self.listbox_view.model().rowsRemoved.connect(self.handle_rows_changed)
self.listbox_view.model().layoutChanged.connect(self.handle_rows_changed)
self.btn = QPushButton("Get Value", self)
self.btn.setEnabled(False)
self.btn.setGeometry(850, 400, 200, 50)
self.btn.clicked.connect(lambda: print(self.getSelectedItem()))
def getSelectedItem(self):
item = self.listbox_view.currentItem()
return item.text() if item is not None else ""
def handle_rows_changed(self):
self.btn.setEnabled(bool(self.listbox_view.count()))

How to drag the icon when using drag and drop

Currently I have the following code that executes a drag and drop function and generates a new button by dragging and dropping another.
But what I would like to know is:
How can I make an image of him when I drag the mouse button?
something like this:
This is the code you used
In this I would like to obtain the same effect as in the gif previously shown but with the Qpushbutton
from PyQt5.QtWidgets import QPushButton, QWidget, QApplication
from PyQt5.QtCore import Qt, QMimeData
from PyQt5.QtGui import QDrag
import sys
class Button(QPushButton):
def __init__(self, title, parent):
super().__init__(title, parent)
def mouseMoveEvent(self, e):
if e.buttons() != Qt.RightButton:
return
mimeData = QMimeData()
drag = QDrag(self)
drag.setMimeData(mimeData)
drag.setHotSpot(e.pos() - self.rect().topLeft())
dropAction = drag.exec_(Qt.MoveAction)
def mousePressEvent(self, e):
super().mousePressEvent(e)
if e.button() == Qt.LeftButton:
print('press')
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setAcceptDrops(True)
self.button = Button('Button', self)
self.button.move(100, 65)
self.setWindowTitle('Click or Move')
self.setGeometry(300, 300, 280, 150)
def dragEnterEvent(self, e):
e.accept()
def dropEvent(self, e):
position = e.pos()
self.button.move(position)
self.create(position)
e.setDropAction(Qt.MoveAction)
e.accept()
def create(self,position):
print(position)
self.position = position
self.newButton = QPushButton("new",self)
self.newButton.move(self.position)
self.newButton.resize(150,50)
self.newButton.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
ex.show()
app.exec_()
You have to take a widget image using the grab() method then set it to QDrag using the setPixmap() method. Also I have created a method that creates a widget of the same type of the source with the same q-properties (that does not imply that it is a copy of the widget since there are non copyable elements). On the other hand it is advisable to use the right click since the left click will interfere with the clicked signal
from PyQt5 import QtCore, QtGui, QtWidgets
def fake_copy_widget(widget, parent):
t = type(widget)
w = t(parent)
mo = widget.metaObject()
for i in range(mo.propertyCount()):
prop = mo.property(i)
if prop.isWritable() and prop.isReadable():
name = prop.name()
w.setProperty(name, widget.property(name))
return w
class Button(QtWidgets.QPushButton):
def mouseMoveEvent(self, e):
if e.buttons() & QtCore.Qt.RightButton:
pos = self.mapFromGlobal(QtGui.QCursor().pos())
ba = QtCore.QByteArray()
ds = QtCore.QDataStream(ba, QtCore.QIODevice.WriteOnly)
ds << pos
mimeData = QtCore.QMimeData()
mimeData.setData("application/x-pos", ba)
pixmap = self.grab()
drag = QtGui.QDrag(self)
drag.setHotSpot(pos)
drag.setPixmap(pixmap)
drag.setMimeData(mimeData)
drag.setHotSpot(e.pos() - self.rect().topLeft())
dropAction = drag.exec_(QtCore.Qt.MoveAction)
class Example(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setAcceptDrops(True)
button = Button('Button', self)
button.move(100, 65)
self.setWindowTitle('Click or Move')
self.setGeometry(300, 300, 280, 150)
def dragEnterEvent(self, e):
if e.mimeData().hasFormat("application/x-pos"):
e.accept()
def dropEvent(self, e):
position = e.pos()
button = e.source()
mimedata = e.mimeData()
p = QtCore.QPoint()
ba = mimedata.data("application/x-pos")
ds = QtCore.QDataStream(ba)
ds >> p
self.create(QtCore.QRect(position - p, button.size()), button, self)
e.accept()
def create(self, geometry, widget, parent):
button = fake_copy_widget(widget, parent)
button.setGeometry(geometry)
button.show()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())

PyQt - Custom scrolling with QListWidget

I am trying to figure out a way to customize the scrollbars for QListWidget to have the scrollbars above and below the QListWidget instead of the normal vertical and horizontal scrollbars.
Please check out my example below if you don't understand what I mean.
In the example below I use QPushButtons with QTimers controlling the scrolling in place of the scrollbars but what I am looking for are scrollbars like the ones in QMenu when menu scrolling is enabled.
If that is not an option, I am wondering if there is a scrollbar signal or something that I could try to use to know when the scrollbars are normally activated? That way I can show/hide the buttons as needed. Thanks.
import sys
from PyQt5.QtCore import pyqtSignal, QTimer, Qt
from PyQt5.QtGui import QPainter
from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout, \
QApplication, QStyle, QListWidget, QStyleOptionButton, QListWidgetItem
class UpBtn(QPushButton):
mouseHover = pyqtSignal()
def __init__(self):
QPushButton.__init__(self)
self.setMouseTracking(True)
self.timer = QTimer()
def paintEvent(self, event):
painter = QPainter()
painter.begin(self)
opt = QStyleOptionButton()
self.initStyleOption(opt)
self.style().drawControl(QStyle.CE_ScrollBarSubLine, opt, painter, self)
painter.end()
def startScroll(self):
self.mouseHover.emit()
def enterEvent(self, event):
self.timer.timeout.connect(self.startScroll)
self.timer.start(120)
def leaveEvent(self, event):
self.timer.stop()
class DwnBtn(QPushButton):
mouseHover = pyqtSignal()
def __init__(self):
QPushButton.__init__(self)
self.setMouseTracking(True)
self.timer = QTimer()
def paintEvent(self, event):
painter = QPainter()
painter.begin(self)
opt = QStyleOptionButton()
self.initStyleOption(opt)
self.style().drawControl(QStyle.CE_ScrollBarAddLine, opt, painter, self)
painter.end()
def startScroll(self):
self.mouseHover.emit()
def enterEvent(self, event):
self.timer.timeout.connect(self.startScroll)
self.timer.start(120)
def leaveEvent(self, event):
self.timer.stop()
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.layout = QVBoxLayout()
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.setSpacing(0)
self.upBtn = UpBtn()
self.upBtn.setFixedWidth(230)
self.layout.addWidget(self.upBtn)
self.listWidget = QListWidget()
self.listWidget.setFixedWidth(230)
self.listWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.listWidget.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.layout.addWidget(self.listWidget)
self.downBtn = DwnBtn()
self.downBtn.setFixedWidth(230)
self.layout.addWidget(self.downBtn)
self.setLayout(self.layout)
self.upBtn.clicked.connect(self.upBtnClicked)
self.upBtn.mouseHover.connect(self.upBtnClicked)
self.downBtn.clicked.connect(self.downBtnClicked)
self.downBtn.mouseHover.connect(self.downBtnClicked)
for i in range(100):
item = QListWidgetItem()
item.setText("list item " + str(i))
self.listWidget.addItem(item)
def upBtnClicked(self):
cur = self.listWidget.currentRow()
self.listWidget.setCurrentRow(cur - 1)
def downBtnClicked(self):
cur = self.listWidget.currentRow()
self.listWidget.setCurrentRow(cur + 1)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
EDIT:
Here is an example image for what I am talking about. This is a scrollable QMenu.
EDIT:
Scrollable QMenu code.
Uncomment the commented parts to get a fixed size like in the image. Normally Qmenu scrolling only works when the menu items exceed the screen height. I am just looking for the top and bottom hover style scrolling but to be used in QListWidget.
import sys
from PyQt5.QtCore import QPoint, QEvent
from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout, \
QApplication, QAction, QMenu, QProxyStyle, QStyle
class MyMenu(QMenu):
def event(self, event):
if event.type() == QEvent.Show:
self.move(self.parent().mapToGlobal(QPoint(-108, 0)))
return super(MyMenu, self).event(event)
# class CustomStyle(QProxyStyle):
# def pixelMetric(self, QStyle_PixelMetric, option=None, widget=None):
# if QStyle_PixelMetric == QStyle.PM_MenuScrollerHeight:
# return 15
# if QStyle_PixelMetric == QStyle.PM_MenuDesktopFrameWidth:
# return 290
# else:
# return QProxyStyle.pixelMetric(self, QStyle_PixelMetric, option, widget)
class MainWindow(QWidget):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.layout = QVBoxLayout()
self.btn = QPushButton("Button")
self.btn.setFixedHeight(30)
self.btn.setFixedWidth(100)
self.myMenu = MyMenu("Menu", self.btn)
self.btn.setMenu(self.myMenu)
self.layout.addWidget(self.btn)
self.setLayout(self.layout)
menus = []
for _ in range(5):
myMenus = QMenu("Menu"+str(_+1), self.btn)
# myMenus.setFixedHeight(120)
myMenus.setStyleSheet("QMenu{menu-scrollable: 1; }")
menus.append(myMenus)
for i in menus:
self.btn.menu().addMenu(i)
for item in range(100):
action = QAction("item" + str(item), self)
i.addAction(action)
if __name__ == '__main__':
app = QApplication(sys.argv)
# app.setStyle(CustomStyle())
w = MainWindow()
w.show()
app.exec_()
The idea is to obtain the row of the upper and lower element that will decide whether the buttons are hidden or not, for that we use the method itemAt () that returns the item given the geometrical coordinates. On the other hand I have improved this calculation has to do every time they change the number of items in the QListView for that we use the signals of the internal model.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Button(QtWidgets.QPushButton):
moveSignal = QtCore.pyqtSignal()
def __init__(self, *args, **kwargs):
super(Button, self).__init__(*args, **kwargs)
self.m_timer = QtCore.QTimer(self, interval=120)
self.m_timer.timeout.connect(self.moveSignal)
self.setMouseTracking(True)
self.setFixedHeight(20)
def mouseReleaseEvent(self, e):
super(Button, self).mousePressEvent(e)
self.setDown(True)
def enterEvent(self, e):
self.setDown(True)
self.m_timer.start()
super(Button, self).enterEvent(e)
def leaveEvent(self, e):
self.setDown(False)
self.m_timer.stop()
super(Button, self).leaveEvent(e)
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
self.setFixedWidth(230)
icon = self.style().standardIcon(QtWidgets.QStyle.SP_ArrowUp)
self.upBtn = Button(icon=icon)
self.upBtn.moveSignal.connect(self.moveUp)
icon = self.style().standardIcon(QtWidgets.QStyle.SP_ArrowDown)
self.downBtn = Button(icon=icon)
self.downBtn.moveSignal.connect(self.moveDown)
self.listWidget = QtWidgets.QListWidget()
self.listWidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.listWidget.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
layout = QtWidgets.QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
layout.addWidget(self.upBtn)
layout.addWidget(self.listWidget)
layout.addWidget(self.downBtn)
self.adjust_buttons()
self.create_connections()
def create_connections(self):
self.listWidget.currentItemChanged.connect(self.adjust_buttons)
model = self.listWidget.model()
model.rowsInserted.connect(self.adjust_buttons)
model.rowsRemoved.connect(self.adjust_buttons)
model.rowsMoved.connect(self.adjust_buttons)
model.modelReset.connect(self.adjust_buttons)
model.layoutChanged.connect(self.adjust_buttons)
#QtCore.pyqtSlot()
def adjust_buttons(self):
first = self.listWidget.itemAt(QtCore.QPoint())
r = self.listWidget.row(first)
self.upBtn.setVisible(r != 0 and r!= -1)
last = self.listWidget.itemAt(self.listWidget.viewport().rect().bottomRight())
r = self.listWidget.row(last)
self.downBtn.setVisible( r != (self.listWidget.count() -1) and r != -1)
#QtCore.pyqtSlot()
def moveUp(self):
ix = self.listWidget.moveCursor(QtWidgets.QAbstractItemView.MoveUp, QtCore.Qt.NoModifier)
self.listWidget.setCurrentIndex(ix)
#QtCore.pyqtSlot()
def moveDown(self):
ix = self.listWidget.moveCursor(QtWidgets.QAbstractItemView.MoveDown, QtCore.Qt.NoModifier)
self.listWidget.setCurrentIndex(ix)
#QtCore.pyqtSlot(str)
def add_item(self, text):
item = QtWidgets.QListWidgetItem(text)
self.listWidget.addItem(item)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
for i in range(100):
window.add_item("item {}".format(i))
window.show()
sys.exit(app.exec_())

FramelessWindowHint & WindowStaysOnTopHint won't work

I want my PyQt5 program to be frameless and always be on top of the screen. When I use the WindowStayOnTopHint it works fine, but when I use it with the FramelessWindowHint it becomes frameless, but does not stay on top of the screen. After doing some research I found this, and it said to try to use setMask, but I could not get it to work.
Here is my code:
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import QApplication, QLabel
class Invisible(QLabel):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.__press_pos = None
self.initUI()
def initUI(self):
self.setWindowFlags(Qt.WindowStaysOnTopHint)
self.setMask() #This is where I use the setMask function, but it raises an error
self.setAttribute(Qt.WA_TranslucentBackground)
self.setText("Drag me...")
self.setFont(QFont("Times", 50, QFont.Bold))
self.adjustSize()
self.move(QApplication.instance().desktop().screen().rect().center()
- self.rect().center())
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.__press_pos = event.pos()
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.__press_pos = None
def mouseMoveEvent(self, event):
if self.__press_pos:
self.move(self.pos() + (event.pos() - self.__press_pos))
def main():
app = QApplication(sys.argv)
w = Invisible()
w.show()
return app.exec_()
if __name__ == '__main__':
sys.exit(main())
This code gives me the error:
TypeError: arguments did not match any overloaded call:
setMask(self, QBitmap): not enough arguments
setMask(self, QRegion): not enough arguments
It is asking for more arguments, but when I give it more arguments it says that there are too many arguments.
How do I fix this?
The problem in your case is that you must activate both properties with the operator |
self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
Complete code:
import sys
from PyQt5.QtCore import Qt, QPoint
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import QApplication, QLabel, QStyle
class Invisible(QLabel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.__press_pos = QPoint()
self.initUI()
def initUI(self):
self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground)
self.setText("Drag me...")
self.setFont(QFont("Times", 50, QFont.Bold))
self.adjustSize()
self.setGeometry(
QStyle.alignedRect(
Qt.LeftToRight,
Qt.AlignCenter,
self.size(),
QApplication.instance().desktop().availableGeometry()
)
)
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.__press_pos = event.pos()
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.__press_pos = QPoint()
def mouseMoveEvent(self, event):
if not self.__press_pos.isNull():
self.move(self.pos() + (event.pos() - self.__press_pos))
def main():
app = QApplication(sys.argv)
w = Invisible()
w.show()
return app.exec_()
if __name__ == '__main__':
sys.exit(main())
setMask() serves to give a different border to the widget, for example for an example I got the following:

Categories

Resources