PyQt5 中带有框架的自定义标题栏 [英] Custom Titlebar with frame in PyQt5

查看:154
本文介绍了PyQt5 中带有框架的自定义标题栏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为 Windows/Linux 开发一个支持开源 Markdown 的最小笔记应用程序.我正在尝试删除标题栏并添加我自己的按钮.我想要一个标题栏,只有两个自定义按钮,如图所示

目前我有这个:

我尝试修改窗口标志:

  • 没有窗口标志,窗口既可以调整大小又可以移动.但没有自定义按钮.
  • 使用self.setWindowFlags(QtCore.Qt.FramelessWindowHint),窗口没有边框,但不能移动或调整窗口大小
  • 使用self.setWindowFlags(QtCore.Qt.CustomizeWindowHint),窗口可以调整大小但不能移动,也不能去掉窗口顶部的白色部分.

任何帮助表示赞赏.您可以在 GitHub

解决方案

以下是您必须遵循的步骤:

  1. 拥有您的 MainWindow,无论是 QMainWindow、QWidget,还是您想要继承的任何 [widget].
  2. 设置它的标志,self.setWindowFlags(Qt.FramelessWindowHint)
  3. 实现自己的移动.
  4. 实现您自己的按钮(关闭、最大、最小)
  5. 实现您自己的调整大小.

这是一个带有移动和按钮实现的小例子.您仍然应该使用相同的逻辑实现调整大小.

导入系统从 PyQt5.QtCore 导入 QPoint从 PyQt5.QtCore 导入 Qt从 PyQt5.QtWidgets 导入 QApplication从 PyQt5.QtWidgets 导入 QHBoxLayout从 PyQt5.QtWidgets 导入 QLabel从 PyQt5.QtWidgets 导入 QPushButton从 PyQt5.QtWidgets 导入 QVBoxLayout从 PyQt5.QtWidgets 导入 QWidget类 MainWindow(QWidget):def __init__(self):super(MainWindow, self).__init__()self.layout = QVBoxLayout()self.layout.addWidget(MyBar(self))self.setLayout(self.layout)self.layout.setContentsMargins(0,0,0,0)self.layout.addStretch(-1)self.setMinimumSize(800,400)self.setWindowFlags(Qt.FramelessWindowHint)self.pressing = 错误类 MyBar(QWidget):def __init__(self, parent):super(MyBar, self).__init__()self.parent = 父母打印(self.parent.width())self.layout = QHBoxLayout()self.layout.setContentsMargins(0,0,0,0)self.title = QLabel("我自己的酒吧")btn_size = 35self.btn_close = QPushButton("x")self.btn_close.clicked.connect(self.btn_close_clicked)self.btn_close.setFixedSize(btn_size,btn_size)self.btn_close.setStyleSheet("背景色:红色;")self.btn_min = QPushButton("-")self.btn_min.clicked.connect(self.btn_min_clicked)self.btn_min.setFixedSize(btn_size, btn_size)self.btn_min.setStyleSheet("背景颜色:灰色;")self.btn_max = QPushButton("+")self.btn_max.clicked.connect(self.btn_max_clicked)self.btn_max.setFixedSize(btn_size, btn_size)self.btn_max.setStyleSheet("背景色:灰色;")self.title.setFixedHeight(35)self.title.setAlignment(Qt.AlignCenter)self.layout.addWidget(self.title)self.layout.addWidget(self.btn_min)self.layout.addWidget(self.btn_max)self.layout.addWidget(self.btn_close)self.title.setStyleSheet("""背景颜色:黑色;白颜色;""")self.setLayout(self.layout)self.start = QPoint(0, 0)self.pressing = 错误def resizeEvent(self, QResizeEvent):super(MyBar, self).resizeEvent(QResizeEvent)self.title.setFixedWidth(self.parent.width())def mousePressEvent(self, event):self.start = self.mapToGlobal(event.pos())self.pressing = 真def mouseMoveEvent(self, event):如果自我压制:self.end = self.mapToGlobal(event.pos())self.movement = self.end-self.startself.parent.setGeometry(self.mapToGlobal(self.movement).x(),self.mapToGlobal(self.movement).y(),self.parent.width(),self.parent.height())self.start = self.enddef mouseReleaseEvent(self, QMouseEvent):self.pressing = 错误def btn_close_clicked(self):self.parent.close()def btn_max_clicked(self):self.parent.showMaximized()def btn_min_clicked(self):self.parent.showMinimized()如果 __name__ == "__main__":app = QApplication(sys.argv)mw = 主窗口()mw.show()sys.exit(app.exec_())

以下是一些提示:

选项 1:

  1. 在每个角落和侧面都有一个带有小部件的 QGridLayout(例如左侧、左上角、菜单栏、右上角、右下角、右下角、左下角和左下角)
  2. 通过方法 (1),当您点击每个边框时,您会知道,您只需定义每个边框的大小并将每个边框添加到相应位置即可.
  3. 当你点击每一个时,以各自的方式对待它们,例如,如果你点击左边的一个并向左拖动,你必须把它放大,同时向左移动它似乎停在了正确的位置并增加了宽度.
  4. 将此推理应用于每条边,每条边都按其必须的方式行事.

选项 2:

  1. 您可以通过点击位置检测您点击的位置,而不是使用 QGridLayout.

  2. 验证点击的 x 是否小于移动 pos 的 x 以了解它是向左还是向右移动以及它被点击的位置.

  3. 计算方法同Option1

选项 3:

  1. 可能还有其他方法,但我只是想到了这些方法.例如,使用您说可以调整大小的 CustomizeWindowHint,所以您只需要实现我给您的示例即可.漂亮!

提示:

  1. 小心localPos(在自己的小部件内),globalPos(与您的屏幕相关).例如:如果您单击左侧小部件的最左侧,则其 'x' 将为零,如果您单击中间(内容)的最左侧,它也将为零,但如果您 mapToGlobal,您将具有不同的值根据屏幕的位置.
  2. 在调整大小或移动时要注意,当你必须增加宽度或减少,或者只是移动,或两者兼而有之时,我建议你在纸上画画,在实施之前弄清楚调整大小的逻辑是如何工作的的蓝色.

祝你好运:D

I'm working on an opensource markdown supported minimal note taking application for Windows/Linux. I'm trying to remove the title bar and add my own buttons. I want something like, a title bar with only two custom buttons as shown in the figure

Currently I have this:

I've tried modifying the window flags:

  • With not window flags, the window is both re-sizable and movable. But no custom buttons.
  • Using self.setWindowFlags(QtCore.Qt.FramelessWindowHint), the window has no borders, but cant move or resize the window
  • Using self.setWindowFlags(QtCore.Qt.CustomizeWindowHint), the window is resizable but cannot move and also cant get rid of the white part at the top of the window.

Any help appreciated. You can find the project on GitHub here.

Thanks..

This is my python code:

from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets, uic
import sys
import os
import markdown2 # https://github.com/trentm/python-markdown2
from PyQt5.QtCore import QRect
from PyQt5.QtGui import QFont

simpleUiForm = uic.loadUiType("Simple.ui")[0]

class SimpleWindow(QtWidgets.QMainWindow, simpleUiForm):
    def __init__(self, parent=None):
        QtWidgets.QMainWindow.__init__(self, parent)
        self.setupUi(self)
        self.markdown = markdown2.Markdown()
        self.css = open(os.path.join("css", "default.css")).read()
        self.editNote.setPlainText("")
        #self.noteView = QtWebEngineWidgets.QWebEngineView(self)
        self.installEventFilter(self)
        self.displayNote.setContextMenuPolicy(QtCore.Qt.NoContextMenu)
        #self.setWindowFlags(QtCore.Qt.FramelessWindowHint)

    def eventFilter(self, object, event):
        if event.type() == QtCore.QEvent.WindowActivate:
            print("widget window has gained focus")
            self.editNote.show()
            self.displayNote.hide()
        elif event.type() == QtCore.QEvent.WindowDeactivate:
            print("widget window has lost focus")
            note = self.editNote.toPlainText()
            htmlNote = self.getStyledPage(note)
            # print(note)
            self.editNote.hide()
            self.displayNote.show()
            # print(htmlNote)
            self.displayNote.setHtml(htmlNote)
        elif event.type() == QtCore.QEvent.FocusIn:
            print("widget has gained keyboard focus")
        elif event.type() == QtCore.QEvent.FocusOut:
            print("widget has lost keyboard focus")
        return False

The UI file is created in the following hierarchy

解决方案

Here are the steps you just gotta follow:

  1. Have your MainWindow, be it a QMainWindow, or QWidget, or whatever [widget] you want to inherit.
  2. Set its flag, self.setWindowFlags(Qt.FramelessWindowHint)
  3. Implement your own moving around.
  4. Implement your own buttons (close, max, min)
  5. Implement your own resize.

Here is a small example with move around, and buttons implemented. You should still have to implement the resize using the same logic.

import sys

from PyQt5.QtCore import QPoint
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QHBoxLayout
from PyQt5.QtWidgets import QLabel
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtWidgets import QVBoxLayout
from PyQt5.QtWidgets import QWidget



class MainWindow(QWidget):

    def __init__(self):
        super(MainWindow, self).__init__()
        self.layout  = QVBoxLayout()
        self.layout.addWidget(MyBar(self))
        self.setLayout(self.layout)
        self.layout.setContentsMargins(0,0,0,0)
        self.layout.addStretch(-1)
        self.setMinimumSize(800,400)
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.pressing = False


class MyBar(QWidget):

    def __init__(self, parent):
        super(MyBar, self).__init__()
        self.parent = parent
        print(self.parent.width())
        self.layout = QHBoxLayout()
        self.layout.setContentsMargins(0,0,0,0)
        self.title = QLabel("My Own Bar")

        btn_size = 35

        self.btn_close = QPushButton("x")
        self.btn_close.clicked.connect(self.btn_close_clicked)
        self.btn_close.setFixedSize(btn_size,btn_size)
        self.btn_close.setStyleSheet("background-color: red;")

        self.btn_min = QPushButton("-")
        self.btn_min.clicked.connect(self.btn_min_clicked)
        self.btn_min.setFixedSize(btn_size, btn_size)
        self.btn_min.setStyleSheet("background-color: gray;")

        self.btn_max = QPushButton("+")
        self.btn_max.clicked.connect(self.btn_max_clicked)
        self.btn_max.setFixedSize(btn_size, btn_size)
        self.btn_max.setStyleSheet("background-color: gray;")

        self.title.setFixedHeight(35)
        self.title.setAlignment(Qt.AlignCenter)
        self.layout.addWidget(self.title)
        self.layout.addWidget(self.btn_min)
        self.layout.addWidget(self.btn_max)
        self.layout.addWidget(self.btn_close)

        self.title.setStyleSheet("""
            background-color: black;
            color: white;
        """)
        self.setLayout(self.layout)

        self.start = QPoint(0, 0)
        self.pressing = False

    def resizeEvent(self, QResizeEvent):
        super(MyBar, self).resizeEvent(QResizeEvent)
        self.title.setFixedWidth(self.parent.width())

    def mousePressEvent(self, event):
        self.start = self.mapToGlobal(event.pos())
        self.pressing = True

    def mouseMoveEvent(self, event):
        if self.pressing:
            self.end = self.mapToGlobal(event.pos())
            self.movement = self.end-self.start
            self.parent.setGeometry(self.mapToGlobal(self.movement).x(),
                                self.mapToGlobal(self.movement).y(),
                                self.parent.width(),
                                self.parent.height())
            self.start = self.end

    def mouseReleaseEvent(self, QMouseEvent):
        self.pressing = False


    def btn_close_clicked(self):
        self.parent.close()

    def btn_max_clicked(self):
        self.parent.showMaximized()

    def btn_min_clicked(self):
        self.parent.showMinimized()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    mw = MainWindow()
    mw.show()
    sys.exit(app.exec_())

Here are some tips:

Option 1:

  1. Have a QGridLayout with widget in each corner and side(e.g. left, top-left, menubar, top-right, right, bottom-right, bottom and bottom left)
  2. With the approach (1) you would know when you are clicking in each border, you just got to define each one size and add each one on their place.
  3. When you click on each one treat them in their respective ways, for example, if you click in the left one and drag to the left, you gotta resize it larger and at the same time move it to the left so it will appear to be stopped at the right place and grow width.
  4. Apply this reasoning to each edge, each one behaving in the way it has to.

Option 2:

  1. Instead of having a QGridLayout you can detect in which place you are clicking by the click pos.

  2. Verify if the x of the click is smaller than the x of the moving pos to know if it's moving left or right and where it's being clicked.

  3. The calculation is made in the same way of the Option1

Option 3:

  1. Probably there are other ways, but those are the ones I just thought of. For example using the CustomizeWindowHint you said you are able to resize, so you just would have to implement what I gave you as example. BEAUTIFUL!

Tips:

  1. Be careful with the localPos(inside own widget), globalPos(related to your screen). For example: If you click in the very left of your left widget its 'x' will be zero, if you click in the very left of the middle(content)it will be also zero, although if you mapToGlobal you will having different values according to the pos of the screen.
  2. Pay attention when resizing, or moving, when you have to add width or subtract, or just move, or both, I'd recommend you to draw on a paper and figure out how the logic of resizing works before implementing it out of blue.

GOOD LUCK :D

这篇关于PyQt5 中带有框架的自定义标题栏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆