I want to make a QListWidget with a resize handle at the bottom (similar to text fields you see on webpages like this one). I've seen a few people asking the same question out there but did not find a complete example.
I have some code which is nearly there, but it flickers during resize so I'm guessing there is something I am missing about resize policies or layouts or something...
Here's my "working" example. The theory is pretty simple, you just measure the distance of the mouse move in the widget's mousePressEvent and resize/reposition accordingly. Unfortunately I'm missing something basic and I don't know what:
from PyQt4 import QtGui
import sys
class Grip(QtGui.QLabel):
def __init__(self, parent, move_widget):
super(Grip, self).__init__(parent)
self.move_widget = move_widget
self.setText("+")
self.min_height = 50
self.mouse_start = None
self.height_start = self.move_widget.height()
self.resizing = False
self.setMouseTracking(True)
def showEvent(self, event):
super(Grip, self).showEvent(event)
self.reposition()
def mousePressEvent(self, event):
super(Grip, self).mousePressEvent(event)
self.resizing = True
self.height_start = self.move_widget.height()
self.mouse_start = event.pos()
def mouseMoveEvent(self, event):
super(Grip, self).mouseMoveEvent(event)
if self.resizing:
delta = event.pos() - self.mouse_start
height = self.height_start + delta.y()
if height > self.min_height:
self.move_widget.setFixedHeight(height)
else:
self.move_widget.setFixedHeight(self.min_height)
self.reposition()
def mouseReleaseEvent(self, event):
super(Grip, self).mouseReleaseEvent(event)
self.resizing = False
def reposition(self):
rect = self.move_widget.geometry()
self.move(rect.right(), rect.bottom())
class Dialog(QtGui.QDialog):
def __init__(self):
super(Dialog, self).__init__()
layout = QtGui.QVBoxLayout()
self.setLayout(layout)
list_widget = QtGui.QListWidget()
layout.addWidget(list_widget)
gripper = Grip(self, list_widget)
layout.addWidget(QtGui.QLabel("Test"))
self.setGeometry(200, 500, 200, 500)
Ok turns out I was really damn close. I post an answer here for anyone else looking to solve a similar problem!
All I really needed to change from my original code was to reference the globalPos()
instead of the local pos()
. Thanks for the help, and S. Nick in particular for spotting that the move event was causing the issue.
from PyQt4 import QtGui
import sys
class Grip(QtGui.QLabel):
def __init__(self, parent, move_widget):
super(Grip, self).__init__(parent)
self.move_widget = move_widget
self.setText("+")
self.min_height = 50
self.mouse_start = None
self.height_start = self.move_widget.height()
self.resizing = False
self.setMouseTracking(True)
self.setCursor(QtCore.Q.SizeVerCursor)
def showEvent(self, event):
super(Grip, self).showEvent(event)
self.reposition()
def mousePressEvent(self, event):
super(Grip, self).mousePressEvent(event)
self.resizing = True
self.height_start = self.move_widget.height()
self.mouse_start = event.globalPos()
def mouseMoveEvent(self, event):
super(Grip, self).mouseMoveEvent(event)
if self.resizing:
delta = event.globalPos() - self.mouse_start
height = self.height_start + delta.y()
if height > self.min_height:
self.move_widget.setFixedHeight(height)
else:
self.move_widget.setFixedHeight(self.min_height)
self.reposition()
def mouseReleaseEvent(self, event):
super(Grip, self).mouseReleaseEvent(event)
self.resizing = False
def reposition(self):
rect = self.move_widget.geometry()
self.move(rect.right(), rect.bottom())
class Dialog(QtGui.QDialog):
def __init__(self):
super(Dialog, self).__init__()
layout = QtGui.QVBoxLayout()
self.setLayout(layout)
list_widget = QtGui.QListWidget()
layout.addWidget(list_widget)
gripper = Grip(self, list_widget)
layout.addWidget(QtGui.QLabel("Test"))
self.setGeometry(200, 500, 200, 500)