I'm trying to draw an overlay on screen to have a user capture details about elements on the screen.
Apologize if this is really simple, I've been bumbling around all morning trying to figure it out.
I'm currently trying this one, I'm guessing I don't understand the nuance of main window and nested window well enough that's causing my struggle: https://forum.qt.io/topic/93408/transparent-widget/12
The features I'd like are:
- Works with PyQT6
- Normal Looking Window
- Normal Drag Move Behavior
- Normal Resize Behavior
- Fully Opaque Window top bar and Frame borders
- Not [setWindowOpacity(.8)]
- Fully Transparent Content of Frame
- OK if only works on Windows
I'm looking for something that LOOKS similar to this:https://i.stack.imgur.com/eu68P.png
from: Qt - Draw a fully transparent window in Windows without using WA_TranslucentBackground
I understand the BlurBehindWindow is no longer available.
I keep getting a window with a black background, no matter what I set. If I enable "FramelessWindowHint" I get the transparent behavior I want, but then I can't drag my window around or resize it.
So I found another post on stack overflow that added handles to resize the frameless window, but it's kind of ugly when you move the left side the right side jumps around and jitters as it resizes in an unpleasant way.
This one is all of the mutations I tried
import sys
from PyQt6.QtGui import QPainter, QPen, QColor, QBitmap
from PyQt6.QtWidgets import QMainWindow, QApplication, QWidget
from PyQt6.QtCore import Qt
class Clear(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 280, 270)
#self.setStyleSheet("background:transparent")
#self.setWindowFlags(Qt.WindowType(0x00000800))
#self.setWindowOpacity(.8)
#self.setWindowFlags(Qt.WindowType.Window)
#self.setWindowFlags(Qt.WindowType.WindowType_Mask)
self.setWindowFlags(Qt.WindowType.WindowStaysOnTopHint)
#self.setWindowFlags(Qt.WindowType.WindowTransparentForInput)
self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
#self.autoFillBackground()
#self.setAttribute(Qt.WidgetAttribute.WA_NoSystemBackground)
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
#self.setAttribute(Qt.WidgetAttribute.WA_OpaquePaintEvent, False)
#self.setStyleSheet("background:transparent")
#self.setStyleSheet("background-color: rgba(0, 0, 0, 0)")
#self.setAttribute(Qt.WidgetAttribute.WA_X11OpenGLOverlay)
#self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground)
#self.setAttribute(Qt.WidgetAttribute.WA_)
#self.setWindowFlags(Qt.WindowType.WindowTitleHint)
self.show()
def paintEvent(self, e):
qp = QPainter()
qp.backgroundMode()
qp.begin(self)
self.drawLines(qp)
qp.end()
def drawLines(self, qp):
pen = QPen(QColor('blue'), 2, Qt.PenStyle.SolidLine)
qp.setPen(pen)
qp.drawLine(20, 40, 250, 40)
pen.setStyle(Qt.PenStyle.DashLine)
qp.setPen(pen)
qp.drawLine(20, 80, 250, 80)
pen.setStyle(Qt.PenStyle.DashDotLine)
qp.setPen(pen)
qp.drawLine(20, 120, 250, 120)
pen.setStyle(Qt.PenStyle.DotLine)
qp.setPen(pen)
qp.drawLine(20, 160, 250, 160)
pen.setStyle(Qt.PenStyle.DashDotDotLine)
qp.setPen(pen)
qp.drawLine(20, 200, 250, 200)
pen.setStyle(Qt.PenStyle.CustomDashLine)
pen.setDashPattern([1, 4, 5, 4])
qp.setPen(pen)
qp.drawLine(20, 240, 250, 240)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Clear()
sys.exit(app.exec())
This one is the frameless with the jittery behavior when resizing and no drag bar on top.
from PyQt6 import QtCore, QtGui, QtWidgets
import sys
from PyQt6.QtGui import QPainter, QPen, QColor, QBitmap
#from PyQt6.QtWidgets import QMainWindow, QApplication
from PyQt6.QtCore import Qt
class SideGrip(QtWidgets.QWidget):
def __init__(self, parent, edge):
QtWidgets.QWidget.__init__(self, parent)
if edge == QtCore.Qt.Edge.LeftEdge:
self.setCursor(QtCore.Qt.CursorShape.SizeHorCursor)
self.resizeFunc = self.resizeLeft
elif edge == QtCore.Qt.Edge.TopEdge:
self.setCursor(QtCore.Qt.CursorShape.SizeVerCursor)
self.resizeFunc = self.resizeTop
elif edge == QtCore.Qt.Edge.RightEdge:
self.setCursor(QtCore.Qt.CursorShape.SizeHorCursor)
self.resizeFunc = self.resizeRight
else:
self.setCursor(QtCore.Qt.CursorShape.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.MouseButton.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 = None
class Main(QtWidgets.QMainWindow):
_gripSize = 8
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
#toggle this to make frameless,
# but the Frameless is really ugly when resizing, the sides jump around.
self.setWindowFlags(QtCore.Qt.WindowType.FramelessWindowHint)
self.sideGrips = [
SideGrip(self, QtCore.Qt.Edge.LeftEdge),
SideGrip(self, QtCore.Qt.Edge.TopEdge),
SideGrip(self, QtCore.Qt.Edge.RightEdge),
SideGrip(self, QtCore.Qt.Edge.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)]
@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)
sys.exit(app.exec())
I got a frameless invisible window with a handle so it can resize! Now I need to make a top bar I can drag it around with, and some sort of borders to show where the window is.
"""
Test Qt Frameless Window resizing with QSizeGrip
Maxwell Grady, September 2017.
"""
import sys
from PyQt6 import QtCore, QtWidgets
import sys
from PyQt6.QtGui import QPainter, QPen, QColor, QBitmap
from PyQt6.QtWidgets import QMainWindow, QApplication, QWidget, QFrame
from PyQt6.QtCore import Qt
#from qtmodern.styles import dark
#from qtmodern.windows import ModernWindow
def main():
app = QtWidgets.QApplication(sys.argv)
#dark(app) # qtmodern
window = QtWidgets.QWidget()
# if you are not using qtmodern darkstyle, you can still make the QWidget resizeable and frameless by uncommenting the code below then commenting out the qtmodern code
#flags = QtCore.Qt.WindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)
#window.setWindowFlags(flags)
window.setGeometry(QtCore.QRect(300, 300, 640, 480)) # arbitrary size/location
window.setWindowFlags(Qt.WindowType.Widget)
#window.setParent()
window.setWindowFlags(Qt.WindowType.FramelessWindowHint)
#window.setWindowFlags(Qt.WindowType.WindowStaysOnTopHint)
window.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
window.setAttribute(Qt.WidgetAttribute.WA_NoSystemBackground)
#window.setAttribute(Qt.WidgetAttribute.WA_InputMethodTransparent)
layout = QtWidgets.QVBoxLayout()
sizegrip = QtWidgets.QSizeGrip(window)
layout.addWidget(sizegrip, 0, QtCore.Qt.AlignmentFlag.AlignBottom | QtCore.Qt.AlignmentFlag.AlignRight)
change_size_button = QtWidgets.QPushButton()
change_size_button.setText('a big button that does nothing')
#change_size_button.clicked.connect(lambda: window.setStyleSheet('QWidget#vc{background-color: transparent}'))
layout.addWidget(change_size_button)
window.setLayout(layout)
mw = window # qtmodern
mw.show()
sys.exit(app.exec())
if __name__ == '__main__':
main()