在一个 PyQt 布局中找到与小部件相关的索引。

huangapple go评论61阅读模式
英文:

Find the index related to the widgets inside a layout PyQt

问题

在您提供的代码中,您想要通过单击按钮来打印按钮的索引。但是,您遇到了一个问题,即当单击按钮时,self.widget.childAt(e.pos()) 返回 None,并且索引为 -1。问题在于您尝试使用 self.widget.childAt(e.pos()) 来查找被点击的按钮,但这种方法通常用于查找子部件,而不是按钮。

要解决这个问题,您可以通过直接连接按钮的点击事件来获得按钮的索引,而不需要使用 self.widget.childAt(e.pos())。下面是修改后的代码:

from PyQt6.QtWidgets import *
from PyQt6.QtCore import *

class CustomPushButton(QPushButton):
    def __init__(self, name):
        super().__init__(name)
        self.clicked.connect(self.buttonClicked)
    
    def buttonClicked(self):
        print("Button clicked:", self.text())

class Main(QMainWindow):
    def __init__(self):
        super().__init__()
        self.box = QHBoxLayout()
        self.widget = QWidget()
        self.widget.setLayout(self.box)

        self.button = CustomPushButton('button1')
        self.button2 = CustomPushButton('button2')
        self.button3 = CustomPushButton('button3')

        self.box.addWidget(self.button)
        self.box.addWidget(self.button2)
        self.box.addWidget(self.button3)

        self.setCentralWidget(self.widget)
        self.show()

app = QApplication([])
m = Main()
m.show()
app.exec()

这个修改后的代码会在每个按钮的点击事件中直接打印按钮的文本,而不需要使用索引或 self.widget.childAt(e.pos())。这样,您可以轻松地确定哪个按钮被点击了。

英文:

I have a central widget(self.widget) and inside it a QHBoxLayout(self.box)

And I have placed three buttons inside this layout

What I want is to print the button index by clicking on each button

For example, when I click on button 3, the index of this button will be printed

But when I click on one of the buttons, none is returned for me

and index -1 is returned

Because the button becomes none, it naturally gets the wrong index

Note that I know that I can find the clicked button in other ways

But I want to find the clicked button using self.widget.childAt(e.pos())

Where is the problem?

code:

from PyQt6.QtWidgets import *
from PyQt6.QtCore import *
from PyQt6.QtGui import *

class CustomPushButton(QPushButton):
    signal = pyqtSignal(QEvent)
    def __init__(self, name):
        super().__init__(name)

    def mousePressEvent(self, event):
        self.signal.emit(event)
        

class main(QMainWindow):
    def __init__(self):
        super().__init__()
        self.box = QHBoxLayout()
        self.widget = QWidget()
        self.widget.setLayout(self.box)

        self.button = CustomPushButton('button1')
        self.button2 = CustomPushButton('button2')
        self.button3 = CustomPushButton('button3')

        self.box.addWidget(self.button)
        self.box.addWidget(self.button2)
        self.box.addWidget(self.button3)


        self.button.signal.connect(self.findIndex)
        self.button2.signal.connect(self.findIndex)
        self.button3.signal.connect(self.findIndex)

        self.setCentralWidget(self.widget)
        self.show()
        
    def findIndex(self,e):
        button = self.widget.childAt(e.pos())
        print(button)
        print('button',self.box.indexOf(button))


app = QApplication([])
m = main()
m.show()
app.exec()

答案1

得分: 0

每个小部件都有自己的本地坐标系,QMouseEvent.pos() 方法返回该小部件本地坐标系中的 QPoint。然而,QWidget.childAt() 方法期望参数处于它自己的坐标系中。

因此,当您发送由按钮生成的事件,然后将其位置应用于父小部件的坐标系时,很难在那些坐标下找到按钮。

您可以改为在发射信号之前将坐标映射到按钮的父项,使用 mapToParent 方法,并将映射后的位置的 QPoint 结果作为信号参数发送。

例如:

from PyQt6.QtWidgets import *
from PyQt6.QtCore import *
from PyQt6.QtGui import *

class CustomPushButton(QPushButton):
    signal = pyqtSignal(QPoint)
    def __init__(self, name):
        super().__init__(name)

    def mousePressEvent(self, event):
        self.signal.emit(self.mapToParent(event.pos()))
        

class main(QMainWindow):
    def __init__(self):
        super().__init__()
        self.box = QHBoxLayout()
        self.widget = QWidget()
        self.widget.setLayout(self.box)

        self.button = CustomPushButton('button1')
        self.button2 = CustomPushButton('button2')
        self.button3 = CustomPushButton('button3')

        self.box.addWidget(self.button)
        self.box.addWidget(self.button2)
        self.box.addWidget(self.button3)

        self.button.signal.connect(self.findIndex)
        self.button2.signal.connect(self.findIndex)
        self.button3.signal.connect(self.findIndex)

        self.setCentralWidget(self.widget)

        
    def findIndex(self,e):
        button = self.widget.childAt(e)
        print(button)
        print('button',self.box.indexOf(button))


app = QApplication([])
m = main()
m.show()
app.exec()

应用以上更改后,按下每个按钮后,终端输出如下:

<__main__.CustomPushButton object at 0x000001FEFF4CA440>
button 0
<__main__.CustomPushButton object at 0x000001FEFF4CA4D0>
button 1
<__main__.CustomPushButton object at 0x000001FEFF4CA560>
button 2

另外,您在构造函数内部和底部后两次调用了 show(),这是不必要的。

英文:

Each widget has it's own local coordinate system, and the QMouseEvent.pos() method returns a QPoint in that widgets local coordinate system. However the QWidget.childAt() method expects the parameter to be in it's own coordinate system.

So when you send the event generated by the button and then apply its position to the parent widgets coordinate system, it is unlikely that the button will be found at those coordinates.

What you can do instead is map the coordinates to the buttons parent using the mapToParent method, prior to emitting the signal, and sending the QPoint result for the mapped position as the signal parameter.

For example:

from PyQt6.QtWidgets import *
from PyQt6.QtCore import *
from PyQt6.QtGui import *

class CustomPushButton(QPushButton):
    signal = pyqtSignal(QPoint)
    def __init__(self, name):
        super().__init__(name)

    def mousePressEvent(self, event):
        self.signal.emit(self.mapToParent(event.pos()))
        

class main(QMainWindow):
    def __init__(self):
        super().__init__()
        self.box = QHBoxLayout()
        self.widget = QWidget()
        self.widget.setLayout(self.box)

        self.button = CustomPushButton(&#39;button1&#39;)
        self.button2 = CustomPushButton(&#39;button2&#39;)
        self.button3 = CustomPushButton(&#39;button3&#39;)

        self.box.addWidget(self.button)
        self.box.addWidget(self.button2)
        self.box.addWidget(self.button3)


        self.button.signal.connect(self.findIndex)
        self.button2.signal.connect(self.findIndex)
        self.button3.signal.connect(self.findIndex)

        self.setCentralWidget(self.widget)

        
    def findIndex(self,e):
        button = self.widget.childAt(e)
        print(button)
        print(&#39;button&#39;,self.box.indexOf(button))


app = QApplication([])
m = main()
m.show()
app.exec()

After applying the above changes, after pushing each button the terminal output looks like:

&lt;__main__.CustomPushButton object at 0x000001FEFF4CA440&gt;
button 0
&lt;__main__.CustomPushButton object at 0x000001FEFF4CA4D0&gt;
button 1
&lt;__main__.CustomPushButton object at 0x000001FEFF4CA560&gt;
button 2

P.S. You are also calling show() twice. once inside the constructor and again in afterwards toward the bottom, which is unnecessary.

答案2

得分: 0

在文档警告只要对象结构是受控的并且已知,可以使用QObject.sender()来处理与纯粹的面向对象编程相关的方面:

    def findIndex(self, e):
        button = self.sender()
        ...

这种方式甚至不需要在信号中发送事件,这虽然不是被禁止的,但在概念上和潜在上是危险的,通常不建议这样做。

一个更简单的方法是让信号发射实例本身:

class CustomPushButton(QPushButton):
    signal = pyqtSignal(object)
    def __init__(self, name):
        super().__init__(name)

    def mousePressEvent(self, event):
        self.signal.emit(self)


class main(QMainWindow):
    ...
    def findIndex(self, button):
        print('button', self.box.indexOf(button))

这可能看起来不是非常优雅,而且在一些非常特殊的边缘情况下可能会出现问题(线程,多进程),但对于像这样的简单情况来说,这是足够的,我们可以假设当信号被接收时对象仍然存在。

请注意,信号的object参数是为了简化与Python/C++绑定的层次结构相关的一些方面。如果您知道自己在做什么,可以使用QWidgetQPushButton代替。

英文:

While the documentation warns about doing it for strictly OOP related aspects, as long as the object structure is contained and known, you can use QObject.sender():

    def findIndex(self,e):
        button = self.sender()
        ...

In that way you don't even need to send the event within the signal, which is not forbidden, but conceptually and potentially dangerous, and normally discouraged as such.

An even more simpler approach would be to let the signal emit the instance itself:

class CustomPushButton(QPushButton):
    signal = pyqtSignal(object)
    def __init__(self, name):
        super().__init__(name)

    def mousePressEvent(self, event):
        self.signal.emit(self)


class main(QMainWindow):
    ...
    def findIndex(self, button):
        print(&#39;button&#39;, self.box.indexOf(button))

This may not look completely elegant, and it could be an issue in some very special, fringe cases (threading, multiprocessing), but it's fine enough for simpler situations like these, in which we can assume that the object is still existing when the signal is received.

Note that the object argument of the signal is to simplify some aspects related to the Python/C++ layering of the binding. If you do know what you're doing, you can use QWidget or QPushButton instead.

huangapple
  • 本文由 发表于 2023年7月11日 14:52:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/76659341.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定