I want to know how to create a grid that connects to an excel or text file, and when run, it shows who is supposed to be in that grid space for the day and can be updated daily within the excel file.
This is the code that I have/found but I want to connect this GUI to an excel file.
Thanks in advance for your attention!
from tkinter import *
class Cell():
FILLED_COLOR_BG = "green"
EMPTY_COLOR_BG = "white"
FILLED_COLOR_BORDER = "green"
EMPTY_COLOR_BORDER = "black"
def __init__(self, master, x, y, size):
self.master = master
self.abs = x
self.ord = y
self.size= size
self.fill= False
def _switch(self):
self.fill= not self.fill
def draw(self):
if self.master != None :
fill = Cell.FILLED_COLOR_BG
outline = Cell.FILLED_COLOR_BORDER
if not self.fill:
fill = Cell.EMPTY_COLOR_BG
outline = Cell.EMPTY_COLOR_BORDER
xmin = self.abs * self.size
xmax = xmin + self.size
ymin = self.ord * self.size
ymax = ymin + self.size
self.master.create_rectangle(xmin, ymin, xmax, ymax, fill = fill, outline = outline)
class CellGrid(Canvas):
def __init__(self,master, rowNumber, columnNumber, cellSize, *args, **kwargs):
Canvas.__init__(self, master, width = cellSize * columnNumber , height = cellSize * rowNumber, *args, **kwargs)
self.cellSize = cellSize
self.grid = []
for row in range(rowNumber):
line = []
for column in range(columnNumber):
line.append(Cell(self, column, row, cellSize))
self.grid.append(line)
self.switched = []
self.bind("<ButtonRelease-1>", lambda event: self.switched.clear())
self.draw()
def draw(self):
for row in self.grid:
for cell in row:
cell.draw()
def _eventCoords(self, event):
row = int(event.y / self.cellSize)
column = int(event.x / self.cellSize)
return row, column
def handleMouseClick(self, event):
row, column = self._eventCoords(event)
cell = self.grid[row][column]
cell._switch()
cell.draw()
self.switched.append(cell)
def handleMouseMotion(self, event):
row, column = self._eventCoords(event)
cell = self.grid[row][column]
if cell not in self.switched:
cell._switch()
cell.draw()
self.switched.append(cell)
if __name__ == "__main__" :
app = Tk()
grid = CellGrid(app, 5, 2, 100)
grid.pack()
app.mainloop()
self.bind("<Button-1>", self.handleMouseClick)
self.bind("<B1-Motion>", self.handleMouseMotion)
Related
I am imaging fluorescent cells from a 384-well plate and my software spits out a formatted excel analysis of the data (16 rowsx24 columns of images turns into a list of data, with 2 measurements from each well, ~800 data points). Because there is a lot of manual interaction with the data, I want to automate my work by taking the information that's indexed in the excel sheet and map it as a tkinter grid. I want to take my data, have it formatted from a list back to the original 16x24 display. I want to be able to interact with that data, among other things, but as a newbie in python, I'm having a difficult time mapping this data.
I used pandas to read my dataframe, but can't seem to display my list to the appropriate grid box. Ideally, I'd like the values in the excel list to display on the corresponding cell in the tkinter grid. For example, in my excel list, [1]:https://imgur.com/a/N8HAtYl, the two data points from each well are listed. I want to take the values from each cell and display them to the corresponding grid in tkinter, so the average of values of "A1" would be mapped onto column 1, row 1 of the grid, A2 would be col 2 row 1, A3 would be col 3 row 1, and so on and so forth.
Any help would be great, thank you.
from tkinter import *
import pandas as pd
from pandas import ExcelWriter
from pandas import ExcelFile
df = pd.read_excel('/Users/Zammam/PycharmProjects/Filipin_Analysis_Zammam/data.xlsx', sheet_name='Dataps')
print(()
class Cell():
FILLED_COLOR_BG = "green"
EMPTY_COLOR_BG = "white"
FILLED_COLOR_BORDER = "green"
EMPTY_COLOR_BORDER = "black"
def __init__(self, master, x, y, size):
self.master = master
self.abs = x
self.ord = y
self.size= size
self.fill= False
def _switch(self):
""" Switch if the cell is filled or not. """
self.fill= not self.fill
def draw(self):
if self.master != None :
fill = Cell.FILLED_COLOR_BG
outline = Cell.FILLED_COLOR_BORDER
if not self.fill:
fill = Cell.EMPTY_COLOR_BG
outline = Cell.EMPTY_COLOR_BORDER
xmin = self.abs * self.size
xmax = xmin + self.size
ymin = self.ord * self.size
ymax = ymin + self.size
self.master.create_rectangle(xmin, ymin, xmax, ymax, fill = fill, outline = outline)
class CellGrid(Canvas):
def __init__(self,master, rowNumber, columnNumber, cellSize, *args, **kwargs):
Canvas.__init__(self, master, width = cellSize * columnNumber , height = cellSize * rowNumber, *args, **kwargs)
self.cellSize = cellSize
self.grid = []
for row in range(rowNumber):
line = []
for column in range(columnNumber):
line.append(Cell(self, column, row, cellSize))
self.grid.append(line)
#memorize the cells that have been modified to avoid many switching of state during mouse motion.
self.switched = []
#bind click action
self.bind("<Button-1>", self.handleMouseClick)
#bind moving while clicking
self.bind("<B1-Motion>", self.handleMouseMotion)
#bind release button action - clear the memory of midified cells.
self.bind("<ButtonRelease-1>", lambda event: self.switched.clear())
self.draw()
def draw(self):
for row in self.grid:
for cell in row:
cell.draw()
def create_text(self):
for row in self.grid:
for cell in row:
for i in df:
cell.create_text(text = df)
def _eventCoords(self, event):
row = int(event.y / self.cellSize)
column = int(event.x / self.cellSize)
return row, column
def handleMouseClick(self, event):
row, column = self._eventCoords(event)
cell = self.grid[row][column]
cell._switch()
cell.draw()
#add the cell to the list of cell switched during the click
self.switched.append(cell)
def handleMouseMotion(self, event):
row, column = self._eventCoords(event)
cell = self.grid[row][column]
if cell not in self.switched:
cell._switch()
cell.draw()
self.switched.append(cell)
if __name__ == "__main__" :
app = Tk()
grid = CellGrid(app, 16, 24, 15)
grid.pack()
app.mainloop()
I'm not familiar with pandas so I will use openpyxl to demonstrate. It will probably be something similar with pandas or even simpler.
from openpyxl import load_workbook
import tkinter as tk
root = tk.Tk()
file = "your_excel_file.xlsx"
wb = load_workbook(file, data_only=True)
ws = wb.active
r = 0
for row in ws:
c = 0
for cell in row:
tk.Label(root,text=cell.value).grid(row=r,column=c)
c+=1
r+=1
root.mainloop()
A quick look at using pandas instead:
df = pd.read_excel('your_excel_file.xlsx',header=None)
for i, row in df.iterrows():
c = 0
for cell in row:
tk.Label(root, text=cell).grid(row=i, column=c)
c += 1
I am currently working on a chess program in python (Tkinter), I have got a board to generate with buttons so that I can click on pieces, I planned to use a canvas for each of the chess pieces and then move them around. Is there any way to make it so I can click through the canvas?
try:
from Tkinter import *
except:
from tkinter import *
class grid_piece():
def __init__(self, canvas, row, column, colour_boolean, size):
colour_1 = '#934904'
colour_2 = '#fce2c9'
self.row = row
self.column = column
grid_piece = Button(canvas, height = size // 100,
width = size // 50,
command=lambda: my_board.button_press([row, column]))
grid_piece.grid(row=row, column = column)
if colour_boolean:
grid_piece.configure(bg = colour_1, fg = colour_1)
else:
grid_piece.configure(bg = colour_2, fg = colour_2)
class pawn():
def __init__(self, size, canvas, position):
self.pawn_canvas = Canvas(canvas, width = size // 8, height = size // 8
)
self.pawn_canvas.grid(row = position[0], column = position[1])
class Board():
def __init__(self, master, size):
self.canvas = Canvas(master, width=size, height=size)
self.canvas.pack()
self.size = size
def generate_board(self):
colour_boolean = True
self.board_array = []
for column in range(8):
column_pieces = []
colour_boolean = not colour_boolean
for row in range(8):
colour_boolean = not colour_boolean
piece = grid_piece(self.canvas, row, column, colour_boolean, self.size)
column_pieces.append(piece)
self.board_array.append(column_pieces)
def display_pieces(self):
white_pieces = []
black_pieces = []
for x in range(8):
Pawn = pawn(self.size, self.canvas, [1, x])
white_pieces.append(Pawn)
def button_press(self, position):
print(position)
def onclick(*args):
print(*args)
root = Tk()
my_board = Board(root, 600)
my_board.generate_board()
my_board.display_pieces()
root.mainloop()
Here is an image of my board, the white squares are blank canvases that I will turn into objects once I create the pieces.
When I run Python script in python3 it works well but in python 2.7 I get an error and I should run this code in Python 2.7 :
this is my code:I want to test some algorithms on path-finding and want to create a window consisting of grids where I can drag my mouse while clicked to create walls and delete them with right click and drag
import Tkinter
class Cell():
FILLED_COLOR_BG = "green"
EMPTY_COLOR_BG = "white"
FILLED_COLOR_BORDER = "green"
EMPTY_COLOR_BORDER = "black"
def __init__(self, master, x, y, size):
""" Constructor of the object called by Cell(...) """
self.master = master
self.abs = x
self.ord = y
self.size= size
self.fill= False
def _switch(self):
""" Switch if the cell is filled or not. """
self.fill= not self.fill
def draw(self):
""" order to the cell to draw its representation on the canvas """
if self.master != None :
fill = Cell.FILLED_COLOR_BG
outline = Cell.FILLED_COLOR_BORDER
if not self.fill:
fill = Cell.EMPTY_COLOR_BG
outline = Cell.EMPTY_COLOR_BORDER
xmin = self.abs * self.size
xmax = xmin + self.size
ymin = self.ord * self.size
ymax = ymin + self.size
self.master.create_rectangle(xmin, ymin, xmax, ymax, fill = fill, outline = outline)
class CellGrid(Canvas):
def __init__(self,master, rowNumber, columnNumber, cellSize, *args, **kwargs):
Canvas.__init__(self, master, width = cellSize * columnNumber , height = cellSize * rowNumber, *args, **kwargs)
self.cellSize = cellSize
self.grid = []
for row in range(rowNumber):
line = []
for column in range(columnNumber):
line.append(Cell(self, column, row, cellSize))
self.grid.append(line)
#memorize the cells that have been modified to avoid many switching of state during mouse motion.
self.switched = []
#bind click action
self.bind("<Button-1>", self.handleMouseClick)
#bind moving while clicking
self.bind("<B1-Motion>", self.handleMouseMotion)
#bind release button action - clear the memory of midified cells.
self.bind("<ButtonRelease-1>", lambda event: self.switched.clear())
self.draw()
def draw(self):
for row in self.grid:
for cell in row:
cell.draw()
def _eventCoords(self, event):
row = int(event.y / self.cellSize)
column = int(event.x / self.cellSize)
return row, column
def handleMouseClick(self, event):
row, column = self._eventCoords(event)
cell = self.grid[row][column]
cell._switch()
cell.draw()
#add the cell to the list of cell switched during the click
self.switched.append(cell)
def handleMouseMotion(self, event):
row, column = self._eventCoords(event)
cell = self.grid[row][column]
if cell not in self.switched:
cell._switch()
cell.draw()
self.switched.append(cell)
if __name__ == "__main__" :
app = Tk()
grid = CellGrid(app, 50, 50, 10)
grid.pack()
app.mainloop()
And error:
NameError: name 'Canvas' is not defined
How can I change my code in order to runnig this in Python 2.7?
Well, it's not defined all right, I wonder how could it run on Py3, unless Tkinter there dumped Canvas into your namespace somehow.
You probably wanted to do class CellGrid(Tkinter.Canvas), but I don't remember the exact structure of Tkinter offhand, so it might be in a subpackage or something.
You need to import elements of module you want to use, in your case:
from Tkinter import Canvas, Tk
You can also do:
from Tkinter import *
but it is not recomended way of doing. Your code may not work right, if you use many modules, and they two of them happend to have same name of method.
Last way is just
import Tkinker
but then every time you use element of this module, you need to add full path so if you wanna use Canvas, you need to useTkinker.Canvas.
Also remember that in Python 2.7 you import Tkinter and in 3.X import tkinter
I want to test some algorithms on path-finding and want to create a window consisting of grids where I can drag my mouse while clicked to create walls and delete them with right click and drag. I just started with computer science and am fascinated with the algorithms however i don't know how to create the interactive window for it.
The example grid can be viewed here:
A* pathfindind visualisation
Here come a solution. You can use the list of list of cells objects contained in the CellGrid.grid object to apply your pathfinding algorithm on. Have fun.
from tkinter import *
class Cell():
FILLED_COLOR_BG = "green"
EMPTY_COLOR_BG = "white"
FILLED_COLOR_BORDER = "green"
EMPTY_COLOR_BORDER = "black"
def __init__(self, master, x, y, size):
""" Constructor of the object called by Cell(...) """
self.master = master
self.abs = x
self.ord = y
self.size= size
self.fill= False
def _switch(self):
""" Switch if the cell is filled or not. """
self.fill= not self.fill
def draw(self):
""" order to the cell to draw its representation on the canvas """
if self.master != None :
fill = Cell.FILLED_COLOR_BG
outline = Cell.FILLED_COLOR_BORDER
if not self.fill:
fill = Cell.EMPTY_COLOR_BG
outline = Cell.EMPTY_COLOR_BORDER
xmin = self.abs * self.size
xmax = xmin + self.size
ymin = self.ord * self.size
ymax = ymin + self.size
self.master.create_rectangle(xmin, ymin, xmax, ymax, fill = fill, outline = outline)
class CellGrid(Canvas):
def __init__(self,master, rowNumber, columnNumber, cellSize, *args, **kwargs):
Canvas.__init__(self, master, width = cellSize * columnNumber , height = cellSize * rowNumber, *args, **kwargs)
self.cellSize = cellSize
self.grid = []
for row in range(rowNumber):
line = []
for column in range(columnNumber):
line.append(Cell(self, column, row, cellSize))
self.grid.append(line)
#memorize the cells that have been modified to avoid many switching of state during mouse motion.
self.switched = []
#bind click action
self.bind("<Button-1>", self.handleMouseClick)
#bind moving while clicking
self.bind("<B1-Motion>", self.handleMouseMotion)
#bind release button action - clear the memory of midified cells.
self.bind("<ButtonRelease-1>", lambda event: self.switched.clear())
self.draw()
def draw(self):
for row in self.grid:
for cell in row:
cell.draw()
def _eventCoords(self, event):
row = int(event.y / self.cellSize)
column = int(event.x / self.cellSize)
return row, column
def handleMouseClick(self, event):
row, column = self._eventCoords(event)
cell = self.grid[row][column]
cell._switch()
cell.draw()
#add the cell to the list of cell switched during the click
self.switched.append(cell)
def handleMouseMotion(self, event):
row, column = self._eventCoords(event)
cell = self.grid[row][column]
if cell not in self.switched:
cell._switch()
cell.draw()
self.switched.append(cell)
if __name__ == "__main__" :
app = Tk()
grid = CellGrid(app, 50, 50, 10)
grid.pack()
app.mainloop()
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