(Python beginner, excuse me if the question is too childish) Below the label which says “Hello”, create a label or bar or whatever to show the updating positions of my two turtles (what I mean by updating is that as a turtle moves the two coordinates of its position changes at the same time)
import Tkinter
import turtle
def run_turtles(*args):
for t, d in args:
t.circle(250, d)
root.after_idle(run_turtles, *args)
root = Tkinter.Tk()
root.withdraw()
frame = Tkinter.Frame(bg='black')
Tkinter.Label(frame, text=u'Hello', bg='grey', fg='white').pack(fill='x')
canvas = Tkinter.Canvas(frame, width=750, height=750)
canvas.pack()
frame.pack(fill='both', expand=True)
turtle1 = turtle.RawTurtle(canvas)
turtle2 = turtle.RawTurtle(canvas)
turtle1.ht(); turtle1.pu()
turtle1.left(90); turtle1.fd(250); turtle1.lt(90)
turtle1.st(); turtle1.pd()
turtle2.ht(); turtle2.pu()
turtle2.fd(250); turtle2.lt(90)
turtle2.st(); turtle2.pd()
root.deiconify()
run_turtles((turtle1, 3), (turtle2, 4))
root.mainloop()
Thank You Very Very Much!!
Save a reference to the Label: turtleLabel = Tkinter.Label(frame, text=u'Hello', bg='grey', fg='white')
And then in your run_turtles-function you can update the label by setting its text:
turtleLabel['text'] = "Here be coordinates"
You can also change the text with the configure method:
turtleLabel.configure(text="Here be coordinates")
Note that you can't call pack in the same statement that you create the widget; packreturns None which would negate trying to save the reference to the widget.
Related
This code works:
import tkinter
root = tkinter.Tk()
canvas = tkinter.Canvas(root)
canvas.grid(row = 0, column = 0)
photo = tkinter.PhotoImage(file = './test.gif')
canvas.create_image(0, 0, image=photo)
root.mainloop()
It shows me the image.
Now, this code compiles but it doesn't show me the image, and I don't know why, because it's the same code, in a class:
import tkinter
class Test:
def __init__(self, master):
canvas = tkinter.Canvas(master)
canvas.grid(row = 0, column = 0)
photo = tkinter.PhotoImage(file = './test.gif')
canvas.create_image(0, 0, image=photo)
root = tkinter.Tk()
test = Test(root)
root.mainloop()
The variable photo is a local variable which gets garbage collected after the class is instantiated. Save a reference to the photo, for example:
self.photo = tkinter.PhotoImage(...)
If you do a Google search on "tkinter image doesn't display", the first result is this:
Why do my Tkinter images not appear? (The FAQ answer is currently not outdated)
from tkinter import *
from PIL import ImageTk, Image
root = Tk()
def open_img():
global img
path = r"C:\.....\\"
img = ImageTk.PhotoImage(Image.open(path))
panel = Label(root, image=img)
panel.pack(side="bottom", fill="both")
but1 = Button(root, text="click to get the image", command=open_img)
but1.pack()
root.mainloop()
Just add global to the img definition and it will work
The problem is Python automatically deletes the references to the variable by a process known as Garbage Collection. The solution is to save the reference or to create a new reference.
The following are the ways:
Using self to increase the reference count and to save the reference.
import tkinter
class Test:
def __init__(self, master):
canvas = tkinter.Canvas(master)
canvas.grid(row = 0, column = 0)
self.photo = tkinter.PhotoImage(file = './test.gif') # Changes here
canvas.create_image(0, 0, image=self.photo) # Changes here
root = tkinter.Tk()
test = Test(root)
root.mainloop()
Saving it to a list to increase the reference count and to save the reference.
import tkinter
l=[]
class Test:
def __init__(self, master):
canvas = tkinter.Canvas(master)
canvas.grid(row = 0, column = 0)
photo = tkinter.PhotoImage(file = './test.gif')
l.append(photo)
canvas.create_image(0, 0, image=photo)
root = tkinter.Tk()
test = Test(root)
root.mainloop()
While using method 2, you can either make a global list as i did or use list inside the class. Both would work.
Some useful links:
About Garbage Collection 1
About Garbage Collection 2 (More useful)
As a rule of thumb, whenever you create your image in an indented block of code you need to safe a reference to that image. This is because of the python's automated garbage collection and it collects everything with a refcount of 0 when it destroys/leaves that frame/page/indented block of code.
The canonical way to deal with it is to have a list of images somewhere in the global namespace and add your image-references to that list. This is convenient but not very efficient and should be used for small applications.
import tkinter as tk
global_image_list = []
global_image_list.append(tk.PhotoImage(file = 'test.png'))
An more efficient way is to bound an attribute to your widget or class that holds that reference for you, as Bryan proposed in his answer. It doesn't make a difference if you do self.image or widget.image that was assigned widget = tk.Widget(.. before. But this also might not the right approach if you want to use that image further even when the widget is destroyed and garbage collected.
import tkinter as tk
root = tk.Tk()
label = tk.Label(root, text='test')
label.image = tk.PhotoImage(file = 'test.png')
label.configure(image=label.image)
Just add global photo as the first line inside the function.
I want to create a search box in Python 3. I am aware of entry widget and buttons, but I just want something more elegant like this. Is it even possible to create something closer to the one in the image? If yes, kindly throw some light on this topic. TIA
You can do this using ttk if you create a new element using an image for the search icon you could embed it into a text widget using the following code. In this case we add a theme provided 'pin' icon but this element could be easily replaced. The demo looks like this with the original entry on top and the new style below:
The vsapi element engine is only available on Windows but by using the image element engine to define your custom element this would work on all Tk platforms.
import tkinter as tk
import tkinter.ttk as ttk
class SearchEntry(ttk.Widget):
"""
Customized version of a ttk Entry widget with an element included in the
text field. Custom elements can be created using either the vsapi engine
to obtain system theme provided elements (like the pin used here) or by using
the "image" element engine to create an element using Tk images.
Note: this class needs to be registered with the Tk interpreter before it gets
used by calling the "register" static method.
"""
def __init__(self, master, **kw):
kw["style"] = "Search.Entry"
ttk.Widget.__init__(self, master, 'ttk::entry', kw)
def get(self):
return self.tk.call(self._w, 'get')
def set(self, value):
self.tk.call(self._w, 'set', value)
#staticmethod
def register(root):
style = ttk.Style()
# There seems to be some argument parsing bug in tkinter.ttk so cheat and eval
# the raw Tcl code to add the vsapi element for a pin.
root.eval('''ttk::style element create pin vsapi EXPLORERBAR 3 {
{pressed !selected} 3
{active !selected} 2
{pressed selected} 6
{active selected} 5
{selected} 4
{} 1
}''')
#style.element_create("pin", "vsapi", "EXPLORERBAR", "3", [(["selected"], 4),([], 1)])
style.layout("Search.Entry", [
("Search.Entry.field", {'sticky': 'nswe', 'children': [
("Search.Entry.background", {'sticky':'nswe', 'children': [
("Search.Entry.padding", {'sticky':'nswe', 'children': [
("Search.Entry.textarea", {'sticky':'nswe'})
]})
]}),
("Search.Entry.pin", {'sticky': 'e'})
]})
])
style.configure("Search.Entry", padding=(1, 1, 14, 1))
style.map("Search.Entry", **style.map("TEntry"))
if __name__ == '__main__':
root = tk.Tk()
text = tk.StringVar()
SearchEntry.register(root)
frame = ttk.Frame(root)
text.set("some example text ...")
e1 = ttk.Entry(frame, textvariable=text)
e2 = SearchEntry(frame, textvariable=text)
e1.grid(sticky="news", padx=2, pady=2)
e2.grid(sticky="news", padx=2, pady=2)
frame.grid(sticky = "news", padx=2, pady=2)
root.grid_columnconfigure(0, weight = "1")
root.grid_rowconfigure(0, weight = "1")
root.mainloop()
I am trying to select multiple objects using mouse just like in windows click and drag. i am using tkinter in python to buils this gui. i am creating objects as shown in below code.
import Tkinter as tk
from Tkinter import *
root = Tk()
w= Canvas(root, width=800, height=768)
w.grid()
w.create_line(200,200,300,300, width=3, tags="line1")
w.create_oval(150,100,170,300, fill="red", tags="oval")
mainloop()
what i am trying to do is if i drag my mouse over multiple objects some def should return the tags of the objects. how can i do this.
Thank you
Save the coordinates on a button-down event, and then on a button-up event use the find_enclosed or find_overlapping method of the canvas to find all items enclosed by the region.
The following code renders a rectangle that follows the cursor and returns a list of element IDs of elements within the rectangle. It uses bindings for when the mouse is pressed within the canvas, when it moves, and when it is released. I then kept a list of elements with their canvas identifier and used that to back lookup the object from the list of selected IDs.
# used to record where dragging from
originx,originy = 0,0
canvas.bind("<ButtonPress-1>", __SelectStart__)
canvas.bind("<B1-Motion>", __SelectMotion__)
canvas.bind("<ButtonRelease-1>", __SelectRelease__)
# binding for drag select
def __SelectStart__(self, event):
oiginx = canvas.canvasx(event.x)
originy = canvas.canvasy(event.y)
selectBox = canvas.create_rectangle(originx,originy,\
originx,originy)
# binding for drag select
def __SelectMotion__(self, event):
xnew = canvas.canvasx(event.x)
ynew = canvas.canvasy(event.y)
# correct cordinates so it gives (upper left, lower right)
if xnew < x and ynew < y:
canvas.coords(selectBox,xnew,ynew,originx,originy)
elif xnew < x:
canvas.coords(selectBox,xnew,originy,originx,ynew)
elif ynew < y:
canvas.coords(selectBox,originx,ynew,xnew,originy)
else:
canvas.coords(selectBox,originx,originy,xnew,ynew)
# binding for drag select
def __SelectRelease__(self, event):
x1,y1,x2,y2 = canvas.coords(selectBox)
canvas.delete(selectBox)
# find all objects within select box
selectedPointers = []
for i in canvas.find_withtag("tag"):
x3,y3,x4,y4 = canvas.coords(i)
if x3>x1 and x4<x2 and y3>y1 and y4<y2:
selectedPointers.append(i)
Callback(selectedPointers)
# function to receive IDs of selected items
def Callback(pointers):
print(pointers)
I have two groups of codes and the first part is a turtle graphics window and second part is a Tkinter window. How should I those two parts together to one window?
My first part of the code
from turtle import *
def move(thing, distance):
thing.circle(250, distance)
def main():
rocket = Turtle()
ISS = Turtle()
bgpic('space.gif')
register_shape("ISSicon.gif")
ISS.shape("ISSicon.gif")
rocket.speed(10)
ISS.speed(10)
counter = 1
title("ISS")
screensize(750, 750)
ISS.hideturtle()
rocket.hideturtle()
ISS.penup()
ISS.left(90)
ISS.fd(250)
ISS.left(90)
ISS.showturtle()
ISS.pendown()
rocket.penup()
rocket.fd(250)
rocket.left(90)
rocket.showturtle()
rocket.pendown()
rocket.fillcolor("white")
while counter == 1:
move(ISS, 3)
move(rocket, 4)
main()
Second part
from Tkinter import *
control=Tk()
control.title("Control")
control.geometry("200x550+100+50")
cline0=Label(text="").pack()
cline1=Label(text="Speed (km/s)").pack()
control.mainloop()
Thanks a lot ;)
Uhm, I'm not sure if mixing them is a good idea. This turtle module frequently uses the update command from Tcl, and this will very likely cause problems when more involved code is added in the mix (it is nice that apparently turtle can live with it). Anyway, one way to mix both is by using RawTurtle in place of Turtle, so you can pass your own Canvas which turtle will adjust for its needs.
Here is an example (I also replaced the infinite loop by an infinite re-schedule, basically):
import Tkinter
import turtle
def run_turtles(*args):
for t, d in args:
t.circle(250, d)
root.after_idle(run_turtles, *args)
root = Tkinter.Tk()
root.withdraw()
frame = Tkinter.Frame(bg='black')
Tkinter.Label(frame, text=u'Hello', bg='grey', fg='white').pack(fill='x')
canvas = Tkinter.Canvas(frame, width=750, height=750)
canvas.pack()
frame.pack(fill='both', expand=True)
turtle1 = turtle.RawTurtle(canvas)
turtle2 = turtle.RawTurtle(canvas)
turtle1.ht(); turtle1.pu()
turtle1.left(90); turtle1.fd(250); turtle1.lt(90)
turtle1.st(); turtle1.pd()
turtle2.ht(); turtle2.pu()
turtle2.fd(250); turtle2.lt(90)
turtle2.st(); turtle2.pd()
root.deiconify()
run_turtles((turtle1, 3), (turtle2, 4))
root.mainloop()
I am using Python 3.1 by the way.
I am trying to build a simple GUI using Tkinter - label, text entry field, button on the first row and editable text area with scrollbar to the right and on the bottom of it - on the second row. Please help me fix up the layout. What I have below does not quite work. If I have to use a grid, I will. I wish to keep the code very simple - I want to "sell" Python to some of my coworkers. So, I want to get a somewhat decent look and feel. Suggest better padding if you do not mind. Also, if my variable names, etc. seem weird, then please make a note.
At the same time I want to pretend that this is a throw-away script which I have not spent much time on. Since I am asking for your help, it ain't so, but they do not need to know ;). So, I do not want to introduce fancy code to create nice borders, etc. I just want something that is visually appealing, clean and simple. If I do not, then my presentation will not achieve its goal.
Thank you, my code is below:
class App:
def __init__(self, parent):
frame = Frame(parent)
self.__setup_gui(frame) # Call Helper
frame.pack(padx=15, pady=15)
parent.title('To be changed')
def __setup_gui(self, frame):
# First Row
self.cs_label = Label(frame, text='Change Set: ')
self.cs_label.pack(side=LEFT, padx=10, pady=10)
self.cs_val = Entry(frame, width=10)
self.cs_val.pack(side=LEFT, padx=10, pady=10)
self.get_button = Button(frame, text='Get', command=self.get_content)
self.get_button.pack(side=LEFT, padx=10, pady=10)
# Text area and scrollbar
self.text_area = Text(frame, height=10, width=50, background='white')
# Put a scroll bar in the frame
scroll = Scrollbar(frame)
self.text_area.configure(yscrollcommand=scroll.set)
self.text_area.pack(side=TOP)
scroll.pack(side=RIGHT,fill=Y)
self.clipboard_var = IntVar()
self.notepad_var = IntVar()
def get_content(self):
print(self.clipboard_var.get())
print(self.notepad_var.get())
###################################################################################################
if __name__ == '__main__':
root = Tk()
app = App(root)
root.mainloop()
You definitely want the grid manager -- Pack only works for a vertical or horizontal stackup by itself. You can use multiple frames to work around it, but I find it's easier to expand a GUI if you just do it with Grid to start.
Here's what I've worked up real quick based what you said and the code. I reduced/removed the padding -- it looked huge for me -- and I set up two scrollbars, in a subframe to make the padding work out more easily. Note that to make the horizontal scrollbar useful your Text area needs to have wrap=NONE; otherwise you might as well use the easy 'ScrolledText' widget from tkinter.scrolledtext and skip the horizontal scroll bar.
I've now reframed things a bit to allow for resize, with a minimum size that shows the top buttons -- see the uses of minsize and row/columnconfigure.
BTW, it looks like your variables aren't being pulled from anywhere -- is that intentional?
from tkinter import *
class App:
def __init__(self, parent):
self.__setup_gui(parent) # Call Helper
parent.title('To be changed')
def __setup_gui(self, parent):
# First Row
self.rowframe = Frame(parent)
self.rowframe.grid()
self.cs_label = Label(self.rowframe, text='Change Set: ')
self.cs_label.grid(row=0, column=0, padx=2, pady=2)
self.cs_val = Entry(self.rowframe, width=10)
self.cs_val.grid(row=0, column=1, padx=2, pady=2)
self.get_button = Button(self.rowframe, text='Get', command=self.get_content)
self.get_button.grid(row=0, column=2, padx=2, pady=2)
parent.update_idletasks()
parent.minsize(width=self.rowframe.winfo_width(), height=self.rowframe.winfo_height())
# Text area and scrollbars
self.textframe = Frame(parent)
self.textframe.grid(row=1, columnspan=2, padx=2, pady=2, sticky=N+S+E+W)
self.hscroll = Scrollbar(self.textframe, orient=HORIZONTAL)
self.vscroll = Scrollbar(self.textframe)
self.text_area = Text(self.textframe, height=10, width=50, wrap=NONE, background='white', yscrollcommand=self.vscroll.set, xscrollcommand=self.hscroll.set)
self.text_area.grid(row=0, column=0, sticky=N+S+E+W)
self.hscroll.config(command=self.text_area.xview)
self.hscroll.grid(row=1, column=0, sticky=E+W)
self.vscroll.config(command=self.text_area.yview)
self.vscroll.grid(row=0, column=1, sticky=N+S)
# Row 0 defaults to 0
parent.rowconfigure(1, weight=1)
parent.columnconfigure(1, weight=1)
# Textarea setup
self.textframe.rowconfigure(0, weight=1)
self.textframe.columnconfigure(0, weight=1)
self.clipboard_var = IntVar()
self.notepad_var = IntVar()
def get_content(self):
print(self.clipboard_var.get())
print(self.notepad_var.get())
###################################################################################################
if __name__ == '__main__':
root = Tk()
app = App(root)
root.mainloop()
Now, all that said...you might get more visual appeal with PyGTK, PyQt, or wxPython, though tkinter coming "standard" is a nice feature.