I have my process working with Tkinter, but when I run my code my program freeze I understandt It´s because of the main loop, I have been reading the documentation of Tkinter and reading some other questions but I can´t really understand how to implement the Progress Bar to my process, my code It´s really simple it just dowload some information and then save It like an excel but It takes some time to do the process, so how can I implement the progress bar here.
root=Tk()
root.title('Info Dowload')
root.iconbitmap('Logo.ico')
root['bg'] = '#E1FAF9'
#VariablesInicio
fecha1=Entry(root)
fecha2=Entry(root)
date1.insert(0, "ejm:2022-03-15")
date2.insert(0, "ejm:2022-03-15")
#Label
label1=Label(root,text="First Date(aaaa-mm-dd):",font=('Bahnschrift',11))
label2=Label(root,text="Second date(aaaa-mm-dd):",font=('Bahnschrift',11))
def Click():
boton1.config(state= "disable")
label3=Label(root,text="Working with: "+date1.get())
label4=Label(root,text="Working with: "+date2.get())
label3.grid(row=3,column=0)
label4.grid(row=4,column=0)
startFac=str(date1.get())+' 00:00:00'
endFac=str(date2.get())+' 23:59:59'
##First Query
startMF=str(date1.get())
endMF=str(date2.get())
startMF=startMF.replace('-','/')
endMF=endMF.replace('-','/')
df=query1(startMF, endMF)
df1=query2(startFac, endFac, df)
sales=pd.merge(left=df,right=df1,how='left',on=['code1','code2','code3'])
sales.to_excel('sales.xlsx',index=None)
#Button
import tkinter as tk
boton1=tk.Button(root,text='Ejecutar',bg='#20bebe',fg='white',height=1,width=6,command=Click)
#Print info
label1.grid(row=0,column=0)
label2.grid(row=1,column=0)
fecha1.grid(row=0,column=1)
fecha2.grid(row=1,column=1)
boton1.grid(row=2,column=0)
root.mainloop()
In order to add a progress bar, you can use the Progressbar class as follows:
progressbar = Progressbar(root, orient='horizontal',mode='indeterminate',length=<your preferred length>)
Additionally, you would also have to define when to start and stop displaying your progress bar. In order to do this, add the pb.start() and pb.stop() commands at the start and end of your click() function respectively.
[Suggested by #Matiss]
In order to solve the problem of the progress bar not moving, you can import threading module and use it as follows:
root=Tk()
root.title('Info Dowload')
root.iconbitmap('Logo.ico')
root['bg'] = '#E1FAF9'
#VariablesInicio
fecha1=Entry(root)
fecha2=Entry(root)
date1.insert(0, "ejm:2022-03-15")
date2.insert(0, "ejm:2022-03-15")
#Label
label1=Label(root,text="First Date(aaaa-mm-dd):",font =('Bahnschrift',11))
label2=Label(root,text="Second date(aaaa-mm-dd):",font =('Bahnschrift',11))
def Click():
boton1.config(state= "disable")
label3=Label(root,text="Working with: "+date1.get())
label4=Label(root,text="Working with: "+date2.get())
label3.grid(row=3,column=0)
label4.grid(row=4,column=0)
progressbar.start()
t1.start()
def download():
startFac=str(date1.get())+' 00:00:00'
endFac=str(date2.get())+' 23:59:59'
##First Query
startMF=str(date1.get())
endMF=str(date2.get())
startMF=startMF.replace('-','/')
endMF=endMF.replace('-','/')
df=query1(startMF, endMF)
df1=query2(startFac, endFac, df)
sales=pd.merge(left=df,right=df1,how='left',on=['code1','code2','code3'])
sales.to_excel('sales.xlsx',index=None)
progressbar.stop()
#Code for multithreading
t1 = threading.Thread(target=download)
#Code for progress bar
progressbar = Progressbar(root, orient='horizontal',mode='indeterminate',length=<your preferred length>)
progressbar.grid()
Related
I'm trying to see if there's a way to type a number between 1 and 4 into an entry box, then go to the next entry box (with the number entered into the box; the code below skips to the next entry without entering anything)
I'm creating a program that will take item-level data entry to be computed into different subscales. I have that part working in different code, but would prefer not to have to hit tab in between each text entry box since there will be a lot of them.
Basic code:
from tkinter import *
master = Tk()
root_menu = Menu(master)
master.config(menu = root_menu)
def nextentrybox(event):
event.widget.tk_focusNext().focus()
return('break')
Label(master, text='Q1',font=("Arial",8)).grid(row=0,column=0,sticky=E)
Q1=Entry(master, textvariable=StringVar)
Q1.grid(row=0,column=1)
Q1.bind('1',nextentrybox)
Q1.bind('2',nextentrybox)
Q1.bind('3',nextentrybox)
Q1.bind('4',nextentrybox)
Label(master, text='Q2',font=("Arial",8)).grid(row=1,column=0,sticky=E)
Q2=Entry(master, textvariable=StringVar)
Q2.grid(row=1,column=1)
Q2.bind('1',nextentrybox)
Q2.bind('2',nextentrybox)
Q2.bind('3',nextentrybox)
Q2.bind('4',nextentrybox)
### etc for rest of questions
### Scale sums, t-score lookups, and report generator to go here
file_menu = Menu(root_menu)
root_menu.add_cascade(label = "File", menu = file_menu)
file_menu.add_separator()
file_menu.add_command(label = "Quit", command = master.destroy)
mainloop()
Thanks for any help or pointers!
The simplest solution is to enter the event keysym before proceeding to the next field.
In the following example, notice how I added a call to event.widget.insert before moving the focus:
def nextentrybox(event):
event.widget.insert("end", event.keysym)
event.widget.tk_focusNext().focus()
return('break')
I've been working on this for a while and I can't find any information about adding a row to a window. I seen it done with pyside2 and qt, witch would work but the users are using multiple versions of Maya (2016 = pyside, 2017=pyside2).
I want it like adding a widget in in pyside. I done it where adding a row is a function like add row 1, add row 2, and add row 3 but the script get to long. I need to parent to rowColumnLayout and make that unique in order to delete that later. Also I have to query the textfield in each row. Maybe a for loop that adds a number to the row? I really don't know but this is what I have so far:
from maya import cmds
def row( ):
global fed
global info
item=cmds.optionMenu(mygroup, q=True, sl=True)
if item == 1:
cam=cmds.optionMenu(mygroup, q=True, v=True)
fed=cmds.rowColumnLayout(nc = 1)
cmds.rowLayout(nc=7)
cmds.text(l= cam )
cmds.text(l=u'Frame Range ')
start = cmds.textField('textField3')
cmds.text(l=u' to ')
finish = cmds.textField('textField2')
cmds.button(l=u'render',c='renderTedd()')
cmds.button(l=u'delete',c='deleteRow()')
cmds.setParent (fed)
def deleteRow ():
cmds.deleteUI(fed, layout=True)
if item == 2:
print item
global red
cam1=cmds.optionMenu(mygroup, q=True, v=True)
red = cmds.rowColumnLayout()
cmds.rowLayout(nc=7)
cmds.text(l= cam1 )
cmds.text(l=u'Frame Range ')
start = cmds.textField('textField3')
cmds.text(l=u' to ')
finish = cmds.textField('textField2')
cmds.button(l=u'render',c='renderTedd()')
cmds.button(l=u'delete',c='deleteRow2()')
cmds.setParent (red)
def deleteRow2 ():
cmds.deleteUI(red, control=True)
def cameraInfo():
info=cmds.optionMenu(mygroup, q=True, sl=True)
print info
def deleteRow ():
cmds.deleteUI(fed, control=True)
def getCamera():
layers=pm.ls(type="renderLayer")
for layer in layers:
pm.editRenderLayerGlobals(currentRenderLayer=layer)
cameras=pm.ls(type='camera')
for cam in cameras:
if pm.getAttr(str(cam) + ".renderable"):
relatives=pm.listRelatives(cam, parent=1)
cam=relatives[0]
cmds.menuItem(p=mygroup,label=str (cam) )
window = cmds.window()
cmds.rowColumnLayout(nr=10)
mygroup = cmds.optionMenu( label='Colors', changeCommand='cameraInfo()' )
getCamera()
cmds.button(l=u'create camera',aop=1,c='row ()')
cmds.showWindow( window )
This is totally doable with cmds. The trick is just to structure the code so that the buttons in each row know and can operate on the widgets in that row; once that works you can add rows all day long.
To make it work you want to do two things:
Don't use the string form of callbacks. It's never a good idea, for reasons detailed here
Do use closures to make sure your callbacks are referring to the right widgets. Done right you can do what you want without the overhead of a class.
Basically, this adds up to making a function which generates both the gui items for the row and also generates the callback functions -- the creator function will 'remember' the widgets and the callbacks it creates will have access to the widgets. Here's a minimal example:
def row_test():
window = cmds.window(title='lotsa rows')
column = cmds.columnLayout()
def add_row(cameraname) :
cmds.setParent(column)
this_row = cmds.rowLayout(nc=6, cw6 = (72, 72, 72, 72, 48, 48) )
cmds.text(l= cameraname )
cmds.text(l=u'Frame Range')
start = cmds.intField()
finish = cmds.intField()
# note: buttons always fire a useless
# argument; the _ here just ignores
# that in both of these callback functions
def do_delete(_):
cmds.deleteUI(this_row)
def do_render(_):
startframe = cmds.intField(start, q=True, v=True)
endframe = cmds.intField(finish, q=True, v=True)
print "rendering ", cameraname, "frames", startframe, endframe
cmds.button(l=u'render',c=do_render)
cmds.button(l=u'delete',c=do_delete)
for cam in cmds.ls(type='camera'):
add_row(cam)
cmds.showWindow(window)
row_test()
By defining the callback functions inside of add_row(), they have access to the widgets which get stored as start and finish. Even though start and finish will be created over and over each time the function runs, the values they store are captured by the closures and are still available when you click a button. They also inherit the value of cameraname so the rendering script can get that information as well.
At the risk of self-advertising: if you need to do serious GUI work using cmds you should check out mGui -- a python module that makes working with cmds gui less painful for complex projects.
I have the following program which is used to create a very basic GUI:
from __future__ import with_statement
from Tkinter import *
import tkFont
import test
class main_fun():
def init_proc(self):
stLabelStatus['text']="Started executing the test...."
time.sleep(5)
def procA(self):
print "inside ProcA...."
stLabelStatus['text'] = "Running ProcA...."
time.sleep(5)
def procB(self):
print "inside ProcB...."
stLabelStatus['text'] = "Running ProcB...."
time.sleep(5)
def procC(self):
print "inside ProcC...."
stLabelStatus['text'] = "Running ProcC...."
time.sleep(5)
def procD(self):
print "inside ProcD...."
stLabelStatus['text'] = "Running ProcD...."
time.sleep(5)
def procE(self):
print "inside ProcE...."
stLabelStatus['text'] = "Running ProcE...."
time.sleep(5)
def procF(self):
print "inside ProcF...."
stLabelStatus['text'] = "Running ProcF...."
time.sleep(5)
def main_control(self):
stLabelStatus.config(text='Running Tests....')
self.init_proc()
self.procA()
self.procB()
self.procC()
self.procD()
self.procE()
self.procF()
tests=main_fun()
stGUI = Tk()
stGUI.geometry("450x300+50+50")
stGUI.resizable(0,0)
stGUI.title("Testing GUI")
arial12 = tkFont.Font(root = stGUI, family='Arial', size=12, weight='bold')
arial16 = tkFont.Font(root = stGUI, family='Arial', size=16, weight='bold')
stLabelTestNumlbl = Label(stGUI,text="Test No.")
stLabelTestNumlbl['font'] = arial12
stLabelTestNumlbl.place(x=120,y=40)
stInputTestNum = Entry(stGUI, width=6)
stInputTestNum['font'] = arial12
stInputTestNum.place(x=250,y=40)
stLabelStatuslbl = Label(stGUI,text="Status")
stLabelStatuslbl['font'] = arial12
# stLabelStatus.pack()
# stLabelStatus.grid(row=0,column=0)
stLabelStatuslbl.place(x=20,y=180)
stLabelResultlbl = Label(stGUI,text="Result")
stLabelResultlbl['font'] = arial12
stLabelResultlbl.place(x=20,y=240)
stButtonStart = Button(stGUI,text="Start", command=tests.main_control)
stButtonStart['font'] = arial16
stButtonStart.place(x=180,y=110)
stLabelStatus = Label(stGUI, text="", anchor=CENTER)
stLabelResult = Label(stGUI, text="", anchor=CENTER)
stLabelStatus.place(x=90,y=183)
stLabelResult.place(x=90,y=243)
stGUI.mainloop()
Even though the GUI was created, I have a couple of issues here:
there is a small window titled tk that opens up along with my window titled Testing GUI (See the image below). I remember it was not there when I started out, looks like something that I recently added created it. I can't really understand exactly what though. How can I remove that tiny pest window or at least hide it?
I'm unable to update the Status label at runtime even though I've inserted stLabelStatus['text']="Running ProcA...." and similar statements as required in the flow. All that I can see is the very last line in the flow (procF is the last called function) stLabelStatus['text']="Running ProcF....".
How can I update the status during runtime as per my requirement? I'm sure its possible and some logical mistake I'm doing.
Sorry, I missed that command refers correctly to the tests object (of your custom class).
But this
#!python3
from tkinter import *
class main_fun():
def main_c():
print("main_c")
print(stLabelStatus)
stLabelStatus['text']="Status updated...."
tests=main_fun()
stGUI = Tk()
stGUI.geometry("450x300+50+50")
stGUI.resizable(0,0)
stGUI.title("Testing GUI")
stButtonStart = Button(stGUI,text="Start", command=tests.main_c)
stButtonStart.place(x=180,y=100)
stLabelStatus = Label(stGUI, text="", anchor=CENTER)
stLabelStatus.place(x=90,y=183)
stGUI.mainloop()
gives this output:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python35\lib\tkinter\__init__.py", line 1550, in __call__
return self.func(*args)
TypeError: main_c() takes 0 positional arguments but 1 was given
which states an exact problem and probably would yield the answer in web search results.
But taking the hint at arguments, one will notice the class definition and its only method. You'd look at the Python documentation or tutorial on classes and notice that you're doing it wrong:
class does not have parameters, but the method must have at least one (self):
class main_fun:
def main_c(self):
print("main_c")
print(stLabelStatus)
stLabelStatus['text']="Status updated...."
Success!
Tested with a fresh Python 2.7.13:
#!python2
#coding=utf-8
from Tkinter import *
def my_method():
stLabelStatus['text']="Status updated...."
stGUI = Tk()
stButtonStart = Button(
stGUI,
text="Start",
command=my_method
)
stButtonStart.place(x=0, y=0)
stLabelStatus = Label(stGUI)
stLabelStatus.place(x=0, y=30)
stGUI.mainloop()
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.
I have created a panedwindow in python tkinter with two panes. It will open fine on it's own but within an if statement it no longer opens
First I had just the code for the panedwindow on it's own but I wanted to use it within another section of code. It won't work within an if statement, it appears to be ignored. Where have I gone wrong?
from tkinter import *
import time
ticketCost=6
username="Rob"
code = input("Enter code: ")
if code == "123":
year=str(time.localtime()[0])
month=str(time.localtime()[1])
day=str(time.localtime()[2])
hour=str(time.localtime()[3])
minute=str(time.localtime()[4])
ticketTime=str(hour+":"+minute)
ticketDate=str(day+"/"+month+"/"+year)
ticketInfo="Bus ticket\nSingle\nDate: "+ticketDate+"\nTime: "+ticketTime+"\nPassengers: "+
...str(int(ticketCost/3))+"\nPrice: "+str(ticketCost)+" credits"
ticketWindow = PanedWindow(orient=VERTICAL,bg="white")
ticketWindow.pack(fill=BOTH, expand=1)
top = Label(ticketWindow, text="top pane")
photo = PhotoImage(file='Coach 1.gif')
top.config(image=photo,bg="white")
top.image = photo
ticketWindow.add(top)
bottom = Label(ticketWindow, text="bottom pane")
bottom.config(text=ticketInfo)
bottom.config(bg="white")
ticketWindow.add(bottom)
print("\nThank you", username)
else:
print("no")
You do not appear to be making a root window, and are not starting the event loop.