简体   繁体   English

PyQt5 按钮未连接

[英]PyQt5 buttons not connecting

I am trying to build a simple GUI using PyQT5, with 3 buttons to open file browsers and one more to run processing with the selected files, but I can't get my buttons to connect to the functions needed to carry this out.我正在尝试使用 PyQT5 构建一个简单的 GUI,其中有 3 个按钮用于打开文件浏览器,还有一个用于对所选文件进行处理,但是我无法让按钮连接到执行此操作所需的功能。

In the Ctrl class, the _connect_signals function doesn't seem to be calling _input_select .Ctrl类中, _connect_signals函数似乎没有调用_input_select Can anyone help me figure out why?谁能帮我弄清楚为什么?

import sys

# Import QApplication and the required widgets from PyQt5.QtWidgets
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtWidgets import QVBoxLayout
from PyQt5.QtWidgets import QWidget
from PyQt5.QtWidgets import QFileDialog


# Create a subclass of QMainWindow to setup the calculator's GUI
class UI(QMainWindow):
    """App's View (GUI)."""

    def __init__(self):
        """View initializer."""
        super().__init__()
        # Set some main window's properties
        self.setFixedSize(300, 150)
        # Set the central widget and the general layout
        self.generalLayout = QVBoxLayout()
        self._centralWidget = QWidget(self)
        self.setCentralWidget(self._centralWidget)
        self._centralWidget.setLayout(self.generalLayout)
        # Create the buttons
        self._create_buttons()

    def _create_buttons(self):
        """Create the buttons."""
        self.buttons = {}
        buttons_layout = QVBoxLayout()
        # Button text | position on the QVBoxLayout
        buttons = {
            "Select input file...": 0,
            "Select config file...": 1,
            "Select output file...": 2,
            "Run": 3,
        }
        # Create the buttons and add them to the grid layout
        for btnText, pos in buttons.items():
            self.buttons[btnText] = QPushButton(btnText)
            buttons_layout.addWidget(self.buttons[btnText], pos)
        # Add buttons_layout to the general layout
        self.generalLayout.addLayout(buttons_layout)


# Create a Controller class to connect the GUI and the model
class Ctrl:
    """App's Controller."""

    def __init__(self, setup, view):
        """Controller initializer."""
        self._view = view
        self._setup = setup
        # Connect signals and slots
        self._connect_signals()

    def _input_select(self):    # Not being called
        print("input selection")

        options = QFileDialog.Options()
        file_select, _ = QFileDialog.getOpenFileNames(
            self,
            'Select Input File...',
            '',
            'CSV Files (*.csv);;All Files (*)',
            options=options
        )
        if file_select:
            self._setup["input"] = file_select

    def _connect_signals(self):
        """Connect signals and slots."""
        self._view.buttons["Select input file..."].clicked.connect(self._input_select)  # Not working!


# Client code
def main():
    """Main function."""
    # Create an instance of `QApplication`
    app = QApplication(sys.argv)
    # Show the app's GUI
    view = UI()
    view.show()
    setup = {}
    # Create instance of the controller
    Ctrl(setup=setup, view=view)
    # Execute app's main loop
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

In case it helps, I started out by butchering this example code from a Real Python tutorial, but must have broken it along the way.如果有帮助,我首先从 Real Python 教程中删除了此示例代码,但必须在此过程中破坏它。

The problem is that you are not keeping any persistent reference to the Ctrl() instance you are creating.问题是您没有保留对正在创建的Ctrl()实例的任何持久引用。 This results in python garbage collecting it as soon as the instance is created.这会导致 Python 在实例创建后立即对其进行垃圾收集。

To solve the issue, just assign it to a variable:要解决这个问题,只需将它分配给一个变量:

def main():
    """Main function."""
    # Create an instance of `QApplication`
    app = QApplication(sys.argv)
    # Show the app's GUI
    view = UI()
    view.show()
    setup = {}
    # Create instance of the controller
    ctrl = Ctrl(setup=setup, view=view)
    # Execute app's main loop
    sys.exit(app.exec_())

Some considerations:一些考虑:

  • while separating logic from interface is usually good practice, it's a concept that needs to be used with care, as sometimes it only makes things more complex than they should be.虽然将逻辑与界面分离通常是一种很好的做法,但这是一个需要谨慎使用的概念,因为有时它只会使事情变得比应有的复杂。 Most of the times (especially with simple programs), it only makes a bigger codebase without giving any actual benefit: it's harder to read and to debug, and you'll probably end up continuously switching from the logic parts and the ui parts of your code;大多数时候(尤其是简单的程序),它只会产生更大的代码库而没有带来任何实际好处:更难阅读和调试,而且您可能最终会不断地从逻辑部分和用户界面部分切换代码;
  • your code shows one of the drawback of that concept: when you create the file dialog, you're using self , but in that case it refers to the Ctrl instance, while the argument should be the UI instance instead (which will result in a crash, as Qt will get an unexpected argument type);您的代码显示了该概念的缺点之一:当您创建文件对话框时,您使用的是self ,但在这种情况下,它指的是Ctrl实例,而参数应该是UI实例(这将导致崩溃,因为 Qt 会得到一个意想不到的参数类型); you can use self._view instead, but, as said, the whole separation in this case just makes things unnecessarily complex;您可以使用self._view代替,但是, self._view ,在这种情况下,整个分离只会使事情变得不必要地复杂;
  • using strings for dictionary keys that refer to internal objects is rarely a good idea (especially when using long descriptive strings like you did);使用字符串作为引用内部对象的字典键很少是一个好主意(尤其是当像你一样使用长描述性字符串时);
  • when importing more than one element from a module, it's usually better to group them instead of using single line imports: it makes the code tidier and easier to read and inspect: from PyQt5.QtWidgets import (QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget, QFileDialog)从一个模块导入多个元素时,通常最好将它们分组而不是使用单行导入:它使代码更整洁,更易于阅读和检查: from PyQt5.QtWidgets import (QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget, QFileDialog)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM