Soy nuevo en python y aún mas nuevo usando Pyqt así que he estado revisando y estudiando algunos ejemplos. Encontré este ejemplo que, en resumen, crea una ventana sin borde usando QtCore.Qt.FramelessWindowHint y luego le crea grips personalizados en los bordes y las esquinas que permiten redimensionzar la ventana, aquí el enlace: https://stackoverflow.com/questions/62807295/how-to-resize-a-window-from-the-edges-after-adding-the-property-qtcore-qt-framel
El código parece funcionar bien pero encontré un detalle. Primero debo aclarar que usé el mismo código del ejemplo, lo único que modifiqué es que usé setMinimumSize() para establecer un tamaño minimo de 100 x 100 para la ventana. Aquí el código que estoy usando:
from PyQt5 import QtCore, QtGui, QtWidgetsclass SideGrip(QtWidgets.QWidget): def __init__(self, parent, edge): QtWidgets.QWidget.__init__(self, parent) if edge == QtCore.Qt.LeftEdge: self.setCursor(QtCore.Qt.SizeHorCursor) self.resizeFunc = self.resizeLeft elif edge == QtCore.Qt.TopEdge: self.setCursor(QtCore.Qt.SizeVerCursor) self.resizeFunc = self.resizeTop elif edge == QtCore.Qt.RightEdge: self.setCursor(QtCore.Qt.SizeHorCursor) self.resizeFunc = self.resizeRight else: self.setCursor(QtCore.Qt.SizeVerCursor) self.resizeFunc = self.resizeBottom self.mousePos = None def resizeLeft(self, delta): window = self.window() width = max(window.minimumWidth(), window.width() - delta.x()) geo = window.geometry() geo.setLeft(geo.right() - width) window.setGeometry(geo) def resizeTop(self, delta): window = self.window() height = max(window.minimumHeight(), window.height() - delta.y()) geo = window.geometry() geo.setTop(geo.bottom() - height) window.setGeometry(geo) def resizeRight(self, delta): window = self.window() width = max(window.minimumWidth(), window.width() + delta.x()) window.resize(width, window.height()) def resizeBottom(self, delta): window = self.window() height = max(window.minimumHeight(), window.height() + delta.y()) window.resize(window.width(), height) def mousePressEvent(self, event): if event.button() == QtCore.Qt.LeftButton: self.mousePos = event.pos() def mouseMoveEvent(self, event): if self.mousePos is not None: delta = event.pos() - self.mousePos self.resizeFunc(delta) def mouseReleaseEvent(self, event): self.mousePos = Noneclass Main(QtWidgets.QMainWindow): _gripSize = 8 def __init__(self): QtWidgets.QMainWindow.__init__(self) self.setWindowFlags(QtCore.Qt.FramelessWindowHint) self.sideGrips = [ SideGrip(self, QtCore.Qt.LeftEdge), SideGrip(self, QtCore.Qt.TopEdge), SideGrip(self, QtCore.Qt.RightEdge), SideGrip(self, QtCore.Qt.BottomEdge), ] # corner grips should be "on top" of everything, otherwise the side grips # will take precedence on mouse events, so we are adding them *after*; # alternatively, widget.raise_() can be used self.cornerGrips = [QtWidgets.QSizeGrip(self) for i in range(4)] self.setMinimumSize(100, 100) @property def gripSize(self): return self._gripSize def setGripSize(self, size): if size == self._gripSize: return self._gripSize = max(2, size) self.updateGrips() def updateGrips(self): self.setContentsMargins(*[self.gripSize] * 4) outRect = self.rect() # an "inner" rect used for reference to set the geometries of size grips inRect = outRect.adjusted(self.gripSize, self.gripSize, -self.gripSize, -self.gripSize) # top left self.cornerGrips[0].setGeometry( QtCore.QRect(outRect.topLeft(), inRect.topLeft())) # top right self.cornerGrips[1].setGeometry( QtCore.QRect(outRect.topRight(), inRect.topRight()).normalized()) # bottom right self.cornerGrips[2].setGeometry( QtCore.QRect(inRect.bottomRight(), outRect.bottomRight())) # bottom left self.cornerGrips[3].setGeometry( QtCore.QRect(outRect.bottomLeft(), inRect.bottomLeft()).normalized()) # left edge self.sideGrips[0].setGeometry( 0, inRect.top(), self.gripSize, inRect.height()) # top edge self.sideGrips[1].setGeometry( inRect.left(), 0, inRect.width(), self.gripSize) # right edge self.sideGrips[2].setGeometry( inRect.left() + inRect.width(), inRect.top(), self.gripSize, inRect.height()) # bottom edge self.sideGrips[3].setGeometry( self.gripSize, inRect.top() + inRect.height(), inRect.width(), self.gripSize) def resizeEvent(self, event): QtWidgets.QMainWindow.resizeEvent(self, event) self.updateGrips()app = QtWidgets.QApplication([])m = Main()m.show()m.resize(240, 160)app.exec_()
Ahora que la ventana tiene un tamaño mínimo establecido evidentemente los grips no permiten redimensionar la ventana por debajo de esas dimensiones, esto funciona bien. El problema ocurre cuando doy click izquierdo en cualquier grip de las esquinas (los laterales funcionan bien) y lo arrastro de tal manera que al soltar el "click" este está por fuera de la ventana (esto es posible debido a que se estableció un tamaño mínimo para la ventana). Entonces, después de soltar el "click" y al poner el cursor nuevamente sobre la ventana este adquiere el cursor del grip por el cual entró a la ventana. Es decir, si entra por un lateral de la ventana el cursor adquiere SizeHorCursor para TODA la ventana y lo mismo ocurre si entra por la parte de arriba, por la parte de abajo o por las esquinas, adquiere el cursor del grip por el que pasa para toda la ventana. Si se da click en cualquier parte de la ventana o sobre los grips el problema se arregla y todo funciona bien de nuevo.
Entiendo que el cursor solo debería cambiar si está sobre los grips así que no entiendo por qué sucede esto. Recalco que esto solo ocurre con los grips de las esquinas, los laterales responden bien al evento que describo. Mi suposición es que el evento de soltar el click no se registra correctamente al estar fuera de la ventana pero no sé que hacer al respecto. Entiendo que parece un problema estético solamente pero me gustaría entender por qué pasa esto y arreglarlo de ser posible o tener una solución alternativa que haga lo mismo.
Esta en mi primera pregunta en Stackoverflow así que espero haberla redactado bien y haber proporcionado información suficiente para que me puedan ayudar con este problema, Gracias!