Reputation: 871
I have implemented a matplotlib chart into my tkinter window, but every time I hit 'OK' and add it to the application, it resizes the entire window and also cuts off the axes ticks. I played around with figsize
and canvas
, but didn't manage to solve it. Does anyone have an explanation for this behaviour?
I have simplified the code so that it runs by clicking 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()
Upvotes: 3
Views: 879
Reputation: 173
I have had the same problem as you. I solved the problem by adding the following:
import ctypes
ctypes.windll.shcore.SetProcessDpiAwareness(1)
But keep in mind that this also has an effect on your tkinter window geometries.
Upvotes: 0
Reputation: 876
This one has been a thorn in my side for a while. I was getting the exact same behavior that you described until I included "ctypes.windll.shcore.SetProcessDpiAwareness(1)". It's a DPI issue, though there was no way within tkinter that I could find to fix it.
Per the Microsoft documentation for SetProcessDpiAwareness(value):
value: The DPI awareness value to set. Possible values are from the PROCESS_DPI_AWARENESS enumeration.
A "value" of 1 (as above) points to:
PROCESS_SYSTEM_DPI_AWARE: System DPI aware. This app does not scale for DPI changes. It will query for the DPI once and use that value for the lifetime of the app. If the DPI changes, the app will not adjust to the new DPI value. It will be automatically scaled up or down by the system when the DPI changes from the system value.
The example code below is based on a simplified version of what you provided and appears to fix the issue:
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()
Upvotes: 4