I'm trying to create a fairly simple e-reader, and I've managed to use tkinter to create something akin to one. But what I can't seem to work out is how to create a scrollbar to allow the user to scroll through the text at will. I can get it working in other pieces of coding, but I can't make it work within this program and I can't work out what the problem is. I've put my simple e-reader, without the attempted scrollbar below.
from Tkinter import *
import tkFileDialog
import ScrolledText
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("File dialog")
self.pack(fill=BOTH, expand=1)
menubar = Menu(self.parent)
self.parent.config(menu=menubar)
fileMenu = Menu(menubar)
fileMenu.add_command(label="Open", command=self.onOpen)
menubar.add_cascade(label="File", menu=fileMenu)
self.txt = Text(self)
self.txt.pack(fill=BOTH, expand=1)
def onOpen(self):
ftypes = [('Python files', '*.py'), ('All files', '*')]
dlg = tkFileDialog.Open(self, filetypes = ftypes)
fl = dlg.show()
if fl != '':
text = self.readFile(fl)
self.txt.insert(END, text)
def readFile(self, filename):
f = open(filename, "r")
text = f.read()
return text
self.txt = ScrolledText(self, undo=True)
self.txt['font'] = ('consolas', '12')
self.txt.pack(expand=True, fill='both')
def main():
root = Tk()
ex = Example(root)
root.geometry("300x250+300+300")
root.mainloop()
if __name__ == '__main__':
main()
Adding a scrollbar to a text widget requires you to do two things in addition to creating the actual widgets:
you must set the yscrollcommand attribute of the text widget to point to the set method of the scrollbar
you must set the command attribute of the scrollbar to point to the yview method of the text widget
For example:
self.text.configure(yscrollcommand=self.scrollbar.set)
self.scrollbar.configure(command=self.text.yview)
I've long wondered whether this could really be the answer to your question, but after the discussion in the comments, it seems it is: You just put the code creating the ScrolledText in the wrong place!
Try moving these three lines (that are now outside of the class, causing a NameError for self)
self.txt = ScrolledText(self, undo=True)
self.txt['font'] = ('consolas', '12')
self.txt.pack(expand=True, fill='both')
to where these lines are in your initUI method (replace these lines)
self.txt = Text(self)
self.txt.pack(fill=BOTH, expand=1)
With other words, in your initUI method, instead of creating a Text widget, create a ScrolledText. Also, change import ScrolledText to from ScrolledText import ScrolledText so that ScrolledText is the actual widget, and not the module defining the widget.
Related
i'm just new to programming in python.
I'd like to create a sample project wherein I import a text file in a Button click command and display it in an Text widget. The Button function is in a different class called ButtonAction, and the widget is created in another class called Window. The problem is I don't have an idea how to get the existing Text widget so i can append the imported data.
I removed some formatting codes.
Code:
from tkinter.filedialog import *
class Window(Frame):
def __init__(self):
super().__init__()
self.initgui()
def initgui(self):
self.pack(fill=BOTH, expand=True)
textarea = Text(self)
textarea.grid(row=0, column=0, columnspan=2, rowspan=4,
padx=10, pady=10, sticky=E + W + S + N)
imprtbutton = Button(self, text="Import XML", command=ButtonAction().import_onclick)
imprtbutton.grid(row=0, column=2, padx=5, sticky=S)
class ButtonAction:
def __init__(self):
pass
def import_onclick(self):
file = askopenfile(mode='r')
if file is not None:
content = file.read()
print(content) #just so i can see if i successfully imported my txt file
if __name__ == '__main__':
root = Tk()
root.geometry("500x500+300+200")
root.title("Test Project")
gui = Window()
root.mainloop()
Hello and welcome to Stackoverflow.
When I started your script, I was not able to see the "Import XML" button at all. I needed to resize the window first. That is the reason, why I played around with the .pack and .grid calls in your code.
To solve your problem, I added the argument callback to your import_onclick method. This provides a means for the ButtonAction class to return the data back to the Window class. Here the new method update_text is responsible for filling the content of textarea.
Because we now need an additional argument for import_onclick, I wrapped the former ButtonAction().import_onclick argument into a lambda:
lambda: ButtonAction().import_onclick(self.update_text)
The resulting code works fine for me:
from tkinter.filedialog import *
class Window(Frame):
def __init__(self):
super().__init__()
self.textarea = Text(self)
self.initgui()
def initgui(self):
self.pack(fill=BOTH, expand=True)
self.textarea.pack(side=LEFT, padx=5, pady=5)
import_button = Button(self,
text="Import XML",
command=lambda: ButtonAction().import_onclick(self.update_text)
)
import_button.pack(side=RIGHT, padx=5, pady=5)
def update_text(self, text):
self.textarea.delete(1.0, END)
self.textarea.insert(END, text)
class ButtonAction:
def __init__(self):
pass
def import_onclick(self, callback):
file = askopenfile(mode='r')
if file is not None:
content = file.read()
# just so i can see if i successfully imported my txt file
print(f"read {len(content)} lines")
callback(content)
if __name__ == '__main__':
root = Tk()
root.title("Test Project")
gui = Window()
root.mainloop()
I'm trying to write my fisrt script in python & tkinter.
I block to display a new frame from a function (def onDisplay) when a click is made from menu bar button, but nothing append.
No error is display :-(
The new frame is made with dynamic checkboxes from text files:
txt file:
item1
item2
...
item100
A screen of the GUI:
Here my code:
from tkinter import Tk, Frame, Menu, Checkbutton, Text, TOP, BOTH, X, N, LEFT, BooleanVar
from tkinter.ttk import Frame, Label, Entry
import glob
class Example(Frame):
def __init__(self):
super().__init__()
self.initUI()
#self.display_srv()
def initUI(self):
self.master.title("Submenu")
menubar = Menu(self.master)
self.master.config(menu=menubar)
fileMenu = Menu(menubar)
submenu = Menu(fileMenu)
submenu.add_command(label="lst1", command=self.onDisplay)
submenu.add_command(label="lst2")
submenu.add_command(label="lst3")
fileMenu.add_cascade(label='Listing', menu=submenu, underline=0)
fileMenu.add_separator()
fileMenu.add_command(label="Exit", underline=0, command=self.onExit)
menubar.add_cascade(label="File", underline=0, menu=fileMenu)
#The frame i tried to display
def onDisplay(self):
self.master.title("display it")
self.pack(fill=BOTH, expand=True)
frame1 = Frame(self)
frame1.pack(fill=X)
path = '/root/liste/*.txt'
files=glob.glob(path)
count = 0
for file in files:
with open(file, 'r') as lst_file:
for item in lst_file:
# Need to split all item by 10
Checkbutton(self, text=item.rstrip()).grid(row=count//10, column=count%10)
count += 1
def onClick(self):
if self.var.get() == True:
self.master.title("Checkbutton")
else:
self.master.title("")
def onExit(self):
self.quit()
def main():
root = Tk()
root.geometry("850x550+300+300")
app = Example()
root.mainloop()
if __name__ == '__main__':
Many thanks for any help
Regards,
The main problem is that you're mixing grid and pack in the same parent container. You are calling pack on frame1 but you are calling grid on the checkbuttons, and they both have a master or parent of self.
That can't work, because each of grid and pack will try to resize the container according to it's rules, triggering the other one to reconfigure according to it's rules, and so on until the end of time.
So, simply changing your call of .grid(...) to .pack(...) on the checkbuttons will fix that problem.
My guess is that you intended to put the checkbuttons inside frame1. If that's the case, you need to specify frame1 as the master for the checkbuttons. For readability and ease of debugging I also recommend placing the call to grid on a separate line. With that, you can continue to use grid for the checkbuttons and pack for everything else.
cb = Checkbutton(frame1, text=item.rstrip())
cb.grid(row=count//10, column=count%10)
I posted the correct code:
from tkinter import Tk, Frame, Menu, Checkbutton, Text, TOP, BOTH, X, N, LEFT, BooleanVar
from tkinter.ttk import Frame, Label, Entry
import glob
class Example(Frame):
def __init__(self):
super().__init__()
self.initUI()
#self.display_srv()
def initUI(self):
self.master.title("Submenu")
menubar = Menu(self.master)
self.master.config(menu=menubar)
fileMenu = Menu(menubar)
submenu = Menu(fileMenu)
submenu.add_command(label="lst1", command=self.onDisplay)
submenu.add_command(label="lst2")
submenu.add_command(label="lst3")
fileMenu.add_cascade(label='Listing', menu=submenu, underline=0)
fileMenu.add_separator()
fileMenu.add_command(label="Exit", underline=0, command=self.onExit)
menubar.add_cascade(label="File", underline=0, menu=fileMenu)
def onDisplay(self):
self.master.title("display it")
self.pack(fill=BOTH, expand=True)
frame1 = Frame(self)
frame1.pack(fill=X)
path = '/root/liste/*.txt'
files=glob.glob(path)
count = 0
for file in files:
with open(file, 'r') as lst_file:
for item in lst_file:
cb = Checkbutton(frame1, text=item.rstrip())
cb.grid(row=count//10, column=count%10)
count += 1
def onClick(self):
if self.var.get() == True:
self.master.title("Checkbutton")
else:
self.master.title("")
def onExit(self):
self.quit()
def main():
root = Tk()
root.geometry("850x550+300+300")
app = Example()
root.mainloop()
if __name__ == '__main__':
main()
I have created a menu bar and a textbox, in the textbox I like to show the info in the variable that belongs to the chooice that been made in the menu.
But I can not figure out how.
Heres what I managed to put together by different guides on the web. Im verry new to python and Im still struggeling with the concept on objects and classes so any help at all will be greatly appriciated.
from tkinter import Tk, Frame, Menu
from tkinter import *
class Example(Frame):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.master.title("syntax explenation for shopfloor")
menubar = Menu(self.master)
self.master.config(menu=menubar)
syntaxMenu = Menu(menubar, tearoff=False)
submenu = Menu(syntaxMenu)
syntaxMenu.add_cascade(label='Math', menu=submenu, underline=0)
submenu.add_command(label="abs()", command=self.onSyntaxabs)
submenu.add_command(label="cos()", command=self.onSyntaxcos)
submenu.add_command(label="sin()", command=self.onSyntaxsin)
submenu.add_cascade(label="log()", command=self.onSyntaxlog)
submenu = Menu(syntaxMenu)
syntaxMenu.add_cascade(label='Inqurie', menu=submenu, underline=0)
submenu.add_command(label="inqiureText()", command=self.onSyntaxinquire)
menubar.add_cascade(label="Syntax", underline=0, menu=syntaxMenu)
def onSyntaxabs():
info="Convert to absolute value, abs(-10) will respond with 10."
def onSyntaxcos():
info="Returns cosinus value in decimal degrees"
def onSyntaxcos():
info="Returns cosinus value in decimal degrees"
def onSyntaxlog():
info="Returns the natural logarithm"
def onSyntaxinquire():
info="Creates a pop-up box that you can enter text in."
def main():
root = Tk()
root.geometry("500x600")
app = Example()
S = Scrollbar(root)
T = Text(root, height=20, width=60, bg="lightblue")
S.pack(side=RIGHT, fill=Y)
T.pack(side=LEFT, fill=BOTH)
T.pack(side=RIGHT, fill=BOTH)
S.config(command=T.yview)
T.config(yscrollcommand=S.set)
info=?
T.insert(END, info)
mainloop( )
if __name__ == '__main__':
main()
Here's a little push forward:
from tkinter import Tk, Frame, Menu, Scrollbar, Text
from tkinter import RIGHT, LEFT, BOTH, END, Y
from tkinter import StringVar
class Example(Frame):
def __init__(self, master):
super().__init__(master)
self.pack(fill=BOTH, expand=True)
self.initUI()
def initUI(self):
self.master.title("syntax explenation for shopfloor")
menubar = Menu(self.master)
self.master.config(menu=menubar)
syntaxMenu = Menu(menubar, tearoff=False)
submenu = Menu(syntaxMenu)
syntaxMenu.add_cascade(label='Math', menu=submenu, underline=0)
submenu.add_command(label="abs()", command=self.onSyntaxabs)
submenu.add_command(label="cos()", command=self.onSyntaxcos)
submenu.add_command(label="sin()", command=self.onSyntaxsin)
submenu.add_cascade(label="log()", command=self.onSyntaxlog)
submenu = Menu(syntaxMenu)
syntaxMenu.add_cascade(label='Inqurie', menu=submenu, underline=0)
submenu.add_command(label="inqiureText()", command=self.onSyntaxinquire)
menubar.add_cascade(label="Syntax", underline=0, menu=syntaxMenu)
S = Scrollbar(self) # Should probably be an instance member as well
self.text = Text(self, height=20, width=60, bg="lightblue")
S.pack(side=RIGHT, fill=Y)
self.text.pack(side=LEFT, fill=BOTH)
self.text.pack(side=RIGHT, fill=BOTH)
S.config(command=self.text.yview)
self.text.config(yscrollcommand=S.set)
self.setText("?")
def setText(self, text):
self.text.delete(1.0,END)
self.text.insert(END, text)
def onSyntaxabs(self):
self.setText("Convert to absolute value, abs(-10) will respond with 10.")
def onSyntaxsin(self):
self.setText("Returns sinus value in decimal degrees")
def onSyntaxcos(self):
self.setText("Returns cosinus value in decimal degrees")
def onSyntaxlog(self):
self.setText("Returns the natural logarithm")
def onSyntaxinquire(self):
self.setText("Creates a pop-up box that you can enter text in.")
def main():
root = Tk()
root.geometry("500x600")
app = Example(master=root)
app.mainloop()
if __name__ == '__main__':
main()
In general, I'd try to avoid importing all those symbols and just do something like
import tkinter as tk
then where you write Frame or RIGHT you'd use tk.Frame or tk.RIGHT.
Also, I moved all your widget creation inside of the class. So now your Text widget is a class member (self.text) and can be accessed and controlled easily from within the class -- all of your onSyntax___ methods call setText (another class method) which sets the Text widget's contents.
Hope it helps.
Thank you all for your input.
And I'll will continue to work with your post jedward.
I'll look in to Eclipse and Py Charm since I really need something like that.
I've read a few threads all over the internet regarding clearing a text box on tkinter. Basically everyone says it's simple:
text.delete("1.0", END)
However, perhaps it has something to do with the way I structured it, or the way I'm calling it, but for some reason, this does not work for me. It simply does nothing.
I've tried re-positioning the def, and re-writing the text.delete("1.0", END) in a number of ways, most of which lead me to other errors, but I cannot seem to get this to work.
Ultimately, what I'm trying to accomplish is that when I click a button, the text box will clear, before populating with new information.
Below is my code.
from tkinter import *
from PIL import Image, ImageTk
import functions
class MainWindow(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("pyTicket")
# TOOLBAR ####################################################
toolbar = Frame(self.parent, bd=1, relief=RAISED)
self.img = Image.open("Icons\startupcheck.png")
eimg = ImageTk.PhotoImage(self.img)
startupButton = Button(toolbar, text="Re-Check ", image=eimg, compound="left", relief=RAISED, command=self.StartUpChecker)
startupButton.image = eimg
startupButton.pack(side=RIGHT, padx=2, pady=2)
toolbar.pack(side=TOP, fill=X)
self.parent.config(menu=menubar)
self.pack(anchor=N, side=TOP, fill=X, expand=False)
# TOOLBAR ####################################################
# TEXTBOX ####################################################
self.textbox = Text(self, wrap="word", height=5)
self.textbox.pack(side="bottom", fill="both", expand=True)
self.textbox.tag_configure("TextBox", foreground="#b22222")
self.pack(anchor=S, side=BOTTOM, fill=BOTH, expand=True)
# TEXTBOX ####################################################
# Functions ###################################################
def StartUpChecker(self):
self.clear_text()
functions.StartUpChecker()
def clear_text(self):
self.textbox.delete("1.0", END)
class TextRedirector(object):
def __init__(self, widget, tag="stdout"):
self.widget = widget
self.tag = tag
def write(self, str):
self.widget.configure(state="normal")
self.widget.insert("end", str, (self.tag,))
self.widget.configure(state="disabled")
def main():
root = Tk()
#Width X Height
root.geometry("500x300+300+300")
root.update()
root.minsize(400,200)
app = MainWindow(root)
root.mainloop()
if __name__ == '__main__':
main()
You don't appear to actually use the TextRedirector class in the code you posted, but if you're using it in your actual code, note that its .write() method leaves the textbox in a disabled state - which prevents ALL modifications, even those resulting from code instead of direct user action. Your .clear_text() method needs to temporarily enable the textbox so that you can modify it, exactly as .write() does.
Widget tk.Entry from example_script.py do not save value 'textvariable' field.
example_script.py:
import Tkinter as tk
class App(tk.Frame):
def __init__(self, master, text):
tk.Frame.__init__(self, master)
textVar = tk.StringVar()
textVar.set(text)
entryVar = tk.Entry(self, textvariable=textVar).pack()
self.pack()
def main():
root = tk.Tk()
text = ['text1', 'text2', 'text3']
for i in text:
App(root, i)
root.mainloop()
main_script.py:
import Tkinter import example_script as ex
if __name__ == '__main__':
root = Tkinter.Tk()
Tkinter.Button(root, text='press', command=lambda: ex.main()).pack()
root.mainloop()
If I change row 'entryVar = tk.Entry(self, textvariable=textVar).pack()' to
entryVar = tk.Entry(self)
entryVar.pack()
entryVar.insert(0, text)
field's value is updated. Why?
How will be correct open new window from imported script? Tkinter.Toplevel() is not suitable. Now I use subprocess.Popen.
When you do entryVar = tk.Entry(self).pack(), entryVar will be set to None because that is what pack() returns. When you call pack on a separate line, entryVar gets set to what you think it does.
You cannot create two instances of the Tk class in one program. Tkinter is not designed to work that way.