Uploaded image for project: 'Qt for Python'
  1. Qt for Python
  2. PYSIDE-1550

Frameless Window в Pyside6

    XMLWordPrintable

Details

    • Bug
    • Resolution: Out of scope
    • Not Evaluated
    • None
    • 6.0.3
    • PySide
    • None
    • Linux/Other display system, Windows

    Description

      Я пишу программу на Pyside6 и использую свой собственный titlebar. При запуске с pyside6 у меня возникает эта проблема.  При запуске с помощью Pyside6 результат на первом скриншоте. (Проблема в том что между тайтлбаром и окном существует разрыв(прозрачное пространство))

      Используя PyQt5, все в порядке (2 скриншот)

      Какие отличия в работе этих фреймворков могут вызвать эту проблему и что следует использовать для ее устранения. Код в обоих фреймворках одинаковый, но результат разный, я не могу понять, почему это происходит.

      TextEditorQt.py

      from PySide6 import QtGui
      from TextEditorUI import *
      from PySide6.QtWidgets import QApplication
      from TextEditorUI import Ui_MainWindow, QMainWindow  # импорт нашего сгенерированного файла
      from PySide6.QtCore import QSettings, QPoint, QSize, QFile
      from Titlebar import FramelessWindow
      
      
      
      class MainWindow(QMainWindow, Ui_MainWindow):
          def __init__(self, parent=None):
              super(MainWindow, self).__init__(parent)
              self.setWindowIcon(QtGui.QIcon('icon.ico'))
      
      
              self.setupUi(self)
              self.curFile = ''
              self.setCurrentFile('')
              self.createStatusBar()
      
              self.textEdit.document().contentsChanged.connect(self.documentWasModified)
      
              self.setCurrentFile('')
              self.settings = QSettings('Matewriter', 'Matewriter')
              self.exit_action.triggered.connect(QApplication.quit)
              self.save_action.triggered.connect(self.save)
              self.open_action.triggered.connect(self.open)
              self.newfile_action.triggered.connect(self.newFile)
              self.saveas_action.triggered.connect(self.saveAs)
              self.open_action.setShortcut('Ctrl+O')
              self.newfile_action.setShortcut('Ctrl+N')
              self.save_action.setShortcut('Ctrl+S')
              # Конфиги окна
              windowScreenGeometry = self.settings.value("windowScreenGeometry")
              windowScreenState = self.settings.value("windowScreenState")
              if windowScreenGeometry:
                  self.restoreGeometry(windowScreenGeometry)
      
              else:
                  self.resize(600)
      
              if windowScreenState:
                  self.restoreState(windowScreenState)
      
          def closeEvent(self, event):
              self.settings.setValue("windowScreenGeometry", self.saveGeometry())
              self.settings.setValue("windowScreenState", self.saveState())
              if self.maybeSave():
                  self.writeSettings()
                  event.accept()
              else:
                  event.ignore()
      
          def newFile(self):
              if self.maybeSave():
                  self.textEdit.clear()
                  self.setCurrentFile('')
      
          def open(self):
              if self.maybeSave():
                  fileName, _ = QFileDialog.getOpenFileName(self)
                  if fileName:
                      self.loadFile(fileName)
      
          def save(self):
              if self.curFile:
                  return self.saveFile(self.curFile)
      
              return self.saveAs()
      
          def saveAs(self):
              fileName, _ = QFileDialog.getSaveFileName(self)
              if fileName:
                  return self.saveFile(fileName)
      
              return False
      
          def documentWasModified(self):
              self.setWindowModified(self.textEdit.document().isModified())
      
          def createStatusBar(self):
              self.statusBar().showMessage("Ready")
      
          def readSettings(self):
              settings = QSettings("MateWriter")
              pos = settings.value("pos", QPoint(200, 200))
              size = settings.value("size", QSize(400, 400))
              self.resize(size)
              self.move(pos)
      
          def writeSettings(self):
              settings = QSettings("MateWriter")
              settings.setValue("pos", self.pos())
              settings.setValue("size", self.size())
      
          def maybeSave(self):
              if self.textEdit.document().isModified():
                  ret = QMessageBox.warning(self, "MateWriter",
                                            "The document has been modified.\nDo you want to save "
                                            "your changes?",
                                            QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
      
                  if ret == QMessageBox.Save:
                      return self.save()
      
                  if ret == QMessageBox.Cancel:
                      return False
      
              return True
      
          def loadFile(self, fileName):
              file = QFile(fileName)
              if not file.open(QFile.ReadOnly | QFile.Text):
                  QMessageBox.warning(self, "MateWriter",
                                      "Cannot read file %s:\n%s." % (fileName, file.errorString()))
                  return
      
              inf = QTextStream(file)
              QApplication.setOverrideCursor(Qt.WaitCursor)
              self.textEdit.setPlainText(inf.readAll())
              QApplication.restoreOverrideCursor()
      
              self.setCurrentFile(fileName)
              self.statusBar().showMessage("File loaded", 2000)
      
          def saveFile(self, fileName):
              file = QFile(fileName)
              if not file.open(QFile.WriteOnly | QFile.Text):
                  QMessageBox.warning(self, "MateWriter",
                                      "Cannot write file %s:\n%s." % (fileName, file.errorString()))
                  return False
      
              outf = QTextStream(file)
              QApplication.setOverrideCursor(Qt.WaitCursor)
              outf << self.textEdit.toPlainText()
              QApplication.restoreOverrideCursor()
      
              self.setCurrentFile(fileName)
              self.statusBar().showMessage("File saved", 2000)
              return True
      
          def setCurrentFile(self, fileName):
              self.curFile = fileName
              self.textEdit.document().setModified(False)
              self.setWindowModified(False)
      
              if self.curFile:
                  shownName = self.strippedName(self.curFile)
              else:
                  shownName = 'untitled.txt'
      
              self.setWindowTitle(" %s[*] - MateWriter" % shownName)
      
          def strippedName(self, fullFileName):
              return QFileInfo(fullFileName).fileName()
      
      
      if __name__ == '__main__':
          import sys
      
          app = QApplication(sys.argv)
          app.setStyle('windowsvista')
          w = FramelessWindow()
          w.setWindowTitle('MateWriter')
          w.setWindowIcon(QIcon('icon-white.ico'))
          #    w.setWidget(MainWindow(MainWindow))       # Добавить свое окно
          w.setWidget(MainWindow())  # !!!
          w.show()
          sys.exit(app.exec_())
      
      

      Titlebar.py

       from PySide6.QtCore import Qt, Signal, QPoint, QFileInfo
      from PySide6.QtGui import QFont, QEnterEvent, QPainter, QColor, QPen, QIcon
      from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
                                   QSpacerItem, QSizePolicy, QPushButton)
      
      
      
      class TitleBar(QWidget):
      
          # Сигнал минимизации окна
          windowMinimumed = Signal()
          # увеличить максимальный сигнал окна
          windowMaximumed = Signal()
          # сигнал восстановления окна
          windowNormaled = Signal()
          # сигнал закрытия окна
          windowClosed = Signal()
          # Окно мобильных
          windowMoved = Signal(QPoint)
          # Сигнал Своя Кнопка +++
          signalButtonMy = Signal()
      
      
          def __init__(self, *args, **kwargs):
              super(TitleBar, self).__init__(*args, **kwargs)
      
      
              # Поддержка настройки фона qss
              self.setAttribute(Qt.WA_StyledBackground, True)
              self.mPos     = None
              self.iconSize = 20                     # Размер значка по умолчанию
      
              # Установите цвет фона по умолчанию, иначе он будет прозрачным из-за влияния родительского окна
              self.setAutoFillBackground(True)
              palette = self.palette()
              palette.setColor(palette.Window, QColor(240, 240, 240))
              self.setPalette(palette)
              # Подключение стиля
              self.setStyleSheet('Titlebar.qss')
              self.setStyleSheet(open("Titlebar.qss", "r").read())
              # макет
              layout = QHBoxLayout(self, spacing=0)
              layout.setContentsMargins(0, 0, 0, 0)
      
              # значок окна
              self.iconLabel = QLabel(self)
      
              self.iconLabel.setMargin(10)
      #       self.iconLabel.setScaledContents(True)
              layout.addWidget(self.iconLabel)
      
      
              # название окна
              self.titleLabel = QLabel(self)
              self.titleLabel.setMargin(2)
              layout.addWidget(self.titleLabel)
      
              # Средний телескопический бар
              layout.addSpacerItem(QSpacerItem(
                  40, 100, QSizePolicy.Expanding, QSizePolicy.Minimum))
      
              # Использовать шрифты Webdings для отображения значков
              font = self.font() or QFont()
              font.setFamily('Webdings')
      
      
              # Своя Кнопка
              self.buttonMy = QPushButton(self, clicked=self.showButtonMy,  objectName='buttonMy')
              self.buttonMy.setIcon(QIcon('icon-white.ico'))
              layout.addWidget(self.buttonMy)
      
      
              # Свернуть кнопку
              self.buttonMinimum = QPushButton(
                  '0', self, clicked=self.windowMinimumed.emit, font=font, objectName='buttonMinimum')
              layout.addWidget(self.buttonMinimum)
      
              # Кнопка Max / restore
              self.buttonMaximum = QPushButton(
                  '1', self, clicked=self.showMaximized, font=font, objectName='buttonMaximum')
              layout.addWidget(self.buttonMaximum)
      
              # Кнопка закрытия
              self.buttonClose = QPushButton(
                  'r', self, clicked=self.windowClosed.emit, font=font, objectName='buttonClose')
              layout.addWidget(self.buttonClose)
      
              # начальная высота
              self.setHeight()
      
          # +++ Вызывается по нажатию кнопки buttonMy
          def showButtonMy(self):
              print("Своя Кнопка ")
              self.signalButtonMy.emit()
      
          def showMaximized(self):
              if self.buttonMaximum.text() == '1':
                  # Максимизировать
                  self.buttonMaximum.setText('2')
                  self.windowMaximumed.emit()
              else:  # Восстановить
                  self.buttonMaximum.setText('1')
                  self.windowNormaled.emit()
      
          def setHeight(self, height=38):
              """ Установка высоты строки заголовка """
              self.setMinimumHeight(height)
              self.setMaximumHeight(height)
              # Задайте размер правой кнопки  ?
              self.buttonMinimum.setMinimumSize(height, height)
              self.buttonMinimum.setMaximumSize(height, height)
              self.buttonMaximum.setMinimumSize(height, height)
              self.buttonMaximum.setMaximumSize(height, height)
              self.buttonClose.setMinimumSize(height, height)
              self.buttonClose.setMaximumSize(height, height)
      
              self.buttonMy.setMinimumSize(height, height)
              self.buttonMy.setMaximumSize(height, height)
      
          def setTitle(self, title):
              """ Установить заголовок """
              self.titleLabel.setText(title)
      
          def setIcon(self, icon):
              """ настройки значокa """
              self.iconLabel.setPixmap(icon.pixmap(self.iconSize, self.iconSize))
      
          def setIconSize(self, size):
              """ Установить размер значка """
              self.iconSize = size
      
          def enterEvent(self, event):
              self.setCursor(Qt.ArrowCursor)
              super(TitleBar, self).enterEvent(event)
      
          def mouseDoubleClickEvent(self, event):
              super(TitleBar, self).mouseDoubleClickEvent(event)
              self.showMaximized()
      
          def mousePressEvent(self, event):
              """ Событие клика мыши """
              if event.button() == Qt.LeftButton:
                  self.mPos = event.pos()
              event.accept()
      
          def mouseReleaseEvent(self, event):
              ''' Событие отказов мыши '''
              self.mPos = None
              event.accept()
      
          def mouseMoveEvent(self, event):
              if event.buttons() == Qt.LeftButton and self.mPos:
                  self.windowMoved.emit(self.mapToGlobal(event.pos() - self.mPos))
              event.accept()
      
      
      # Перечислить верхнюю левую, нижнюю правую и четыре неподвижные точки
      Left, Top, Right, Bottom, LeftTop, RightTop, LeftBottom, RightBottom = range(8)
      
      
      class FramelessWindow(QWidget):
      
          # Четыре периметра
          Margins = 5
      
          def __init__(self, *args, **kwargs):
              super(FramelessWindow, self).__init__(*args, **kwargs)
              self._pressed  = False
              self.Direction = None
              self.resize(762, 580)
      
              # Фон прозрачный
              self.setAttribute(Qt.WA_TranslucentBackground, True)
      
              # Нет границы
              self.setWindowFlag(Qt.FramelessWindowHint)
              # Отслеживание мыши
              self.setMouseTracking(True)
      
              # макет
              layout = QVBoxLayout(self, spacing=0)
              # Зарезервировать границы для изменения размера окна без полей
              layout.setContentsMargins(
                  self.Margins, self.Margins, self.Margins, self.Margins)
              # Панель заголовка
              self.titleBar = TitleBar(self)
              layout.addWidget(self.titleBar)
      
      
              # слот сигнала
              self.titleBar.windowMinimumed.connect(self.showMinimized)
              self.titleBar.windowMaximumed.connect(self.showMaximized)
              self.titleBar.windowNormaled.connect(self.showNormal)
              self.titleBar.windowClosed.connect(self.close)
              self.titleBar.windowMoved.connect(self.move)
              self.windowTitleChanged.connect(self.titleBar.setTitle)
              self.windowIconChanged.connect(self.titleBar.setIcon)
      
          def setTitleBarHeight(self, height=38):
              """ Установка высоты строки заголовка """
              self.titleBar.setHeight(height)
      
          def setIconSize(self, size):
              """ Установка размера значка """
              self.titleBar.setIconSize(size)
      
          def setWidget(self, widget):
              """ Настройте свои собственные элементы управления """
              if hasattr(self, '_widget'):
                  return
              self._widget = widget
              # Установите цвет фона по умолчанию, иначе он будет прозрачным из-за влияния родительского окна
              self._widget.setAutoFillBackground(True)
              palette = self._widget.palette()
              palette.setColor(palette.Window, QColor(240, 240, 240))
              self._widget.setPalette(palette)
              self._widget.installEventFilter(self)
              self.layout().addWidget(self._widget)
      
          def move(self, pos):
              if self.windowState() == Qt.WindowMaximized or self.windowState() == Qt.WindowFullScreen:
                  # Максимизировать или полноэкранный режим не допускается
                  return
              super(FramelessWindow, self).move(pos)
      
          def showMaximized(self):
              """ Чтобы максимизировать, удалите верхнюю, нижнюю, левую и правую границы.
                  Если вы не удалите его, в пограничной области будут пробелы. """
              super(FramelessWindow, self).showMaximized()
              self.layout().setContentsMargins(0, 0, 0, 0)
      
          def showNormal(self):
              """ Восстановить, сохранить верхнюю и нижнюю левую и правую границы,
                  иначе нет границы, которую нельзя отрегулировать """
              super(FramelessWindow, self).showNormal()
              self.layout().setContentsMargins(
                  self.Margins, self.Margins, self.Margins, self.Margins)
      
          def eventFilter(self, obj, event):
              """ Фильтр событий, используемый для решения мыши в других элементах
                  управления и восстановления стандартного стиля мыши """
              if isinstance(event, QEnterEvent):
                  self.setCursor(Qt.ArrowCursor)
              return super(FramelessWindow, self).eventFilter(obj, event)
      
          def paintEvent(self, event):
              """ Поскольку это полностью прозрачное фоновое окно, жесткая для поиска
                  граница с прозрачностью 1 рисуется в событии перерисовывания, чтобы отрегулировать размер окна. """
              super(FramelessWindow, self).paintEvent(event)
              painter = QPainter(self)
              painter.setPen(QPen(QColor(255, 255, 255, 1), 2 * self.Margins))
              painter.drawRect(self.rect())
      
          def mousePressEvent(self, event):
              """ Событие клика мыши """
              super(FramelessWindow, self).mousePressEvent(event)
              if event.button() == Qt.LeftButton:
                  self._mpos = event.pos()
                  self._pressed = True
      
          def mouseReleaseEvent(self, event):
              ''' Событие отказов мыши '''
              super(FramelessWindow, self).mouseReleaseEvent(event)
              self._pressed = False
              self.Direction = None
      
          def mouseMoveEvent(self, event):
              """ Событие перемещения мыши """
              super(FramelessWindow, self).mouseMoveEvent(event)
              pos = event.pos()
              xPos, yPos = pos.x(), pos.y()
              wm, hm = self.width() - self.Margins, self.height() - self.Margins
              if self.isMaximized() or self.isFullScreen():
                  self.Direction = None
                  self.setCursor(Qt.ArrowCursor)
                  return
              if event.buttons() == Qt.LeftButton and self._pressed:
                  self._resizeWidget(pos)
                  return
              if xPos <= self.Margins and yPos <= self.Margins:
                  # Верхний левый угол
                  self.Direction = LeftTop
                  self.setCursor(Qt.SizeFDiagCursor)
              elif wm <= xPos <= self.width() and hm <= yPos <= self.height():
                  # Нижний правый угол
                  self.Direction = RightBottom
                  self.setCursor(Qt.SizeFDiagCursor)
              elif wm <= xPos and yPos <= self.Margins:
                  # верхний правый угол
                  self.Direction = RightTop
                  self.setCursor(Qt.SizeBDiagCursor)
              elif xPos <= self.Margins and hm <= yPos:
                  # Нижний левый угол
                  self.Direction = LeftBottom
                  self.setCursor(Qt.SizeBDiagCursor)
              elif 0 <= xPos <= self.Margins and self.Margins <= yPos <= hm:
                  # Влево
                  self.Direction = Left
                  self.setCursor(Qt.SizeHorCursor)
              elif wm <= xPos <= self.width() and self.Margins <= yPos <= hm:
                  # Право
                  self.Direction = Right
                  self.setCursor(Qt.SizeHorCursor)
              elif self.Margins <= xPos <= wm and 0 <= yPos <= self.Margins:
                  # выше
                  self.Direction = Top
                  self.setCursor(Qt.SizeVerCursor)
              elif self.Margins <= xPos <= wm and hm <= yPos <= self.height():
                  # ниже
                  self.Direction = Bottom
                  self.setCursor(Qt.SizeVerCursor)
      
          def _resizeWidget(self, pos):
              """ Отрегулируйте размер окна """
              if self.Direction == None:
                  return
              mpos = pos - self._mpos
              xPos, yPos = mpos.x(), mpos.y()
              geometry = self.geometry()
              x, y, w, h = geometry.x(), geometry.y(), geometry.width(), geometry.height()
              if self.Direction == LeftTop:          # Верхний левый угол
                  if w - xPos > self.minimumWidth():
                      x += xPos
                      w -= xPos
                  if h - yPos > self.minimumHeight():
                      y += yPos
                      h -= yPos
              elif self.Direction == RightBottom:    # Нижний правый угол
                  if w + xPos > self.minimumWidth():
                      w += xPos
                      self._mpos = pos
                  if h + yPos > self.minimumHeight():
                      h += yPos
                      self._mpos = pos
              elif self.Direction == RightTop:       # верхний правый угол
                  if h - yPos > self.minimumHeight():
                      y += yPos
                      h -= yPos
                  if w + xPos > self.minimumWidth():
                      w += xPos
                      self._mpos.setX(pos.x())
              elif self.Direction == LeftBottom:     # Нижний левый угол
                  if w - xPos > self.minimumWidth():
                      x += xPos
                      w -= xPos
                  if h + yPos > self.minimumHeight():
                      h += yPos
                      self._mpos.setY(pos.y())
              elif self.Direction == Left:            # Влево
                  if w - xPos > self.minimumWidth():
                      x += xPos
                      w -= xPos
                  else:
                      return
              elif self.Direction == Right:           # Право
                  if w + xPos > self.minimumWidth():
                      w += xPos
                      self._mpos = pos
                  else:
                      return
              elif self.Direction == Top:             # выше
                  if h - yPos > self.minimumHeight():
                      y += yPos
                      h -= yPos
                  else:
                      return
              elif self.Direction == Bottom:          # ниже
                  if h + yPos > self.minimumHeight():
                      h += yPos
                      self._mpos = pos
                  else:
                      return
              self.setGeometry(x, y, w, h)
      

      Мой код на Pyside6

      Attachments

        1. hqZwB-1.png
          hqZwB-1.png
          5 kB
        2. pyside1550.zip
          9 kB
        3. vGNZO.png
          vGNZO.png
          4 kB

        Issue Links

          No reviews matched the request. Check your Options in the drop-down menu of this sections header.

          Activity

            People

              crmaurei Cristian Maureira-Fredes
              farodin Денис Лозицкий
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Gerrit Reviews

                  There are no open Gerrit changes