0

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

example I found

The features I'd like are:

  1. Works with PyQT6
  2. Normal Looking Window
  3. Normal Drag Move Behavior
  4. Normal Resize Behavior
  5. Fully Opaque Window top bar and Frame borders
  6. Not [setWindowOpacity(.8)]
  7. Fully Transparent Content of Frame
  8. 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()
rrz
  • 33
  • 4

0 Answers0