Python Tkinter text editor does not save font to text file - python-2.7

Currently, I am working on a GUI text editor with python and tkinter. Thanks to the great people at SO (thank you Rinzler), I have managed to modify the font of the text. However, I am unable to save the font and font size to the txt file.
I know that this should be possible as Notepad can modify and save a txt file with a specified font.
This is the code to save to a file:
def file_saveas():
filename = tkFileDialog.asksaveasfile(mode='w', defaultextension=".txt")
if filename is None: # asksaveasfile return `None` if dialog closed with "cancel".
return
text2save = str(textPad.get(1.0, END)) # starts from `1.0`, not `0.0`
filename.write(text2save)
filename.close()
print filename
This is the code (courtesy of Rinzler) to change the font:
def choose_font():
global root, textPad # I hate to use global, but for simplicity
t = Tkinter.Toplevel()
font_name = Tkinter.Label(t, text='Font Name: ')
font_name.grid(row=0, column=0, sticky='nsew')
enter_font = Tkinter.Entry(t)
enter_font.grid(row=0, column=1, sticky='nsew')
font_size = Tkinter.Label(t, text='Font Size: ')
font_size.grid(row=1, column=0, sticky='nsew')
enter_size = Tkinter.Entry(t)
enter_size.grid(row=1, column=1, sticky='nsew')
# associating a lambda with the call to text.config()
# to change the font of text (a Text widget reference)
ok_btn = Tkinter.Button(t, text='Apply Changes',
command=lambda: textPad.config(font=(enter_font.get(),
enter_size.get())))
print font
ok_btn.grid(row=2, column=1, sticky='nsew')
done = Tkinter.Button(t, text='Get rid of Pushy!', command=t.destroy)
done.grid(row=4, column=1, sticky='nsew')
# just to make strechable widgets
# you don't strictly need this
for i in range(2):
t.grid_rowconfigure(i, weight=1)
t.grid_columnconfigure(i, weight=1)
t.grid_rowconfigure(2, weight=1)
Finally, this is the code that reads the font and other configuration information:
font = (fontname, size)
textPad.config(
borderwidth=0,
font=font ,
foreground="green",
background="black",
insertbackground="white", # cursor
selectforeground="blue", # selection
selectbackground="#008000",
wrap="word",
width=64,
undo=True, # Tk 8.4
)
I have searched the internet without coming up with any answers as to why the font and text size are not saved. Any help would be greatly appreciated.
I am using python 2.7.7 , Tkinter, and this is being run on Windows 7.
Any help manipulation an rtf file would also be helpful (currently, I see the tags and not the end format).

There is no support for this in tkinter. You will have to pick a file fomat that supports fonts (rtf, .docx, .html, etc), convert the data in the widget to this format, and then write it to a file.

Notepad can only have a custom font and size for its editor window, it doesn't save it to the file, it just remembers the user's custom settings, and applies them to its window when you use it.
The tkinter text widget can be horrible to save formatting to another format, I've tried converting it to XML to save to a .docx but I haven't been successful. I have used my own format which is a plain text file with an 'index' of the tkinter Text widget tags at the start and their line&column indexes, then a marker for where the document begins, then the document. This cannot hold images though, and it opens with all the formatting index when you open it in another word processor.
XML is ideal for opening and saving the tkinter text contents - use an xml parser to open, then wite a recursive function to add text with tags as you go. (If you want rich text, this, like xml, is an iterative format - elements inside elements, so could be done like i'm describing below for xml, but you need to write your own rich text parser)
import xml.etree.ElementTree as etree
e = etree.fromstring(string)
#create an element tree of the xml file
insert_iter(e)
#call the recursive insert function
def insert_iter(element):
#recursive insert function
text.insert("end", element.text, tagname)
#insert the elements text
for child in element:
insert_iter(child)
#iterate through the element's child elements, calling the recursive function for each
text.insert("end", child.tail, tagname)
#insert the text after the child element
text.tag_config(tagname, **attrib)
#configure the text
'attrib' is a dictionary eg. {"foreground":"red", "underline":True} would make the text you insert have red font and black underline,
'tagname' is a random string, and needs to be automatically created by your program
To save the file, make a function to do the reverse. I wouldn't bother with using the xml library for this - as tkinter outputs the correct format, just write it manually, but make sure to escape it
from xml.sax.saxutils import escape
data = text.dump("1.0", "end")
print(data[0:500]) # print some of the output just to show how the dump method works
output = ''
#get contents of text widget (including all formatting, in order) and create a string to add the output file to
for line in data:
if line[0] == "text":
#add the plain text to the output
output += escape(line[1])
elif line[0] == "tagon":
#add a start xml tag, with attributes for the given tkinter tag
name = 'font'
attrib = ""
tag = #the dictionary you stored in your program when creating this tag
for key in tag:
attrib += "%s='%s' "%(key, escape(tag[key]))
output += "<%s %s>"%(name, attrib)
elif line[0] == "tagoff":
#add a closing xml tag
output += '</%s>'%name

Related

Sublime Text Plugin Open file symbol

I was trying to execute a command to open a file at the line of a text symbol.
I tried to achieve that using the show_overlay command, but it will not open correctly if you try to use the ‘#’ in the search.
Example. create a project that contains a file,
app/code/community/MagicToolbox/MagicZoomPlus/Model/Observer.php:
class MagicToolbox_MagicZoomPlus_Model_Observer {
public function fixLayoutUpdates($observer) {
}
}
if you try to create a plugin with command like this:
class MyPluginCommand(sublime_plugin.TextCommand):
def run(self, view):
sublime_api.window_run_command(
self.view.window().id(),
'show_overlay',
{
'overlay': 'goto',
'show_files': True,
'text': 'MagicToolbox/MagicZoomPlus/Model/Observer#fixLayoutUpdates'
}
)
it will not display the file on first hand. but if you remove all the text from the search input, and then paste it again MagicToolbox/MagicZoomPlus/Model/Observer#fixLayoutUpdates it will display the file and highlight the function.
Maybe there is another way to achieve the same.
After some investigation, it looks like when you provide text to the show_overlay it's just applied directly to the contents of the text field and used as a simple filter for initial display without any of the extra handling that would occur if you entered the text manually. Thus when you insert text that includes special goto features such as the # that you're using here, it's applied strictly as a filter on the files being displayed but no extra action happens.
As you've noted, opening the overlay and then entering the text as two actions has the desired effect. So in order to do this via a plugin, you need to do this in two actions:
import sublime
import sublime_plugin
class ExampleCommand(sublime_plugin.TextCommand):
def run(self, edit):
self.view.window().run_command("show_overlay", {
"overlay": "goto",
"show_files": True
})
self.view.window().run_command("insert", {
"characters": "Observer#fixLayoutUpdates"
})
Here we first open the overlay and then use the insert command to insert the text. The important key here is asking the window to run the insert command. Although insert is a TextCommand, asking the window to execute it tells the window to forward the command to whatever view happens to have the input focus in that window, which is the input area in the overlay.
If the command that you're doing this in isn't modifying the selection or contents of the file on it's own, you can shorten this up by making it a WindowCommand instead and using self.window instead of self.view.window().

How to write user input (taken from Tkinter's Entry) in newly created file

I wanted to get user input from user using Tkinter's Entry something like this
from Tkinter import *
top = Tk()
label = Label(top, text="Enter your bio")
entry = Entry(top, bd = 2)
def create_new():
new_file = open('file.txt', 'w+')
user_input = str(entry) # I ALSO TRIED WITHOUT str()
new_file.write(user_input) #still doesn't work
button = Button(top, text = "SAVE", fg ="red", command=create_new)
label.pack()
entry.pack()
button.pack()
top.mainloop()
When I add my info in the field and hit SAVE, it does create a new file.txt but it doesn't write my info into the file.txt
file.txt only has some numbers like these
.22775808
.22710272
.22382592
etc...
Any ideas on how can I fix this? Also what do these numbers mean and why are they here?
from tkinter import *
top = Tk()
label = Label(top, text="Enter your bio")
entry = Entry(top, bd = 2)
def create_new():
new_file = open('file.txt', 'w+')
user_input = (entry).get() # I ALSO TRIED WITHOUT str()
new_file.write(user_input) #still doesn't work
button = Button(top, text = "SAVE", fg ="red", command=create_new)
label.pack()
entry.pack()
button.pack()`enter code here`
top.mainloop()
You aren't writing the contents of the Entry, you're writing the Entry itself - which from Python's point of view is just the randomly-generated name of the actual widget which lives in the embedded Tcl/Tk interpreter. Use entry.get() for the actual contents.
You're also forgetting to close the file after you write to it, so anything you do manage to write may not show up immediately.

Cannot dynamically change font in python Tkinter text editor

Recently I have been working on a GUI python plain text editor. The code calls this function:
def TimesNewRoman():
global fontname
global font
fontname = "Times New Roman"
print font
The variables are:
fontname = "Calibri"
size = "14"
font = fontname + " " + size
And Tkinter reads the font with the code:
textPad.config(
borderwidth=0,
font=font ,
foreground="green",
background="black",
insertbackground="white", # cursor
selectforeground="blue", # selection
selectbackground="#008000",
wrap="word",
width=64,
undo=True, # Tk 8.4
)
However, I cannot get it to work. I get no errors but the font remains Calibri. I have searched the internet looking for anything that might allow me to dynamically change the font of the text canvas, but I have not succeeded in finding one that works. Any help in implementing a font modifying feature would be very much appreciated.
I am using python 2.7.7, Tkinter, and I am running this on Windows 7.
Your function should change the font name to "Times New Roman". Are you sure you are calling the function?
Just for completeness, as also Bryan Oakley stated, you should use the tuple syntax when specifying a font name with more than one word (like I am doing in the example below).
If it's ok to dynamically change the font of the Text widget with the click of a button, then the following could be a simple solution that uses a Toplevel widget to let the user write the font and size:
import Tkinter as tk
def choose_font():
global m, text # I hate to use global, but for simplicity
t = tk.Toplevel(m)
font_name = tk.Label(t, text='Font Name: ')
font_name.grid(row=0, column=0, sticky='nsew')
enter_font = tk.Entry(t)
enter_font.grid(row=0, column=1, sticky='nsew')
font_size = tk.Label(t, text='Font Size: ')
font_size.grid(row=1, column=0, sticky='nsew')
enter_size = tk.Entry(t)
enter_size.grid(row=1, column=1, sticky='nsew')
# associating a lambda with the call to text.config()
# to change the font of text (a Text widget reference)
ok_btn = tk.Button(t, text='Apply Changes',
command=lambda: text.config(font=(enter_font.get(),
enter_size.get())))
ok_btn.grid(row=2, column=1, sticky='nsew')
# just to make strechable widgets
# you don't strictly need this
for i in range(2):
t.grid_rowconfigure(i, weight=1)
t.grid_columnconfigure(i, weight=1)
t.grid_rowconfigure(2, weight=1)
m = tk.Tk()
text = tk.Text(m)
text.pack(expand=1, fill='both')
chooser = tk.Button(m, text='Choose Font', command=choose_font)
chooser.pack(side='bottom')
tk.mainloop()
When you click Choose Font, another window shows up, where you can insert the font name and the font size. You can apply the new font name and font size through the click of another Button Apply Changes, which uses a lambda.
Note that I have not handled any possible wrong inputs (for example inserting a letter for the size), you can do it by your own.
The problem is how you are specifying the font. You should use a tuple rather than a string. Try this:
font = (fontname, size)
textPad.config(
...,
font=font,
...
)
A good place for tkinter documentation is effbot.org. On the page about widget styling it says this about specifying the font:
Note that if the family name contains spaces, you must use the tuple
syntax described above.

How to display the file path in Entry widget in windows path format

I have written a Tkinter program in which the Browse button is used to select a file and the selected file's complete path gets displayed in the Entry widget. But my problem is, it's displaying the path with 'forward'(/) slashes instead of the conventional windows format of 'backward'(\) slashes. This is strange for me since I'm working on windows os.
Why this occurs ? Is there any before hand fix for this, instead of replace string option ?
my code:
def selectfile():
fileName = askopenfilename(parent=root, title='Choose a file', initialdir='C:\\')
custName.set(fileName) #Populate the text field with the selected file
#create the 'filepath' field
custName = StringVar(None)
filepath = Entry(root, width ='50', textvariable=custName).pack(anchor=W)
#Create the 'Browse' button
browseButton = Button(root, text="Browse", relief = 'raised', width=8, command=selectfile, cursor='hand2').place(x=325,y=16)
Expected Output in Entry widget:
c:\data\file.txt
Actual Output in Entry widget:
c:/data/file.txt
You can always use replace() to replace "/" with "\" for string. Here's link with docs about replace() method:
https://docs.python.org/2/library/string.html?highlight=replace#string.replace
This is workaround fix. Try looking more deeply inside Tkinter's docs for actual answer why this occurs.

Write wxListBox to .txt file in Python

I have a wxListBox filled with strings. I would like to write the contents of my wxListBox to a .txt file.
I have tried:
def saveDB(self, parent):
listBox = ""
for i in range(self.listBox.GetCount()):
listBox = self.listBox.GetString(i) + "\n"
This does not seem to be getting the job done. How can I write the contents to a .txt file?
listBox = ""
for i in range(self.listBox.GetCount()):
listBox = self.listBox.GetString(i) + "\n"
This just got the listbox content into a 'listBox' variable.
You need to write to a file, something like this should do.
f = open('yourfile.txt', 'w')
f.write(listBox)
f.close()