Why overriding mousePressEvent in PyQt makes clicking signal doesn't trigger? - python

I am writing a software with Python 3.5 and PyQt5. When I override mousePressEvent of QTableWidget the cellClicked signal doesn't trigger anymore. I need overriding mousePressEvent for getting the pos of mouse clicking and cellClicked for doing another job for cells that are clicked. Why it is like this? Where is the problem?
This is my code for overriding the events:
class QTableWidget(QTableWidget):
def checking(self):
if (self.endgingposx - self.startingposx) >= 50:
self.clear()
def mousePressEvent(self,event):
event.accept()
self.startingposx = event.x()
def mouseReleaseEvent(self,event):
event.accept()
self.endgingposx = event.x()
self.checking()
and this is my clicking signal and it's connected slot:
self.main_table.cellClicked.connect(self.populatescnte)
def populatescnte(self,row,column):
itemnum = self.scentence_listw.count()
exec("item{} = QListWidgetItem(QIcon('content/img/food.png'),'{}')".format(itemnum+1,self.main_table.cellWidget(row,column).text))
exec("self.scentence_listw.addItem(item{})".format(itemnum+1))
self.scentence_listw is a QListWidget.
Thank you in advance.
Update
Thank you every body this code works for me:
class myTableWidget(QTableWidget):
def checking(self):
if (self.endgingposx - self.startingposx) >= 50:
self.clear()
def mousePressEvent(self,event):
super().mousePressEvent(event)
self.startingposx = event.x()
def mouseReleaseEvent(self,event):
super().mouseReleaseEvent(event)
self.endgingposx = event.x()
self.checking()

The event isn't being propagated because you have accepted it in your handler.
You allow need to call the the original handler.
FWIW, you can hook up a handler to itemSelectionChanged and get the selectedItems on the table rather than tracking positions.
import sys
from PyQt5.QtWidgets import (
QApplication, QWidget, QTableWidget,
QTableWidgetItem, QGridLayout
)
class Widget(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 300, 220)
self.setWindowTitle('Simple')
table = TrackedTableWidget()
layout = QGridLayout()
layout.addWidget(table, 0, 0)
self.setLayout(layout)
self.show()
class TrackedTableWidget(QTableWidget):
def __init__(self, *args, **kwargs):
super().__init__()
self.initUI()
def initUI(self):
self.setRowCount(2)
self.setColumnCount(2)
self.setItem(0, 0, QTableWidgetItem('30', 0));
self.setItem(0, 1, QTableWidgetItem('40', 0));
self.setItem(1, 0, QTableWidgetItem('50', 0));
self.setItem(1, 1, QTableWidgetItem('60', 0));
self.cellClicked.connect(self._handle_cell_clicked)
self.itemSelectionChanged.connect(self._handle_item_selection)
def _handle_cell_clicked(self, row, column):
print('cell_clicked', row, column)
print('selected cell positions', self.startingposx, self.endingposx)
def _handle_item_selection(self, *args):
print(self.selectedItems())
print('item_selection', args)
def mousePressEvent(self, event):
print('mousePressEvent')
self.startingposx = event.x()
super().mousePressEvent(event)
def mouseReleaseEvent(self, event):
print('mouseReleaseEvent')
self.endingposx = event.x()
super().mouseReleaseEvent(event)
if __name__ == '__main__':
app = QApplication(sys.argv)
widget = Widget()
sys.exit(app.exec_())
Reference
About accepting events

I handle the event as follow:
class Ui(QtWidgets.QMainWindow):
def __init__(self):
super(Ui, self).__init__() # Call the inherited classes __init__ method
uic.loadUi('./mainwindow.ui', self) # Load the .ui file
self.myButton.pressed.connect(self.mousePress)
self.show() # Show the GUI
def mousePress(self):
print("Mouse pressed")

Related

How can i capture a mouse clicks onto the main Window in a child class

I would like to capture mouse clicks onto the MainWindow in a child class of the application.
I've tried the following but without success:
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
from abc import ABCMeta, abstractmethod
class BaseView(object):
def __init__(self,parent, page=None):
self.parent = parent
self.page = page
#abstractmethod
def preprare_view(self):
pass
#abstractmethod
def clean_up_view(self):
pass
class FooView(BaseView):
def __init__(self, parent, page):
super(FooView, self).__init__(parent, page)
self.parent = parent
def mousePressEvent(self, QMouseEvent):
print(parent.QMouseEvent.pos())
def mouseReleaseEvent(self, QMouseEvent):
cursor = QtGui.QCursor()
print(parent.cursor.pos())
class Example(QtWidgets.QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.foo = "fooview"
self.Foo = FooView(self,self.foo)
qbtn = QtWidgets.QPushButton('Quit', self)
qbtn.resize(qbtn.sizeHint())
qbtn.move(50, 50)
self.setGeometry(0, 0, 1024, 768)
self.setWindowTitle('Quit button')
self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint)
self.show()
def main():
app = QtWidgets.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I'm wondering how I can capture the mouse clicks in the FooView child class - is that even possible?
There is terminology that the OP uses that is confusing, for example FooView is a child class of BaseView but that has nothing to do with Qt so it is irrelevant for this case so I will omit that class and show the example of how another class can obtain information about the click event of a widget.
The logic is to create a class that inherits from QObject and apply an event filter to the other widget, then override the eventFilter method where the events of the widget are obtained.
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
class MouseObserver(QtCore.QObject):
def __init__(self, widget):
super(MouseObserver, self).__init__(widget)
self._widget = widget
self.widget.installEventFilter(self)
#property
def widget(self):
return self._widget
def eventFilter(self, obj, event):
if obj is self.widget:
if event.type() == QtCore.QEvent.MouseButtonPress:
print(event.pos(), QtGui.QCursor.pos())
elif event.type() == QtCore.QEvent.MouseButtonRelease:
print(event.pos(), QtGui.QCursor.pos())
return super(MouseObserver, self).eventFilter(obj, event)
class Example(QtWidgets.QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.observer = MouseObserver(self)
qbtn = QtWidgets.QPushButton("Quit", self)
qbtn.resize(qbtn.sizeHint())
qbtn.move(50, 50)
self.setGeometry(0, 0, 1024, 768)
self.setWindowTitle("Quit button")
self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint)
self.show()
def main():
app = QtWidgets.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

Getting drag and drop target pyqt5

I am working on a drag and drop GUI in pyqt5 and am trying to get the target widget of the drag and drop operation but when i try the target() function of the QDrag object i returns <main.MainWindow object at 0x0000025FDAC09EE0> and I dont know how to use that. I want to access the index of the widget in a QGridLayout so that I can make the two widgets swap places.
Here is my code:
import sys
from PyQt5.QtCore import Qt, QMimeData
from PyQt5.QtGui import QDrag
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QPushButton, QWidget, QApplication, QGridLayout, QScrollArea, QMainWindow, QSlider
class Stroj:
def __init__(self, rok, naziv, trajanje):
self.rok = rok
self.naziv = naziv
self.trajanje = trajanje
class Button(QPushButton):
drag = 0
def __init__(self, title, parent):
super().__init__(title, parent)
def mouseMoveEvent(self, e):
if e.buttons() != Qt.LeftButton:
return
mimeData = QMimeData()
mimeData.setText(self.text())
self.drag = QDrag(self)
self.drag.setMimeData(mimeData)
self.drag.setPixmap(self.grab())
self.drag.setHotSpot(self.rect().center())
dropAction = self.drag.exec_(Qt.MoveAction)
class MainWindow(QMainWindow):
layout = QGridLayout()
btns = []
snd = ""
i = 0
j = 0
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setAcceptDrops(True)
self.scroll = QScrollArea()
self.widget = QWidget()
self.drag = QDrag(self)
SL = []
for x in range(30):
self.btns.append(x)
for x in range(30):
self.btns[x] = Button(str(x), self)
self.btns[x].setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
self.layout.addWidget(self.btns[x], self.i, self.j)
if(self.j > 5):
self.j = 0
self.i += 1
else:
self.j += 1
self.widget.setLayout(self.layout)
self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.scroll.setWidgetResizable(True)
self.scroll.setWidget(self.widget)
self.setCentralWidget(self.scroll)
self.setWindowTitle('Raspored')
self.setGeometry(350, 75, 950, 750)
def dragEnterEvent(self, e):
self.snd = e.mimeData().text()
e.accept()
def dragMoveEvent(self, e):
e.accept()
def dropEvent(self, e):
sender = self.snd
position = e.pos()
position.setX(int(position.x() - self.btns[int(sender)].width() / 2))
position.setY(int(position.y() - self.btns[int(sender)].height() / 2))
self.btns[int(sender)].move(position)
print(self.layout.indexOf(e.source()))
print(e.source().drag.target())
e.setDropAction(Qt.MoveAction)
e.accept()
def main():
app = QApplication(sys.argv)
main = MainWindow()
main.show()
app.exec_()
if __name__ == '__main__':
main()
The target of a drop event is always the widget that receives the drop action, so it's pretty obvious that if you intercept the event from the main window instance, you'll get the main window as target.
If you need to find the widget at a specific position, you need to use QApplication.widgetAt(pos).
In the following example, modified from the given code, I'm accepting the dragEnter/dragMove events only when the source is a Button instance, and the target is not the same. Then I switch those buttons using their position in the layout.
class MainWindow(QMainWindow):
# ...
def dragEnterEvent(self, e):
e.accept()
def dragMoveEvent(self, e):
source = e.source()
target = QApplication.widgetAt(self.mapToGlobal(e.pos()))
if (isinstance(e.source(), Button) and isinstance(target, Button) and target != source):
e.accept()
else:
e.ignore()
def dropEvent(self, e):
source = e.source()
target = QApplication.widgetAt(self.mapToGlobal(e.pos()))
if (not isinstance(source, Button) or not isinstance(target, Button)
or target == source):
return
layout = self.widget.layout()
sourceIndex = layout.indexOf(source)
sourcePos = layout.getItemPosition(sourceIndex)
targetIndex = layout.indexOf(target)
targetPos = layout.getItemPosition(targetIndex)
layout.addWidget(source, *targetPos)
layout.addWidget(target, *sourcePos)
e.accept()
Consider that this is a very simple implementation: you should also ensure that the widgets are actually children of the same window and are in the same layout.

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_())

How to avoid mouse events being eaten by QGraphicsView

I want mouse events to reach the appropriate QGraphicsItem but they get no further than the QGraphicsView.
I have reduced my code to 41 lines, commented out event handlers but to no avail. It is either handled by QGraphicsView if that has a handler or, if not, is not caught at all.
I'm sure I'm missing something obvious but I can't see it.
from PyQt5.QtWidgets import *
class MyFrame(QGraphicsView):
def __init__(self, parent=None):
super().__init__(parent)
self.setScene(QGraphicsScene())
self.scene().addItem(Group())
def mouseReleaseEvent(self, event):
print('in QGraphicsView')
return QGraphicsView.mouseReleaseEvent(self, event)
class Group(QGraphicsItemGroup):
def __init__(self, parent=None):
super().__init__()
item = MyEllipse(0, 0, 20, 20)
self.addToGroup(item)
def mouseReleaseEvent(self, event):
print('in QGraphicsItemGroup')
return QGraphicsItemGroup.mouseReleaseEvent(self, event)
class MyEllipse(QGraphicsEllipseItem):
def mouseReleaseEvent(self, event):
print('in QGraphicsEllipseItem')
return QGraphicsEllipseItem.mouseReleaseEvent(self, event)
if __name__ == '__main__':
app = QApplication([])
f = MyFrame()
f.show()
app.exec_()
mouseReleaseEvent is called if and only if the event that the mousePressEvent handles is accepted, so with the following code the event will arrive at QGraphicsItemGroup:
class Group(QGraphicsItemGroup):
def __init__(self, parent=None):
super().__init__()
item = MyEllipse(0, 0, 20, 20)
self.addToGroup(item)
def mousePressEvent(self, event):
QGraphicsItemGroup.mousePressEvent(self, event)
event.accept()
def mouseReleaseEvent(self, event):
print('in QGraphicsItemGroup')
QGraphicsItemGroup.mouseReleaseEvent(self, event)
But as #ekhumoro points out, the QGraphicsItemGroup acts as a single element so events are not transported to the items they handle.
If you want to detect when the item is pressed you can use the following method:
class Group(QGraphicsItemGroup):
def __init__(self, parent=None):
super().__init__()
self._item = MyEllipse(0, 0, 20, 20)
self.addToGroup(self._item)
def mousePressEvent(self, event):
QGraphicsItemGroup.mousePressEvent(self, event)
event.accept()
def mouseReleaseEvent(self, event):
print('in QGraphicsItemGroup')
if self._item.mapToParent(self._item.boundingRect()).containsPoint(event.pos(), Qt.OddEvenFill):
print("_item")
QGraphicsItemGroup.mouseReleaseEvent(self, event)

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_())

Categories

Resources