Integrating file and text widget python/tkinter - python

The scenario is that i want to:
-Open a specific text file
-Import text from text file to be shown in a text widget
-Then the text from the text widget to then replace what was in the text file before, with what is currently in the text widget.
Currently i have worked out how to open the specific text file into the text widget and for it to be displayed however i can not figure out how to do the last step.
I have tried to define a 'Save' function however have not got anywhere, you can see this below.
Current code for step 1 and 2:
class PropertynewsA(Propertynews):
def __init__(self):
Propertynews.__init__(self)
def save():
file.write(txt)
file.close()
messagebox.showinfo('Saved!','Your Message has been Saved')
self.delete
file = open('PropertynewsA.txt', 'r+') #Text file i am using
with open('PropertynewsA.txt') as file: # Use file to refer to the file object
data = file.read() #date=current text in text file
#Text widget
Time = strftime("%d %b %Y\n")#getting gmt time
txt = Text(self.GenericGui,height=14, width=53,font=('TkDefaultFont',12)) #Creating text widget
txt.insert('1.0',data) #showing text from text file in widget
txt.tag_configure('format', background='lightblue', font='helvetica 14 bold', relief='raised')
txt.focus()
txt.place(x=8,y=40) #places text widget
If anyone can assist me here that would be great
Cheers guys

Once you know how the Widget indices work and you know the insert and get methods on the Text widget:
starting_text = "THIS WOULD COME FROM A FILE"
...
textbox = TEXT_WIDGET_SETUP_ALREADY
textbox.insert("1.0",starting_text)
...
ending_text = textbox.get("1.0","end-1c")
The tricky part is accessing the text while the program is closing but not after the widget is destroyed (or you get a _tkinter.TclError: invalid command name ".4384096888" error):
import tkinter as tk
class Text(tk.Text):
def destroy(self):
global ending_text
ending_text = self.get("1.0","end-1c")
super(Text,self).destroy()
although if you use from tkinter import * notation you will need to call your class something other then Text as well as probably not using ending_text as a global variable, but that was the easiest way to show how to do it.
This is the full code I used for testing with IO, although if you don't understand how to work with files already there are references elsewhere.
import tkinter as tk
filename = "test.txt"
class Text(tk.Text):
def destroy(self):
global ending_text
ending_text = self.get("1.0","end-1c")
super(Text,self).destroy()
try:
with open(filename) as f:
text = f.read()
except IOError:
text = ""
root = tk.Tk()
textbox = Text(root)
textbox.insert("1.0",text)
textbox.grid()
#this would probably just be put in the destroy method
def finish(event=None):
with open(filename,"w") as f:
f.write(ending_text)
textbox.bind("<Destroy>",finish) #this will happen after Text.destroy() so textbox.get() fails if used from this point
root.mainloop()

I created a simple user interface that will allow you to open the text file of your choice from a file dialog. I prefer to set the options of this later apart for scalability reasons (in case you would love, in the future, to read document files instead, for instance):
# Opening file options
self.file_options={}
self.file_options['defaultextension'] = '.txt'
self.file_options['filetypes'] = [('text files', '.txt'), ('all files', '.*')]
self.file_options['parent'] = self.parent
self.file_options['title'] = 'Open a text file'
self.file_options['initialdir']='/home/'
I am runing Linux, so in case you're on MS Windows OS, you may change self.file_options['initialdir']='/home/' to whatever directory path you want. Note that you can also remove it, in which case the file dialog window will prompt you to the directory where you are running the application, by default.
The method initialize_user_interface() does what its name reflects. Mainly, it offers a comfortable way to exit the application on a click and choose the file to read:
self.filemenu.add_command(label="Open",command=self.text_replacement)
self.filemenu.add_command(label="Exit",command=self.parent.quit)
You can then add a Text widget. Preferably, it is better to have a scrollable text zone in case you stumble on a large content file.
For this, you need to create the scrollbar, and attach it to the Text widget because the Text widget do not maintain its own scrollbars.
Here is the complete program:
'''
Created on Feb 25, 2016
#author: begueradj
'''
import Tkinter # Tkinter -> tkinter in Python3
import Tkconstants
import tkFileDialog
class Begueradj(Tkinter.Frame):
""" Get text file content and past it into a scrollable Text widget.
Replace the content of the file with the pre-existing Text widget content.
"""
def __init__(self,parent):
""" Set some class variables:
mainly the file dialog interface options.
"""
Tkinter.Frame.__init__(self,parent)
self.parent=parent
# Opening file options
self.file_options={}
self.file_options['defaultextension'] = '.txt'
self.file_options['filetypes'] = [('text files', '.txt'), ('all files', '.*')]
self.file_options['parent'] = self.parent
self.file_options['title'] = 'Open a text file'
self.file_options['initialdir']='/home/'
self.initialize_user_interface()
def initialize_user_interface(self):
""" Design of the user interface.
It mainly consists of a bar menu and a horizontally & vertically
scrollable Text widget in case the text file to read is large.
"""
self.parent.title("Text replacement")
# Set the bar menu and its items
self.menubar=Tkinter.Menu(self.parent)
self.filemenu=Tkinter.Menu(self.menubar,tearoff=0)
self.filemenu.add_command(label="Open",command=self.text_replacement)
self.filemenu.add_command(label="Exit",command=self.parent.quit)
self.menubar.add_cascade(label="File",menu=self.filemenu)
self.parent.config(menu=self.menubar)
self.parent.grid_rowconfigure(0,weight=1)
self.parent.grid_columnconfigure(0,weight=1)
# Set the horizontal and vertical scrollbars
self.hscrollbar=Tkinter.Scrollbar(self.parent,orient=Tkconstants.HORIZONTAL)
self.hscrollbar.grid(row=1,column=0,sticky=Tkinter.E+Tkinter.W)
self.vscrollbar=Tkinter.Scrollbar(self.parent)
self.vscrollbar.grid(row=0,column=1,sticky=Tkinter.N+Tkinter.S)
# Set the Text widget and make it scrollable
self.text=Tkinter.Text(self.parent,wrap=Tkinter.NONE,bd=0,
xscrollcommand=self.hscrollbar.set,
yscrollcommand=self.vscrollbar.set)
self.text.grid(row=0,column=0,sticky=Tkinter.E+Tkinter.W+Tkinter.S+Tkinter.N)
self.text.insert("1.0","Original text here")
self.hscrollbar.config(command=self.text.xview)
self.vscrollbar.config(command=self.text.yview)
def text_replacement(self):
""" Return the name of a file
opened in read mode
"""
self.filename = tkFileDialog.askopenfilename(**self.file_options)
if self.filename:
self.original=self.text.get("0.0","end-1c")
print self.original
with open(self.filename) as self.filetoread:
self.txtfilecontent=self.filetoread.read()
self.filetoread.close()
self.text.delete("1.0", Tkinter.END) # Erase the previous Text widget content
self.text.insert("1.0", self.txtfilecontent)
with open(self.filename,'w') as self.filetowrite:
self.filetowrite.write(self.original)
self.filetowrite.close()
def main():
""" Main method to be executed.
Instantiate Begueradj class
"""
root=Tkinter.Tk()
b=Begueradj(root)
root.geometry("300x250+300+300")
root.mainloop()
if __name__=="__main__":
""" Run the application
"""
main()
Application demo:
The demo consists of 3 screenshots showing:
Original text set into the Text widget and a file dialog window to pick up the file to read.
Loading the content of file1.txt into Tkinter.Text widget
Check if the original file of the Text widget is saved (replaces) in file1.txt

Related

Close askopenfilename if it loses focus

I am working on a cross platform project to control a CNC router written in python 2 with a GUI created using kivy.
Overall I love kivy however the kivy file chooser is slow and doesn't look the way most users would expect it to. I would like to use a file chooser which has a native look for most users so I'm looking at using the tkinter askopenfilename() function to give me a UI native file chooser.
This works alright, except that if the user clicks outside of the file chooser to bring the main program to the front, the file chooser is left in the background and the UI is locked up because askopenfilename() is blocking.
Is there a way to make askopenfilename() close automatically if it loses focus?
Any and all help is appreciated.
Here is the code as it stands:
def openFile(self):
'''
Open The Pop-up To Load A File
Creates a new pop-up which can be used to open a file.
'''
root = Tk()
root.withdraw() # we don't want a full GUI, so keep the root window from appearing
initialDir = path.dirname(self.data.gcodeFile)
if initialDir is "":
initialDir = path.expanduser('~')
validExtensions = self.data.config.get('Ground Control Settings', 'validExtensions').replace(",", "")
root.bind("<FocusOut>", self.app_lost_focus)
filename = askopenfilename( parent=root, initialdir = initialDir, filetypes = (("Gcode files", validExtensions),("all files","*.*")), title = "Open Gcode File") # show an "Open" dialog box and return the path to the selected file
if filename is not "":
self.data.gcodeFile = filename
self.data.config.set('Maslow Settings', 'openFile', str(self.data.gcodeFile))
self.data.config.write()
#close the parent popup
self.parentWidget.close()
When filedialog.askopenfilename is called method without creating any GUI itself, it calls underlying widget.tk.call("tk_getOpenFile", **options), which then calls ::tk::dialog::file::. Which seems to handle the file-dialog-gui, on which tkinter doesn't have control over directly at least.
Perhaps a good workaround would be to hide the kivy window while the process is ongoing.

Tkinter Browse Button Self Deleting

I am trying to create a browse button in tkinter. I have created the open folder dialog box but when i set it to the button it will exit out of the window.
My ultimate goal is to:
1) click on the button and bring up the file dialogue box
2) select a file
3) insert the file name into an Entry Widget for later use
I should note that I am using multiple window frames for the code that follows is summed up.
import os
import sys
import Tkinter as tk
from tkFileDialog import askopenfilename
def openFile(entryWidgetName):
tk.Tk().withdraw()
filename = askopenfilename()
entryWidgetName.delete(0,tk.END)
entryWidgetName.insert(0,filename)
return
class Welcome():
def __init__(self,master):
self.buttonNewTemplate = tk.Button(self.master, text = 'Create a New Template', command = self.gotoNewTemplate).place(x=100, y=250)
def gotoNewTemplate(self):
root2 = tk.Toplevel(self.master)
newTemplate = NewTemplate(root2)
class NewTemplate():
def __init__(self, master):
#Entry Windows
self.uploadFile = tk.Entry(self.sectionFrame2, width = 80).grid(row=4, column = 1, sticky = 'w')
#Buttons
self.buttonBrowse=tk.Button(self.sectionFrame2, text='Browse', fg='blue', command=lambda:openFile(uploadFile)).grid(row=4, column = 0, padx = 10, sticky = 'w')
Every time I click the browse button the second window destroys itself bringing me back to the main page.
Does anyone have any suggestions?
A tkinter application can only have a single instance of Tk. You are creating at least two: one explicitly in openFile, and one from somewhere else in your code either implicitly or explicitly.
Since the only way to call openFile is from a button click, and the only way to have a button click is to have a button, and the only way to have a button is to already have a root window, you need to remove the statement tk.Tk().withdraw() since that is creating a new root window.
There may be other problems in your code, but it's impossible to know based on the incomplete code in the question.

redirect stdout to tkinter text widget

I'm trying to redirect the stdout of a function to a tkinter text widget.
The problem I am running into is that it writes each line to a new window instead of listing everything in one.
The function scans a directory and lists any file that is 0k. If no files are 0k it prints that. So, the problem is that if there are 30 0k files in a directory, it will open 30 windows with a single line in each.
Now, I know what the problem is. If you look in my function code Zerok() I am telling it:
if os.stat(filename).st_size==0:
redirector(filename)
I know that every time os.stat sees a file that is 0k it is then sending that to redirector, that's why its a new window for each file.
I just have no idea how to fix it.
Complete code below.
Thanks for the help.
import Tkinter
from Tkinter import *
import tkFileDialog
class IORedirector(object):
'''A general class for redirecting I/O to this Text widget.'''
def __init__(self,text_area):
self.text_area = text_area
class StdoutRedirector(IORedirector):
'''A class for redirecting stdout to this Text widget.'''
def write(self,str):
self.text_area.write(str,False)
def redirector(inputStr):
import sys
root = Tk()
sys.stdout = StdoutRedirector(root)
T = Text(root)
T.pack()
T.insert(END, inputStr)
####This Function checks a User defined directory for 0k files
def Zerok():
import os
sys.stdout.write = redirector #whenever sys.stdout.write is called, redirector is called.
PATH = tkFileDialog.askdirectory(initialdir="/",title='Please select a directory')
for root,dirs,files in os.walk(PATH):
for name in files:
filename=os.path.join(root,name)
if os.stat(filename).st_size==0:
redirector(filename)
else:
redirector("There are no empty files in that Directory")
break
#############################Main GUI Window###########################
win = Tk()
f = Frame(win)
b1 = Button(f,text="List Size")
b2 = Button(f,text="ZeroK")
b3 = Button(f,text="Rename")
b4 = Button(f,text="ListGen")
b5 = Button(f,text="ListDir")
b1.pack()
b2.pack()
b3.pack()
b4.pack()
b5.pack()
l = Label(win, text="Select an Option")
l.pack()
f.pack()
b2.configure(command=Zerok)
win.mainloop()
The fix is simple: don't create more than one redirector. The whole point of the redirector is that you create it once, and then normal print statements will show up in that window.
You'll need to make a couple of small changes to your redirector function. First, it shouldn't call Tk; instead, it should create an instance of Toplevel since a tkinter program must have exactly one root window. Second, you must pass a text widget to IORedirector since it needs to know the exact widget to write to.
def redirector(inputStr=""):
import sys
root = Toplevel()
T = Text(root)
sys.stdout = StdoutRedirector(T)
T.pack()
T.insert(END, inputStr)
Next, you should only call this function a single time. From then on, to have data appear in the window you would use a normal print statement.
You can create it in the main block of code:
win = Tk()
...
r = redirector()
win.mainloop()
Next, you need to modify the write function, since it must write to the text widget:
class StdoutRedirector(IORedirector):
'''A class for redirecting stdout to this Text widget.'''
def write(self,str):
self.text_area.insert("end", str)
Finally, change your Zerok function to use print statements:
def Zerok():
...
if os.stat(filename).st_size==0:
print(filename)
else:
print("There are no empty files in that Directory")
break
The above solution is very complete; I was able to essentially copy and paste it into my code with only one small modification. I'm not entirely sure why, but the StdoutRedirector requires a flush method.
I'm guessing it's because sys.stdout calls a flush() method when it exits, but I'm haven't waded into the docs deep enough to actually understand what that means.
Running the above code in the Jupyter environment caused the code to hang indefinitely until the kernel was restarted. The console kicks the following errors:
sys.stdout.flush()
AttributeError: 'StdoutRedirector' object has no attribute 'flush'
ERROR:tornado.general:Uncaught exception, closing connection.
The simple solution is to make a small change to the StdoutRedirector class by adding a flush method.
class StdoutRedirector(IORedirector):
'''A class for redirecting stdout to this Text widget.'''
def write(self,str):
self.text_area.insert("end", str)
def flush(self):
pass
Thanks to the giants that came before me and offered this very clear explanation.

Redirect my console output to my Tkinter text area widget

I'm a newbie on tkinter, my code can run but I need my text widget to display only the result variable in the callback() function not including the 'askopenfilename' method.
from Tkinter import *
from tkFileDialog import *
import os
root = Tk()
root.geometry('900x700')
path = StringVar()
#browse pdf files
def callback():
f = askopenfilename(title='Open Files',initialdir='C:\Users\shantini\Desktop\PDF',
filetypes=[('Files of type:','*.PDF'),('Files of type:','*.pdf')])
path.set(f)
result = os.popen('pdfid.py'+' '+f).read()
return result
#labelframe(text pdf output)
label=LabelFrame(root, text="PDF Analysis Output")
label.pack(side=BOTTOM, anchor=W, fill=BOTH, expand=YES)
text = Text(label,bg='white')
text.pack(fill=BOTH, expand=YES)
text.insert(INSERT,callback())
root.mainloop()
If you disable Text widget, you make it read-only, so you cant add text to it. So to add text, make it normal, or remove the state parameter. I changed your callback to reflect the comments:
def callback():
f = askopenfilename(title='Open Files',initialdir='/tmp',
filetypes=[('Files of type:','*.PDF'),('Files of type:','*.pdf')])
result = open(f).read() # I also changed this as I dont have `pdfid.py` to test the code
text_area.insert(INSERT, result)
print result
I slightly change the input files and folder, as I work in linux and cant use windows paths. Hope this helps.

Get command window output to display in widget with tkinter

Quick Project Summary: Make a python widget using Tkinter that displays data from several json and txt files. Needs to work in Windows.
Where I'm At: Everything is going great with the json files. But I'm running into trouble with the txt files. I can parse the information I need out of the necessary files with this code:
from Tkinter import *
import re
results = open("sample_results.txt", "r")
for line in results:
if re.match("(.*)test(.*)", line):
print line
if re.match("(.*)number(.*)", line):
print line
if re.match("(.*)status(.*)", line):
print line
if re.match("(.*)length(.*)", line):
print line
Problem: It displays all the data in the command shell NOT in a seperate widget.
I would like to simply move all of this information from the command shell to a text box widget (or a tkmessage widget but I feel that the text box would be more appropriate). A very long google search process gave me lots of code that doesn't work - any tips? Thanks!
Note: This is NOT all the code - just the part I need help fixing
Here's what I think you want. You want your application to open files and parse them out. For each parsed line, you would like it to insert the text (or append the text) to a text control. I would create a method for each file type to do the parsing. Then I would loop over each file and call the parser as appropriate. When you are finished parsing, you can call
self.textbox.insert(tkinter.END, parsed_text)
Another way to do this is to redirect stdout to your text control and then print the parsed line. I find this latter approach to be a bit more flexible, especially when I want to call a separate program with subprocess and read its output bit by bit. Here's one way to do it with Tkinter:
import ScrolledText
import sys
import tkFileDialog
import Tkinter
########################################################################
class RedirectText(object):
""""""
#----------------------------------------------------------------------
def __init__(self, text_ctrl):
"""Constructor"""
self.output = text_ctrl
#----------------------------------------------------------------------
def write(self, string):
""""""
self.output.insert(Tkinter.END, string)
########################################################################
class MyApp(object):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
self.root = parent
self.root.title("Redirect")
self.frame = Tkinter.Frame(parent)
self.frame.pack()
self.text = ScrolledText.ScrolledText(self.frame)
self.text.pack()
# redirect stdout
redir = RedirectText(self.text)
sys.stdout = redir
btn = Tkinter.Button(self.frame, text="Open file", command=self.open_file)
btn.pack()
#----------------------------------------------------------------------
def open_file(self):
"""
Open a file, read it line-by-line and print out each line to
the text control widget
"""
options = {}
options['defaultextension'] = '.txt'
options['filetypes'] = [('all files', '.*'), ('text files', '.txt')]
options['initialdir'] = '/home'
options['parent'] = self.root
options['title'] = "Open a file"
with tkFileDialog.askopenfile(mode='r', **options) as f_handle:
for line in f_handle:
print line
#----------------------------------------------------------------------
if __name__ == "__main__":
root = Tkinter.Tk()
root.geometry("800x600")
app = MyApp(root)
root.mainloop()
One approach is to use a simple tkinter label:
# somewhere in your main class, I suppose:
self.log_txt = tkinter.StringVar()
self.log_label = tkinter.Label(self.inputframe, textvariable=self.log_txt, justify=tkinter.LEFT)
self.log_label.pack(anchor="w")
Then, a really simple approach for putting text into that label:
def log(self, s):
txt = self.log_txt.get() + "\n" + s
self.log_txt.set(txt)
Alternatively, you can use a tkinter.Text widget. In that case you can insert text with the insert method:
self.textbox = tkinter.Text(parent)
self.textbox.insert(tkinter.END, "some text to insert")
One resource I like is http://effbot.org/tkinterbook/text.htm. Unfortunately it is quite hard to go from that text to working Python code :(
Here is a little example program with an ugly little tkinter GUI that adds text to a textbox:
#!/usr/bin/env python
try:
import tkinter
except ImportError:
import Tkinter as tkinter
import _tkinter
import platform
class TextBoxDemo(tkinter.Tk):
def __init__(self, parent):
tkinter.Tk.__init__(self, parent)
self.parent = parent
self.wm_title("TextBoxDemo")
self.textbox = tkinter.Text(self)
self.textbox.pack()
self.txt_var = tkinter.StringVar()
self.entry = tkinter.Entry(self, textvariable=self.txt_var)
self.entry.pack(anchor="w")
self.button = tkinter.Button(self, text="Add", command=self.add)
self.button.pack(anchor="e")
def add(self):
self.textbox.insert(tkinter.END, self.txt_var.get())
if __name__ == '__main__':
try:
app = TextBoxDemo(None)
app.mainloop()
except _tkinter.TclError as e:
if platform.system() == 'Windows':
print(e)
print("Seems tkinter will not run; try running this program outside a virtualenv.")

Categories