диаграмма matplotlib сжимает окно tkinter

#python #matplotlib #tkinter

#python #matplotlib #tkinter

Вопрос:

Я внедрил диаграмму matplotlib в свое окно tkinter, но каждый раз, когда я нажимаю «ОК» и добавляю ее в приложение, она изменяет размер всего окна, а также обрезает метки осей. Я поиграл с figsize и canvas , но не смог ее решить. У кого-нибудь есть объяснение такого поведения?

Я упростил код, чтобы он выполнялся, нажав OK.

Прежде чем нажать OK:

Перед

После нажатия кнопки OK:

После

 from tkinter import *
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

class StockApp(Frame):
    def __init__(self, master):
        super(StockApp, self).__init__(master)
        self.place()
        self.widgets()

    def widgets(self):
        # width * height
        root.geometry('500x400')
        root.configure(bg='#FFFFFF')

        # Header
        header_label = Label(text='Stock Prices', font=(
            'Calibri', 22, 'bold'), bg='#FFFFFF')
        header_label.place(x=30, y=15)

        # Enter your API key
        api_key_label = Label(text='API key', font=(
            'Calibri', 10), bg='#FFFFFF')
        api_key_label.place(x=30, y=65)

        self.api_key_field = Entry(
            width=32, font=('Calibri', 10), bg='#F4F4F4')
        self.api_key_field.config(show="*")
        self.api_key_field.place(x=30, y=90)

        # Enter an index
        index_label = Label(text='Stock index', font=(
            'Calibri', 10), bg='#FFFFFF')
        index_label.place(x=280, y=65)

        self.index_field = Entry(width=15, font=('Calibri', 10), bg='#F4F4F4')
        self.index_field.place(x=280, y=90)

        # OK button
        ok_btn = Button(text='OK', command=self.ok, font=(
            'Calibri', 8), bg='#F4F4F4', width=5)
        ok_btn.place(x=400, y=88)

    def call_api(self):
        pass
       
    def format_df(self):

        self.df = pd.DataFrame({'date': ['2020-11-06', '2020-11-07', '2020-11-08', '2020-11-09'], 'adj_close': [200, 210, 205, 215]})
        self.df['date'] = pd.to_datetime(self.df['date'])
        self.df.set_index('date', inplace = True)

    def draw_chart(self):

        #plot data
        fig, ax = plt.subplots(figsize=(2,1), dpi=50)
        self.df.plot(ax=ax)

        # #set major ticks format
        ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))

        ax.spines['top'].set_visible(False)
        ax.spines['right'].set_visible(False)

        ax.get_legend().remove()

        canvas = FigureCanvasTkAgg(fig, master=root)
        canvas.draw() # TK-Drawingarea
        # Do I need to place both?
        canvas.get_tk_widget().place(x = 30, y = 150)
        canvas._tkcanvas.place(x = 30, y = 150)

    def ok(self):
        self.call_api()
        self.format_df()
        self.draw_chart()


if __name__ == "__main__":
    root= Tk()
    app= StockApp(root)
    root.title('Stock prices')
    mainloop()
  

Комментарии:

1. figsize=(2,1) Слишком маленький. Попробуйте увеличить значения. И вы использовали place() для размещения виджетов, это не уменьшит окно.

2. Если я изменю figsize на что-то большее, например (10, 5), диаграмма даже больше не помещается в окно, и ее размер все равно изменяется.

3. Как показано на рисунках, окно не сжимается графиком.

4. Да, я протестировал ваш код и не могу воспроизвести вашу проблему.

5. Достаточно справедливо. Тогда это странно, но я мог бы попытаться запустить его на другой машине. Редактировать: я просто запустил его в Jupyter Notebook, и он работает просто отлично. Очень странно.

Ответ №1:

Это было бельмом на глазу некоторое время. Я получал точно такое же поведение, которое вы описали, пока не включил «ctypes.windll.shcore.SetProcessDpiAwareness(1)». Это проблема с DPI, хотя в tkinter я не мог найти способа ее исправить.

Согласно документации Microsoft для SetProcessDpiAwareness(значение):

значение: значение DPI для настройки. Возможные значения взяты из перечисления PROCESS_DPI_AWARENESS .

«Значение», равное 1 (как указано выше), указывает на:

PROCESS_SYSTEM_DPI_AWARE: система поддерживает DPI. Это приложение не масштабируется для изменения точек на дюйм. Он будет запрашивать DPI один раз и использовать это значение в течение всего срока службы приложения. Если DPI изменится, приложение не будет адаптироваться к новому значению DPI. Она будет автоматически увеличиваться или уменьшаться системой при изменении DPI по сравнению с системным значением.

Приведенный ниже пример кода основан на упрощенной версии предоставленного вами и, похоже, устраняет проблему:

 from tkinter import *
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import ctypes


class StockApp(Frame):
    def __init__(self, master):
        self.master = master
        super(StockApp, self).__init__(master)
        self.place()
        self.widgets()

    def widgets(self):
        # width * height
        root.geometry('500x400')

        # OK button
        ok_btn = Button(text='OK', command=self.ok, bg='#F4F4F4', width=5)
        ok_btn.pack(side=tk.TOP)

    def format_df(self):
        self.df = pd.DataFrame({'date': ['2020-11-06', '2020-11-07', '2020-11-08', '2020-11-09'], 'adj_close': [200, 210, 205, 215]})
        self.df['date'] = pd.to_datetime(self.df['date'])
        self.df.set_index('date', inplace=True)

    def draw_chart(self):
        fig, ax = plt.subplots(figsize=(2, 1), dpi=100)
        self.df.plot(ax=ax)

        canvas = FigureCanvasTkAgg(fig, master=root)
        canvas.draw()
        canvas.get_tk_widget().pack(side=tk.TOP)

    def ok(self):
        self.format_df()
        self.draw_chart()


if __name__ == "__main__":
    ctypes.windll.shcore.SetProcessDpiAwareness(1)
    root= Tk()

    app= StockApp(root)
    root.title('Stock prices')
    mainloop()
  

Ответ №2:

У меня была та же проблема, что и у вас. Я решил проблему, добавив следующее:

 import ctypes
 
ctypes.windll.shcore.SetProcessDpiAwareness(1)
  

Но имейте в виду, что это также влияет на геометрию вашего окна tkinter.