我正在努力用一个自定义按钮替换小部件(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),它会起作用,但是当使用垂直、水平布局等时,按钮不会与现有按钮对齐
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()
不过,正如评论中所指出的,这只应在实际需要时才执行,即在程序生命周期内随时替换小部件。
如果您想使用自定义小部件更改小部件的行为,那么您应该考虑小部件升级,或者至少安装事件过滤器。