Finding widgets on a grid (tkinter) - python

Using the tkinter module, suppose I create a grid with 50 button widgets and each of those widgets has different text. I need to be able to specify some way of typing in a row and column so I can get that widget's text at that location.
For example, if I need the widget's text at the third row in the second column of the grid. I've searched the docs but that tells me how to get info about widgets, when I need info about the grid.

There's no need to create your own function or keep a list/dictionary, tkinter already has a built-in grid_slaves() method.
It can be used as frame.grid_slaves(row=some_row, column=some_column)
Here's an example with a grid of buttons showing how grid_slaves() retrieves the widget, as well as displaying the text.
import tkinter as tk
root = tk.Tk()
# Show grid_slaves() in action
def printOnClick(r, c):
widget = root.grid_slaves(row=r, column=c)[0]
print(widget, widget['text'])
# Make some array of buttons
for r in range(5):
for c in range(5):
btn = tk.Button(root, text='{} {}'.format(r, c),
command=lambda r=r, c=c: printOnClick(r, c))
btn.grid(row=r, column=c)
tk.mainloop()

You got a previous answer relative to a method to save button objects in a dictionary in order to recover them using their (column, row) position in a grid.
So if self.mybuttons is your dictionary of lists of buttons as described in previous answer, then you can get the text at position row, col as this:
abutton = self.mybuttons[arow][acolumn]
text_at_row_col = abutton["text"]
On the other hand, if what you need is to get the text from the button callback:
button.bind("<Button-1>", self.callback)
then you can get the button text from the event, you do not need to know its row/col position, only to press it:
def callback(self, event):
mybutton = event.widget
text_at_row_col = mybutton["text"]

Related

How to set Text widget insert cursor by event.x_root and event.y_root in Tkinter/TkinterDnD

I want to implement a Text Drag-n-Drop feature in my text editor. So far I get the text that I've dropped by event.data, and the position where I dropped the text by event.x_root and event.y_root. Please help me out with setting insert cursor of tk.Text by dropped position.
I'm using tkdnd2.8 as an extension and TkinterDnD2 as a wrapper.
Here is my code:
import tkinter as tk
import TkinterDnD2.TkinterDnD as tkdnd
def drop(event):
print("x: %d"%event.x_root, "y: %d"%event.y_root, "text:'%s'"%event.data)
root = tkdnd.Tk()
text = tk.Text(root, width=50, height=10)
text.pack()
text.drop_target_register("DND_Text")
text.dnd_bind('<<Drop:DND_Text>>', drop)
root.mainloop()
If I drag a piece of text "SomeText" onto my text widget, here is the console output:
x: 60 y: 306 text:'SomeText'
Now I'm struggle on how to convert the position from pixel scale to character scale so that I can set it to text widget's INSERT cursor and insert text into my text widget.
Or, can I just emit a "<Button-1>" and "<ButtonRelease-1>" event at certain position? If so, how can I do that?
You can specify an index by a coordinate using the format #x,y. From the canonical tcl/tk documentation
#x,y
Indicates the character that covers the pixel whose x and y coordinates within the text's window are x and y.
You should use the event x and y for the widget, not for the root. This is a standard, documented feature of the Text widget. You can use an f-string to create the index like so:
index = f"#{event.x},{event.y}"
The above will yield something like "#100,200".
You can use that index in any method call that takes an index. To convert that to a numerical index you can pass it to the index method. For example:
numerical_index = text.index(index)

A calculator using tkinter library

I want to have a calculator with 4 buttons and 2 entry. What is my problem? I don't know how to fix that it can't multiply sequence by non-int of type str nor can't multiply entry * entry.
Also I don't know where is my output and how use that.
from tkinter import ttk
from tkinter import *
import tkinter as tk
#the environment
calc=tk.Tk()
calc.title("Calculator")
#-------------------------------------
#lables and their data entry
tk.Label(calc,text="enter your first number:").grid(row=0)
tk.Label(calc,text="enter your second number:").grid(row=1)
e1=tk.Entry(calc)
e2=tk.Entry(calc)
e1.grid(row=0, column=1)
e2.grid(row=1, column=1)
x1=e1.get()
x2=e2.get()
tk.Label(calc,text="your result is:").grid(row=3)
# a free variable for get output,is this really need?
lbl=list()
#---------------------------
#the functions but my entry are entry type
def prod():
lbl=print(x1*x2)
def div():
lbl=print(x1/x2)
def sum():
lbl=print(x1+x2)
def min():
lbl=print(x1-x2)
#-------------------------------
#buttons and function in them as a command
btn1=tk.Button(calc,text="*",command=prod()).grid(row=0,column=2)
btn2=tk.Button(calc,text="/",command=div()).grid(row=1,column=2)
btn3=tk.Button(calc,text="+",command=sum()).grid(row=2,column=2)
btn4=tk.Button(calc,text="-",command=min()).grid(row=3,column=2)
#--------------------------------------------------------------------
#The answer which i need it
print("your answer is:")
calc.mainloop()
In GUI programming, you need to get the value of widgets at the time you need them, not at the time you create them.
Also, widgets return string values. If you are doing math on them, you need to convert them to numbers first.
If you want to display the result in the window, you can update a label widget with the configure method
For example, your prod definition could look something like this:
def prod():
x1 = int(e1.get())
x2 = int(e2.get())
result = x1 * x2
result_label.configure(text=str(result))
You then need to do two other things. First, you need to create the label for the result, and store the widget in a variable. To do that you must separate the creation of the widget from the layout since calling grid would cause the variable to be set to None
result_label = tk.Label(calc,text="your result is:")
result_label.grid(row=3)
Finally, your buttons need to be given a reference to the function. What you're currently doing is calling the function and assigning the result to the command attribute.
Your button should look like the following. Notice that prod does not have () after it:
btn1 = tk.Button(calc, text="*", command=prod)
...
btn1.grid(row=0,column=2)
On a side note, I think that for all widgets with a common parent, grouping all widget creation code together and then all calls to grid or pack together makes the code easier to read, easier to maintain, and easier to visualize.
For example:
btn1 = tk.Button(...)
btn2 = tk.Button(...)
btn3 = tk.Button(...)
btn4 = tk.Button(...)
btn1.grid(row=0,column=2)
btn2.grid(row=1,column=2)
btn3.grid(row=2,column=2)
btn4.grid(row=3,column=2)

Get individual listbox item properties in tkinter using itemcget?

I wrote the below code to bind event and do operations on individual listbox items.
import tkinter as tk
root = tk.Tk()
custom_list = tk.Listbox(root)
custom_list.grid(row=0, column=0, sticky="news")
def onselect_listitem(event):
w = event.widget
index = int(w.curselection()[0])
value = w.get(index)
print(index, value, " color : ",custom_list.itemcget(index,'background'))
custom_list.itemconfig(index, fg='gray', selectforeground="gray")
custom_list.bind('<Double-Button-1>', onselect_listitem)
for k in range(20):
custom_list.insert(k, " --------- " + str(k))
root.mainloop()
I am having trouble using itemcget to get the background properties while itemconfig works properly. Everything else is working. Can someone tell me if there is something wrong? I am trying to obtain the current item background color via index of the item in the listbox. The part with custom_list.itemcget doesn't print anything.
Thanks
From the New Mexico tech Tkinter reference:
.itemcget(index, option)
Retrieves one of the option values for a specific line in the listbox. For option values, see itemconfig below. If the given option has not been set for the given line, the returned value will be an empty string.
So since you haven't set the background option, itemcget returns an empty string. You can see this working by changing the print to custom_list.itemcget(index,'fg'). The first time you doubleclick you get an empty sting because you haven't set it, the second time it prints gray.

Which Tkinter functions should i use to find the coordinates of a randomly placed(in the canvas) text entry and how?

Thanks for looking into my question. I'll try to give you a big and small picture of what I'm trying to do here.
Big Picture:
So, basically I'm trying to make a simple mindmapping program where, after the first entry, every text I input unto the Entry widget is linked to the previous text via a line widget. So like this: Hello----There, and then Hello----There----Yo. Actually, I'm hoping for more modifications in the future like being able to rearrange the links via some metric I have yet explored, but this is basically it.
Small/Specific Picture:
I realize that in order to do this, I will have to find a way to acquire all the xy coordinates of every text drawn on the canvas (text I drew on the canvas by using the random function). I need the coordinates of the first text and the coordinates of the second text so I can use those to draw the line to visually link the two texts. I thought of using an array to list down all inputted text, but I realize that only stores the text and not the location of the text on the canvas. I have explored using tags, or using the coords functions or using the bbox function, but to no avail. Any clues on how to go about this? I would highly appreciate it, Thanks. :)
import Tkinter
import random
a = Tkinter.Tk()
b = Tkinter.Canvas(a, width=1000, height=500)
b.pack()
def c(event):
b.create_text(random.randint(50,940), random.randint(50,480), anchor="center", text=d.get())
f.append(d.get())
d.delete(0, 'end')
print f
#this function creates a randomly located text taken from the entry widget below and, at the same time, appends the text in the list known as f''
d = Tkinter.Entry(a)
d.pack()
d.bind("<Return>", c)
d.focus()
b.create_line(300, 250, 600, 290)
#this is my very early attempt at linking text inputted and drawn on the Canvas
f = []
a.mainloop()
Simply assign random values to variables before you use it to create text on canvas, and keep on list with object ID and text.
x = random.randint(...)
y = random.randint(...)
obj_id = b.create_text(x, y, ...)
f.append([x, y, obj_id, d.get()])
BTW: if you have obj_id then you can also do
x,y = b.coords(obj_id)

Python tkinter create buttons with loop and edit their parameters

I'm making a program with Tkinter where I'd need to use a "for i in range" loop, in order to create 81 Text Widgets named like :
Text1
Text2
Text3
...
and dispose them in a square (actually to make a grill for a sudoku game of a 9*9 size). After I created these 81 Text Widgets I need to place them (using .place() ) and entering their position parameters.
After this, I will need to collect the values that the user entered in these Text Widgets.
I'm a newbie and I don't really know how to code this.
Here is my actual code but the problem is I can't modify the parameters once the dictionnary is created and i don't know how to access to the Text Widgets parameters. Maybe using a dictionnary is not the appropriate solution to do what I want.
d = {}
Ypos = 100
for i in range(9):
Xpos = 100
for j in range(9):
d["Text{0}".format(i)]= Text(window, height=1, width=1,relief = FLAT,font=("Calibri",16))
d["Text{0}".format(i)].place(x = Xpos+,y = Ypos)
Xpos += 35
yPos += 35
Thanks for helping
Don't use a complex key for the dictionary, it makes the code more complex without adding any benefit.
Since you're creating a grid, use row and column rather than i and j. This will make your code easier to understand. Also, don't use place if you're creating a grid. Tkinter has a geometry manager specifically for creating a grid.
Since you're creating a text widget with a height of one line, it makes more sense to use an entry widget.
Here's an example:
import Tkinter as tk
root = tk.Tk()
d = {}
window = tk.Frame(root, borderwidth=2, relief="groove")
window.pack(fill="both", expand=True)
for row in range(9):
for column in range(9):
entry = tk.Entry(window, width=1, relief="flat", font=("Calibri",16))
entry.grid(row=row, column=column, sticky="nsew")
d[(row,column)] = entry
root.mainloop()
Whenever you need to access the data in a cell, you can use the row and column easily:
value = d[(3,4)].get()
print("row 3, column 4 = %s" % value)

Categories

Resources