GUI application using Tkinter - Drag and Drop - python

I have recently been using WxPython to create a GUI Network simulator like Cisco packet tracer but if I am honest it has been extremely difficult trying to find examples of what I need etc etc. Iv resorted back to the old faithful Tk.
My program thus far has a menu bar which consists of a File > Exit. It also has an Exit button at the bottom Right hand side of the application. As well as this it has a canvas of a set size and a variety of buttons which when clicked produce a small image of the hardware on the canvas. This was done using PIL
What I need next is to be able to drag these images around the canvas and this is proving a little difficult. I have looked at the following example of how it has been broken down and I kind of understand how you need an on click definition, motion (going from a to b) and on release definition, but how do I apply it to my code that I already have?
Here is the link to what I referenced above:
http://www.python-forum.org/pythonforum/viewtopic.php?f=4&p=75789
Finally Here is the code I have already. I can appreciate that the layout and structure of my code isn't great as I am fairly new to programming but any guidance / examples / visual representations would be amazing.
from Tkinter import*
from PIL import Image, ImageTk
class AllTkinterWidgets:
def __init__(self, master):
frame= Frame(master, width=900, height=600)
frame.pack()
iframe5 = Frame (frame, bd=2, relief=RAISED)
iframe5.pack(expand=1, fill=X, pady=10, padx=5)
c = Canvas(iframe5, bg='white', width=600, height=500)
c.pack()
# definitions to print hardware images to the canvas
# -----------------------------------------------------------------------
def show_imageRouter():
c.create_image(30,30, image=image1)
def show_imageSwitch():
c.create_image(30,60, image=image2)
def show_imageServer():
c.create_image(30,100, image=image3)
def show_imageIpPhone():
c.create_image(30,140, image=image4)
def show_imageWirelessRouter():
c.create_image(30,180, image=image5)
def show_imageHost():
c.create_image(30, 220, image=image6)
# Network hardware buttons created
# ----------------------------------------------------
self.button = Button(frame, text = "Router", height= 1, width= 8, padx=2, pady=2,command=show_imageRouter)
self.button.pack(side = LEFT)
self.button = Button(frame, text = "Switch",height= 1, width= 8, padx=2, pady=2, command=show_imageSwitch)
self.button.pack(side = LEFT)
self.button = Button(frame, text = "Server",height= 1, width= 8, padx=2, pady=2, command=show_imageServer)
self.button.pack(side = LEFT)
self.button = Button(frame, text = "IP Phone",height= 1, width= 8, padx=2, pady=2, command=show_imageIpPhone)
self.button.pack(side = LEFT)
self.button = Button(frame, text = "Wireless Router",height= 1, width= 12, padx=2, pady=2, command=show_imageWirelessRouter)
self.button.pack(side = LEFT)
self.button = Button(frame, text = "Host",height= 1, width= 8, padx=2, pady=2, command=show_imageHost)
self.button.pack(side = LEFT)
self.button = Button(frame, text = "Cabling",height= 1, width= 8, padx=2, pady=2)
self.button.pack(side = LEFT)
self.button = Button(frame, text = "Square",height= 1, width= 8, padx=2, pady=2)
self.button.pack(side = LEFT)
# Create the image objects for the hardware Images
# ----------------------------------------------------------------------
imageFile = "router.png"
image1 = ImageTk.PhotoImage(Image.open(imageFile))
imageFile = "switch.png"
image2 = ImageTk.PhotoImage(Image.open(imageFile))
imageFile = "Server.png"
image3 = ImageTk.PhotoImage(Image.open(imageFile))
imageFile = "ipPhone.png"
image4 = ImageTk.PhotoImage(Image.open(imageFile))
imageFile = "WirelessRouter.png"
image5 = ImageTk.PhotoImage(Image.open(imageFile))
imageFile = "Host.png"
image6 = ImageTk.PhotoImage(Image.open(imageFile))
root = Tk()
all = AllTkinterWidgets(root)
def Exit():
print "Exit"
# Create an Exit Button
toolbar = Frame(root)
b = Button(toolbar, text="Exit", width=6, height=3, command=Exit)
b.pack(side=RIGHT, padx=2, pady=2)
toolbar.pack(side=BOTTOM, fill=X)
# Press Esc to quit
root.bind("<Escape>", exit)
# Creation of a menu File > Exit
menu = Menu(root)
root.config(menu=menu)
filemenu = Menu(menu)
menu.add_cascade(label="File", menu=filemenu)
filemenu.add_command(label="Exit", command=Exit)
root.mainloop()
Sorry if the indentation is a bit strange. I've adjusted it to make it block together here.

This answer to the question "board drawing code to move an oval" shows how to drag an object on a canvas.

Related

How to add a Scrollbar on the main window when only Label and Button are used

I know how to add a scrollbar on a tkinter window, frame, canvas.
I also know how to do it on a listbox.
Problem is, I have a window that doesn't have any of those, and only use Label and Button:
from tkinter import *
test1 = 100
test2 = 100
test3 = 100
test4 = 100
root = Tk()
root.title("Program")
root.geometry('350x250')
# first group of labels & buttons
label = Label(root, text="test1")
label.grid(row=0, column=0, columnspan=2)
label = Label(root, text=test1)
label.grid(row=1, column=0, columnspan=2)
button = Button(root, text="Up")
button.grid(row=2, column=0)
button = Button(root, text="Down")
button.grid(row=2, column=1)
#
label = Label(root, text="test2")
label.grid(row=3, column=0, columnspan=2)
label = Label(root, text=test2)
label.grid(row=4, column=0, columnspan=2)
button = Button(root, text="Up")
button.grid(row=5, column=0)
button = Button(root, text="Down")
button.grid(row=5, column=1)
#
label = Label(root, text="test3")
label.grid(row=6, column=0, columnspan=2)
label = Label(root, text=test3)
label.grid(row=7, column=0, columnspan=2)
button = Button(root, text="Up")
button.grid(row=8, column=0)
button = Button(root, text="Down")
button.grid(row=8, column=1)
#
label = Label(root, text="test4")
label.grid(row=9, column=0, columnspan=2)
label = Label(root, text=test4)
label.grid(row=10, column=0, columnspan=2)
button = Button(root, text="Up")
button.grid(row=11, column=0)
button = Button(root, text="Down")
button.grid(row=11, column=1)
root.mainloop()
The above has a small window resolution on purpose, because, while it may work in maximizing the window, once there are too many Label's text or Button, then a Scrollbar will be needed. This is intended to test that.
How can I add a scrollbar to the above code?
You need a scrollable frame. See example here: https://gist.github.com/mp035/9f2027c3ef9172264532fcd6262f3b01
And for buttons and labels, instead of using root as parent, use the scrollable frame as parent. For example:
from tkinter import *
c1 = "#999999"
c2 = "#000000"
class ScrollFrame(Frame):
"""
A simple scrollable frame class for tkinter
Source: https://gist.github.com/mp035/9f2027c3ef9172264532fcd6262f3b01
"""
def __init__(self, parent):
# create a frame (self)
super().__init__(parent, background=c1)
# place canvas on self
self.canvas = Canvas(
self, bd=0, bg=c1, relief="flat", highlightthickness=0
)
# place a frame on the canvas, this frame will hold the child widgets
self.viewPort = Frame(self.canvas, background=c1)
self.viewPort.grid_columnconfigure(0, weight=1)
# place a scrollbar on self
self.vsb = Scrollbar(self, orient="vertical", command=self.canvas.yview)
# attach scrollbar action to scroll of canvas
self.canvas.configure(yscrollcommand=self.vsb.set)
# pack scrollbar to right of self
self.vsb.pack(side="right", fill="y")
# pack canvas to left of self and expand to fil
self.canvas.pack(side="left", fill="both", expand=True)
self.canvas_frame = self.canvas.create_window(
(0, 0),
window=self.viewPort,
anchor="nw", # add view port frame to canvas
tags="self.viewPort",
)
# bind an event whenever the size of the viewPort frame changes.
self.viewPort.bind("<Configure>", self.onFrameConfigure)
self.canvas.bind("<Configure>", self.FrameWidth)
self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)
def _on_mousewheel(self, event):
self.canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
def FrameWidth(self, event):
canvas_width = event.width
self.canvas.itemconfig(self.canvas_frame, width=canvas_width)
def onFrameConfigure(self, event):
"""Reset the scroll region to encompass the inner frame"""
# whenever the size of the frame changes, alter the scroll region respectively.
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
tests = [100, 99, 98, 101]
root = Tk()
root.title("Program")
root.geometry('350x250')
scroll_frame = ScrollFrame(root)
for i, testi in enumerate(tests):
# grouping labels and buttons together in a subframe
# so that the row numbers of the labels and buttons
# are always 0 to 2 within the sub-frame
f1 = Frame(scroll_frame.viewPort)
# first group of labels & buttons
label = Label(f1, text=f"test{i}")
label.grid(row=0, column=0, columnspan=2)
label = Label(f1, text=testi)
label.grid(row=1, column=0, columnspan=2)
button = Button(f1, text="Up")
button.grid(row=2, column=0)
button = Button(f1, text="Down")
button.grid(row=2, column=1)
# defining this group to have 2 columns with equal weight
f1.grid_columnconfigure(0, weight=1)
f1.grid_columnconfigure(1, weight=1)
# expand this sub-frame horizontally to it's parent, sticking to West and East of parent frame
f1.grid(sticky="we", ipadx=2)
# adding a separator
Frame(f1, height=1, background=c2).grid(
sticky="we", pady=5, padx=5, columnspan=2
)
# expand the scroll_frame in all 4 directions to fill the parent frame, sticking to West, East, North and South of parent frame
scroll_frame.grid(sticky="wens", row=0, column=0)
# set root frame only has 1 column (filled by scroll_frame)
root.grid_columnconfigure(0, weight=1)
root.mainloop()
I recently stumbled on an easier way to do that (also use less code to do so):
import tkinter as tk
root = tk.Tk()
text = tk.Text(wrap="none")
vsb = tk.Scrollbar(orient="vertical", command=text.yview)
text.configure(yscrollcommand=vsb.set)
vsb.pack(side="right", fill="y")
text.pack(fill="both", expand=True)
for i in range(30):
test1 = "test" + str(i)
test2 = "Button" + str(i)
c = tk.Label(text=test1)
k = tk.Label(text=i)
b = tk.Button(text=test2)
d = tk.Button(text=test2)
text.window_create("end", window=c)
text.insert("end", "\n")
text.window_create("end", window=k)
text.insert("end", "\n")
text.window_create("end", window=b)
text.window_create("end", window=d)
text.insert("end", "\n")
text.configure(state="disabled")
root.mainloop()
This is based on this answer. It doesn't use a canvas.
There is also another similar answer to the one I accepted on this post, here.

Why frame border / line appear after click text widget (the frame is inside canvas) tkinter

i've the problem for make an GUI apps with python tkinter.
here is my sample code
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.geometry('300x300')
root.title('CSV Editor')
notebook = ttk.Notebook(root)
notebook.pack(pady=10, expand=True)
tab_home = ttk.Frame(notebook, width=300, height=300)
notebook.add(tab_home, text='Home')
fr_home = tk.Frame(tab_home, background="white")
fr_home.grid(row=0, column=0)
fr_home_container_canvas = tk.Frame(fr_home, background="red")
fr_home_container_canvas.grid(row=0, column=0, sticky='nw')
fr_home_container_canvas.grid_rowconfigure(0, weight=1)
fr_home_container_canvas.grid_columnconfigure(0, weight=1)
fr_home_container_canvas.grid_propagate(False)
canvas_home = tk.Canvas(fr_home_container_canvas)
canvas_home.grid(row=0, column=0, sticky="news")
vsb = tk.Scrollbar(fr_home_container_canvas, orient="vertical", command=canvas_home.yview)
vsb.grid(row=0, column=1, sticky='ns')
canvas_home.configure(yscrollcommand=vsb.set)
fr_home_widget_canvas = tk.Frame(canvas_home, background="yellow")
canvas_home.create_window((0, 0), window=fr_home_widget_canvas, anchor='nw')
fr_home_widget_canvas.config(width=300, height=300, padx=10)
fr_home_container_canvas.config(width=300, height=300)
canvas_home.config(scrollregion=canvas_home.bbox("all"))
text_widget = tk.Text(fr_home_widget_canvas, width = 30, height = 10)
text_widget.grid(column=0, row=0)
root.mainloop()
if i run this code, this is the preview
enter image description here
but when i click inside the text widget, in the frame appear line / border like this
enter image description here
What is that line / border? how to remove it?
thank you so much :)
It is the highlight background which can be removed by setting highlightthickness=0:
canvas_home = tk.Canvas(fr_home_container_canvas, highlightthickness=0)

Resizing in a grid manager tkinter

Using tkinter, I wanted to make an interface containing a few buttons on the left, which would be fairly static and align with the widgets on the right fairly nicely.
On the right, I wanted an Entry widget above a Text widget, both of which would resize accordingly (the Entry widget only on the X axis).
This code accomplishes most of that, except the Text widget does not resize and the Entry widget only resizes to align with the Text widget. Upon trying to column/rowconfigure the root, the top frame resizes awkwardly.
Here's a picture of the tkinter interface from this code:
from tkinter import *
def main():
root = Tk()
root.geometry("300x400")
framet = Frame(root)
frameb = Frame(root)
framet.grid(row=0, column=0, sticky='ew')
frameb.grid(row=1, column=0, sticky='news')
button1 = Button(framet, text='one', width=8)
button1.grid(row=0, column=0)
button2 = Button(frameb, text='two', width=8)
button2.grid(row=1, column=0, sticky='n')
entry1 = Entry(framet)
entry1.grid(row=0, column=1, sticky='ew')
text1 = Text(frameb, highlightbackground='black', highlightthickness=1)
text1.grid(row=1, column=1, sticky='news')
framet.columnconfigure(1, weight=1)
if __name__ == '__main__':
main()
As you can see, the Entry and Text widgets do not resize. How could I accomplish this whilst still having the Buttons remain static, and not moving anything (only resizing)?
Would this work for you? I changed the lines with # comments, I think, I can't really remember what I did I just tryed to get it working, one problem which I'm not happy with though is the entry widget is not the same height as the button, I guess you could manually set its height but..
from tkinter import *
def main():
root = Tk()
root.grid_columnconfigure(1,weight=1) # the text and entry frames column
root.grid_rowconfigure(0,weight=1) # all frames row
buttonframe = Frame(root)
buttonframe.grid(row=0, column=0, sticky="nswe")
entry_text_frame = Frame(root)
entry_text_frame.grid(row=0, column=1, sticky="nswe")
entry_text_frame.grid_columnconfigure(0,weight=1) # the entry and text widgets column
entry_text_frame.grid_rowconfigure(1,weight=1) # the text widgets row
button1 = Button(buttonframe, text='one', width=8)
button1.grid(row=0, column=0, sticky='nswe')
button2 = Button(buttonframe, text='two', width=8)
button2.grid(row=1, column=0, sticky='nswe')
entry1 = Entry(entry_text_frame)
entry1.grid(row=0, column=0, sticky='nswe')
text1 = Text(entry_text_frame, highlightbackground='black', highlightthickness=1)
text1.grid(row=1, column=0, sticky='news')
root.geometry("300x400")
if __name__ == '__main__':
main()

Checkbox not showing up python

I am making a GUI that will control a robot.
This is my code so far:
from Tkinter import * #Importing TKinter
class Application(Frame): #Making a frame
def __init__(self, master=None):
Frame.__init__(self, master)
self.master.title("Vinny's Myro Controller Version 0.1") #Setting the name of the program/window
self.master.geometry("550x365+300+300") #Window dimensions
self.master.rowconfigure(0, weight=2) #how much space the rows take
self.master.columnconfigure(0, weight=1) #how much space the columns take
''' \/ BOTTOM BUTTONS \/ '''
self.master.button1 = Button(master, text = "Take a Picture") #Bottom button that is supposed to take a pic.
self.master.button1.grid(row=6, column=0, sticky=W+E)
self.master.button2 = Button(master, text = "Honk the Horn") #Bottom button that is supposed to honk the horn.
self.master.button2.grid(row=6, column=1, sticky=W+E)
self.master.button3 = Button(master, text = "Get Sensor Data") #Bottom button that is supposed to get data from the sensors.
self.master.button3.grid(row=6, column=2, sticky=W+E)
''' /\ BOTTOM BUTTONS /\ '''
''' \/ LEFT RED FRAME THAT CONTAINS THE ARROWS THAT WILL MOVE THE ROBOT \/ '''
self.frame1 = Frame(master, bg="red") #Background color
self.frame1.grid(row = 0, column = 0, rowspan = 1, columnspan = 1, sticky = W+E+N+S, padx=10, pady=10)
self.frame1.forward = Button(self.frame1, text = "Forward", width=30, height=3) #Forward Button
self.frame1.forward.place(x=63, y=75) #Button Position
self.frame1.right = Button(self.frame1, text = "Right", width=12, height=3) #Right Button
self.frame1.right.place(x=189, y=131) #Button Position
self.frame1.backward = Button(self.frame1, text = "Backward", width=30, height=3) #Backward Button
self.frame1.backward.place(x=63, y=187) #Button Position
self.frame1.left = Button(self.frame1, text = "Left", width=12, height=3) #Left Button
self.frame1.left.place(x=63, y=131) #Button Position
''' /\ LEFT RED FRAME THAT CONTAINS THE ARROWS THAT WILL MOVE THE ROBOT /\ '''
self.frame2 = Frame(master, bg="green")
self.frame2.grid(row = 0, column = 1, rowspan = 3, columnspan = 3, sticky = W+E+N+S, padx=10, pady=10)
self.frame2.button5 = Button(self.frame2, text = "test")
self.frame2.button5.grid(row=6, column=2)
self.frame2.light = BooleanVar()
self.frame2.chk1 = Checkbutton(self, text = "Lights", variable = self.frame2.light, command = 1+1)
self.frame2.chk1.grid(row = 0, column = 3, padx=10, pady = 10)
root = Tk()
app = Application(master=root)
app.mainloop()
Note this piece of code:
self.frame2.light = BooleanVar()
self.frame2.chk1 = Checkbutton(self, text = "Lights", variable = self.frame2.light, command = 1+1)
self.frame2.chk1.grid(row = 0, column = 3, padx=10, pady = 10)
Somehow, I can place a button inside my frame, but not a checkbox. I need this checkbox in order to get data from the robot's sensors.
I've tried using grid and place.
Could anybody help me?
Screenshot:
http://d.pr/i/iRX4
Thanks
The problem is that you are creating the checkbutton as a child of self rather than as a child of self.frame2. Change the checkbutton to be:
self.frame2.chk1 = Checkbutton(self.frame2, ...)

How to line up buttons on Tkinter

I'm using rows to layout my Tkinter program.
I usually use pack so my buttons would automatically place itself and not over lap, using rows, my buttons are overlapping, and I have to align them by changing their padx value. I was wondering if there was an easier way to do this.
from sys import argv
from Tkinter import *
from PIL import Image, ImageTk, ImageFilter
import random
script, infile = argv
class MyApp(object):
def __init__(self):
self.root = Tk()
self.root.wm_title("ImagePro")
# Original
original = Image.open(infile)
(w, h) = (original.size[0], original.size[1])
tkpi = ImageTk.PhotoImage(original)
label = Label(self.root, image=tkpi)
label.grid(row =0, column=0, padx=5,pady=5)
img = original.copy().convert("L")
tkpi2 = ImageTk.PhotoImage(img)
label = Label(self.root, image=tkpi2)
label.grid(row =0, column=1, padx=5,pady=5)
Label(self.root, text = "Original").grid(row=1, column=0)
Label(self.root, text = "Modified").grid(row=1, column=1)
Button(self.root, text = "Brighten").grid(row=2, column=0, sticky=W)
Button(self.root, text = "Darken").grid(row=2, column=0, sticky=W, padx=60)
Button(self.root, text = "Warm").grid(row=2, column=0, sticky=W, padx=112)
Button(self.root, text = "Cool").grid(row=2, column=0, sticky=W, padx=158)
self.root.mainloop()
MyApp()
To do this you need to start using frames. A frame acts as a container for widgets. Add all the buttons to a frame, then add that frame to the root container.
class MyApp(object):
def __init__(self):
self.root = Tk()
self.root.wm_title("ImagePro")
#Original
original = Image.open(infile)
(w, h) = (original.size[0], original.size[1])
tkpi = ImageTk.PhotoImage(original)
label = Label(self.root, image=tkpi)
label.grid(row =0, column=0, padx=5,pady=5)
img = original.copy().convert("L")
tkpi2 = ImageTk.PhotoImage(img)
label = Label(self.root, image=tkpi2)
label.grid(row =0, column=1, padx=5,pady=5)
Label(self.root, text = "Original").grid(row=1, column=0)
Label(self.root, text = "Modified").grid(row=1, column=1)
self.buttonframe = Frame(self.root)
self.buttonframe.grid(row=2, column=0, columnspan=2)
Button(self.buttonframe, text = "Brighten").grid(row=0, column=0)
Button(self.buttonframe, text = "Darken").grid(row=0, column=1)
Button(self.buttonframe, text = "Warm").grid(row=0, column=2)
Button(self.buttonframe, text = "Cool").grid(row=0, column=3)
self.root.mainloop()
Each button goes in its own column within the frame, and the frame goes at the bottom of the main container.
If you use the same row,column couple for several widgets, they will appear in the same cell of the grid. Thus something like this should do the trick.
Button(self.root, text = "Brighten").grid(row=2, column=0)
Button(self.root, text = "Darken").grid(row=2, column=1)
Button(self.root, text = "Warm").grid(row=2, column=2)
Button(self.root, text = "Cool").grid(row=2, column=3)
You will find detailed information on this documentation of grid geometry manager http://effbot.org/tkinterbook/grid.htm

Categories

Resources