I have this very easy program which I want to display one random line from a file each time I click on the Button.
Problem is a new line is display at startup of the program, but nothing happens when I click the button, can someone explain me why ?
from random import randrange
from tkinter import *
def entree():
n=randrange(251)
fs = open('lexique','r')
liste = fs.readlines()
return liste[n]
fen = Tk()
fen.title("lexique anglais politique")
defi = StringVar()
defi.set(entree())
lab = Label(fen, textvariable=defi).pack()
Button(fen, text='Beste Bat', command=entree).pack()
fen.mainloop()
As stated in one of the comments (by #matszwecja), your entree() function doesn't really do anything appart from returning a value.
Nothing in your code updates the actual label. Try something like this :
from random import randrange
from tkinter import *
def entree():
n=randrange(251)
fs = open('lexique','r')
liste = fs.readlines()
return liste[n]
def update_label():
lab.config(text=entree())
fen = Tk()
fen.title("lexique anglais politique")
lab = Label(fen, text=entree())
lab.pack()
Button(fen, text='Beste Bat', command=update_label).pack()
fen.mainloop()
In this example, the entree() function is used to go get a line from your file, and the update_label() function is used to actually update the label.
Also, if you want to be able to update a label, you'll have to pack it after assigning it to a variable.
On a side note, it could be worth noting that hardcoding values that could change in the future is generally considered bad practice. In that regard, I think coding the entree() function this way might be a better idea :
def entree():
fs = open('lexique','r')
liste = fs.readlines()
n=randrange(len(liste))
return liste[n]
This way, if you ever add or remove lines to your "lexique" file, you will not have to change the code.
Related
I have a script that continuously updates numbers on a text file in the same directory. I made a GUI using tkinter to display these numbers. I'm having a hard time getting the GUI to update the numbers in real time. It will display the numbers as they were when the GUI first started, but will not update the display as the text file changes. Here's some basic code to demonstrate:
def read_files(file, line):
old = open(f'{file}.txt', 'r').readlines()[line]
new = old.replace('\n', '')
return new
number = read_files('Statistics', 0)
number_label = Label(frame1, text=f'{number}')
number_label.grid(row=0, column=0)
The above code shows the number from the text file as it was when the GUI first opened. However, it does not update the number as its value in the text file changes. I did some reading around and also tried the following:
def read_files(file, line):
old = open(f'{file}.txt', 'r').readlines()[line]
new = old.replace('\n', '')
return new
number = read_files('Statistics', 0)
number_label = StringVar()
number_label.set(number)
number_display = Label(frame1, text=f'{number_label.get()}')
number_display.grid(row=0, column=0)
This has the same effect. It shows the value retrieved from the text file at the moment the GUI was opened, but does not update it as the text file is updated. Any help is appreciated.
Since there is no complete code, take a look at this example:
from tkinter import *
root = Tk()
def update_gui():
number = read_files('Statistics', 0) # Call the function
number_display.config(text=number) # Change the text of the label, also same as text=f'{number}'
root.after(1000,update_gui) # Repeat this function every 1 second
def read_files(file, line):
old = open(f'{file}.txt', 'r').readlines()[line]
new = old.replace('\n', '')
return new
number_display = Label(root) # Empty label to update text later
number_display.grid(row=0, column=0)
update_gui() # Initial call to the function
root.mainloop()
Here the label is created outside the function but the text is not given, but inside the function we are repeating the function every 1 second, with after, and changing the text of the label with config method. I have avoided the use of StringVar() as it's of no use here, you can just store it in a normal variable and use it.
Here is a plagiarized look at how after should be used:
def after(self, ms, func=None, *args):
"""Call function once after given time.
MS specifies the time in milliseconds. FUNC gives the
function which shall be called. Additional parameters
are given as parameters to the function call. Return
identifier to cancel scheduling with after_cancel."""
I have a student that is working on a task and trying to use global variables within a couple functions across different files. I have had to include excerpts of the files. The first file is the main one and when you click the results button in that program (from the main window it creates) it should call the other file and pass a variable. But, he gets the following error...
Results.py", line 44, in GiveResults if row[0] == sname: NameError: name 'sname' is not defined
I'm hoping that someone with much better ability and knowledge might be able to point us in the right direction to remedy this. If there is a better way to share the code on here then please also let me know.
Thanks,
Scott
'Solution.py'
#This imports the tkinter module of python
from tkinter import *
#This imports the other windows for use later
import Results, New_User, Edit_User
#This part forms the main window and controls things such as size, colour, and message
main = Tk()
main.title('Hello there')
main.geometry("1000x600")
main['background']='light blue'
#This creates a frame for use later
window = Frame(main).pack()
#Defines a function that when called will convert whatever is in the textboxes to variables
def retrieve():
global sname
sname = selectedname.get()
global sboss
sboss = selectedname.get()
'Results.py'
from tkinter import *
#This is defining the function that the first window is calling upon
def GiveResults():
#This is defining the variables as globe for use across windows (Although it isnt working)
global sname
global sboss
global inputt
#Defines a quit function to close the window when called
def quit():
ResultsGiven.destroy()
#This is making the window
ResultsGiven = Tk()
ResultsGiven.title('You did the program')
ResultsGiven.geometry("600x400")
ResultsGiven['background']='light blue'
#Creating a frame
windowr = Frame(ResultsGiven, bg = 'light blue')
#Creating a title
titlefont = ('papyrus', 30)
title = Label(windowr, text='Results', font=titlefont)
title.config(height=1, width=400)
title.pack(side=TOP, fill = 'x')
#Creating a canvas
canvasr = Canvas(windowr, width = 400, height = 400, background = 'light blue')
canvasr.pack()
#This is importing the csv module of python
import csv
#This is opening the csv file created when a new user is made
#It is then creating a reader to check the first column for the name entered in the main window
#When it finds a match it moves along that row to find the class
#(Unfinished)
with open("userbase.csv") as f:
for row in csv.reader(f):
if row[0] == sname:
sclass = str(column[2])```
The title might be a little confusing, so i will describe my question more.
I making a little program that will assist me with studying Chinese, just for myself. This will aid me with coding and in same time with studying.
I encounter a problem with getting the text variable from my button, without function the code work like wanted. But when trying to get random text that shown on the button it cause me a problem, because text doesn't come. All i need it, when button is pressed function check if input is the same as character and give correct/incorrect notice.
I little new to coding, so it can be simple matter, but still appreciate any help.
The code:
#========== Imports ===========#
from tkinter import *
from tkinter import messagebox
import random
#========== Parameters ==========#
CN = Tk()
CN.title("Chinese Test")
CNW = ["爱","八","爸爸","杯子","北京","本","不客气","不","菜","茶","吃","出租车","打电话",
"大","的","点","电脑","电视","电影","东西","都","读","对不起","多","多少","儿子",
"二","饭店","飞机","分钟","高兴","个","工作","汉语","好","号","喝","和","很","后面","回","会","几","家","叫","今天"]
Cword = ""
Cent = StringVar()
def butPress():
global Cword
if (B0.text==Cword): #wrong way to get text
messageText = "Correct"
else:
messageText = "Incorrect"
CNEntry = Entry(CN,textvariable = Cent).grid(row = 0, column = 1, columnspan = 8)
B0 = Button(CN, text = random.choice(CNW),command = lambda:butPress,bd = 3, width = 5, height = 3).grid(row = 6, column = 4, padx = 10, pady = 10)
#========== Pack ==========#
CN.mainloop( )
There's a few things.
First of all, command = lambda:butPress doesn't work. Use command = butPress. You should only use a lambda when you need to pass parameters (e.g. command = lambda:butPress(parameter)), which you don't.
Then there's B0.text. Because you do
B0 = Button(...).grid(...)
B0 is None, because that is what grid() returns. Change it to
B0 = Button(...)
B0.grid(...)
This way B0 is a Button object. To get the current text of it you can't use B0.text, you have to use B0['text'].
You then compare the text to Cword, which is '' and never changes. If you want to compare it to the entered text in the Entry use CNEntry.get() (after again putting grid on a separate line).
I want that when I go to secondpage and back to the mainpage it removes the buttons created in the mainloop. So when I open the second page again, it needs to make the buttons again. with the updated list
Now it shows the buttons 2 times. I know to not add it into a function it will render just 1 time but the point is it will be removed when it is in a function and it need to remaked every time I open the mainpage. So that I can edit for example the list when the application is open and it will be rendered again.
Here is my code:
from tkinter import *
items = ['ijs', 'water', 'lolly', 'laptop']
root = Tk()
def buttonmaker():
for x in items:
button = Button(master=secondpage, text=x ).pack()
def mainpagetosecondpage():
mainpage.pack_forget()
buttonmaker()
secondpage.pack()
def secondpagetomainpage():
secondpage.pack_forget()
mainpage.pack()
#mainpage
mainpage = Frame(master=root)
main_to_second_button = Button(master=mainpage, text='secondpage', command=mainpagetosecondpage).pack()
#scondpage
secondpage = Frame(master=root)
Second_to_main_button = Button(master=secondpage, text='mainpage', command=secondpagetomainpage).pack()
mainpage.pack()
root.mainloop()
If something is unclear, please ask
You need to call buttonmaker only once, during the setup of the second frame:
from tkinter import *
root = Tk()
def buttonmaker():
items = ['ijs', 'water', 'lolly', 'laptop']
for x in items:
button = Button(master=secondpage, text=x )
button.pack()
def mainpagetosecondpage():
mainpage.pack_forget()
secondpage.pack()
def secondpagetomainpage():
secondpage.pack_forget()
mainpage.pack()
#mainpage
mainpage = Frame(master=root)
main_to_second_button = Button(master=mainpage, text='secondpage', command=mainpagetosecondpage)
main_to_second_button.pack()
#scondpage
secondpage = Frame(master=root)
Second_to_main_button = Button(master=secondpage, text='mainpage', command=secondpagetomainpage)
Second_to_main_button.pack()
buttonmaker()
mainpage.pack()
root.mainloop()
Also, you need to avoid putting the layout on the same line as the initialization. Code like variable = Widget(args).pack() will lead to bugs. Use 2 lines like I did above.
I am a python and arcpy user and I have a problem about dependent combobox. Actually I have asked the same topic on here, but no one answer yet. I've got the answer from here and here. But I think I am too newbie on python programming and I don't get the answer clearly.
I try my code like this below, base on the answer that I've got before:
import Tkinter
from Tkinter import *
root = Tkinter.Tk()
bu = StringVar()
bu.set("")
businessunit = ["DUM", "IND", "KAM", "RAP"]
bu_menu = OptionMenu(root, bu, *businessunit, command=Combobox_1)
bu_menu.config(bg="white", fg="dark blue", width=3, relief=GROOVE)
bu_menu.place(x=95, y=110)
sec = StringVar()
sec.set("")
sector = {"DUM":['GRG', 'KBU', 'LBO', 'PLS', 'PLU', 'PPR', 'RPT', 'SBI', 'SKB'],
"IND":['BYS','MER','NGD','PER','SJG','SLJ'],
"KAM":['RSG','SRG','SRY','TSK'],
"RAP":['BAS','CER','LGB','LON','LOS','MDU','MRE','MRW','PEN','PES','PPD','TEE','TEW','TSB','UKU']}
sec_menu = OptionMenu(root, sec, *sector, command=Combobox_2)
sec_menu.config(bg="white", fg="dark blue", width=3, relief=GROOVE)
sec_menu.place(x=155, y=110)
def __init__(self):
def Combobox_1(businessunit):
print bu.get()
def Combobox_2(sector):
print sec.get()
self.Combobox_1.activated[str].connect(self.on_combo_activated)
def on_combo_activated(self, text):
self.Combobox_2.clear()
self.Combobox_2.addItems(self.sector[text])
root.pack()
root.mainloop()
root.destroy()
Please anyone help me on this. Thank you so much for your answer.
edited:
in this case, dependent combobox means:
If DUM is selected in Combobox_1, Combobox_2 will only show GRG,KBU,LBO, etc.
If IND is selected in Combobox_1, Combobox_2 will only show BYS,MER,PER, etc.
If KAM is selected in Combobox_1, Combobox_2 will only show RSG,SRG,SRY, etc.
If RAP is selected in Combobox_1, Combobox_2 will only show BAS,CER,LGB, etc.
I would advise looking at: Change OptionMenu based on what is selected in another OptionMenu
Just change the values as you need them in your menu.
(Also, you don't need to import Tkinter twice, just use "import Tkinter" or "import Tkinter as tk")
Thank you for your contribution. Actually I've got the answer of this question in my question before. Please see this link Graded combobox Menu Python Tkinter
This is called a cascade combo box. Basically in your command (action) you then create the second box with the data based on the selected value of the first. This is easy with a database i dont know of other ways but some sort of array or dictionary should also work fine.
I do it in a class that I call from my app so this is a little over complicated but can be easily simplified if you dont want to bother with the class vals is a dictionary that looks like {'value1':number,'value2':number2} to keep the index needed fro a database call to get more values or whatever. I am using .grid you can use pack or whatever suits you.
def ebox(self, index=None, row=None, column=None, vals=None):
self.value[index] = tk.StringVar()
lis = []
for item in vals:
lis.append(item)
self.combos[index] = ttk.Combobox(self.win, values=lis, textvariable=self.value[index])
self.combos[index].bind("<<ComboboxSelected>>", lambda event, y=vals: self.eboxclk(event, y))
self.combos[index].grid(row=row, column=column)
return
def eboxclk(self, event=None, index=None):
print(event.widget.get(), " widget get ")
print(index[event.widget.get()], " this should be your index")
return
I initialize a dictionary for my widgets, but you could just skip it and just
mycombo = ttk.Combobox(master,values=alist, textvariable=tk.StringVar)
mycombo.bind('<<ComboboxSelected>>', lambda event, dict: dosomething(event, dict)