【问题标题】:Menubar sometimes does not become un-greyed when QFileDialog closes当 QFileDialog 关闭时,菜单栏有时不会变灰
【发布时间】:2021-10-23 03:49:56
【问题描述】:

操作系统:W10。这可能很重要。如果您在不同的平台上得到不同的结果,反馈会很有帮助。

这是一个 MRE。如果您运行它并按 Ctrl+O,菜单标签将变为灰色。如果您通过单击“打开”按钮或使用其助记符 (Alt+O) 在QFileDialog 中选择一个文件,则打开文件对话框将关闭,“文件”和“帮助”菜单将变为非灰色。

但是,如果您再次按 Ctrl+O,这次在“文件名”框中输入文件名(QLineEdit),然后按回车键,对话框将关闭(选择成功) 但“文件”和“帮助”菜单仍然是灰色的。它看起来像这样:

import sys, os 
from PyQt5 import QtWidgets, QtCore, QtGui

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Greying of menus MRE')
        self.setGeometry(QtCore.QRect(100, 100, 400, 200))

        menubar = QtWidgets.QMenuBar(self)
        self.setMenuBar(menubar)
        self.files_menu = QtWidgets.QMenu('&Files', self)
        menubar.addMenu(self.files_menu)
        self.help_menu = QtWidgets.QMenu('&Help', self)
        menubar.addMenu(self.help_menu)
        self.new_action = QtWidgets.QAction('&New', self)
        self.files_menu.addAction(self.new_action)
        self.open_action = QtWidgets.QAction('&Open', self)
        self.files_menu.addAction(self.open_action)
        self.open_action.setShortcut("Ctrl+O")
        self.open_action.triggered.connect(self.open_file)
                
    def focusInEvent(self, event ):
        print('main_window focusInEvent')
        super().focusInEvent(event)

    def focusOutEvent(self, event ):
        print('main_window focusOutEvent')
        super().focusInEvent(event)
        
    def activateWindow(self):
        print('main_window activateWindow')
        super().activateWindow()    
        
    def open_file(self):
        print('open file')
        
        main_window_self = self

        # open_doc_dialog = QtWidgets.QFileDialog(self.get_main_window())
        class OpenDocFileDialog(QtWidgets.QFileDialog):
            def accepted(self):
                print('accepted')
                super().accepted()

            def accept(self):
                print('accept')
                super().accept()
        
            def close(self):
                print('close')
                super().close()
        
            def done(self, r):
                print(f'done r {r}')
                
                # neither of these solves the problem:
                # main_window_self.activateWindow()
                # main_window_self.files_menu.activateWindow()
                super().done(r)
        
            def hide(self):
                print(f'hide')
                super().hide()
                
            def focusInEvent(self, event ):
                print('focusInEvent')
                super().focusInEvent(event)
        
            def focusOutEvent(self, event ):
                print('focusOutEvent')
                super().focusInEvent(event)
                
            def activateWindow(self):
                print('activateWindow')
                super().activateWindow()    
                
        
        open_doc_dialog = OpenDocFileDialog(self)
        open_doc_dialog.setWindowTitle('Choose file')
        open_doc_dialog.setDirectory(os.getcwd())
        # we cannot use the native dialog, because we need control over the UI
        options = open_doc_dialog.Options(open_doc_dialog.DontUseNativeDialog)
        open_doc_dialog.setOptions(options)
        open_doc_button = open_doc_dialog.findChild(QtWidgets.QDialogButtonBox).button(QtWidgets.QDialogButtonBox.Open)
        lineEdit = open_doc_dialog.findChild(QtWidgets.QLineEdit)
        
        # this does not solve the problem
        # lineEdit.returnPressed.disconnect()
        # lineEdit.returnPressed.connect(open_doc_button.click)
        
        print(f'open_doc_button {open_doc_button}, lineEdit {lineEdit}')
        # show the dialog
        dialog_code = open_doc_dialog.exec()
        if dialog_code != QtWidgets.QDialog.Accepted: return
        sel_files = open_doc_dialog.selectedFiles()
        print(f'sel_files: {sel_files}')

                
app = QtWidgets.QApplication([])
main_window = MainWindow()
main_window.show()
sys.exit(app.exec())    

这个问题可以理解,如果不解决,可以参考this answer

请注意,此灰显并非禁用。正如上面链接中所解释的,这与菜单(或其标签)的“活动/非活动状态”有关。菜单始终处于启用状态,尽管在这种情况下,由于它是模态的,所以在显示打开文件对话框时不可能知道。在对话框消失后单击一个菜单,或者只是将鼠标悬停在它上面,就足以将它们都取消灰色...

据我了解,解释是“文件名”框QLineEdit 有一个信号returnPressed,它似乎激活了与使用“选择”时调用的插槽略有不同的东西按钮。你可以看到我已经尝试过尝试重新连接该信号,但无济于事。

QFileDialog 的方法done 似乎被调用,但是对话框关闭(不像close!),所以我尝试“激活”主窗口......然后是个人QMenus。 .. 不起作用。

我不清楚如何处理这种“活动状态”业务,或者为什么连接到returnPressed 的插槽(似乎)无法在另一个插槽设法将“活动状态”返回菜单时这样做。

编辑
搜索 Musicamante 的“未抛光”建议让我想到了这一点:

lineEdit.returnPressed.disconnect()
def return_pressed():
    style = main_window_self.menubar.style()
    style.unpolish(main_window_self.menubar)
    open_doc_button.click()
lineEdit.returnPressed.connect(return_pressed)

...不幸的是,这不起作用。

【问题讨论】:

  • 链接的问题似乎并不完全相关,因为问题与非阻塞对话框有关(这是设计上的正确行为)。您能否确认 QFileDialog 的静态方法也有相同的行为,无论是原生的还是非原生的?同时,作为一种解决方法,您可以在对话框关闭后立即取消抛光,从而强制重新绘制菜单栏。
  • 谢谢。本机文件选择器不会出现此问题。我只是说另一个问题是相关的,如果没有找到它,我仍然会更加摸不着头脑,因为我还没有听说过QWidget 的这种“活动/非活动状态”。你能解释一下你所说的“不抛光”技术是什么意思吗?也许您可以将其作为答案?
  • @mikerodent 我似乎无法在 linux 上重现这个,但我可能误解了这个问题。据我所知,问题是当对话框关闭时主窗口仍然处于非活动状态。但它是真的不活跃,还是只是看起来那样?也就是说,您是否可以在关闭对话框后立即使用键盘与窗口进行交互,还是只是窗口没有正确重绘?
  • 啊,也许是另一个Windoze“奇点”。是的,这完全是关于外表!键盘交互工作正常,实际上在对话框关闭后按 Alt-F 不仅显示“文件”菜单的菜单项,而且“重新激活”两个菜单的标签。 PS 抱歉缺少导入:将修改!
  • 看起来像一个错误,那么。如果您要尝试强制重新绘制的解决方法,您可能需要等到事件循环将控制权返回给主窗口。 single-shot timer 稍有延迟就可以解决问题。

标签: python user-interface pyqt5 windows-10 qfiledialog


【解决方案1】:

这看起来可能是与 Windows 相关的错误,因为我无法在 Linux 上重现它。作为一种解决方法,您可以尝试在对话框关闭后强制重新绘制:

# show the dialog
dialog_code = open_doc_dialog.exec()
self.menubar.repaint()

【讨论】:

    【解决方案2】:

    终于搞定了,感谢 Musicamante 的建议:

    lineEdit.returnPressed.disconnect()
    def return_pressed():
        style = main_window_self.menubar.style()
        style.unpolish(main_window_self.menubar)
        open_doc_button.click()
        main_window_self.menubar.repaint()
    lineEdit.returnPressed.connect(return_pressed)
    

    ...我实际上尝试了几次,只是为了确保它按照预期进行。所以事实上,幸运的是,在这种情况下不需要单次计时器。

    【讨论】:

    • 我怀疑大部分代码都是不必要的。为什么不在exec() 返回后重新绘制? (另外,我只建议使用单次计时器,因为您最初说此解决方案不起作用 - 所以它不再真正相关)。
    • 再次正确:在exec 之后执行self.menubar.repaint() 就是答案:您可能希望将其作为答案,因为它与未来的观众更相关...
    • 嗯,你完成了大部分实际工作 - 我们只是提供了一些提示 ;-) 你也接受你自己的答案。
    • 哈哈,我不确定我是否同意:在exec 之后我永远不会想出那个单行字,我可以很确定!我会给你一个小时左右的时间来决定你是否想要这些分数,之后我会相应地编辑我的答案。
    • @mikerodent 我建议使用 unpolish,因为我不确定(而且,正如我和 ekhumoro 所建议的,您只需要在 exec 之后),但请记住,它很少需要:大多数有时您仅在更改样式上的某些内容时才需要它(包括更改样式表中用作选择器的属性,因为它们只被评估一次);通常在这样做之前,您可以按顺序尝试简单的updaterepaint(它们都在 specific 小部件上,例如,滚动的视口基于区域的小部件,如项目视图、图形视图或文本区域)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-16
    • 1970-01-01
    • 2015-10-02
    相关资源
    最近更新 更多