我一直在使用Tkinter和python为大学创建一个GUI应用程序。 然而,我遇到了一个问题,即当应用程序首次加载时,或者当我使窗口变小时,唯一可见的小部件是Plotter(扩展画布)小部件。但是,如果我展开窗口,其他窗口就会变得可见。
这是我的代码:
from assign2_support import *
import tkinter as tk
from tkinter import *
from tkinter.messagebox import *
import random
from tkinter.filedialog import askopenfilename
def get_station_name(filename):
temp1 = list(filename.split("/"))
temp = list((temp1[len(temp1) - 1]).split("."))
return temp[0]
def isInDict(value, dic):
if value in dic:
return True
else:
return False
#TemperaturePlotApp class
class TemperaturePlotApp(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.stations = TemperatureData()
self.color = ['#f90909', '#ffa405', '#c0c203', '#1abd04', '#058096', '#042ee1',
'#d30af1','#ec06b3']
self.selected = dict()
self.usedColors = dict()
self.master.title("Max Temperature")
self.button = tk.Button(self, text="File", command=self.load_file, width=10)
self.button.pack(side = 'top', anchor = tk.W)
self.plotter = Plotter(self,width=850, height=400, bg="white", highlightthickness=0)
self.plotter.pack(fill='both', expand=tk.YES)
self.plotter.bind("<B1-Motion>", self.onPlotClicked)
self.plotter.bind("<Button 1>", self.onPlotClicked)
# tag all of the drawn widgets TODO delete
self.plotter.addtag_all("all")
self.df = DataFrame(self)
self.df.pack(fill = tk.X, anchor = tk.N, pady = 10)
self.sf = SelectionFrame(self)
self.sf.pack(fill = tk.X, anchor = tk.N)
self.pack(fill = 'both', side = 'left', expand = tk.YES)
def loadStation(self, stationName):
self.stations.load_data(stationName + ".txt")
def onPlotClicked(self, event):
x = event.x
year = self.ct.get_year(x)
self.df.setYear(year)
try:
self.plotter.delete(self.l)
except:
pass
self.l = self.plotter.create_line(x, 0, x, self.plotter.winfo_height(), fill = "black")
for s in self.stations.get_stations():
if self.selected[s] == True:
temp = self.stations.get_data()[s].get_temp(int(year))
print(temp)
self.df.setDatumText(s, temp)
def plotData(self):
self.plotter.delete(tk.ALL)
minY, maxY, minT, maxT = self.stations.get_ranges()
self.ct = CoordinateTranslator(self.plotter.winfo_width(),self.plotter.winfo_height(), minY, maxY, minT, maxT)
self.i = 0
data = self.stations.get_data()
for s in self.stations.get_stations():
firstRun = True
if s in self.usedColors:
pass
else:
self.usedColors[s] = random.choice(self.color)
if self.sf.isCheckButton(s) == False:
self.sf.addCheckButton(s, self.usedColors[s], lambda: self.toggleCheckButton(s))
self.selected[s] = self.stations.is_selected(self.i)
if self.selected[s] == True:
if self.df.isInDataFrameLabels(s) == False:
self.df.addDatum("", self.usedColors[s], s)
if self.df.isHidden(s) == True:
self.df.showDatum(s)
for d in data[s].get_data_points():
if firstRun:
self.lastX, self.lastY = self.ct.temperature_coords(d[0], d[1])
firstRun = False
else:
x, y = self.ct.temperature_coords(d[0], d[1])
self.plotter.create_line(self.lastX, self.lastY, x, y, fill = self.usedColors[s])
self.lastX = x
self.lastY = y
else:
self.df.hideDatum(s)
self.i = self.i + 1
def toggleCheckButton(self, stationName):
if self.selected[stationName] == True:
self.selected[stationName] = False
else:
self.selected[stationName] = True
self.plotData()
def load_file(self):
fname = askopenfilename(filetypes=([("Text files","*.txt")]))
if fname:
fn = get_station_name(fname)
self.loadStation(fn)
self.plotData()
try:
print(fname) # TODO Delete
except:
showinfo("Failed to read file", "failed to read file: " + fname)
return
# Start DataFrame class
class DataFrame(tk.Frame):
def __init__(self,parent, *args,**kwargs):
tk.Frame.__init__(self, parent,*args,**kwargs)
self.lb = dict()
self.l = tk.Label(self, text="Data for ")
self.l.pack(side = 'left')
self.year = tk.Label(self, text="")
self.year.pack(side = 'left')
self.hidden = dict()
def addDatum(self, txt, color, stationName):
l1 = tk.Label(self, text=txt, fg = color)
self.lb[stationName] = l1
l1.pack(side = 'left')
self.hidden[stationName] = False
def setDatumText(self, stationName, txt):
self.lb[stationName].configure(text = txt)
def hideDatum(self, stationName):
self.lb[stationName].pack_forget()
self.hidden[stationName] = True
def showDatum(self, stationName):
self.lb[stationName].pack(side = 'left')
self.hidden[stationName] = False
def isHidden(self, stationName):
return self.hidden[stationName]
def setYear(self, year):
self.year.configure(text = str(year) + ":")
def getDataFrameLabels(self):
return self.lb
def isInDataFrameLabels(self,stationName):
return isInDict(stationName, self.lb)
# Start SelectionFrame Class
class SelectionFrame(tk.Frame):
def __init__(self,parent,*args,**kwargs):
tk.Frame.__init__(self, parent,*args,**kwargs)
self.cb = dict()
self.l = tk.Label(self, text="Station Selection: ").pack(side = 'left')
def addCheckButton(self, text, color, com):
c = tk.Checkbutton(self, text = text, fg = color, activeforeground = color, command = com)
self.cb[text] = c
c.select()
c.pack(side = 'left')
def getCheckButtons(self):
return self.cb
def isCheckButton(self, stationName):
if stationName in self.cb:
return True
else:
return False
# Start Plotter Class
class Plotter(tk.Canvas):
def __init__(self, parent,*args,**kwargs):
Canvas.__init__(self,parent,**kwargs)
self.bind("<Configure>", self.on_resize)
self.height = self.winfo_reqheight()
self.width = self.winfo_reqwidth()
def on_resize(self,event):
# determine the ratio of old width/height to new width/height
wscale = float(event.width)/self.width
hscale = float(event.height)/self.height
self.width = event.width
self.height = event.height
# resize the canvas
self.config(width=self.width, height=self.height)
# rescale all the objects tagged with the "all" tag
self.scale("all",0,0,wscale,hscale)
#Begin TemperatureData class
class TemperatureData:
def __init__(self):
self._data = dict()
self._stationNames = list()
self._stationsSelected = list()
def load_data(self, filename):
station_name = get_station_name(filename)
self._stationNames.append(station_name)
self._stationsSelected.append(True)
station = Station(filename)
self._data[station_name] = station
def get_data(self):
return self._data
def toggle_selected(self, i):
if self._stationsSelected[i] == True:
self._stationsSelected[i] = False
else:
self._stationsSelected[i] = True
def is_selected(self, i):
return self._stationsSelected[i]
def get_stations(self):
return self._stationNames
def get_ranges(self):
min_year = None
max_year = None
min_temp = None
max_temp = None
for k, v in self._data.items():
if min_year == None or max_year == None or min_temp == None or max_temp == None:
min_year, max_year = v.get_year_range()
min_temp, max_temp = v.get_temp_range()
else:
t_min_year, t_max_year = v.get_year_range()
t_min_temp, t_max_temp = v.get_temp_range()
min_year = min(min_year, t_min_year)
max_year = max(max_year, t_max_year)
min_temp = min(min_temp, t_min_temp)
max_temp = max(max_temp, t_max_temp)
return (min_year, max_year, min_temp, max_temp)
#End TemperatureData class
# My support
def load_stations(stations_file):
"""Return the list of station names
load_stations() -> list(str)
"""
fd = open(stations_file, "r")
stations = []
for line in fd:
line = line.strip()
if not line:
continue
stations.append(line)
fd.close()
return stations
##################################################
# !!!!!! Do not change (or add to) the code below !!!!!
###################################################
def main():
root = tk.Tk()
app = TemperaturePlotApp(root)
app.pack()
root.geometry("800x400")
root.mainloop()
if __name__ == '__main__':
main()
如果有人不介意向我指出为什么会发生这种情况,我会非常感激,因为作业将在4小时内到期,我不知道该怎么办。
编辑: assign2_support.py文件代码:
#
# Support for assignment 2
#
# Imports for use in your assignment
import tkinter as tk
import os.path
from tkinter import filedialog
from tkinter import messagebox
# colours for drawing lines and text
COLOURS = ['#f90909', '#ffa405', '#c0c203', '#1abd04', '#058096', '#042ee1',
'#d30af1','#ec06b3']
def load_data_points(filename):
"""Return the data contained in the given file.
load_data_points(str) -> dict(int:float)
"""
fd = open(filename, 'r')
data = {}
for line in fd:
parts = line.split(',')
data[int(parts[0])] = float(parts[1])
return data
class FileExtensionException(Exception):
pass
class Station(object):
"""A class for storing yearly average temperature data for a given station
"""
def __init__(self, stationfile):
""" Constructor: Station(str)"""
self._data = load_data_points(stationfile)
keys = self._data.keys()
self._min_year = min(keys)
self._max_year = max(keys)
temps = self._data.values()
self._min_temp = min(temps)
self._max_temp = max(temps)
base = os.path.basename(stationfile)
if not base.endswith('.txt'):
raise(FileExtensionException())
self._name = base.replace(".txt", "")
def get_temp(self, year):
"""Return the temperature average for the given year.
get_temp(int) -> float
"""
return self._data.get(year)
def get_data_points(self):
"""Return the data as a list of points in year order
get_data_points() -> list((int, float))
"""
return [(year, self._data[year]) for year in sorted(self._data.keys())]
def get_year_range(self):
""" Return the range of years in the data
get_year_range() -> (int, int)
"""
return (self._min_year, self._max_year)
def get_temp_range(self):
"""Return the range of temperatures in the data
get_temp_range() -> (float, float)
"""
return (self._min_temp, self._max_temp)
def get_name(self):
return self._name
def __repr__(self):
return "Station({0})".format(self._name)
class CoordinateTranslator(object):
"""A class which manages translation of data values into (x, y) coordinates.
The application manages real-world data (year, temp), but the Canvas
drawings require (x, y) coordinates. This class
converts between the two.
"""
def __init__(self, width, height, min_year, max_year, min_temp, max_temp):
"""
Create a CoordinateTranslator with the given canvas width/height,
the smallest and largest years and
the smallest and largest temperatures
Constructor: CoordinateTranslator(int, int, int, int, float, float)
"""
self._min_year = min_year
self._max_year = max_year
self._min_temp = min_temp
self._max_temp = max_temp
self.resize(width, height)
def resize(self, width, height):
"""Adjust the scaling factors to account for a new width/height.
After the Canvas resizes, call this method to fix the scaling.
"""
self._xscale = (self._max_year - self._min_year) / width
self._yscale = (self._max_temp - self._min_temp) / height
self._width = width
self._height = height
def temperature_coords(self, year, temperature):
"""Given a year and a temperature,
return (x, y) coordinates to plot.
temperature_coords(int, float) -> (float, float)
"""
return ((year - self._min_year)/ self._xscale,
self._height - (temperature - self._min_temp) / self._yscale)
def get_year(self, x):
"""Given an x coordinate on the Canvas, return the year that it
corresponds to.
get_year(float) -> int
"""
return int(x * self._xscale + 0.5) + self._min_year
## CSSE7030
def best_fit(points):
"""Given points are a list of (x,y) points ordered by x
this function computes the best line fit over that range and
returns the coords of end points of the line.
best_fit(list((floatt, float)) -> ((float, float), (float, float))
"""
count = len(points)
if count == 0:
# needed to avoid division by zero
# return something that will not appear on screen if drawn
return ((-1,-1), (-1, -1))
x_values = [x for x, _ in points]
y_values = [y for _, y in points]
sum_x = sum(x_values)
sum_y = sum(y_values)
sum_x2 = sum(x**2 for x in x_values)
sum_y2 = sum(y**2 for y in y_values)
sum_xy = sum(x*y for x,y in points)
x_mean = sum_x/count
y_mean = sum_y/count
slope = (sum_xy - sum_x * y_mean) / (sum_x2 - sum_x * x_mean)
y_inter = y_mean - slope * x_mean
return ((x_values[0], slope * x_values[0] + y_inter),
(x_values[-1], slope * x_values[-1] + y_inter))
谢谢堆 科里:)
答案 0 :(得分:4)
您正在创建一个请求大小为850x400的画布。您正在将窗口大小修改为800x400。由于窗口中没有足够的空间来容纳所有内容,因此Tkinter必须开始减少小部件或从视图中删除小部件。它不会尝试将窗口小部件减少到其请求的大小以下,因此它不会缩小您的画布。因此,它的下一个选择是从视图开始隐藏窗口小部件。
当tkinter必须从视图中开始隐藏部分或全部窗口小部件时,它将从窗口小部件的最后一个开始在&#34;打包列表中#34; - 调用pack(...)
的最后一个小部件。因此,如果您最后打包画布,在底部框架之前,它将是开始缩小到其请求大小以下的那个。
一个简单的解决方法是删除画布的width和height属性,并删除<Configure>
上的绑定。这使得tkinter可以决定画布的大小,当正确设置时,它意味着它会增长和缩小以适应可用空间。
您还可以将画布的包装保存到最后,这使得它成为第一个开始获取&#34;切断&#34;什么时候没有足够的空间。
有关打包算法的完整说明,请参阅http://tcl.tk/man/tcl8.5/TkCmd/pack.htm#M26