tkinter highlight words by button clicks in Scrolledtext - python

I am trying to move to the next found word in the Scrolledtext and highlight it. The function finds indexes, but somehow I don't see the highlighting. The word is always the same. The search is in the dictionary text. There are usually many instances of the same word in the dictionary. The function see() is tied to a button. I use the highlight_word() in another function that is called see(), that function allows to move from word to word in the Scrolledtext and updates count of the words in a label. And this combination of functions does not work together...
UPDATE.
Thanks to hints from Bryan Oakley and in particular his hint which I found here. text.tag_remove The script now does what I want: it highlights the found word, and when the next found word is highlighted the highlight on the previous word is removed.
import tkinter as tk
import tkinter.font as TkFont
from tkinter import *
from tkinter.scrolledtext import ScrolledText
root = tk.Tk()
customFont = TkFont.Font(family="Fira Code", size=28)
var = tk.StringVar()
root.configure(bg='#3541b7')
root.geometry("1800x1000")
root.grid_columnconfigure(0, weight=1)
root.grid_rowconfigure(1, weight=1)
show = tk.IntVar()
show.set(0)
countVar = tk.StringVar()
idxVar = tk.StringVar()
posis = []
text = ScrolledText(root, width=140, height=400, wrap=WORD)
text.configure(background="#3f3f3f", fg='#dcdccc', font=customFont)
text.grid(row=1, columnspan=20, sticky=N + S + W + E)
text_to_search = '''
tempo
tempo
tempo
'''
text.insert(END, text_to_search)
entry_newwindow = tk.Entry(root, font=('Arial', 30), bg="#ffc299", bd=2)
IDX = "1.0"
prev_IDX = ""
prev_lastIDX = ""
def highlight_word():
global IDX
global prev_IDX
global prev_lastIDX
prev_IDX = ""
word = entry_newwindow.get().strip()
IDX = text.search(word, IDX, nocase=1, stopindex=END)
prev_IDX = IDX
lastIDX = '%s+%dc' % (IDX, len(word))
prev_lastIDX = lastIDX
text.tag_add('word', IDX, lastIDX)
text.tag_config('word', background='blue')
IDX = lastIDX
def see():
word = entry_newwindow.get().strip()
pos = show.get()
lbl_Count.config(text=f'{pos + 1}')
if prev_IDX and prev_lastIDX:
text.tag_remove("word", prev_IDX, prev_lastIDX)
highlight_word()
show.set(pos + 1)
btn_see = tk.Button(root, text='Find', fg='white', bg='#1E7056', font=(
'Fira Code', 21), borderwidth=' 4', command=see)
lbl_Count = tk.Label(root, font=('Fira Code bold', 24),
bg="#81cbfe", borderwidth=4, width=3, relief="groove", text="0")
entry_newwindow.grid(column=0, row=0,sticky="we")
entry_newwindow.focus_set()
btn_see.grid(column=1, row=0)
lbl_Count.grid(column=2, row=0)
root.mainloop()

Related

how to make a button with a different number in tkinter

im trying to name a button what it is named in ent and so it doesn't repeat when the button is pressed again so if you press once it's button1 and again button2
from tkinter import *
def ext():
win1.destroy()
but1 = Button(root, text=txt.get(), height=10, width=30)
but1.grid(padx=3, row=0, column=1)
def create():
global win1
global txt
win1 = Tk()
win1.geometry("200x200")
ent = Entry(win1)
ent.pack(pady=20)
txt = ent.get()
sub = Button(win1, text="Submit", command=ext)
sub.pack()
root = Tk()
root.geometry("750x750")
root.config(background="#6673ED")
create_but = Button(root, text="Create new card", height=10, width=30, command=create)
create_but.grid(row=0,column=0)
root.mainloop()
The code below uses a dictionary of lists to add an integer to any repeating text input. I think this is what the question is about.
import tkinter as tk # import as tk is safer and more flexible
# Globals to keep it simple
names = {} # Dictionary. Will become a dictionary of lists.
row = 1
col = 0
win1 = None
ent = None
def add_text( txt ):
""" Adds txt to the names dictionary if it doesn't already exist.
Adds and integer to the txt if it does already exit """
name = names.get( txt, None )
if name:
name.append( txt + str(len( name )) ) # Append `txt + int` to a list
else:
names[ txt ] = [ txt ] # Add a list of one item to the dict.
# print( names ) # Uncomment to see what is happening.
return names[ txt ][-1]
def ext():
global row, col
txt = ent.get() # Get the text from the entry
win1.destroy() # before the window is destroyed
txt = add_text( txt )
but1 = tk.Button(root, text=txt, height=10, width=30)
but1.grid(padx=3, row=row, column=col) # row and column need to
# change to show all buttons.
col += 1
if col > 2:
row += 1
col = 0
def create():
global win1
global ent
win1 = tk.Toplevel() # Create a second window with tk.Toplevel.
# Never create two tk.Tk objects.
win1.geometry("200x200")
ent = tk.Entry(win1)
ent.pack(pady=20)
ent.focus() # Position the focus in the Entry
# txt = ent.get() # removed as the Entry was being read before data was entered.
# The entry is now read in `ext`.
sub = tk.Button( win1, text="Submit", command=ext )
sub.pack()
root = tk.Tk()
root.geometry("750x750")
root.config(background="#6673ED")
create_but = tk.Button(root, text="Create new card", height=10, width=30, command=create)
create_but.grid(row=0,column=0)
root.mainloop()

How to create a counter for the number of times a button that was created was pressed in tkinter?

So I have made a simple program that allows me to type a label for a button and have it created and clickable in the tkinter gui. Now all I need is to add a function that returns the number of times each individual button is clicked. The problem is that the buttons I created are not actually coded in the input so I've found it difficult to do this. I feel like I would have to use the lambda function but I have no experience at all with it. Help is appreciated, thank you.
Code:
import tkinter as tk
from tkinter import *
window = tk.Tk()
window.title("Tkinter FINAL")
window.geometry("600x400")
window.resizable(width=False, height=False)
WIDTH = 800
HEIGHT = 600
counter_name = tk.Label(window, text="Counter Word", width=20)
counter_name.place(x=460,y=318)
counter_entry = tk.Entry(window, width=20)
counter_entry.place(x=470,y=338)
position_x = 0
position_y = 0
word_dict = {}
def button_function():
word_dict[title] += 1
button_count = 0
def button_maker():
global position_x, position_y, button_count, title
button = tk.Button(window, text=counter_entry.get(), width=10, height=2, command = button_function, fg="red")
button.place(x=position_x,y=position_y)
position_x += 116
button_count += 1
if button_count % 6 == 0:
position_y += 50
position_x = 0
title = counter_entry.get()
word_dict[title] = 0
counter_entry.delete(0,'end')
btnmaker = tk.Button(window, text='Click to create counter', width=17, height=2, command = button_maker, fg="red")
btnmaker.place(x=470,y=358)
btnreset = tk.Button(window, text='RESET', width=10, height=2, command = window.destroy, fg="red")
btnreset.place(x=520,y=500)
window.mainloop()
You need to pass the word entered to button_function() using lambda:
word_dict = {}
def button_function(word):
word_dict[word] += 1
print(word, word_dict[word])
def button_maker():
# get the input word
word = counter_entry.get().strip()
# make sure the input word is unique in the dictionary
if word and word not in word_dict:
count = len(word_dict)
button = tk.Button(window, text=word, width=10, height=2, fg="red",
command=lambda w=word: button_function(w)) # pass the input word to button_function()
button.place(x=count%5*116, y=count//5*60)
word_dict[word] = 0 # init the counter for the input word
counter_entry.delete(0,'end')
Updated code with counter labels:
word_dict = {}
def button_function(word):
count = word_dict[word].get()
word_dict[word].set(count+1)
def button_maker():
word = counter_entry.get().strip()
if word and word not in word_dict:
count = len(word_dict)
row, col = count//5*2, count%5
# create the word button
button = tk.Button(window, text=word, width=10, height=2, fg="red",
command=lambda w=word: button_function(w))
button.grid(row=row, column=col, padx=10, pady=(10,0))
# create the corresponding counter label
var = tk.IntVar() # for the counter value
tk.Label(window, textvariable=var).grid(row=row+1, column=col)
word_dict[word] = var
counter_entry.delete(0, 'end')

tkinter can't change radiobutton appearance after clicking it

I have read through a number of samples and my code seems similar, but when I click on the radio button it does not turn green as expected.
the code expects a csv file with the following line
ref, Lang1, Lang2, Lang3, Lang4
I know the code is executing correctly, with the use of debugging print statements through out the code- many removed to simplify the code
import tkinter as tk
import csv as csv
root = tk.Tk()
Sel_Lang = tk.StringVar()
def radioselect():
global lasthit
temp = int(Sel_Lang.get()) -1
buttonlist[temp].config(bg='green')
buttonlist[temp].grid(row=temp, column=1)
if lasthit != temp:
print('last hit greater then 0')
buttonlist[lasthit].config(bg='white')
buttonlist[lasthit].grid(row=lasthit, column=1)
lasthit = temp
with open('Language.csv') as csvfile:
Langptr = csv.reader(csvfile, delimiter=',')
row1 = next(Langptr) #read the header row
langs = (len(row1))
lang=1
while lang < langs:
##print(row1[lang])
MODES.append((row1[lang], lang))
lang = lang + 1
MODES=[]
lasthit = 0
arraycntr = 0
buttonlist = [0] * len(MODES)
for text, mode in MODES:
''' display for the user to select Language
language choices are taken from the first row in the Language.csv file
'''
buttonlist[arraycntr] = tk.Radiobutton(root, height=2, width=15,
borderwidth=10, text=text, font=("Arial", 24, "bold"), bg='white',
variable=Sel_Lang, value=mode, indicatoron=0)
buttonlist[arraycntr].config(command = lambda :radioselect())
buttonlist[arraycntr].grid(row=mode, column=1)
print('In for loop ', arraycntr, text, mode,
len(MODES),buttonlist[arraycntr])
arraycntr += 1
root.mainloop()
no error messages, but the pressed button does not turn green as expected
You need to add option selectcolor
buttonlist[arraycntr] = tk.Radiobutton(root, height=2, width=15,
borderwidth=10, text=text, font=("Arial", 24, "bold"), bg='white',
variable=Sel_Lang, value=mode, indicatoron=0, selectcolor='green')

Add/delete items from tkinter Listbox permanently

I am trying to create a program that will allow the user to edit the Listbox widget below. I had a (getactive) delete configuration that would visually delete an item from the list. But I had no luck permanently adding or deleting items from the Listbox widget.
Can anybody help me understand how I would configure the Listbox widget to do the above features?
from tkinter import *
modules_list = [
'CLD4002: Introduction to Operating Systems Virtualisation',
'CLD4003: Linux Essentials',
'SEC4001: Introduction to Networking',
'SEC4002: Routing Fundamentals',
'SEC4003: Security Fundamentals',
'SWE4001: Introduction to Software Development',
'CLD5005: Advanced Linux',
'SEC5001: Computing Security',
'SEC5002: Network Architecture',
'SEC5003: Wide Area Networks',
'SEC5005: Enterprise Infrastructure',
'HE5: CHOSEN OPTIONAL MODULE',
'CLD6000: Contemporary Problems Analysis',
'CDL6001: Undergraduate Research Project',
'SEC6003: Operations Management',
'SEC6004: Cloud and Network Security',
'HE6: CHOSEN OPTIONAL MODULE'
]
entries=[]
AVERAGE_TOT = 0 # global variable
CLASSIFICATION = "not Classified" # global variable
def print_Listbox():
z = listbox.get(0, END)
print (z)
# YEAR ONE LABELS
y1 = Label (right_frame, text="Enter Grade")
y1.grid(row=1, column=4)
row_offset = 0+2
for module in modules_list:
#Create labesl from modules_list
lbl = Label(right_frame, text=module)
lbl.grid(row=row_offset, column=3)
mod_code = module[:7] # splitting the string at the 7th character from the beginint
# create entry fields based on number of modules in modules_list
ent= Entry(right_frame, textvariable=mod_code)
ent.grid(row=row_offset, column=4)
entries.append(ent)
row_offset+=1
classification = Label (right_frame, text="Your degree classification is :" + CLASSIFICATION)
average_result = Label (right_frame, text="Your average is " + str(AVERAGE_TOT))
# FINAL AWARD CONFIGURATIONS
classification.grid(row=len(modules_list)+2, column=4)
average_result.grid(row=len(modules_list)+3, column=4)
b1 = Button (right_frame, text="press", command=lambda: setAverage(classification,average_result))
b1.grid(row=len(modules_list)+4, column=4)
def setAverage(classification, average_result):
total = 0
for entry in entries:
thisent = entry.get()
total += int(thisent)
average = total / len(entries)
if average <=39:
degreeclass = "fail"
if average >=40 and average <=49:
degreeclass = "3rd"
if average >=50 and average <=59:
degreeclass = "2:2"
if average >=60 and average <=69:
degreeclass = "2:2"
if average >=70:
degreeclass = "1st"
average_result.config(text="Your percentage is :" + str(average))
classification.config(text="Your degree classification is :" + degreeclass)
main = Tk()
var = StringVar
left_frame = Frame(main)
left_frame.grid(row=0, column=0)
middle_frame = Frame(main)
middle_frame.grid(row=0, column=1)
right_frame = Frame(main)
right_frame.grid(row=0, column=2)
l1 = Label(left_frame, text="Search")
l1.grid(row=0, column=0)
listbox = Listbox(left_frame, font = ("Purisa", 10, "bold"), height=20, width=55)
for i in modules_list:
listbox.insert(END, i)
listbox.grid(rowspan=10)
all_items = listbox.get(0, END)
b1 = Button(middle_frame, text="Add", font = ("Purisa", 10, "bold"))
b1.grid(row=3, column=1, columnspan=1)
b2 = Button(middle_frame, text="Print", font = ("Purisa", 10, "bold"), command=print_Listbox)
b2.grid(row=4, column=1, columnspan=1)
b3 = Button(middle_frame, text="Delete", font = ("Purisa", 10, "bold"))
b3.grid(row=5, column=1, columnspan=1)
main.mainloop()
You can simply use index = listbox.curselection() to get the selected item in the listbox and then use listbox.delete(index) to remove the item from the listbox.
To add new item to listbox, you need to get the item using any input method, for example tkinter.simpledialog, and then use listbox.insert('end', item) to append the item to the listbox.
Therefore, define two new functions for adding and deleting item from listbox:
from tkinter import simpledialog
def add_item():
item = simpledialog.askstring("Input", "Enter item name:")
if item is not None:
listbox.insert('end', item)
def delete_item():
index = listbox.curselection()
listbox.delete(index)
Then modify Add and Delete buttons to call the corresponding function.
BTW, there are issues in your print_Listbox function:
for module in modules_list: should be for module in z:
mod_code = module[:7] # splitting the string at the 7th character from the beginint should be mod_code = module.split(':')[0] because the mod_code in your list are not all 7 characters long.

How get value from Tkinter Var in class?

I'm trying to put old school sequential Tkinter code into class structure code.
So let's consider this example :
import Tkinter as Tk
def StartProcess():
print Text_1_Var.get(), Text_2_Var.get(), Text_3_Var.get()
if __name__ == '__main__':
MainFrame = Tk.Tk()
Tk.Button(MainFrame , text = "Start",command=StartProcess).grid(column=2, row=0)
Tk.Label(MainFrame , text = "1").grid(column=1, row=1)
Text_1_Var = Tk.StringVar()
Text_1 = Tk.Entry(MainFrame , width=40, textvariable = Text_1_Var).grid(column=2, row=1)
Tk.Label(MainFrame , text = "2").grid(column=1, row=2)
Text_2_Var = Tk.StringVar()
Text_2 = Tk.Entry(MainFrame , width=40, textvariable = Text_2_Var).grid(column=2, row=2)
Tk.Label(MainFrame , text = "3").grid(column=1, row=3)
Text_3_Var = Tk.StringVar()
Text_3 = Tk.Entry(MainFrame , width=40, textvariable = Text_3_Var).grid(column=2, row=3)
# etc
MainFrame.mainloop()
On press "Start" it displays values of Entry from 1 to 3.
Now i recode it as follow :
import Tkinter as Tk
def StartProcess():
print "???"
class NewEntry(Tk.Frame):
def __init__(self,master=None,idnumber=None):
Tk.Frame.__init__(self,master)
self.pack(side=Tk.TOP)
self.CreateWidgets(idnumber)
def CreateWidgets(self,idnumber):
Tk.Label(master=self, text = idnumber).grid(column=1, row=0)
self.Text_Var = Tk.StringVar()
self.Text = Tk.Entry(master=self, width=40, textvariable = self.Text_Var).grid(column=2, row=0)
if __name__ == '__main__':
MainFrame = Tk.Tk()
Tk.Button(master=MainFrame,text="Start", command=StartProcess).pack()
for i in range (1, 4): # or more
NewEntry(master=MainFrame,idnumber=str(i))
MainFrame.mainloop()
GUI are both identical. I want to get the same result but i don't know where my function StartProcess should take place and how extract value of each self.Text_Var instance.
It's not enough to create a NewEntry object; you need to save references to them so you can access them later (e.g., from StartProcess).
entries = []
for i in range (1, 4): # or more
e = NewEntry(master=MainFrame,idnumber=str(i))
entries.append(e)
# Or more simply,
# entries = [NewEntry(master=MainFrame, idnumber=str(i)) for i in range(1,4)]
Then, StartProcess becomes something like
def StartProcess():
strings = [x.Text_Var.get() for x in entries]
print " ".join(strings)

Categories