当我缩小窗口时,小部件变得不可见 - Tkinter - Python [英] Widgets become invisible when i shrink my window - Tkinter - Python

查看:37
本文介绍了当我缩小窗口时,小部件变得不可见 - Tkinter - Python的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在使用 Tkinter 和 python 为大学创建一个 GUI 应用程序.然而,我遇到了一个问题,当应用程序首次加载时,或者当我缩小窗口时,唯一可见的小部件是绘图仪(扩展画布)小部件.但是,如果我展开窗口,其他窗口将变得可见.

这是我的代码:

from assign2_support import *将 tkinter 作为 tk 导入从 tkinter 导入 *从 tkinter.messagebox 导入 *随机导入从 tkinter.filedialog 导入 askopenfilenamedef get_station_name(文件名):temp1 = list(filename.split("/"))temp = list((temp1[len(temp1) - 1]).split("."))返回温度[0]def isInDict(value, dic):如果在 dic 中的值:返回真别的:返回错误#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("最高温度")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("", self.onPlotClicked)self.plotter.bind("

如果有人不介意向我指出发生这种情况的原因,我将不胜感激,因为作业将在 4 小时后到期,而我不知道该怎么做.

assign2_support.py 文件代码:

<预><代码>## 支持赋值 2## 用于您的作业的导入将 tkinter 作为 tk 导入导入 os.path从 tkinter 导入文件对话框从 tkinter 导入消息框# 绘制线条和文本的颜色颜色 = ['#f90909'、'#ffa405'、'#c0c203'、'#1abd04'、'#058096'、'#042ee1'、'#d30af1','#ec06b3']def load_data_points(文件名):"""返回给定文件中包含的数据.load_data_points(str) ->字典(整数:浮动)"""fd = 打开(文件名,'r')数据 = {}对于 fd 中的行:零件 = line.split(',')数据[int(parts[0])] = float(parts[1])返回数据类文件扩展异常(异常):经过类站(对象):"""用于存储给定站点的年平均温度数据的类"""def __init__(self, stationfile):"""构造函数:Station(str)"""self._data = load_data_points(stationfile)键 = 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)如果不是 base.endswith('.txt'):提高(文件扩展异常())self._name = base.replace(".txt", "")def get_temp(self, year):"""返回给定年份的平均温度.get_temp(int) ->漂浮"""返回 self._data.get(year)def get_data_points(self):"""以年份顺序点列表形式返回数据get_data_points() ->列表((整数,浮点数))"""返回 [(year, self._data[year]) for year in sorted(self._data.keys())]def get_year_range(self):""" 返回数据中的年份范围get_year_range() ->(整数,整数)"""返回(self._min_year,self._max_year)def get_temp_range(self):"""返回数据中的温度范围get_temp_range() ->(浮动,浮动)"""返回(self._min_temp,self._max_temp)def get_name(self):返回 self._namedef __repr__(self):return "Station({0})".format(self._name)类坐标转换器(对象):"""一个管理将数据值转换为 (x, y) 坐标的类.该应用程序管理真实世界的数据(年份、温度),但 Canvas绘图需要 (x, y) 坐标.这节课在两者之间转换."""def __init__(self, width, height, min_year, max_year, min_temp, max_temp):"""创建一个具有给定画布宽度/高度的 CoordinateTranslator,最小和最大的年份和最小和最大温度构造函数:CoordinateTranslator(int, int, int, int, float, float)"""self._min_year = min_yearself._max_year = max_yearself._min_temp = min_tempself._max_temp = max_tempself.resize(宽度,高度)定义调整大小(自我,宽度,高度):"""调整缩放因子以考虑新的宽度/高度.Canvas 调整大小后,调用此方法修复缩放."""self._xscale = (self._max_year - self._min_year)/宽度self._yscale = (self._max_temp - self._min_temp)/高度self._width = 宽度self._height = 高度def temperature_coords(self, year, temperature):"""给定一年和一个温度,返回 (x, y) 坐标绘制.温度坐标(整数,浮点数)->(浮动,浮动)"""return ((year - self._min_year)/self._xscale,self._height - (温度 - self._min_temp)/self._yscale)def get_year(self, x):"""给定 Canvas 上的 x 坐标,返回它所在的年份对应.get_year(float) ->整数"""返回 int(x * self._xscale + 0.5) + self._min_year## CSSE7030def best_fit(点):"""给定点是由 x 排序的 (x,y) 点列表此函数计算该范围内的最佳直线拟合,并且返回线端点的坐标.best_fit(list((floatt, float)) -> ((float, float), (float, float))"""计数 = len(点)如果计数== 0:# 需要避免被零除# 返回一些绘制后不会出现在屏幕上的东西返回 ((-1,-1), (-1, -1))x_values = [x 代表 x,_ 以点为单位]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/计数y_mean = sum_y/计数斜率 = (sum_xy - sum_x * y_mean)/(sum_x2 - sum_x * x_mean)y_inter = y_mean - 斜率 * x_mean返回 ((x_values[0], 斜率 * x_values[0] + y_inter),(x_values[-1], 斜率 * x_values[-1] + y_inter))

谢谢堆科里 :)

解决方案

您正在创建一个要求大小为 850x400 的画布.您正在将窗口大小固定为 800x400.因为窗口中没有足够的空间来容纳所有东西,Tkinter 必须开始减少小部件,或从视图中删除小部件.它不会尝试将小部件缩小到其请求的大小以下,因此它不会缩小您的画布.因此,下一个选项是开始从视图中隐藏小部件.

当 tkinter 必须开始从视图中隐藏部分或全部小部件时,它从装箱单"中最后一个小部件开始——最后一个小部件调用了 pack(...).因此,如果您最后打包画布,底部帧之前,它将开始缩小到低于其要求的尺寸.

一个简单的解决方法是移除画布的宽度和高度属性,同时移除 上的绑定.这让 tkinter 决定画布的大小,如果设置正确,这意味着它会增长和缩小以适应可用空间.

您还可以将画布的包装保存到最后,这使其成为第一个在空间不足时开始砍掉"的小部件.

有关打包算法的完整说明,请参阅http://tcl.tk/man/tcl8.5/TkCmd/pack.htm#M26

I have been creating a GUI application for university using Tkinter and python. I am however having a problem where when the application first loads, or when i make the window smaller, the only widget visible is the Plotter (extends canvas) widget. If i expand the window however, the others become visible.

This is my code:

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()

If someone wouldnt mind pointing out to me why this is happening, i would much appreciate it, as the assignment is due in 4 hours, and i have no idea what to do.

EDIT: assign2_support.py file code:

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

Thanks heaps Corey :)

解决方案

You are creating a canvas with a requested size of 850x400. You are fixing the window size to be 800x400. Because there's not enough room in the window to fit everything in, Tkinter has to start reducing widgets, or removing widgets from view. It won't attempt to reduce a widget below its requested size, so it's not going to shrink your canvas. So, it's next option is to start hiding widgets from view.

When tkinter has to start hiding part or all of a widget from view, it starts with the widget last in the "packing list" -- the last widget to have called pack(...). Thus, if you pack the canvas last, before the bottom frame, it will be the one that starts getting shrunk below its requested size.

A simple fix is to remove the width and height attributes of the canvas, and also remove the binding on <Configure>. This lets tkinter decide the size of the canvas, which when set up properly, means that it will grow and shrink to fit the available space.

You can also save packing of the canvas to the very last, which makes it the first widget to start getting "chopped off" when there isn't enough room.

For the complete description of the packing algorithm see http://tcl.tk/man/tcl8.5/TkCmd/pack.htm#M26

这篇关于当我缩小窗口时,小部件变得不可见 - Tkinter - Python的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆