from Tkinter import *
root = Tk()
root.title("hello world")
root.geometry('300x200')
root.mainloop()
I want to use some function in Tkinter, while I am familiar with 'graphics'.
How can I use the function of graphics in this Tkinter window?
Here is the definition of Graphics.
# Graphics classes start here
class GraphWin(tk.Canvas):
"""A GraphWin is a toplevel window for displaying graphics."""
def __init__(self, title="Graphics Window",
width=200, height=200, autoflush=True):
master = tk.Toplevel(_root)
master.protocol("WM_DELETE_WINDOW", self.close)
tk.Canvas.__init__(self, master, width=width, height=height)
self.master.title(title)
self.pack()
master.resizable(0,0)
self.foreground = "black"
self.items = []
self.mouseX = None
self.mouseY = None
self.bind("<Button-1>", self._onClick)
self.bind_all("<Key>", self._onKey)
self.height = height
self.width = width
self.autoflush = autoflush
self._mouseCallback = None
self.trans = None
self.closed = False
master.lift()
self.lastKey = ""
if autoflush: _root.update()
More is on the http://mcsp.wartburg.edu/zelle/python/graphics.py
The graphics.py library packages the GraphWin object as a toplevel window. If you want to intergrate a GraphWin into a tkinter window with other frames and wigets you need to modify the graphics.py library and remove the toplevel wrapper around the GraphWin class so that it can be treated as a tkinter Canvas. This means you will need to append the modified library in your source file or rename and import it appropriately.
Here is an example of how it could be done in an App that integrates a Graphwin and another tk.Frame in one root window called "root". I will not include the entire modified library here, only the modified GraphWin init method, but NOTE -- that alone isn't sufficient -- you will need to go through the rest of the GraphWin class and all of the classes and methods in the library that you want to use; Point, Oval etc.. and change every time you read "_root" to "self.master", and also any time you read "master" should now be "self.master" and any "self.update()" should be "self.master.update()" too.
You may decide that the update() method can be removed, as you can now call self.root.update() with your own self.pauseLength = time.sleep(pauseLength) from inside the yourApp class. All of the occurrences of _root.update() in the graphics classes' methods yourApp might call you will have changed to self.master.update(), so you'll see that inside the modified GraphWin class the self.master is then assigned the parameter root, which must now be provided when you instantiate a GraphWin as in this sample implementation.
###################### SAMPLE IMPLEMENTATION #######################
import tkinter as tk
class yourApp:
def __init__(self, yourParameters):
self.root= tk.Tk()
self.canvas = GraphWin(self.root, yourParameters)
self.canvas.pack
self.frameOfWidgets = tk.Frame(self.root, yourParameters)
self.frameOfWidgets.pack()
App = yourApp(yourParameters)
App.root.mainloop()
################## MODIFIED GRAPHWIN CLASS #######################
class GraphWin(tk.Canvas):
"""A GraphWin is no longer in a toplevel wrapper and can be treated like a tk.Canvas in the parent window called root."""`
def __init__(self, root, title="Graphics Window",
width=200, height=200, borderwidth=0, autoflush=True):
self.master = root
self.master.protocol("WM_DELETE_WINDOW", self.close)
tk.Canvas.__init__(self, self.master, width=width, height=height, borderwidth=borderwidth)
self.master.title(title)
self.pack()
self.master.resizable(0, 0)
self.foreground = "black"
self.items = []
self.mouseX = None
self.mouseY = None
self.bind("<Button-1>", self._onClick)
self.bind_all("<Key>", self._onKey)
self.height = height
self.width = width
self.autoflush = autoflush
self._mouseCallback = None
self.trans = None
self.closed = False
self.lastKey = ""
Related
I had trouble wording the question so let me explain. I am learning to use Object Oriented Programming as part of a project, so I decided to start off by creating classes for creating windows and buttons to place on the windows (Labels ect. will come later too). In other words, I want to be able to place button on a "Window1" without specifying "Window1" in the class (and instead pass it through with the CreateButton(values). Here is my code so far, and help is appreaciated. PS. It functioned how it should before I tried the windowname parameter, and I set the window manually to home_window, but I cant seem to get the windowname paramater to pass through properly.
import tkinter as tk
from tkinter import ttk
home_window = tk.Tk()
home_window.title("Rota System")
home_window.geometry("600x600")
def thankyou():
print ("Thank you very much")
class CreateWindow():
def __init__(self, name, title, geometry):
self.name = name
self.title = title
self.geometry = geometry
name = tk.Tk()
name.title(title)
name.geometry(geometry)
class CreateButton():
def __init__(self, name, width, height, x, y, font, size, text, command, windowname):
#I want to be able to place the button on any window I want, not just home_window
self.name = name
self.width = width
self.height = height
self.x = x
self.y = y
self.font = font
self.size = size
self.text = text
self.command = command
self.windowname = windowname
name = tk.Button(windowname, text=text, command=command)
name.config(font=(font, size))
name.place(x=x, y=y, height=height, width=width)
CreateWindow("Test", "Test", "900x600")
CreateButton("Test","200","200","0","0","Courier",10,"Test",thankyou, "Test")
First: tkinter needs object's ID returned by Tk(), Button(), etc. - not names (strings) which you created.
With string names you would have to keep global dictionary {name: object_id} to convert name to object_id.
BTW:
Tk() should be used only to create main window. For other windows you should use Toplevel()
Your code in classes (and they names) rather fit to functions.
Function creates object and use return to return this object so it can be used in other function.
import tkinter as tk
from tkinter import ttk
# --- functions ---
def thank_you():
print ("Thank you very much")
def create_window(title, geometry):
window = tk.Tk()
window.title(title)
window.geometry(geometry)
return window
def create_button(parent, width, height, x, y, font, size, text, command):
button = tk.Button(parent, text=text, command=command)
button.config(font=(font, size))
button.place(x=x, y=y, height=height, width=width)
return button
# --- main ---
window = create_window("Test", "900x600")
button = create_button(window, 200, 200, 0, 0, "Courier", 10, "Test", thank_you)
window.mainloop()
Using classes I would inherit it. So I can assing instance to variable and use in other class as parent.
And I would use nouns as names for classes.
import tkinter as tk
from tkinter import ttk
# --- classes ---
class MainWindow(tk.Tk):
def __init__(self, name, title, geometry):
super().__init__()
self.name = name
self.title(title)
self.geometry(geometry)
#self._title = title
#seff._geometry = geometry
class MyButton(tk.Button):
def __init__(self, parent, name, width, height, x, y, font, size, text, command):
super().__init__(parent, text=text, command=command)
self.name = name
self.config(font=(font, size))
self.place(x=x, y=y, height=height, width=width)
#self._width = width
#self._height = height
#self._x = x
#self._y = y
#self._font = font
#self._size = size
#self._text = text
#self._command = command
# --- functions ---
def thank_you():
print("Thank you very much")
# --- main ---
window = MainWindow("WindowName", "Test", "900x600")
button = MyButton(window, "ButtonName", 200, 200, 0, 0, "Courier", 10, "Test", thank_you)
window.mainloop()
See: PEP 8 -- Style Guide for Python Code -
I don't know if there is about nouns for class names and verbs for functions (or maybe it was in book Clean Code, Robert C. Martin) but usually people use this rule.
When a resize event is triggered, how could I retrieve the toplevel, from which this event was triggered?
I have written a small programm, where the main window has a button that opens another window and each new window gets a binding for the resize method that currently prints the height and width of the window. In the main project, the toplevel is used as an index for lists to retrieve information for that specific window, so it would be ideal to be able to retrieve the toplevel as well. Is that possible, either directly or indirectly?
import tkinter as tk
class MyApp(tk.Frame):
def __init__(self, master = None):
self.main()
tk.Frame.__init__(self, master)
def main(self):
btn = tk.Button(root, text="New Window", command=self.neues_fenster)
btn.grid(row=0, column = 0)
def neues_fenster(self):
top = tk.Toplevel()
top.title("Some Window")
top.minsize(width = 150, height = 150)
top.bind("<Configure>", self.resize)
def resize(self, event):
print("width", event.width, "height", event.height)
if __name__=="__main__":
root = tk.Tk()
myapp = MyApp(master = root)
myapp.mainloop()
In effbot document,You could use event.widget to get the widget.(It is also okay even if it is toplevel).
All the possible attributes of event:
I have made my own widget called 'InventorySlot' and I need the widgets within my custom one to be made onto a Canvas instead of using 'grid' or 'pack'.
import tkinter as tk
from tkinter import *
from tkinter.ttk import *
class Main:
def __init__(self):
self.root = Tk()
self.root.geometry('500x500')
self.test = InventorySlot(self.root)
self.test.grid()
class InventorySlot(tk.Frame):
def __init__(self,parent,*args,**kwargs):
tk.Frame.__init__(self,parent)
self.options = {}
self.options.update(kwargs)
self.slot = tk.Label(self,height=3,width=6,text='',relief=SUNKEN,bg='#8b8b8b',bd=4,padx=1,pady=0)
self.canvas = Canvas(self)
self.canvas.create_window(10,10,window=self.slot)
self.canvas.grid()
MainTk = Main()
MainTk.root.mainloop()
All it shows is a blank Canvas
You need to create the label after creating the canvas. The order of creation determines the stacking order (ie: the z-index). The label on s there, it’s just behind the canvas.
I'm trying to make a custom Tkinter widget by subclassing a Frame to make a scrollable widget that contains items. I have 2 windows, my root window and a toplevel window and I need my custom widget to pack to the toplevel window.
My problem is, that despite making 'top' the parent of my custom widget, it still packs in the root window. I've tried other widgets and they pack to the toplevel window fine.
my code:
from tkinter import *
root = Tk()
root.config(bg="#000000")
root.wm_attributes("-alpha","0.7")
top = Toplevel()
top.config(bg="#000001")
top.wm_attributes("-topmost",1)
top.wm_attributes("-transparentcolor","#000001")
top.wm_title("TOPLEVEL")
class Scrollygrid(Frame):
def __init__(self, parent, columns, h, w):
super(Scrollygrid, self).__init__()
self.scrollbar = Scrollbar(self)
self.scrollbar.pack(side = RIGHT, fill = Y)
self.area = Canvas(self, yscrollcommand=self.scrollbar.set, width = w, height = h, bg = "#000001", bd = 0, highlightthickness = 0)
self.gridframe = Frame(height = h*10, width = w, bg = "#FF0000")
self.gridframe.pack_propagate(0)
self.area.create_window((0, 0), window = self.gridframe)
for i in range(500):
Label(self.gridframe, text = i).pack()
self.area.pack()
self.area.config(scrollregion = (self.area.bbox("all")))
self.scrollbar.config(command = self.area.yview)
def onScroll(event):
self.area.yview_scroll(int(-1*(event.delta/60)), "units")
self.area.bind_all("<MouseWheel>", onScroll)
self.scrollbar.pack_forget() #scroll wheel still works!
testgrid = Scrollygrid(top, 1,root.winfo_screenheight()-80,root.winfo_screenwidth()-(root.winfo_screenwidth()/10))
testgrid.pack(side = RIGHT, anchor = CENTER)
root.mainloop()
the transparent color and alpha level have been left in to make it more immediately obvious which window is which.
top isn't the parent of your custom widget. You're never passing it. Your Scrollygrid is being passed a parent parameter and that's it. Nothing tells it to assign it as the parent as an attribute even. So the parent defaults to the Tk instance, root.
Replace:
super(Scrollygrid, self).__init__()
with:
super(Scrollygrid, self).__init__(parent)
In order to pass the given widget argument as the parent to the superclass, Frame. Essentially making the Toplevel, top your custom class' parent. Upon which you'll get further error(s) but the parent is correctly assigned. Verify by:
print(repr(self.winfo_toplevel()))
I want to make a window in Tk that has a custom titlebar and frame. I have seen many questions on this website dealing with this, but what I'm looking for is to actually render the frame using a canvas, and then to add the contents to the canvas. I cannot use a frame to do this, as the border is gradiented.
According to this website: http://effbot.org/tkinterbook/canvas.htm#Tkinter.Canvas.create_window-method, I cannot put any other canvas items on top of a widget (using the create_window method), but I need to do so, as some of my widgets are rendered using a canvas.
Any suggestions on how to do this? I'm clueless here.
EDIT: Bryan Oakley confirmed that rendering with a canvas would be impossible. Would it then be possible to have a frame with a custom border color? And if so, could someone give a quick example? I'm sort of new with python.
You can use the canvas as if it were a frame in order to draw your own window borders. Like you said, however, you cannot draw canvas items on top of widgets embedded in a canvas; widgets always have the highest stacking order. There is no way around that, though it's not clear if you really need to do that or not.
Here's a quick and dirty example to show how to create a window with a gradient for a custom border. To keep the example short I didn't add any code to allow you to move or resize the window. Also, it uses a fixed color for the gradient.
import Tkinter as tk
class GradientFrame(tk.Canvas):
'''A gradient frame which uses a canvas to draw the background'''
def __init__(self, parent, borderwidth=1, relief="sunken"):
tk.Canvas.__init__(self, parent, borderwidth=borderwidth, relief=relief)
self._color1 = "red"
self._color2 = "black"
self.bind("<Configure>", self._draw_gradient)
def _draw_gradient(self, event=None):
'''Draw the gradient'''
self.delete("gradient")
width = self.winfo_width()
height = self.winfo_height()
limit = width
(r1,g1,b1) = self.winfo_rgb(self._color1)
(r2,g2,b2) = self.winfo_rgb(self._color2)
r_ratio = float(r2-r1) / limit
g_ratio = float(g2-g1) / limit
b_ratio = float(b2-b1) / limit
for i in range(limit):
nr = int(r1 + (r_ratio * i))
ng = int(g1 + (g_ratio * i))
nb = int(b1 + (b_ratio * i))
color = "#%4.4x%4.4x%4.4x" % (nr,ng,nb)
self.create_line(i,0,i,height, tags=("gradient",), fill=color)
self.lower("gradient")
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.wm_overrideredirect(True)
gradient_frame = GradientFrame(self)
gradient_frame.pack(side="top", fill="both", expand=True)
inner_frame = tk.Frame(gradient_frame)
inner_frame.pack(side="top", fill="both", expand=True, padx=8, pady=(16,8))
b1 = tk.Button(inner_frame, text="Close",command=self.destroy)
t1 = tk.Text(inner_frame, width=40, height=10)
b1.pack(side="top")
t1.pack(side="top", fill="both", expand=True)
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
Here is a rough example where the frame, titlebar and close button are made with canvas rectangles:
import Tkinter as tk
class Application(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
# Get rid of the os' titlebar and frame
self.overrideredirect(True)
self.mCan = tk.Canvas(self, height=768, width=768)
self.mCan.pack()
# Frame and close button
self.lFrame = self.mCan.create_rectangle(0,0,9,769,
outline='lightgrey', fill='lightgrey')
self.rFrame = self.mCan.create_rectangle(760,0,769,769,
outline='lightgrey', fill='lightgrey')
self.bFrame = self.mCan.create_rectangle(0,760,769,769,
outline='lightgrey', fill='lightgrey')
self.titleBar = self.mCan.create_rectangle(0,0,769,20,
outline='lightgrey', fill='lightgrey')
self.closeButton = self.mCan.create_rectangle(750,4,760, 18,
activefill='red', fill='darkgrey')
# Binds
self.bind('<1>', self.left_mouse)
self.bind('<Escape>', self.close_win)
# Center the window
self.update_idletasks()
xp = (self.winfo_screenwidth() / 2) - (self.winfo_width() / 2)
yp = (self.winfo_screenheight() / 2) - (self.winfo_height() / 2)
self.geometry('{0}x{1}+{2}+{3}'.format(self.winfo_width(),
self.winfo_height(),
xp, yp))
def left_mouse(self, event=None):
obj = self.mCan.find_closest(event.x,event.y)
if obj[0] == self.closeButton:
self.destroy()
def close_win(self, event=None):
self.destroy()
app = Application()
app.mainloop()
If I were going to make a custom GUI frame I would consider creating it with images,
made with a program like Photoshop, instead of rendering canvas objects.
Images can be placed on a canvas like this:
self.ti = tk.PhotoImage(file='test.gif')
self.aImage = mCanvas.create_image(0,0, image=self.ti,anchor='nw')
More info →here←