G'day All,
I have a little app that os.walks gathering some data. While it is working I thought it might be nice to put an Entry widget with the text "Processing" in it and change that to "Completed" when the walking was done.
The issue is the "Processing" never appears. The processing takes many seconds so it's not like it's over too soon to be seen.
def do_search():
txtProgress.delete(0,END)
txtProgress.insert(0, "Processing Data")
print 'do_search called'
arrayOfDirectories = [] # Store the categories here
global path
print 'The value for path = ' + path # Delete this in final
searchpath = path
print 'The value for searchpath = ' + searchpath # Delete this in final
for (searchpath, directories, files) in os.walk(searchpath):
for directory in directories:
arrayOfDirectories.append(directory) # Create an array or dirs to use for the categories
id = 1
finalJSON = '['
for eachDirectory in arrayOfDirectories:
readpath = os.path.join(path, eachDirectory, 'URLS') # Grab each list of URLs
print('readpath = ' + readpath)
if os.path.exists(readpath):
file = open(readpath) # Open the list of URLs
for lines in file: # Step through each URL in turn
ruleString = '{"id":' + str(id) + ',"enabled":true, "category":"' + eachDirectory + '","description":"' + lines + '","flagged":true,"string":"' + lines + '","name":"","javaClass":"com.untangle.uvm.node.GenericRule","blocked":true}'
#print(ruleString)
finalJSON = finalJSON + ruleString # Create a rule and add it to the final string
id = id + 1 # Increment the id after each rule
file.close() # Close the file when all have been read
It's not a train smash if it doesn't work but I am at a loss to understand why text is not appearing.
As always, all advice gratefully accepted.
In short, you never give your program a chance to display it. Redrawing the screen only happens as the result of a repaint event, so unless the event loop has a chance to service that event nothing will be displayed. This isn't unique to Tkinter -- all GUI toolkits work this way.
The simple solution is to call txtProgress.update_idletasks() whenever you want the screen to be updated. This will allow events that refresh the screen to run. This is not the best solution, but it might solve your immediate problem.
The best solution is to refactor your program to either do the long-running work in a separate thread or process, or break the work down to chunks that can be done one at a time with each iteration of the event loop.
Just to put the final touches on this project. I applied both of Bryan's suggestions and used the .update_idletasks() method as well as employing threading. The final code works as desired and looks like this:
from Tkinter import *
import os
import threading
from tkFileDialog import askdirectory
#####################################
# Put the grunt stuff up here #
#####################################
def loadpath():
print 'loadpath called'
global path
path = askdirectory()
txtPath.delete(0, END)
txtPath.insert(0, path)
def update_the_status():
txtProgress.delete(0,END)
txtProgress.insert(0, "Processing Data")
txtProgress.update_idletasks()
def do_the_search():
print 'do_search called'
arrayOfDirectories = [] # Store the categories here
global path
print 'The value for path = ' + path # Delete this in final
searchpath = path
print 'The value for searchpath = ' + searchpath # Delete this in final
for (searchpath, directories, files) in os.walk(searchpath):
for directory in directories:
arrayOfDirectories.append(directory) # Create an array or dirs to use for the categories
id = 1
finalJSON = '['
for eachDirectory in arrayOfDirectories:
readpath = os.path.join(path, eachDirectory, 'URLS') # Grab each list of URLs
print('readpath = ' + readpath)
if os.path.exists(readpath):
file = open(readpath) # Open the list of URLs
for lines in file: # Step through each URL in turn
ruleString = '{"id":' + str(id) + ',"enabled":true, "category":"' + eachDirectory + '","description":"' + lines + '","flagged":true,"string":"' + lines + '","name":"","javaClass":"com.untangle.uvm.node.GenericRule","blocked":true}'
#print(ruleString)
finalJSON = finalJSON + ruleString # Create a rule and add it to the final string
id = id + 1 # Increment the id after each rule
file.close() # Close the file when all have been read
finalJSON = finalJSON + ']' # Close the JSON array
outputPath = os.path.join(os.path.dirname(path), 'Blacklist.json')
print('Output path = ' + outputPath)
outputFile = open(outputPath, 'w')
outputFile.write(finalJSON)
txtProgress.delete(0,END)
txtProgress.insert(0,"Process Complete")
outputFile.close()
def do_search():
WriteThread = threading.Thread(target=update_the_status())
CalcThread = threading.Thread(target=do_the_search())
WriteThread.start()
CalcThread.start()
def do_quit():
print 'do_quit called'
sys.exit()
#####################################
# Build the interface #
#####################################
# Some global variables
path = ''
# Create the application window and give it a title.
main = Tk()
main.geometry('600x400')
main.title('Blacklist JSON array builder')
# Populate the window with widgets.
lbSpace1 = Label(main, text='')
lbSpace1.grid(row=0, column=0, columnspan=3)
lbDesc = Message(main, width=800, text='After you have unzipped the .tar file select the blacklist folder \nthat has been created using the browse button. Then click start. The Blacklist.json \nfile will be created in the same directory as the Blacklist folder.')
lbDesc.grid(row=1, column=0, columnspan=4, pady=10)
lbPath = Label(main, text='Directory')
lbPath.grid(row=2, column=0, pady=10)
txtPath = Entry(main, width=50)
txtPath.grid(row=2, column=1)
pbPath = Button(main, text='Browse', command=loadpath)
pbPath.grid(row=2, column=2)
lbSpace2 = Label(main, text='')
lbSpace2.grid(row=3, column=0, columnspan=3)
pbStart = Button(main, text='Begin', command=do_search)
pbStart.grid(row=4, column=1, sticky=W, pady=20)
pbQuit = Button(main, text='Quit', command=do_quit)
pbQuit.grid(row=4, column=2, sticky=W)
lbSpace3 = Label(main, text='')
lbSpace3.grid(row=5, column=0, columnspan=3)
txtProgress = Entry(main, width=50)
txtProgress.grid(row=6, column=1)
txtProgress.insert(0,'Waiting')
mainloop()
Thanks for the help guys.
Related
I'm creating a counter to count how many empty cells there are when a user uploads a CSV file. I am also using treeview to display the contents of the CSV. The print("There are", emptyCells.sum(), "empty cells") works and prints the number to the console but I want to display this in a label so the user can view this in the GUI. It is not displaying anything but a "row" is being added to the application after a file has been uploaded where the label should be as everything moves down but no contents are being inserted into the label.
emptyCells = (df[df.columns] == " ").sum()
# print("There are", emptyCells.sum(), "empty cells")
tree.pack(side=BOTTOM, pady=50)
messagebox.showinfo("Success", "File Uploaded Successfully")
stringVariable = StringVar()
printVariable = ("There are", emptyCells.sum(), "empty cells")
#print(printVariable)
stringVariable.set(printVariable)
lbl = Label(windowFrame, textvariable=stringVariable, font=25)
lbl.pack()
According to your question you want to update your tkinter label by a button click. You would do this with something like this:
from tkinter import *
from tkinter import messagebox
root = Tk(className="button_click_label")
root.geometry("200x200")
messagebox.showinfo("Success","Test")
emptyCells = (df[df.columns] == " ").sum()
l1 = Label(root, text="Emptycells?")
def clickevent():
txt = "there are", emptyCells
l1.config(text=txt)
b1 = Button(root, text="clickhere", command=clickevent).pack()
l1.pack()
root.mainloop()
It is not tested with the pandas library but should work for you!
The problem with the tkinter label is not happening when I try to reproduce the problem, the label shows. The cause must be somewhere else in the code.
I've not got pandas installed so I've summed a list instead. This shows a GUI with two labels when I run it.
import tkinter as tk
emptyCells = [ 1, 1, 1, 1, 1, 1, 1 ] # keep it simple.
windowFrame = tk.Tk()
old = tk.StringVar()
stringVariable = tk.StringVar()
old_print = ("There are", sum(emptyCells), "empty cells") # Returns a tuple
printVariable = "There are {} empty cells".format( sum(emptyCells) ) # Returns a string.
old.set( old_print )
stringVariable.set(printVariable)
lbl_old = tk.Label( windowFrame, textvariable = old )
lbl_old.pack()
lbl = tk.Label(windowFrame, textvariable=stringVariable, font=25)
lbl.pack()
windowFrame.mainloop()
Does this work when you run it? Does it help identify where the problem is in the code which doesn't show the labels?
Don't you have the sum you need already in the emptyCells variable? Why do you need to use the .sum() function again in the print statement?
printVariable = f"There are {emptyCells} empty cells"
I have been trying to use tkinter CheckButton widget to edit items in a list - each item of the list is a new checkbutton. I want a save method to save the data to a text file, and a load method to load the info from the text file and mark the checkbutton boxes depending on the items in the list.
Here is my code so far, but the list doesn't seem to change when I check the buttons and update the list/file
Here is my code, I need to know why the list isn't updating when I check the boxes:
import tkinter.messagebox as box
modulesMain = Tk()
moduleChecks = []
def SaveChanges():
# Clear the text file
modules = open("modules.txt", "w") #Write mode to overwrite the whole file
modules.write("") # Put in blank text
modules.close()
modules = open("modules.txt", "a") # Append mode to append the file
for item in moduleChecks:
modules.write(item + "\n")
print(moduleChecks)
appButton = Checkbutton(modulesMain, text = "Test", variable = moduleChecks[0]).grid()
searchButton = Checkbutton(modulesMain, text = "Test", variable = moduleChecks[1]).grid()
Save = Button(modulesMain, text = "Save Changes", command = SaveChanges).grid()
The variable for each of your checkboxes needs to be an IntVar. Your moduleCheck list is currently un-initialised so when you try to access the elements inside it, you'll get an error.
In the below code (modified from yours), I've initialised the moduleCheck to contain two IntVars.
When you press the save button, it will print to the console the current state of the check boxes.
import tkinter as tk
modulesMain = tk.Tk()
moduleChecks = [tk.IntVar() for i in range(2)]
def SaveChanges():
for idx,item in enumerate(moduleChecks):
print(f"{idx} = {item.get()}")
appCheck = tk.Checkbutton(modulesMain, text = "App", variable = moduleChecks[0])
appCheck.grid()
searchCheck = tk.Checkbutton(modulesMain, text = "Check", variable = moduleChecks[1])
searchCheck.grid()
saveButton = tk.Button(modulesMain, text = "Save Changes", command = SaveChanges)
saveButton.grid()
modulesMain.mainloop()
Here is a basic solution.
import tkinter as tk
root = tk.Tk()
#list of options. doubles as checkbox label text
#appending to this list is all that is necessary to create more options
opts = ['App', 'Search']
#create an StringVar for every option
#this way there doesn't have to be type conversions for reading/writing files
vars = [tk.StringVar(value='0') for _ in opts]
#create checkboxes for every option
for opt, var in zip(opts, vars):
tk.Checkbutton(root, text=opt, variable=var, onvalue='1', offvalue='0').grid(sticky='w')
#write the checkbox values to file
def save_options_state():
with open("modules.txt", "w") as file:
file.write(''.join([v.get() for v in vars]))
#set checkbox values from file
def load_options_state():
with open("modules.txt", "r") as file:
for n, var in zip(list(file.read().strip()), vars):
var.set(n)
#save/load buttons
tk.Button(root, text="save", command=save_options_state).grid()
tk.Button(root, text="load", command=load_options_state).grid(row=2, column=1)
root.mainloop()
Here's a runnable example which I think shows how to do everything you've asked about. It assumes the modules.txt file contains data in the following format, where each line consists of a module's name and whether it's checked or not.
Mod1,0
Mod2,0
Mod3,0
The data in the file is used to create a list of Checkbuttons — one for each module — and initially sets to its current status as indicated in the file.
Here's the sample code:
from tkinter import *
import tkinter.messagebox as box
dataFilename = "modules.txt"
moduleChecks = []
def ReadModules():
with open(dataFilename, "r") as file:
for line in (line.strip() for line in file):
moduleName, moduleState = line.split(',')
moduleChecks.append((moduleName, int(moduleState)))
print('read:', moduleChecks)
def SaveChanges():
print('writing:', moduleChecks)
with open(dataFilename, "w") as file:
for checkButton in moduleCheckbuttons:
moduleName = checkButton.cget('text')
moduleState = checkButton.var.get()
file.write(','.join((moduleName, str(moduleState))) + '\n')
ReadModules()
modulesMain = Tk()
moduleCheckbuttons = []
# Create a Checkbutton for each module based on moduleChecks values.
for moduleName, moduleState in moduleChecks:
intVar = IntVar(value=moduleState)
checkButton = Checkbutton(modulesMain, text=moduleName, variable=intVar)
checkButton.var = intVar # Attach reference to variable.
checkButton.grid()
moduleCheckbuttons.append(checkButton)
saveButton = Button(modulesMain, text="Save Changes", command=SaveChanges)
saveButton.grid()
modulesMain.mainloop()
I am doing the Final Year Project about the GUI of Aircrack-ng. In my GUI design, the result of cracking password will be write into the text file. And that i write the code to read the text file and display the content what i need . Now i can display some information(Target AP,ESSID ,KEY) on the testbox. I also need to display the ASCII password. I have to refer the document about re (https://docs.python.org/2/library/re.html) and try it but not successful. I hope someone can teach me how to do.Thank you.
This is the text file about the result of cracking password
https://drive.google.com/open?id=192IQr5Y2VUjIZBdUrAlD5uyQQ8YSOUDw
This is the function and GUI Design about display the detail for wep password
# Use pandas to filter the results and display to textbox
def wepresult():
result = re.search(r"\[([^[]+[^(])\]", wepcrackresult_text.get("1.0", 'end-1c'))
if result:
wepcrackresult_text.delete(0.0, END)
wepcrackresult_text.insert(INSERT, "Target ESSID: " + self.controller.shared_data[
"ESSID"].get() + "\nTarget MAC address: " + self.controller.shared_data[
"BSSID"].get() + "\nKEY:" + result.group(1))
GUI Design
wepcrackresult_button = tk.Button(self, text='4.Check', font='Verdana 14',command = wepresult)
wepcrackresult_button.place(x=2, y=630)
Below is the simplest code I can think of:
import tkinter as tk
def display_file(file_path):
txt.delete('1.0', 'end')
with open(file_path) as f:
file_content_as_string = f.read()
# regex filterings can be made in filte_content_as_string
txt.insert('1.0', file_content_as_string)
if __name__ == '__main__':
root = tk.Tk()
txt = tk.Text(root)
# replace __file__ with path
display_file(__file__)
txt.pack()
root.mainloop()
I have written a program in Python that allow me to change the names of many files all at once. I have one issue that is quite odd.
When I use raw_input to get my desired extension, the GUI will not launch. I don't get any errors, but the window will never appear.
I tried using raw_input as a way of getting a file extension from the user to build the file list. This program will works correctly when raw_input is not used.The section of code that I am referring to is in my globList function. For some reason when raw_imput is used the window will not launch.
import os
import Tkinter
import glob
from Tkinter import *
def changeNames(dynamic_entry_list, filelist):
for index in range(len(dynamic_entry_list)):
if(dynamic_entry_list[index].get() != filelist[index]):
os.rename(filelist[index], dynamic_entry_list[index].get())
print "The files have been updated!"
def drawWindow(filelist):
dynamic_entry_list = []
my_row = 0
my_column = 0
for name in filelist:
my_column = 0
label = Tkinter.Label(window, text = name, justify = RIGHT)
label.grid(row = my_row, column = my_column)
my_column = 1
entry = Entry(window, width = 50)
dynamic_entry_list.append(entry)
entry.insert(0, name)
entry.grid(row = my_row, column = my_column)
my_row += 1
return dynamic_entry_list
def globList(filelist):
#ext = raw_input("Enter the file extension:")
ext = ""
desired = '*' + ext
for name in glob.glob(desired):
filelist.append(name)
filelist = []
globList(filelist)
window = Tkinter.Tk()
user_input = drawWindow(filelist)
button = Button(window, text = "Change File Names", command = (lambda e=user_input: changeNames(e, filelist)))
button.grid(row = len(filelist) + 1 , column = 1)
window.mainloop()
Is this a problem with raw_input?
What would be a good solution to the problem?
This is how tkinter is defined to work. It is single threaded, so while it's waiting for user input it's truly waiting. mainloop must be running so that the GUI can respond to events, including internal events such as requests to draw the window on the screen.
Generally speaking, you shouldn't be mixing a GUI with reading input from stdin. If you're creating a GUI, get the input from the user via an entry widget. Or, get the user input before creating the GUI.
A decent tutorial on popup dialogs can be found on the effbot site: http://effbot.org/tkinterbook/tkinter-dialog-windows.htm
I'm writing a program for my class that allows you to make a recipe, save it and edit it even after closing the program. You obviously need a text file to do this.
I am using an OptionMenu (Tkinter, Python 3.3.3), but I cannot figure out how to keep updating it to have the first option in the list I have made in my text file. So how do I do that?
My code is thus:
###########################################
###########################################
### RECIPE BOOK TASK ##### By 18166 #######
###########################################
###########################################
from tkinter import *
def script ():
#### MAIN ####
fake_window = Tk()
new_recipe_window = fake_window
start_window = fake_window
start_window.title("Recipe Book Task")
#### MAIN ####
## DATA FILE ##
global datafile
datafile = open("StoredRecipes.txt", "a+")
## DATA FILE ##
### Functions ###
def close (x): ## Close Original Window ##
global start_window
global new_recipe_window
(x).withdraw()
def new_recipe ():
new_recipe_window = Tk() ## Making new window ##
new_recipe_window.title("New Recipe")
close(start_window)
recipe_name_label = Label(new_recipe_window, text="Recipe Name: ") ## Making new recipe label ##
recipe_name_label.grid(row=0, column=0)
recipe_name_box = Entry(new_recipe_window) ## Making new recipe entry ##
recipe_name_box.grid(row=0, column=1)
num_people_label = Label(new_recipe_window, text="Number of people: ") ## Making number of people label ##
num_people_label.grid(row=1, column=0)
num_people_box = Entry(new_recipe_window) ## Making number of people entry ##
num_people_box.grid(row=1, column=1)
item_label = Label(new_recipe_window, text="Items: ") ## Making item label ##
item_label.grid(row=2, column=0)
item_box = Entry(new_recipe_window) ## Making item entry ##
item_box.grid(row=2, column=1)
quantity_label = Label(new_recipe_window, text="Quantity: ") ## Making quantity label ##
quantity_label.grid(row=3, column=0)
quantity_box = Entry(new_recipe_window) ## Making quantity entry ##
quantity_box.grid(row=3, column=1)
unit_label = Label(new_recipe_window, text="Unit: ") ## Making unit label ##
unit_label.grid(row=4, column=0)
unit_box = Entry(new_recipe_window) ## Making unit entry ##
unit_box.grid(row=4, column=1)
def write ():
a = recipe_name_box.get()
b = num_people_box.get()
c = item_box.get()
d = quantity_box.get()
e = unit_box.get()
line = (a, b, c, d, e)
datafile.write(str(line) + "\n")
datafile.close()
saved_recipes.config(a)
close(new_recipe_window)
script()
finish_button = Button(new_recipe_window, text="Save and Finish", command=write) ## Making finish button ##
finish_button.grid(row=5, column=0, sticky=S)
# Dropdown Box #
default = StringVar(start_window, 'Recipe 1')
default.set("Select Your Recipe")
saved_recipes = OptionMenu(start_window, default, "Hi")
saved_recipes.grid(row=0, column=1)
# Dropdown Box #
# New Recipe Button #
new_recipe = Button(start_window, text="New Recipe", command=new_recipe)
new_recipe.grid(row=0, column=0)
# New Recipe Button #
script()
(Sorry for the block, I think all is useful to answering possibly?)
I believe you have two different options.
One option you could do is set up a timer to check the text file every couple of seconds, see if it's changed at all, and update your OptionMenu accordingly. You can find more info on how to do this here, but in a nutshell, you'd want your code to look something like:
def recheck(root, option_menu, file_name):
with open(file_name) as my_file:
lines = my_file.readlines():
# `lines` is a list where each item is a single line
# do any checks and updates you need here.
root.after(1000, recheck, root, option_menu, file_name)
# schedule the function to run again after 1000 milliseconds.
def script():
# set up your gui
start_window.after(1000, recheck, start_window, option_menu, "StoredRecipies.txt")
Note: you can find more info on the with statement here: http://effbot.org/zone/python-with-statement.htm
The downside of this is that the update will be a little laggy -- you'll end up rechecking the file only once a second, so the update won't be instantaneous.
Alternatively, you could use something like Watchdog. It's a 3rd party library that you can set up to "watch" a particular file and run a function whenever the file changes. It's much more responsive in that you'll call the function only if the file actually changes, but it might end up being more complicated since you need to figure out how to make it work with Tkinter. I'm going to guess that your code will look roughly like this:
import os.path
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer
def setup_observer(option_menu, filename):
normalized_filename = os.path.normpath(input_filename)
class MyEvent(FileSystemEventHandler):
def on_modified(self, event):
if os.path.normpath(event.src_path) == normalized_filename:
# update your option menu
observer = Observer()
observer.schedule(MyEvent(), '.', recursive=False)
return observer
def script():
# setup gui
observer = setup_observer(option_menu, "myfile.txt")
start_window.mainloop()
To add elements to an OptionList, you can use the following method (from http://www.prasannatech.net/2009/06/tkinter-optionmenu-changing-choices.html)
datafile = open("StoredRecipes.txt", "r")
for line in datafile.readlines():
saved_recipes['menu'].add_command(label=line,
command=lambda temp = line: saved_recipes.setvar(saved_recipes.cget("textvariable"), value = temp))
Which uses (has to use) a closure and an anonymous function -- definitely nothing you should deal with on your level of experience (guessing from the structure of your code).
This snippet adds a command for each line in your file. Because an OptionMenu is something that executes things when elements are selected, you have to provide a command for each line. Right now this is just setting the displayed text to the selected line.
To accomplish this, it uses an anonymous function (lambda) that sets the textvariable of the OptionMenu to the current line.