替换 pyQT 中的现有按钮,保留父小部件、位置等

问题描述 投票:0回答:1

我正在努力用一个自定义按钮替换小部件(Qpushbutton),该按钮具有 qIcon 的输入事件并从外部用户界面中保留嵌套布局结构。

替换后的按钮显示在用户界面中,但它不保留与现有按钮完全相同的位置。

from PySide2.QtWidgets import QPushButton, QDialog, QVBoxLayout, QApplication
from PySide2.QtGui import QPixmap, QIcon
from PySide2.QtCore import QSize
from PySide2.QtUiTools import QUiLoader

class HoverPushButton(QPushButton):
    def __init__(self, normal_pixmap, hover_pixmap, button_size):
        super(HoverPushButton, self).__init__()
        self.icon_normal = QIcon(normal_pixmap)
        self.icon_over = QIcon(hover_pixmap)
        self.setIcon(self.icon_normal)
        self.setIconSize(QSize(button_size, button_size))
        
    def enterEvent(self, event):
        self.setIcon(self.icon_over)
        return super(HoverPushButton, self).enterEvent(event)

    def leaveEvent(self, event):
        self.setIcon(self.icon_normal)
        return super(HoverPushButton, self).leaveEvent(event)

class MyDialog(QDialog):
    def __init__(self):
        super(MyDialog, self).__init__()
        self.setup_ui()

    def setup_ui(self):
        # Load your UI from .ui file
        loader = QUiLoader()
        ui_directory = 'D:/Scripts.Python/UI/'
        ui_file = ui_directory+ 'form.ui'
        self.ui = loader.load(ui_file)
        self.resize(900, 800)

        # Set QVBoxLayout for the QDialog
        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 30, 0, 0)
        layout.addWidget(self.ui)

        # Load sprite sheet
        self.sprite_sheet = QPixmap(ui_directory+ 'sprite_canvas.png')
        self.sprite_sheet_hover = QPixmap(ui_directory+ 'sprite_canvas_hover.png')

        icon_size = 85
        button_size = 35

        # Call the function to replace buttons
        self.brush_button = self.ui.findChild(QPushButton, 'pushButton_penBrush')
        self.brush_button_replaced = self.replace_custom_button(self.brush_button, 1, 1, icon_size, button_size)

    def replace_custom_button(self, existing_button, row, column, icon_size, button_size):
        # Crop images from sprite sheet
        normal_pixmap = self.sprite_sheet.copy(column * icon_size, row * icon_size, icon_size, icon_size)
        hover_pixmap = self.sprite_sheet_hover.copy(column * icon_size, row * icon_size, icon_size, icon_size)
        # Create an instance of HoverPushButton with the specified button_size
        new_button = HoverPushButton(normal_pixmap, hover_pixmap, button_size)
        # Clone minimum size
        new_button.setMinimumSize(existing_button.minimumSize())
        # Clone maximum size
        new_button.setMaximumSize(existing_button.maximumSize())
        new_button.setText(existing_button.text())

        # Replace the existing button with the new custom button   
        replaced = (self.layout().replaceWidget(existing_button, new_button)) 
        existing_button.deleteLater()
        return new_button

if __name__ == '__main__':
    #~ app = QApplication([])
    dialog = MyDialog()
    dialog.show()
    #~ app.exec_()


用户界面

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <layout class="QHBoxLayout" name="horizontalLayout">
   <item>
    <layout class="QHBoxLayout" name="horizontalLayout_2">
     <property name="sizeConstraint">
      <enum>QLayout::SetDefaultConstraint</enum>
     </property>
     <item>
      <layout class="QVBoxLayout" name="verticalLayout">
       <item>
        <widget class="QGroupBox" name="groupBox">
         <property name="title">
          <string>GroupBox</string>
         </property>
         <layout class="QHBoxLayout" name="horizontalLayout_3">
          <item>
           <widget class="QPushButton" name="pushButton_penBrush">
            <property name="minimumSize">
             <size>
              <width>35</width>
              <height>35</height>
             </size>
            </property>
            <property name="maximumSize">
             <size>
              <width>35</width>
              <height>35</height>
             </size>
            </property>
            <property name="text">
             <string/>
            </property>
           </widget>
          </item>
         </layout>
        </widget>
       </item>
      </layout>
     </item>
    </layout>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>



替换现有小部件、维护嵌套结构(父小部件)以便获得正确的布局放置和位置的正确方法是什么?

我尝试使用移动,如果布局被破坏(x,y),它会起作用,但是当使用垂直、水平布局等时,按钮不会与现有按钮对齐

Button not appearing

python pyqt pyside2
1个回答
0
投票

replaceWidget()
的文档实际上解释了它,但必须非常仔细地阅读和理解(我自己低估了它):

如果 options 包含

Qt::FindChildrenRecursively
(默认),则搜索子布局以进行替换。

上面的短语意味着该函数仅查找直接嵌套在给定布局中的布局:子布局添加了

addLayout()
;它考虑该布局中包含的小部件的布局

事实上,

QLayout::replaceWidget()
的原始来源基本上做了以下事情:

    int index = -1;
    for (int u = 0; u < count(); ++u) { // iterate through all layout items
        item = itemAt(u);
        if (!item)
            continue;
        if (item->widget() == from) {
            index = u; // "source" widget has been found, break to replace
            break;
        }
        // if the item is a nested layout, call the function recursively
        if (item->layout() && (options & Qt::FindChildrenRecursively)) {
            QLayoutItem *r = item->layout()->replaceWidget(from, to, options);
            if (r)
                return r;
        }
        // IMPORTANT!!! the for loop ends here, implying that a layout item 
        // containing a widget will *always* be ignored!!!
    }
    if (index == -1)
        return nullptr; // no match found, no substitution

    ... // eventually replace the widget

注意:出于解释目的而编辑的代码;评论是我的。

因此,假设现有小部件实际上是正确设置的布局的一部分(因此,它有一个为其设置了该布局的父小部件,即使“父布局”是主布局中的嵌套布局),适当的代码应该是:

        existing_button.parentWidget().layout().replaceWidget(
            existing_button, new_button)
        existing_button.deleteLater()

不过,正如评论中所指出的,这只应在实际需要时才执行,即在程序生命周期内随时替换小部件。

如果您想使用自定义小部件更改小部件的行为,那么您应该考虑小部件升级,或者至少安装事件过滤器。

© www.soinside.com 2019 - 2024. All rights reserved.