【问题标题】:Open PyQt5 save file dialog from jupyter notebook to save matplotlib figure从 jupyter notebook 打开 PyQt5 保存文件对话框以保存 matplotlib 图
【发布时间】:2020-04-01 19:31:58
【问题描述】:

我想做的事

我正在尝试为 Jupyter Notebook 制作交互式绘图。这些函数都写在不同的文件中,但它们的预期用途是在交互式笔记本会话中。我在 matplotlib 图上有一个 Button 小部件,单击它时,我想打开一个文件对话框,用户可以在其中输入文件名以将图保存到。我在 Mac OSX (Mojave 10.14.6) 上,Tkinter 给我带来了重大问题(完全系统崩溃),所以我正在尝试用 PyQt5 来实现它。

代码

-----------
plotting.py
-----------
from . import file_dialog as fdo
import matplotlib.pyplot as plt
import matplotlib.widgets as wdgts

def plot_stack(stack):
    fig, ax = plt.subplots(figsize=(8, 6))
    plt.subplots_adjust(bottom=0.25, left=-0.1)

    ...  # plotting happens here

    # button for saving
    def dosaveframe(event):
        fname = fdo.save()
        fig.savefig(fname) # to be changed to something more appropriate

    savea = plt.axes([0.65, 0.8, 0.15, 0.05], facecolor=axcolor)
    saveb = Button(savea, "save frame", hovercolor="yellow")
    saveb.on_clicked(dosaveframe)
    savea._button = saveb  # for persistence

    plt.show()

--------------
file_dialog.py
--------------
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import (QWidget, QFileDialog)

class SaveFileDialog(QWidget):

    def __init__(self, text="Save file", types="All Files (*)"):
        super().__init__()
        self.title = text
        self.setWindowTitle(self.title)
        self.types = types
        self.filename = self.saveFileDialog()
        self.show()

    def saveFileDialog(self):
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        filename, _ = (
            QFileDialog.getSaveFileName(self, "Enter filename",
                                        self.types, options=options))
        return filename

def save(directory='./', filters="All files (*)"):
    """Open a save file dialog"""
    app = QApplication([directory])
    ex = SaveFileDialog(types=filters)
    return ex.filename
    sys.exit(app.exec_())

什么不起作用

保存对话框打开,它响应鼠标,但不响应键盘。无论我是否选择小窗口,键盘都会保持与笔记本电脑的连接,因此当我按下“s”时,它会保存笔记本电脑。因此,用户无法输入文件路径。我怎样才能使这项工作?我有 Anaconda、PyQt 5.9.2、matplotlib 3.1.1、jupyter 1.0.0。

【问题讨论】:

  • 另一位用户刚刚发布了this question 一个非常相似的问题(问题不同,但提供的代码是错误的)。我不使用 Jupyter,所以我不能保证它会解决你的问题,但尽管如此,你绝对应该创建一个 QWidget 来显示一个 QFileDialog,也不应该等待一个阻塞函数就像__init__ 中的getSaveFileName 一样(这可能是您问题的根源):只需调用该函数即可获取路径。另外,末尾的sys.exit 完全没用,因为它在return 之后。
  • 感谢您抽出宝贵时间回复我的问题。我应该提到我的小部件解决方案已经是一种解决方法:我之前尝试过的链接中描述的直接简单方法。在这种情况下,我认为文件对话框根本没有打开。然后我发现文件对话框是在浏览器窗口后面打开的。在这两种方法中,我都无法在文件对话框中输入。至少我已经发现这不是 jupyter notebook 独有的行为:如果我从命令行调用save,它还会打开一个我无法输入的文件对话框。会不会是 Mac 上的 Qt 错误?
  • Jupyter 是一个网络应用程序。 tkinter 和 Qt 是独立的应用程序,它们并不能很好地与 Jupyter 一起工作。我认为你可以使用 ipywidgets 在 Jupyter 中做你想做的事情。没有理由从 Jupyter 启动子进程以在 QT 或 tkinter 中运行单独的独立图形界面。

标签: matplotlib jupyter-notebook pyqt5 macos-mojave qfiledialog


【解决方案1】:

我找到了一个非常糟糕、不干净的解决方案,但它似乎有效。出于某种原因,直接打开QFileDialog 不允许我激活它。它在调用它的活动窗口(Jupyter Notebook 中的终端窗口或浏览器)后面打开,并且不响应键盘。因此,以下块中的 save 函数在 Mac 上无法按预期工作:

from PyQt5.QtWidgets import QApplication, QFileDialog

def save(directory='./', filters="All files (*)"):
    app = QApplication([directory])
    path, _ = QFileDialog.getSaveFileName(caption="Save to file",
                                          filter=filters,
                                          options=options)
    return path

如果文件对话框是从小部件打开的,那么会起作用。因此,使用一个从不显示在屏幕上的虚拟小部件对我来说确实有效,至少在命令行中是这样:

from PyQt5.QtWidgets import (QApplication, QFileDialog, QWidget)


class DummySaveFileDialogWidget(QWidget):

    def __init__(self, title="Save file", filters="All Files (*)"):
        super().__init__()
        self.title = title
        self.filters = filters
        self.fname = self.savefiledialog()

    def savefiledialog(self):
        filename, _ = QFileDialog.getSaveFileName(caption=self.title,
                                                  filter=self.filters,
                                                  options=options)
        return filename

def save(directory='./', filters="All files (*)"):
    app = QApplication([directory])
    form = DummySaveFileDialogWidget()
    return form.fname

如果有人找到更优雅的解决方案,请告诉我


编辑:当它从命令行调用时有效,但仍然不是从 Jupyter Notebook。也试过this,没有成功。文件对话框停留在浏览器窗口后面,不响应键盘。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多