I'm trying to create an inspiration app, which displays random words. it works for alpha characters, but when a word has characters like öäü or ß in them, it just displays random gibberish.
Wanted Text: Rindfleischverpackungsetikettierungsgerät
Text displayed: Rindfleischverpackungsetikettierungsgerßt
Another Example:
Here is my code for it (still very basic and functional)
import tkinter as tk
from random import randint
class ButtonBlock(object):
def __init__(self, master):
self.master = master
self.button = []
self.button_val = tk.IntVar()
entry = tk.Entry()
entry.grid(row=0, column=0)
entry.bind('<Return>', self.onEnter)
def onEnter(self, event):
entry = event.widget
num = int(entry.get())
for button in self.button:
button.destroy()
for i in range(1, num+1):
leine = randint(1, 1908815)
print(leinen[leine])
self.button.append(tk.Label(
self.master, text=leinen[leine]))
self.button[-1].grid(sticky='WENS', row=i, column=2, padx=1, pady=1)
def onSelect(self):
print(self.button_val.get())
if __name__ == '__main__':
deutsch = open("WORT.txt", "r")
leinen = deutsch.readlines()
root = tk.Tk()
ButtonBlock(root)
root.mainloop()
Is there any way to allow tkinter to render the characters öäüß properly? (BTW WORT.txt is just a word list)
To read the extended ASCII characters in the text file, specify the encoding when you open the file like this:
deutsch = open("WORT.txt", "r", encoding="latin1")
This needs to be done because if encoding is not specified the default used is platform dependent (and apparently not the "latin1" needed
on your system). See the documentation for the built-in open() function.
Related
I am still learning so bare with me
Basic explanation of the program:
Using Python3, and the tkinter module, I am making a little GUI just to learn the basics.
In the GUI there is a button, the idea is that when you click the button, a random verse of text (from a txt file) is displayed in another widget within the gui (a label in this case).
I can get the program to pull random verses from the text file, and print them in the shell, but not in the gui widget.
The code requires 2 files, the .py and the .txt
I uploaded them to a github repo so it may be easier to get an idea of the program there
Link to the project on github
However I will put the .py code here if it is more convenient that way but it won't run without the txt file.
import random
import tkinter as tk
from tkinter import *
class MainApplication(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
def random_line():
line_num = 0
selected_line = ''
with open('dhammapada.txt') as f:
while 1:
line = f.readline()
if not line: break
line_num += 1
if random.uniform(0, line_num) < 1:
selected_line = line
return(selected_line.strip())
def print_a_verse():
print('\n',random_line())
btn_result = Button(self, fg='Gold', text='New Verse', bg='Black', font='freesansbold, 16', command=print_a_verse) #textvariable=cvt_to, font='freesansbold, 16', fg='Blue')
btn_result.pack(fill=X,side=BOTTOM)#fill=BOTH, expand=1)
lbl_one = Label(self, bg='DarkGrey', fg='White', text='Dhammapada', font='freesansbold, 22')
lbl_one.pack(fill=BOTH, expand=1)
lbl_thr = Label(self, bg='DarkGrey', fg='White', text='The Dhammapada \nSiddartha Gautama - Buddha', font='freesansbold, 18')
lbl_thr.pack(fill=BOTH, expand=1)
lbl_two = Label(self, bg='DarkGrey', fg='Grey')
lbl_two.pack(fill=BOTH, expand=1)
if __name__ == "__main__":
root = tk.Tk()
root.minsize(400,400)
#root.configure(bg='Black')
root.title('Python - Dhammapada Verses')
MainApplication(root).pack(side="top", fill="both", expand=True)
root.mainloop()
I started that repo a few years back when I first got into coding, I was self-teaching online then because of pretty severe epilepsy I had to pretty much abandon sitting in front of a screen for hours so stopped it all. I think it was 2016. This is the first program I have looked at since, and the subject matter of the book has helped me a lot over the years.
I hope somebody can point me in the right direction. I may be making a mess of the whole thing already!
EDIT:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
Try this:
import tkinter as tk
from tkinter import *
class MainApplication(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
btn_result = Button(self, fg='Gold', text='New Verse', bg='Black', font='freesansbold, 16', command=self.print_a_verse) #textvariable=cvt_to, font='freesansbold, 16', fg='Blue')
btn_result.pack(fill=X,side=BOTTOM)#fill=BOTH, expand=1)
lbl_one = Label(self, bg='DarkGrey', fg='White', text='Dhammapada', font='freesansbold, 22')
lbl_one.pack(fill=BOTH, expand=1)
lbl_thr = Label(self, bg='DarkGrey', fg='White', text='The Dhammapada \nSiddartha Gautama - Buddha', font='freesansbold, 18')
lbl_thr.pack(fill=BOTH, expand=1)
lbl_two = Label(self, bg='DarkGrey', fg='Grey')
lbl_two.pack(fill=BOTH, expand=1)
def random_line(self):
return "testing text" # Just for testing
line_num = 0
selected_line = ''
with open('dhammapada.txt') as f:
while 1:
line = f.readline()
if not line: break
line_num += 1
if random.uniform(0, line_num) < 1:
selected_line = line
return(selected_line.strip())
def print_a_verse(self):
print(self.random_line())
if __name__ == "__main__":
root = tk.Tk()
root.minsize(400, 400)
#root.configure(bg='Black')
root.title('Python - Dhammapada Verses')
MainApplication(root).pack(side="top", fill="both", expand=True)
root.mainloop()
I moved all of the widget definitions inside your __init__ method and added self where appropriate to make sure it doesn't raise any errors. I would also suggest that you look at a quick object oriented programming tutorial.
I would start by only reading from the text file once, and storing all verses in a list. The way your code is written now, any time you call random_line, you're opening the file and closing it, which seems unnecessary.
Your method of selecting a random line isn't perfect, and could also be simplified - you read through the file, line by line, and occassionally decide to reassign selected_line depending on whether some random number is below an arbitrary threshold. There are several issues with this - there is no mechanism that prevents selected_line from being overwritten multiple times (which is unnecessary). There is also no guarantee that selected_line will ever be overwritten from its initial empty state. Additionally, the random number is generated via random.uniform(0, line_num) - since line_num increases with each iteration, the likelyhood that the random number will be below your threshold diminishes with each iteration. As a result, lines that are closer to the top of the file are more likely to be chosen.
The solution is to simply use random.choice to randomly select a verse from a list of all possible verses:
import tkinter as tk
class Application(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title("Random Verse")
self.geometry("256x256")
self.resizable(width=False, height=False)
self.button = tk.Button(self, text="Click", command=self.on_click)
self.button.pack()
self.label = tk.Label(self, text="", wraplength=200)
self.label.pack()
self.verses = self.get_verses("dhammapada.txt")
def get_verses(self, file_name):
with open(file_name, "r") as file:
verses = file.read().splitlines()
return verses
def on_click(self):
from random import choice
self.label["text"] = choice(self.verses)
def main():
Application().mainloop()
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
This question already has answers here:
tkinter creating buttons in for loop passing command arguments
(3 answers)
Closed 1 year ago.
I have some code below that creates 26 buttons in a tkinter window with each letter of the alphabet in it. I want the code to print the letter in the button when it is pressed. My code prints z no matter what button is pressed. How could I fix this?
import tkinter as tk
import string
def whenPressed(button):
print(button['text'])
root = tk.Tk()
alphabet = list(string.ascii_lowercase)
for i in alphabet:
btn = tk.Button(root, text = i, command = lambda: whenPressed(btn))
btn.grid(row = alphabet.index(i)//13, column = alphabet.index(i)%13, sticky = 'nsew')
Try this:
from functools import partial
import tkinter as tk
import string
def whenPressed(button, text):
print(text)
root = tk.Tk()
alphabet = list(string.ascii_lowercase)
for i in alphabet:
btn = tk.Button(root, text=i)
command = partial(whenPressed, btn, i)
btn.config(command=command)
row = alphabet.index(i) // 13
column = alphabet.index(i) % 13
btn.grid(row=row, column=column, sticky="news")
You have to update the button's command after you create it using <tkinter.Button>.config(command=...). I also used functools.partial. Its documentation is here. Also it's usually better to also pass in the text instead of having button["text"].
A little bit of updated version to avoid calculations to use with rows and columns:
from functools import partial
import tkinter as tk
import string
def whenPressed(button, text):
print(text)
root = tk.Tk()
alphabet = list(string.ascii_lowercase)
for i in range(2):
for j in range(13):
text = alphabet[13*i+j]
btn = tk.Button(root, text=text)
command = partial(whenPressed, btn, text) # Also can use lambda btn=btn,text=text: whenPressed(btn, text)
btn.config(command=command)
btn.grid(row=i, column=j, sticky="news")
root.mainloop()
I recently learned about the Notebook widget. And I wanted to add tabs dynamically.
from tkinter import *
from tkinter import ttk
root = Tk ()
n = ttk.Notebook (root)
n.pack (fill = X)
tab = 1
def new_tab ():
global tab
text = Text (root)
text.pack ()
n.add (text, text = ("tab" + str (tab)))
tab += 1
def check ():
'' 'code to insert text into Text widget' ''
...
plus = Button (root, text = '+', command = new_tab)
plus.pack (side = LEFT)
check_button = Button (root, text = 'check', command = check)
check_button.pack (side = LEFT)
root.mainloop ()
I added tabs, but when I try to insert any text using insert in the check function, python gives an error. But the problem is not entirely a bug. I wanted to insert text into the text widget in the current tab.
You need to make a text widget that exists outside of the new_tab() function — in your code it's a local variable which cannot be accessed after the function returns.
A simple way (but not the best, since global variables are bad) is to make the text variable a global. A better way would be to use classes to encapsulate your application's data.
Here's an example of the former:
from tkinter import *
from tkinter import ttk
root = Tk()
n = ttk.Notebook(root)
n.pack(fill=X)
tab = 1
text = None
def new_tab():
global tab
global text
text = Text(root)
text.pack()
n.add(text, text=("tab" + str(tab)))
tab += 1
def check():
""" Insert text into Text widget if it exists. """
if text:
text.insert(END, 'new text\n')
plus = Button(root, text='+', command=new_tab)
plus.pack(side=LEFT)
check_button = Button(root, text='check', command=check)
check_button.pack(side=LEFT)
root.mainloop()
For comparison, here's an example of the latter which has a minimal number of globals because it's based on the object-oriented programming paradigm, aka the OOP way, of implementing software — namely by defining a class that encapsulates the whole application.
Note I've also changed how the imports are being done because, for the most part, wildcard imports are also considered a poor programming practice (see Should wildcard import be avoided?).
import tkinter as tk
import tkinter.ttk as ttk
from tkinter.constants import *
class MyApplication(tk.Tk):
def __init__(self):
super().__init__()
self.nb = ttk.Notebook(self)
self.nb.pack(fill=X)
self.tab = 1
self.text = None
self.plus = tk.Button(self, text='+', command=self.new_tab)
self.plus.pack(side=LEFT)
self.check_button = tk.Button(self, text='check', command=self.check)
self.check_button.pack(side=LEFT)
def new_tab(self):
self.text = tk.Text(self)
self.text.pack()
self.nb.add(self.text, text=("tab" + str(self.tab)))
self.tab += 1
def check(self):
""" Insert text into Text widget if it exists. """
if self.text:
self.text.insert(END, 'new text\n')
app = MyApplication()
app.mainloop()
I apologize if I'm not using proper terminology, I'm new to Python and have been doing this leisurely for fun. I'm trying to figure out everything myself by watching some tutorials, and reading online. The problem I'm having is I wanted to make a GUI for a python password generator (very easy first project.) I've created what I thought to be the correct format, but I'm having an issue with the function displaying in the GUI window rather than the terminal. I think it's when I come to the self.output where it messes everything up.
class GenPass:
def __init__(self, master):
frame = Frame()
frame.pack()
self.printButton = Button(frame, text="Generate Password", padx=4, pady=4, command=self.generate)
self.printButton.pack(side=LEFT)
self.quitButton = Button(frame, text="Copy to Clipboard", padx=4, pady=4, command=master.destroy)
self.quitButton.pack(side=LEFT)
self.output = Label(frame, fg="Green")
self.output.place(x=240, y=85)
self.output.config(text=self.generate)
def generate(self):
for i in range(3):
print(random.choice(Words).capitalize(), end='')
for i in range(2):
print(random.choice(Numbers), end='')
for i in range(1):
print(random.choice(Spec_Char))
I expect the outcome to be in the GUI window, there would be the generated password. It comes up in the terminal, but not in the window. When it does come up by making tweaks to the output.pack() it just lists random numbers and the name of the function (ex. 9012381generate)
Inside generate you should create string with password and use self.output.config(text=password) instead of print().
I changed code because it didn't work for me. Now everyone can copy code and run it.
from tkinter import *
import random
import string
words = string.ascii_uppercase
numbers = string.digits
spec_char = '!##$%'
class GenPass:
def __init__(self, master):
frame = Frame(master) # add parent for Frame
frame.pack()
self.printButton = Button(frame, text="Generate Password", padx=4, pady=4, command=self.generate)
self.printButton.pack(side=LEFT)
self.quitButton = Button(frame, text="Copy to Clipboard", padx=4, pady=4, command=master.destroy)
self.quitButton.pack(side=LEFT)
self.output = Label(master, fg="Green")
self.output.pack()
self.generate() # genrate password at start
def generate(self):
password = ''
for i in range(3):
password += random.choice(words)
for i in range(2):
password += random.choice(numbers)
for i in range(1):
password += random.choice(spec_char)
self.output.config(text=password)
root = Tk()
GenPass(root)
root.mainloop()
BTW: every widget should have parent so I add master in Frame(). Maybe it makes no difference here but if you would have many frames or widgets then widget without parent can be displayed in unexpected place.
pack() and place() and grid() shouldn't be mixed in one window or frame because pack() and grid() try to calculate position dynamically and other layour manager can makes problem with it. But using pack/grid/place you can put frame and inside this frame you can use different layour manager (pack/grid/place).
I am trying to retrieve the word before everytime space is entered. For example, If a user types " iam a" i want to retrieve "iam" and then if user types "iam a girl" i want to retrieve "a" .
Following is my code:
import tkinter as tk
import time
class ExampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
lsum = tk.Label(self,borderwidth=2,text="suggestions")
lsum.grid(row=2, column=5, sticky=tk.W)
def func3():
contents = self.text.get("1.0", tk.END)
lsum["text"] = contents
def func4():
self.text.bind("<space>",func3())
self.text= tk.Text(self, height=5, width=30,borderwidth=2)
self.text.pack(side=tk.RIGHT)
self.text.grid(row=0, column=4)
self.text.insert(tk.END, "")
self.v = tk.IntVar()
self.d = tk.IntVar()
self.e = tk.IntVar()
self.radio1=tk.Radiobutton(self, text="آيک گرآم مآڈل ", variable=self.v, value=1,command=func2).grid(column=0,row=1,columnspan=2)
self.radio2=tk.Radiobutton(self, text="دو گرآم مآڈل ", variable=self.d, value=2,command=func3).grid(column=0,row=2,columnspan=2)
self.radio3=tk.Radiobutton(self, text="تین گرآم مآڈل", variable=self.e, value=3).grid(column=0,row=3,columnspan=2)
if __name__ == '__main__':
run = ExampleApp()
run.mainloop()
Kindly Help please.
Assuming that I understand what you're asking (which is tricky enough) you can call .bind() on the Text widget with the event being "<space>".
This would look something like the below:
from tkinter import *
root = Tk()
text = Text(root)
text.pack()
def callback(*args):
print(text.get("1.0", END).split(" ")[len(text.get("1.0", END).split(" "))-1])
text.bind("<space>", callback)
root.mainloop()
This means that everytime the space key is pressed in the Text widget we get a callback which prints the word you're looking for in the widget.