Adding data dynamically to tkinter table - python

I wanted to create a table like below
The data from the rows is generated continuously . I want to create my table in such a way that rows are created dynamically . I wrote below code (Very new to tinkter, may be just 6 hours new) but the no data is inserted .
from scapy.all import *
from scapy.layers.http import HTTPRequest,HTTPResponse,HTTP # import HTTP packet
from tkinter import ttk
import tkinter as tk
def generateData():
sniff_packets()
def sniff_packets():
window.mainloop() # <<---The window loop
window.after(300, process_packet)
sniff(filter="port 80", prn=process_packet, iface="utun2", store=False)
def process_packet(packet):
print("Called to process_packet() ")
http_packet = str(packet)
if packet.haslayer(HTTP):
#if "www.xyz.com" in http_packet:
# print(http_packet)
if 'XYZ' in http_packet:
if HTTPRequest in packet:
http_request = packet[HTTPRequest]
insertDataDynamic((arrangePacket(str(http_request)))
if HTTPResponse in packet:
http_response = packet[HTTPResponse]
insertDataDynamic((arrangePacket(str(http_request)))
def insertDataDynamic(api_data):
print("Called to insertDataDynamic() ")
treev.insert("", 'end', text ="L1",
values =("DATA ", api_data, "HTTP"))
def arrangePacket(httpLayer):
ret = "***************************************GET PACKET****************************************************\n"
ret += "\n".join(httpLayer.split(r"\r\n"))
ret += "\n *****************************************************************************************************\n"
return ret
if __name__ == "__main__":
window = tk.Tk()
window.resizable(width = 1, height = 1)
treev = ttk.Treeview(window, selectmode ='browse')
treev.pack(side ='right')
# Constructing vertical scrollbar
# with treeview
verscrlbar = ttk.Scrollbar(window,
orient ="vertical",
command = treev.yview)
# Calling pack method w.r.to verical
# scrollbar
verscrlbar.pack(side ='right', fill ='x')
# Configuring treeview
treev.configure(xscrollcommand = verscrlbar.set)
# Defining number of columns
treev["columns"] = ("1","2","3")
# Defining heading
treev['show'] = 'headings'
# Assigning the width and anchor to the
# respective columns
treev.column("1", width = 500, anchor ='c')
treev.column("2", width = 500, anchor ='se')
treev.column("3", width = 500, anchor ='se')
# Assigning the heading names to the
# respective columns
treev.heading("1", text ="Name")
treev.heading("2", text ="Sex")
treev.heading("3", text ="Age")
generateData()
Also as soon as the mainloop starts ,the prn function of scapy doesn't work .

I put your function in the mainloop so it will be called when your gui is generated.
Also note that I put the after() method in your function so it will call itself every 300 ms.
from tkinter import ttk
import tkinter as tk
treev = None
window = None
def generateData(self):
#This is my API which makes a rest call and gets data
api_data = restcall()
insertDataDynamic(api_data)
window.after(300, generateData)
def insertDataDynamic(self,api_data):
treev.insert("", 'end', text ="L1",
values =(api_data.name, api_data.gender, api_data.age))
if __name__ == "__main__":
window = tk.Tk()
window.resizable(width = 1, height = 1)
treev = ttk.Treeview(window, selectmode ='browse')
treev.pack(side ='right')
# Constructing vertical scrollbar
# with treeview
verscrlbar = ttk.Scrollbar(window,
orient ="vertical",
command = treev.yview)
# Calling pack method w.r.to verical
# scrollbar
verscrlbar.pack(side ='right', fill ='x')
# Configuring treeview
treev.configure(xscrollcommand = verscrlbar.set)
# Defining number of columns
treev["columns"] = ("1","2","3")
# Defining heading
treev['show'] = 'headings'
# Assigning the width and anchor to the
# respective columns
treev.column("1", width = 500, anchor ='c')
treev.column("2", width = 500, anchor ='se')
treev.column("3", width = 500, anchor ='se')
# Assigning the heading names to the
# respective columns
treev.heading("1", text ="Name")
treev.heading("2", text ="Sex")
treev.heading("3", text ="Age")
generateData()
window.mainloop()
an exampel can be found here:
import tkinter as tk
def test():
print('test')
window.after(300, test)
window = tk.Tk()
test()
window.mainloop()

window.mainloop() will not end until you close the window. It allows to expose the GUI and process events.
There are several questions related to that topic. This one for instance: When do I need to call mainloop in a Tkinter application?

Related

How to make my variable update based on cursor position?

I tried to build off of the solution here. My code is:
from tkinter import mainloop, Tk, Frame, Button, Label, Canvas, PhotoImage, NW
from tkinter import ttk
from tkinter import filedialog
import tkinter as tk
from PIL import Image, ImageTk
class my_class(tk.Tk):
def __init__(self):
super().__init__()
self.geometry=('1400x1400')
self.filename = ''
my_notebook = ttk.Notebook(self)
my_notebook.pack(pady=5)
self.selections = Frame(my_notebook, width = 1100, height = 700)
self.selections.pack(fill = "both", expand=1)
my_notebook.add(self.selections, text = "Selections")
Button(self.selections, text = "Select an Image", command = self.get_image).place(x=10,y=40)
self.image_frame = Frame(my_notebook, width = 1100, height = 700)
self.image_frame.pack(fill = "both", expand=1)
my_notebook.add(self.image_frame, text = "Image")
self.my_canvas = Canvas(self.image_frame, width=800, height=600, bg="white")
self.my_canvas.pack()
self.rgb_var = tk.StringVar(self.image_frame, '0 0 0')
self.rgb_label = tk.Label(self.image_frame, textvariable = self.rgb_var)
self.rgb_label.pack()
self.image_frame.bind('<Motion>', lambda e: self.get_rgb(e))
def get_image(self):
self.filename = filedialog.askopenfilename(initialdir="D:/Python", title="select a file", filetypes = (("png files","*.png"),("jpg files","*.jpg")))
self.img = Image.open(self.filename)
self.img_rgb = self.img.convert('RGB')
dim_x, dim_y = self.img_rgb.size
self.img_tk = ImageTk.PhotoImage(self.img_rgb.resize((dim_x, dim_y)))
self.my_canvas.create_image(dim_x // 2, dim_y // 2, image = self.img_tk)
def get_rgb(self, event):
x, y = event.x, event.y
try:
rgb = self.img_rgb.getpixel((x, y))
self.rgb_var.set(rgb)
except IndexError:
pass # ignore errors if cursor is outside the image
if __name__ == '__main__':
app = my_class()
app.geometry=('1200x900')
app.mainloop()
I can use the button to select an image. Then I click the (Image) tab and see the selected image on the canvas.
I expected the (rgb_var) displayed under the image to update as I move the mouse pointer across the image. Instead the numbers under the image only update when the mouse pointer is in the frame, but outside the canvas. Also the numbers displayed seem to be unrelated to pixels in the image. How can I display the RGB values of a pixel that is (under the mouse pointer) when the mouse pointer is over the image?

Tkinter buttons getting hidden behind other frames

I am trying to create a battlemap for dnd (picture) with adjustable grid and movable enemy/creature tokens. The idea is to drag one of the token from the right onto the map on the left.
The window is made of 3 frames. The frame for the map, the frame for the "new map" button and slider. And then frame for the tokens, which are buttons tiled using button.grid()
I found a drag and drop system here that I'm using to drag the tokens. However, when I bring them over the map, they go behind it and you can't see them (I know they go behind because they can be partially visible between the two frames). Is there any way to bring them to the front?
import tkinter as tk
class DragManager():
def add_dragable(self, widget):
widget.bind("<ButtonPress-1>", self.on_start)
widget.bind("<B1-Motion>", self.on_drag)
widget.bind("<ButtonRelease-1>", self.on_drop)
widget.configure(cursor="hand1")
def on_start(self, event):
# you could use this method to create a floating window
# that represents what is being dragged.
pass
def on_drag(self, event):
# you could use this method to move a floating window that
# represents what you're dragging
event.widget.place(x=event.x_root + event.x, y= event.y_root + event.y)
#when button is dropped, create a new one where this one originally was
def on_drop(self, event):
# find the widget under the cursor
x,y = event.widget.winfo_pointerxy()
target = event.widget.winfo_containing(x,y)
try:
target.configure(image=event.widget.cget("image"))
except:
pass
if x > window.winfo_screenwidth() - 200:
del event.widget
return
if not event.widget.pure:
return
button = tk.Button(master=entity_select_frame, text = "dragable", borderwidth=1, compound="top")
#avoiding garbage collection
button.gridx = event.widget.gridx
button.gridy = event.widget.gridy
button.grid(row = event.widget.gridx, column = event.widget.gridy)
button.grid()
button.pure = True
dnd.add_dragable(button)
window = tk.Tk()
window.geometry("1000x800")
map_frame = tk.Frame()
controls_frame = tk.Frame(width=200, borderwidth=1, relief=tk.RAISED)
tk.Label(master=controls_frame, text="controls here").pack()
entity_select_frame = tk.Frame(width=200, relief=tk.RAISED, borderwidth=1)
dnd = DragManager()
button = tk.Button(master=entity_select_frame, text = "dragable", borderwidth=1)
button.gridx = 0
button.gridy = 0
button.grid(row = 0, column = 0)
button.pure = True
dnd.add_dragable(button)
map_frame.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)
controls_frame.pack(fill=tk.BOTH)
entity_select_frame.pack(fill=tk.BOTH)
window.mainloop()
I played around a little bit and used stuff from this post. I did not structure it as a class and I used the picture frame as my root-frame and put the control-frame inside that. I'm not sure how this would be best combined with your "draw-grid", "token" functionalities etc., however I hope it helps. I did not find a way to drag widgets across frames though (tried to set a new master for the button, recreate it after dropping it etc.). Get the image used in my code from here.
from tkinter import Tk, Frame, Label, Button, Canvas, font
from tkinter import ttk
from PIL import Image, ImageTk
root = Tk()
""" ####################### Configuration parameters ###################### """
image_file_path = "Island_AngelaMaps-1024x768.jpg"
resize_img = False # set to True if you want to resize the image > window size
resize_to = (600, 600) # resolution to rescale image to
""" ####################### Drag and drop functionality ################### """
def make_draggable(widget):
widget.bind("<Button-1>", on_drag_start)
widget.bind("<B1-Motion>", on_drag_motion)
def on_drag_start(event):
widget = event.widget
widget._drag_start_x = event.x
widget._drag_start_y = event.y
def on_drag_motion(event):
widget = event.widget
x = widget.winfo_x() - widget._drag_start_x + event.x
y = widget.winfo_y() - widget._drag_start_y + event.y
widget.place(x=x, y=y)
""" ################################# Layout ############################## """
# picture frame with picture as background
picture_frame = Frame(root)
picture_frame.pack(side="left", anchor="w", fill="both", expand=True)
# load the image
if resize_img:
img = ImageTk.PhotoImage(Image.open(image_file_path).resize(resize_to, Image.ANTIALIAS))
else:
img = ImageTk.PhotoImage(Image.open(image_file_path))
# create canvas, set canvas background to the image
canvas = Canvas(picture_frame, width=img.width(), height=img.height())
canvas.pack(side="left")
canvas.background = img # Keep a reference in case this code is put in a function.
bg = canvas.create_image(0, 0, anchor="nw", image=img)
# subframe inside picture frame for controls
ctrl_subframe = Frame(picture_frame)
ctrl_subframe.pack(side="right", anchor="n")
# separator between picture and controls, inside picture frame
ttk.Separator(picture_frame, orient="vertical").pack(side="right", fill="y")
# underlined label 'Controls' in subframe
ctrl_header = Label(ctrl_subframe, text="Controls", font=("Arial", 10, "bold"))
f = font.Font(ctrl_header, ctrl_header.cget("font"))
f.configure(underline=True)
ctrl_header.configure(font=f)
ctrl_header.pack(side="top", pady=2)
# update window to get proper sizes from widgets
root.update()
# a draggable button, placed below ctrl_header
# (based on X of ctrl_subframe and height of ctrl_header, plus padding)
drag_button = Button(picture_frame, text="Drag me", bg="green", width=6)
drag_button.place(x=ctrl_subframe.winfo_x()+2, y=ctrl_header.winfo_height()+10)
make_draggable(drag_button)
""" ################################ Mainloop ############################# """
root.mainloop()

Python textbox only updates after loop is complete

I have a very long automation that uses a very long WHILE loop. I would like to be able to have the text in a textbox update as the process is progressing. However, based on the research I have done, this does not seem to be possible and all of the results "dump" at once when the WHILE loop completes. This is useless for me.
I would love it if the textbox could update as the SHELL updates as that follows along synchronously with the actual process.
I have made a simple TEST file to see if I can get it to work. Here is the code for the TEST file:
from tkinter import *
import time
import tkinter as tk
import tkinter.font as tkFont
root=Tk()
myFont = tkFont.Font(family = 'Helvetica', size = 18, weight = 'bold')
text_cell_bg="cyan" #TEXT CELL BACKGROUND COLOR
text_cell_fg="black" #TEXT CELL TEXT COLOR
text_cell_height=2 #TEXT CELL HEIGHT
text_cell_width=30 #TEXT CELL WIDTH
button_bg="blue" #BUTTON CELL BACKGROUND COLOR
button_fg="white" #BUTTON CELL TEXT COLOR
button_height=2 #BUTTON CELL HEIGTH
button_width=10 #BUTTON CELL WIDTH
textbox=Text(root)
textbox.insert(END, 'Default Text\n\n')
def count_print ():
count = 0
letter = "A"
while count < 5:
print("Count = ",count,". Letter = ",letter,".")
textbox_value = "Count = {}. Letter = {}.\n".format(count,letter)
textbox.insert(1.0, textbox_value)
count += 1
time.sleep(1)
textbox.pack()
button1=tk.Button(root, text='output', command=count_print, font = myFont,
height=button_height,
width=button_width,
bg = button_bg,
fg = button_fg)
button1.pack()
root.mainloop()
You can call your function using Tkinter from inside the function itself.
Tkinter has a special function for this.
from tkinter import *
import time
import tkinter as tk
import tkinter.font as tk_font
root = Tk()
myFont = tk_font.Font(family='Helvetica', size=18, weight='bold')
text_cell_bg = "cyan" # TEXT CELL BACKGROUND COLOR
text_cell_fg = "black" # TEXT CELL TEXT COLOR
text_cell_height = 2 # TEXT CELL HEIGHT
text_cell_width = 30 # TEXT CELL WIDTH
button_bg = "blue" # BUTTON CELL BACKGROUND COLOR
button_fg = "white" # BUTTON CELL TEXT COLOR
button_height = 2 # BUTTON CELL HEIGHT
button_width = 10 # BUTTON CELL WIDTH
textbox = Text(root)
textbox.insert(END, 'Default Text\n\n')
def count_print(n_times=0): # tell your function how many times you have called it
(on the first call this will be 0)
n = n_times + 1
letter = "A"
if n <= 5:
print("Count = ", n, ". Letter = ", letter, ".")# you can change the n var on these lines to n_times if you want 0-4 rather than printing 1-5.
textbox_value = "Count = {}. Letter = {}.\n".format(n, letter)
textbox.insert(1.0, textbox_value)
root.after(1000, count_print, n) # this number replaces and time.sleep it's set to 1 second right now it also call the count_print function passing n as a variable
textbox.pack()
button1 = tk.Button(root, text='output', command=count_print, font=myFont,
height=button_height,
width=button_width,
bg=button_bg,
fg=button_fg)
button1.pack()
root.mainloop()
I hope this helps. also, I don't use Tkinter for my projects and am definitely not very good at using it either so credit for me knowing about root. after goes to this answer,
Make Tkinter force a "refresh" a text widget in mid-command?

Handling visual overflow in a tkinter frame?

I'm writing a class that adds a scrolling frame. It detects when the frame's contents exceed its height, and then configures the scrollbar. The problem is that when I scroll down, the items in the frame scroll outside of the top of the frame and appear above it.
I've tested it out using plain labels, and it worked fine, but I'm using a class object that has some nested frames, and the child objects are what show up above the scrolling frame.
This is the gist of the code that's giving me problems (please note that the layout doesn't match the full project. I used this as my reference for the ScrollFrame() class.)
Just running this, pressing the button, and scrolling down will show you what's wrong with it.
import tkinter as tk
import tkinter.simpledialog as sd
class ScrollFrame(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
### setting up the objects used ###
self.canvas = tk.Canvas(master)
self.frame = tk.Frame(self.canvas)
self.scrollbar = tk.Scrollbar(master, orient = 'vertical',
command = self.canvas.yview)
### scrollbar moves with current canvas scrollamount ###
self.canvas.configure(yscrollcommand = self.scrollbar.set)
self.scrollbar.pack(side = 'right', fill = 'y')
self.canvas.pack(side = 'left', fill = 'both', expand = True)
### creating frame to pack widgets onto ###
self.canvas.create_window((4, 4), window = self.frame,
anchor = 'nw', tags = 'self.frame')
### setting scrollbar height on load ###
self.frame.bind('<Configure>', self.frameConfig)
### scroll when a user's mouse wheel is used inside the canvas ###
def scrollCanvas(event):
self.canvas.yview_scroll(-1*(event.delta//120), 'units')
self.canvas.bind('<MouseWheel>', scrollCanvas)
### set the scrollregion of the canvas ###
def frameConfig(self, event):
self.canvas.configure(scrollregion = self.canvas.bbox('all'))
class OptionSet(tk.Frame):
def __init__(self, master, **kwargs):
super().__init__()
self.all = tk.Frame(master)
self.all.configure(bd = 1, relief = 'solid')
# independent label
self.text = '' if not kwargs['text'] else kwargs['text']
self.label = tk.Label(text = self.text)
# list of all buttons
self.buttons = tk.Frame()
buttons = [] if not kwargs['buttons'] else kwargs['buttons']
self.button_list = []
if buttons:
for button in buttons:
self.button_list.append(
tk.Button(self.buttons, text = button)
)
self.style()
def style(self, default = 1, **kwargs):
if default:
self.label.pack(in_ = self.all, side = 'left')
for button in self.button_list:
button.pack(side = 'left')
self.buttons.pack(in_ = self.all, side = 'right')
root = tk.Tk()
list_items = []
current = {
'month': 'August'
# ...
}
def btn_fcn(num):
for i in list_items:
i.grid_forget()
'''
# get event as input
event = sd.askstring('Event Input',
f"What is happening on {current['month']} {num}?")
# insert new list_item
list_items.append(OptionSet(event_list.frame, text = event,
buttons = ['Edit', 'Delete']))
print(event)
'''
for i in range(10):
list_items.append(OptionSet(event_list.frame, text = 'test',
buttons = ['Edit', 'Delete']))
for i in list_items:
i.all.grid(sticky = 'we')
tk.Button(root, text = 'Add', command = lambda: btn_fcn(22)).pack()
event_list = ScrollFrame(root)
event_list.pack()
root.mainloop()
I want the buttons and labels to cut off outside of the ScrollFrame. I don't know whether they're overflowing from the frame or the canvas, but they should cut off normally if all goes according to plan.
Thanks.
The problem is that you're being very sloppy with where you create your widgets. For example, you aren't putting the canvas and scrollbar in the ScrollFrame, you're putting it in master. Every widget defined in ScrolLFrame needs to be in self or a child of self.
The same is true with OptionSet - you're putting the inner frame (self.all) a d the other widgets in master rather than inside the OptionSet itself.
My recommendation is to temporarily remove all of the OptionSet code (or just don't use it) and instead, just add labels to the scroll frame. Focus on getting that working without the complexity of a custom class being added to a custom class. Once you are able to scroll just labels, the you can add back in the OptionSet code.

Adjust scrollbar height in Tkinter?

I have created a scrollbar in Tkinter and it's working fine, but the size of the Scroll Button is not being scaled correctly (normally it's adjusted to the size of the scrollable area).
I'm placing all my widgets with a .pack(), therefore the .grid sticky configuration is something I would like to avoid.
My question is: Which part of the scrollbar configuration is responsible for scaling the height?
The code example:
master = Tk()
FrameBIG = Frame(master)
Main = Canvas(FrameBIG,height = 1200,width =1500,scrollregion=Main.bbox("all"))
scroll = Scrollbar(FrameBIG ,orient="vertical", command=Main.yview)
scroll.pack(side="right", fill="y")
Main.pack(side = BOTTOM, anchor = NW,fill="x")
FrameBIG.pack(anchor = W, fill = "x")
The code
Main = Canvas(FrameBIG,height=1200,width=1500,scrollregion=Main.bbox("all"))
is wrong because Main does not exists yet. It should be
Main = Canvas(FrameBIG,background="blue", height = 500,width =500)
Main.configure(scrollregion=Main.bbox("all"))
But it is meaningless because Main canvas was created right now and is empty (so the bbox method returns None)
When you created the scrollbar with
scroll = Scrollbar(FrameBIG ,orient="vertical", command=Main.yview)
you forgot to complete the two step contract between scroll and Main, so you have to add the line below (just after the creation of scroll)
Main.configure(yscrollcommand=scroll.set)
Now the code is like this
from tkinter import *
master = Tk()
FrameBIG = Frame(master)
Main = Canvas(FrameBIG,background="blue", height = 500,width =500)
Main.configure(scrollregion=Main.bbox("all"))
scroll = Scrollbar(FrameBIG ,orient="vertical", command=Main.yview)
Main.configure(yscrollcommand=scroll.set)
scroll.pack(side="right", fill="y")
Main.pack(side = BOTTOM, anchor = NW,fill="x")
FrameBIG.pack(anchor = W, fill = "x")
master.mainloop()
Now you can notice that the scroll bar does not have the button. Its because the Main canvas is empty. Let's add something to the Main canvas
FrameBIG.pack(anchor = W, fill = "x")
# creates a diagonal from coordinates (0,0) to (500,1000)
Main.create_line(0, 0, 500, 1000)
master.mainloop()
Now the line is there but the scroll button is not there yet, why?
Because you have to update the scrollregion of the Main canvas.
So let's do it with
FrameBIG.pack(anchor = W, fill = "x")
Main.create_line(0, 0, 500, 1000)
Main.configure(scrollregion=Main.bbox("all"))
master.mainloop()
Now it is working properly.
Here the complete code.
from tkinter import *
master = Tk()
FrameBIG = Frame(master)
Main = Canvas(FrameBIG,background="blue", height = 500,width =500)
Main.configure(scrollregion=Main.bbox("all"))
scroll = Scrollbar(FrameBIG ,orient="vertical", command=Main.yview)
Main.configure(yscrollcommand=scroll.set)
scroll.pack(side="right", fill="y")
Main.pack(side = BOTTOM, anchor = NW,fill="x")
FrameBIG.pack(anchor = W, fill = "x")
Main.create_line(0, 0, 500, 1000)
Main.configure(scrollregion=Main.bbox("all"))
master.mainloop()
In the next question, post a question with a complete working code that shows up you problem. You will get faster and better answers, ok?
Have a nice day.

Categories

Resources