简体   繁体   English

QFileDialog.getOpenFileName 将按钮文本从“打开”更改为“删除”

[英]QFileDialog.getOpenFileName change button text from 'Open' to 'Remove'

I am using QFileDialog.getOpenFileName(self,'Remove File', "path", '*.pdf') to select a file and get the path in order to remove it from my application.我正在使用QFileDialog.getOpenFileName(self,'Remove File', "path", '*.pdf')到 select 一个文件并获取路径以便将其从我的应用程序中删除。 The issue is the QFileDialog.getOpenFileName window button says 'Open' when selecting a file which will be confusing to the user.问题是 QFileDialog.getOpenFileName window 按钮在选择文件时显示“打开”,这会让用户感到困惑。

Is there any way to change the button text from 'Open' to 'Remove'/'Delete'有什么方法可以将按钮文本从“打开”更改为“删除”/“删除”

When using the static method QFileDialog::getOpenFileName() the first thing is to obtain the QFileDialog object and for that we use a QTimer and the findChild() method:当使用静态方法QFileDialog::getOpenFileName() ,第一件事是获取QFileDialog对象,为此我们使用QTimerfindChild()方法:

    # ...
    QtCore.QTimer.singleShot(0, self.on_timeout)
    filename, _ = QtWidgets.QFileDialog.getOpenFileName(...,
        options=QtWidgets.QFileDialog.DontUseNativeDialog)

def on_timeout(self):
    dialog = self.findChild(QtWidgets.QFileDialog)
    # ...

Then you can get the text iterating over the buttons until you get the button with the searched text and change it:然后你可以让文本在按钮上迭代,直到你得到带有搜索文本的按钮并更改它:

for btn in dialog.findChildren(QtWidgets.QPushButton):
    if btn.text() == "&Open":
        btn.setText("Remove")

That will work at the beginning but every time you interact with the QTreeView they show, update the text to the default value, so the same logic will have to be applied using the currentChanged signal from the selectionModel() of the QTreeView but for synchronization reasons it is necessary Update the text later using another QTimer.singleShot() , the following code is a workable example:这将在开始时起作用,但每次与它们显示的QTreeView交互时,将文本更新为默认值,因此必须使用来自QTreeViewselectionModel()currentChanged信号应用相同的逻辑,但出于同步原因有必要稍后使用另一个QTimer.singleShot()更新文本,以下代码是一个可行的示例:

import sys

from PyQt5 import QtCore, QtWidgets


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        button = QtWidgets.QPushButton("Press me")
        button.clicked.connect(self.on_clicked)

        lay = QtWidgets.QVBoxLayout(self)
        lay.addWidget(button)

    @QtCore.pyqtSlot()
    def on_clicked(self):
        QtCore.QTimer.singleShot(0, self.on_timeout)
        filename, _ = QtWidgets.QFileDialog.getOpenFileName(
            self,
            "Remove File",
            "path",
            "*.pdf",
            options=QtWidgets.QFileDialog.DontUseNativeDialog,
        )

    def on_timeout(self):
        dialog = self.findChild(QtWidgets.QFileDialog)
        dialog.findChild(QtWidgets.QTreeView).selectionModel().currentChanged.connect(
            lambda: self.change_button_name(dialog)
        )
        self.change_button_name(dialog)

    def change_button_name(self, dialog):
        for btn in dialog.findChildren(QtWidgets.QPushButton):
            if btn.text() == self.tr("&Open"):
                QtCore.QTimer.singleShot(0, lambda btn=btn: btn.setText("Remove"))


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)

    w = Widget()
    w.show()

    sys.exit(app.exec_())

The first step can be avoided if the static method is not used and create the dialog using an instance of QFileDialog :如果不使用静态方法并使用QFileDialog的实例创建对话框,则可以避免第一步:

import sys

from PyQt5 import QtCore, QtWidgets


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        button = QtWidgets.QPushButton("Press me")
        button.clicked.connect(self.on_clicked)

        lay = QtWidgets.QVBoxLayout(self)
        lay.addWidget(button)

    @QtCore.pyqtSlot()
    def on_clicked(self):
        dialog = QtWidgets.QFileDialog(
            self,
            "Remove File",
            "path",
            "*.pdf",
            supportedSchemes=["file"],
            options=QtWidgets.QFileDialog.DontUseNativeDialog,
        )
        self.change_button_name(dialog)
        dialog.findChild(QtWidgets.QTreeView).selectionModel().currentChanged.connect(
            lambda: self.change_button_name(dialog)
        )
        if dialog.exec_() == QtWidgets.QDialog.Accepted:
            filename = dialog.selectedUrls()[0]
            print(filename)

    def change_button_name(self, dialog):
        for btn in dialog.findChildren(QtWidgets.QPushButton):
            if btn.text() == self.tr("&Open"):
                QtCore.QTimer.singleShot(0, lambda btn=btn: btn.setText("Remove"))


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)

    w = Widget()
    w.show()

    sys.exit(app.exec_())

While I appreciate the solution provided by @eyllanesc , I'd like to propose a variation.虽然我很欣赏@eyllanesc 提供的解决方案,但我想提出一个变体。

Under certain circumstances, the code for that answer might fail, specifically:在某些情况下,该答案的代码可能会失败,特别是:

  • the delay that X11 suffers from mapping windows; X11遭受映射windows的延迟;
  • the checking of localized button strings;检查本地化的按钮字符串;
  • the selection using the file name edit box;使用文件名编辑框进行选择;

Considering the above, I propose an alternate solution, based on the points above.考虑到上述情况,我根据以上几点提出了一个替代解决方案。

For obvious reasons, the main point remains: the dialog must be non-native.出于明显的原因,要点仍然存在:对话框必须是非本地的。

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

class FileDialogTest(QWidget):
    def __init__(self):
        super().__init__()
        layout = QHBoxLayout(self)

        self.fileEdit = QLineEdit()
        layout.addWidget(self.fileEdit)

        self.selectBtn = QToolButton(icon=QIcon.fromTheme('folder'),  text='…')
        layout.addWidget(self.selectBtn)

        self.selectBtn.clicked.connect(self.showSelectDialog)

    def checkSelectDialog(self):
        dialog = self.findChild(QFileDialog)
        if not dialog.isVisible():
            # wait for dialog finalization, as testOption might fail
            return

        # dialog found, stop the timer and delete it
        self.sender().stop()
        self.sender().deleteLater()

        if not dialog.testOption(dialog.DontUseNativeDialog):
            # native dialog, we cannot do anything!
            return
        
        def updateOpenButton():
            selection = tree.selectionModel().selectedIndexes()
            if selection and not tree.model().isDir(selection[0]):
                # it's a file, change the button text
                button.setText('Select my precious file')

        tree = dialog.findChild(QTreeView)
        button = dialog.findChild(QDialogButtonBox).button(
            QDialogButtonBox.Open)

        # check for selected files on open
        updateOpenButton()

        # connect the selection update signal
        tree.selectionModel().selectionChanged.connect(
            lambda: QTimer.singleShot(0, updateOpenButton))

    def showSelectDialog(self):
        QTimer(self, interval=10, timeout=self.checkSelectDialog).start()
        path, filter = QFileDialog.getOpenFileName(self, 
            'Select file', '<path_to_file>', 
            "All Files (*);;Python Files (*.py);; PNG Files (*.png)", 
            options=QFileDialog.DontUseNativeDialog)
        if path:
            self.fileEdit.setText(path)


if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    ex = FileDialogTest()
    ex.show()
    sys.exit(app.exec())

Obviously, for PyQt6 you'll need to use the proper Enum namespaces (ie QFileDialog.Option.DontUseNativeDialog , etc.).显然,对于 PyQt6,您需要使用正确的 Enum 名称空间(即QFileDialog.Option.DontUseNativeDialog等)。

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

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