Откройте диалог сохранения файла PyQt5 из блокнота jupyter, чтобы сохранить рисунок matplotlib - PullRequest
0 голосов
/ 01 апреля 2020

Что я хочу сделать

Я пытаюсь создать интерактивный сюжет для Jupyter Notebook. Все функции написаны в разных файлах, но они предназначены для использования в интерактивных сеансах записной книжки. У меня есть виджет Button на рисунке matplotlib, который, при нажатии которого я хочу открыть диалоговое окно файла, где пользователь может ввести имя файла, чтобы сохранить рисунок. Я нахожусь на Ma c 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.

1 Ответ

0 голосов
/ 02 апреля 2020

Я нашел очень дрянное, не чистое решение, но, похоже, оно работает. По какой-то причине открытие QFileDialog напрямую не позволяет мне активировать его. Он открывается за активным окном, из которого он был вызван (окно терминала или браузер в Jupyter Notebook) и не отвечает на клавиатуру. Таким образом, функция save в следующем блоке НЕ работает должным образом для Ma c:

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 . Также попробовал это , безуспешно. Диалоговое окно файла остается за окном браузера и не отвечает на клавиатуру.

...