Related
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)
This question already has answers here:
how to make a graph fill all the window
(3 answers)
Closed 3 years ago.
I have a TKinter canvas where i'm displaying a plot. The problem is that I want the plot to fit to the entire canvas and take up the most possible area but can't get it to change size. Any recommendations as to how I could achieve this? Below is the class for the window i'm displaying the plot on.
import numpy as np
import cv2
from PIL import Image, ImageTk
import matplotlib.pyplot as plt
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
"""
See if can draw new window on exteral screen
"""
#Define the target, source and output arrays. Source has to be completely white otherwise it kills everything
def initialize(x,y):
xarr = np.zeros(x)
yarr = np.zeros(y)
target = np.meshgrid(xarr,yarr)
target = target[0]
source = np.meshgrid(xarr,yarr)
source = source[0]
output = np.meshgrid(xarr,yarr)
output = output[0]
for i in range(x):
for n in range(y):
source[n][i] = 1
return target, source, output
# creates trap between XTrapMin-XTrapMax and YTrapMin-YTrapMax on Array
def trap(xtmi,xtma,xs,ytmi,ytma,ys,array):
for i in range(xs):
if xtmi < i < xtma:
for n in range(ys):
if ytmi < n < ytma:
array[n][i] = 255
return
#Returns the amplitude of a complex number
def Amplitude(x):
if isinstance(x, complex):
return np.sqrt(x.real**2+x.imag**2)
else:
return np.abs(x)
#Returns the phase of a complex number
def Phase(z):
return np.angle(z)
#Main GS algorithm implementation using numpy FFT package
#performs the GS algorithm to obtain a phase distribution for the plane, Source
#such that its Fourier transform would have the amplitude distribution of the plane, Target.
def GS(target,source):
A = np.fft.ifft2(target)
for i in range(5):
B = Amplitude(source) * np.exp(1j * Phase(A))
C = np.fft.fft2(B)
D = Amplitude(target) * np.exp(1j * Phase(C))
A = np.fft.ifft2(D)
output = Phase(A)
return output
#Make array into PIL Image
def mkPIL(array):
im = Image.fromarray(np.uint8(array))
return im
def up():
global ytmi
global ytma
ytmi += 10
ytma += 10
return
def down():
global ytmi
global ytma
ytmi -= 10
ytma -= 10
return
def right():
global xtmi
global xtma
xtmi += 10
xtma += 10
return
def left():
global xtmi
global xtma
xtmi -= 10
xtma -= 10
return
xtmi = 127
xtma = 129
xs = 256
ytmi = 127
ytma = 129
ys = 256
root = tk.Tk()
root.attributes('-fullscreen', True)
def main():
app = Lower(root)
root.mainloop()
class Lower:
def __init__(self, master):
self.master = master
self.frame = tk.Frame(self.master).pack()
self.displayimg = tk.Button(self.frame, text = 'Display', width = 25, command = self.plot)
self.displayimg.pack()
self.makewidg()
def makewidg(self):
fig = plt.figure(figsize=(100,100), frameon=False) #changing figsize doesnt cange the size of the plot display
fig.tight_layout()
self.ax = fig.add_subplot(111)
self.ax.set_yticklabels([])
self.ax.set_xticklabels([])
self.canvas = FigureCanvasTkAgg(fig, master=self.master)
self.canvas.get_tk_widget().pack(expand=True)
self.canvas.draw()
self.new_window()
def new_window(self):
self.newWindow = tk.Toplevel()
self.app = Display(self.newWindow)
def plot(self):
global xtmi, xtma, xs, ytmi, ytma, ys, i
target,source,output=initialize(xs,ys)
trap(xtmi,xtma,xs,ytmi,ytma,ys,target)
output = GS(target,source)
self.ax.imshow(output, cmap='gray')
self.ax.set_yticklabels([])
self.ax.set_xticklabels([])
self.canvas.draw()
self.ax.clear()
def kill(self):
root.destroy()
class Display:
def __init__(self, master):
self.master = master
self.frame = tk.Frame(self.master)
self.frame.pack()
self.up = tk.Button(self.frame, text = 'Up', width = 25, command = up)
self.up.pack()
self.down = tk.Button(self.frame, text = 'Down', width = 25, command = down)
self.down.pack()
self.right = tk.Button(self.frame, text = 'Right', width = 25, command = right)
self.right.pack()
self.left = tk.Button(self.frame, text = 'Left', width = 25, command = left)
self.left.pack()
self.kill = tk.Button(self.frame, text = 'Kill', width = 25, command = self.kill)
self.kill.pack()
def kill(self):
root.destroy()
main()
Edit: ive added my code so that you can run it for yourself. The plot is rather large but changing figsize doesn't allow me to get it to cover the entire canvas
I added the line: self.canvas.figure.tight_layout() to def makewidg(self): like this:
class Lower:
def __init__(self, master):
self.master = master
self.frame = tk.Frame(self.master).pack()
self.displayimg = tk.Button(self.frame, text = 'Display', width = 25, command = self.plot)
self.displayimg.pack()
self.makewidg()
def makewidg(self):
fig = plt.figure(figsize=(100,100), frameon=False) #changing figsize doesnt cange the size of the plot display
fig.tight_layout()
self.ax = fig.add_subplot(111)
self.ax.set_yticklabels([])
self.ax.set_xticklabels([])
self.canvas = FigureCanvasTkAgg(fig, master=self.master)
self.canvas.get_tk_widget().pack(expand=True)
self.canvas.figure.tight_layout()
self.canvas.draw()
self.new_window()
def new_window(self):
self.newWindow = tk.Toplevel()
self.app = Display(self.newWindow)
def plot(self):
global xtmi, xtma, xs, ytmi, ytma, ys, i
target,source,output=initialize(xs,ys)
trap(xtmi,xtma,xs,ytmi,ytma,ys,target)
output = GS(target,source)
self.ax.imshow(output, cmap='gray')
self.ax.set_yticklabels([])
self.ax.set_xticklabels([])
self.canvas.draw()
self.ax.clear()
def kill(self):
root.destroy()
Could you please verify if that solved your problem?
Add fig.subplots_adjust(left=0, right=1, top=1, bottom=0) in place of fig.tight_layout() in your method makewidg, that will squash all the margins to nothing. It will push axis labels etc out of view, so you can add back in a small margin by using number close to 0 and 1 instead.
You can use the plot controls to adjust the margins to your liking and then use those numbers in the script once it looks the way you want:
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
I have a problem using the bind option so I can click a button in my keyboard,
and call a function once the key is pressed.
I tried taking other codes that have the similar purpose and I saw that I'm doing pretty much the same, though I still have some problem I must've missed.
I will be very thankful if you help me with this.
Here's my code:
# -*- coding: utf-8 -*-
#Imports
from Tkinter import *
from PIL import ImageTk, Image
import time
import tkMessageBox
#===========================================================================================================================================#
#Tkinter Class
class App(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
#===========================================================================================================================================#
#Game Pieces Classes
class Board:
def __init__(self, rows = 7, columns = 6, picture_height = None, picture_width = None):
self.rows = rows
self.columns = columns
self.picture = ImageTk.PhotoImage(Image.open(r"C:\Users\Ariel\Desktop\Python\4inarow.gif"))
self.picture_height = self.picture.height()
self.picture_width = self.picture.width()
def create_and_pack(self, canvas):
board_item = canvas.create_image(700, 370, image = self.picture)
def __str__(self):
print "Rows:", self.rows, "Columns:", self.columns
class Disk:
def __init__(self, player_number = None):
self.picture = ImageTk.PhotoImage(Image.open(r"C:\Users\Ariel\Desktop\Python\me" + str(player_number) + ".gif"))
self.player_number = player_number
def create_and_pack(self, canvas, x, y):
disk_item = canvas.create_image(x, y, image = self.picture)
def get_x_parameter(self, number):
#X growing by 70~73 per number
x = 330
for i in range(7):
if number == i:
x = x + i * 72
return x
def get_y_parameter(self, number):
#Y lowered by 70~73 per number
y = 635
for i in range(6):
if number == i:
y = y - i * 72
return y
def place(self, canvas, x, y):
#First left down circle parameters
#480, 635
canvas.move(self.picture, x, y)
def __str__(self):
print "The disk's picture string:", self.picture, "The player disk's number:", self.player_number
#===========================================================================================================================================#
#Game Class
class Game:
def __init__(self, board = None, disk1 = None, disk2 = None):
self.disk1 = disk1
self.disk2 = disk2
self.board = board
#===========================================================================================================================================#
#KeyboardClass
class Keyboard:
def __init__(self, key_number = None):
self.key_number = key_number
def press_and_place(self, canvas, number, function):
canvas.focus_set()
canvas.bind("<" + str(number) + ">", function)
#===========================================================================================================================================#
#Main.
myapp = App()
myapp.master.title("4 in a Row")
myapp.master.maxsize(2000, 1200)
#---------------------------------------------------------------------------------------------------#
GameBoard = Board(7, 6)
FirstPlayerDisk = Disk(1)
SecondPlayerDisk = Disk(2)
GameClass = Game(GameBoard, FirstPlayerDisk, SecondPlayerDisk)
#---------------------------------------------------------------------------------------------------#
#Creating Canvas and placing the disks and the board.
board_canvas = Canvas(width = GameBoard.picture_width, height = GameBoard.picture_height)
board_canvas.pack(expand=1, fill=BOTH)
GameBoard.create_and_pack(board_canvas)
FirstPlayerDisk.create_and_pack(board_canvas, 330, 635)
SecondPlayerDisk.create_and_pack(board_canvas, 260, 635)
#---------------------------------------------------------------------------------------------------#
#Creating Keyboard instance and placing the first disk in the last row in a column of choice
number_choice = 3
KeyboardClass = Keyboard(number_choice)
first_player_x = FirstPlayerDisk.get_x_parameter(number_choice)
first_player_y = FirstPlayerDisk.get_y_parameter(number_choice)
KeyboardClass.press_and_place(board_canvas, number_choice, FirstPlayerDisk.place(board_canvas, first_player_x, first_player_y))
#---------------------------------------------------------------------------------------------------#
myapp.mainloop()
Thanks a lot in advance.
I believe your problem lies in this line:
KeyboardClass.press_and_place(board_canvas, number_choice, FirstPlayerDisk.place(board_canvas, first_player_x, first_player_y))
the third argument FirstPlayerDisk.place(board_canvas, first_player_x, first_player_y) is actually a None type as
def place(self, canvas, x, y):
#First left down circle parameters
#480, 635
canvas.move(self.picture, x, y)
returns None
From How to bind a keypress to a button in Tkinter and this site, you need to pass the function, that is, simply FirstPlayerDisk.place (no parenthesis following it).