#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.