Tkinter save text in editor - python

I went here: http://effbot.org/zone/vroom.htm
And tried this out:
filename = raw_input("Filename?")
editor = Text()
editor.pack(fill=Y, expand=1)
editor.config(font="Courier 12")
editor.focus_set()
mainloop()
#save
f = open(filename, "w")
text = str(editor.get(0.0,END))
try:
f.write(text.rstrip())
f.write("\n")
However, I was given an error:
TclError: invalid command name ".40632072L"
How can i fix this problem?
I'm not comfortable with object-oriented programming, so I would prefer an imperative solution (without any class keywords).

The problem is that, after the mainloop finishes, all of your widgets, including editor, get destroyed, so you can't call editor.get.
What you want to do is add some code that stashes the value of editor in a plain old string while the main loop is running, and then use that variable. For example:
text=''
def stash(*args):
global text
text = str(editor.get(0.0,END))
editor.bind_all('<<Modified>>', stash)
Or, of course, do the simpler thing: write the file from within the GUI instead of after the GUI has exited. If you go look farther down the same page, you'll see how they do that.

Related

Widgets appearing late

So I'm building this program
def buffer():
global buf
frame2 = ttk.Frame(notebk, relief = RAISED)
if buf==0:
notebk.add(frame2, text = 'Buffer pH')
notebk.select(frame2)
buf=1
else:
notebk.select(frame2)
import buffer
buffer.main(frame2)
This part of the code is supposed to call another program I've written somewhere in a separate file on a new tab.
def main(x):
g=x
op=Label(g,text="Hello") #...and so on
The program works but the widgets on the new tab appear either significantly late or after the window is configured in some way (moved/resized).
Any tips on how I can fix this?

Not able to to call function with button in tkinter

I'm new to Tkinter, and also new to this forum. I am trying to learn to use Tkinter, and I have a problem!
I want to save some text to a text file by writing the text and then press a button to run a function that saves the info. But it seems like my "command" does not start the function.
def ny_artikel():
artikel_data = open ("artikel_databas.txt", "w")
artikel_data.write(ny_artikel.get())
artikel_data.close ()
spara_artikel = Button(new_product_window, text ="Save new article", command = ny_artikel)
spara_artikel.grid(row=7, column=1)
ny_artikel is an entry box used in my program, but I think it's too many rows to paste it all in here.
When I press the button, nothing at all happens. Not even an error message.
I assume, that the code in your answer is only part of your python file. I tried it out with Entry e in my example and it works right:
import tkinter
def ny_artikel():
with open('artikel_databas.txt', 'w') as artikel_data:
artikel_data.write(e.get())
main = tkinter.Tk()
e = tkinter.Entry(main)
e.grid(row=0, column=0)
spara_artikel = tkinter.Button(main, text ="Save new article", command = ny_artikel)
spara_artikel.grid(row=1, column=0)
main.mainloop()
As alternative I used 'with' 'as' in ny_artikel() function, which automatically closes the file. Using file.close() works fine as well.
What is the python keyword "with" used for?

How can I perform an action after mainloop() has been called in Tkinter?

I want to make a GUI command line using the Text widget. For debugging purposes, I am trying to print whatever the user types into the separate GUI window to the system terminal. I know that it is frowned upon to mix GUI and Text Based commands into the same script, but I am just debugging, so forgive me 😉
Here is my code:
from Tkinter import *
main = Tk()
console = Text(main)
console.pack()
main.mainloop()
while True:
text = console.get("1.0", "end-1c")
print(text)
My current issue is that when the mainloop starts, (of course) the while loop doesn't. If I were to move the while loop in front of the mainloop call, it would never call mainloop. I really want it to continuously check for new text.
Is there a way to like "pause" the mainloop, or just carry out the command, maybe on a new thread or something?
I want to avoid using main.after(), but if that is the only way, then so be it. ¯\(°_o)/¯
I recommend using main.after(), as it's the canonical way to do things like this in Tkinter. The following will also ensure that it only tries to print every second, instead of as fast as the console can handle it (as the while loop in your code would do if it worked).
def print_console():
print(console.get("1.0", "end-1c"))
main.after(1000, print_console)
print_console()
main.mainloop()
You can also bind widgets to "Modified"
from Tkinter import *
class TextModified():
def __init__(self):
root = Tk()
self.txt = Text(root)
self.txt.pack()
self.txt.focus_set()
self.txt.bind('<<Modified>>', self.changed)
Button(text='Exit', command=root.quit).pack()
root.mainloop()
def changed(self, value=None):
flag = self.txt.edit_modified()
if flag: # prevent from getting called twice
print "changed called", self.txt.get("1.0", "end-1c")
## reset so this will be called on the next change
self.txt.edit_modified(False)
TM=TextModified()

Python GUI programming using drag and drop, also incorporating stdout redirect

I'm new to programming & new to python. I've just developed my first script, it prosesses file, but at the moment only from the commandline.
This is just a hobby to me so my job does not depend on it :-)
I've spent a few days now trying to get my head around python gui development & have come to the conclusion that I must be stupid.
I've looked at wxpython & Tkinter & do not understand either, although Tkinter seems to be the easier out of the two. I've even looked at wysiwyg tools like Boa Contrictor & wxglade. I do not even understand how to use those. I would prefer to just stick with my editor & code manually anyway.
My problem is this:
I would like to create a desktop window with either 1 or two objects, depending on what works best. If just one object then a text box of some sort, if 2 objects then a text box & an image.
I want to be able to drag file from a file manager & drop them on my script window, this is just to pass the filenames to my script.
I than want to redirect stdout to an object within my desktop window so that all script output appears within the desktop window.
I'm not sure if one object can do both things or not. If it can than just a text box would suffice, else drop files onto image & have redirected output going to the text box.
I have found drag & drop examples on the web but nothing which incorporates stdout redirect, & I've not been able to successfully modify any of the examples that I've come across.
If somee kind sole has the time to demonstrate how to achieve what I want & explain how its works I would greatfully appreciate it!
----EDIT ----
I've been playing around with 2 examples & have managed to hash the 2 together in order to get what I wanted working. Code is below. It's not cleaned up yet ( old comments etc... ), but it works.
#!/usr/bin/python
# The next two lines are not necessary if you installed TkDnd
# in a proper place.
import os
from Tkinter import *
os.environ['TKDND_LIBRARY'] = '/home/clinton/Python/tkdnd2.6/'
import Tkinter
from untested_tkdnd_wrapper import TkDND
class Redir(object):
# This is what we're using for the redirect, it needs a text box
def __init__(self, textbox):
self.textbox = textbox
self.textbox.config(state=NORMAL)
self.fileno = sys.stdout.fileno
def write(self, message):
# When you set this up as redirect it needs a write method as the
# stdin/out will be looking to write to somewhere!
self.textbox.insert(END, str(message))
root = Tkinter.Tk()
dnd = TkDND(root)
textbox = Tkinter.Text()
textbox.pack()
def handle(event):
event.widget.insert(END, event.data)
content = textbox.get("0.0",Tkinter.END)
filename = content.split()
dnd.bindtarget(textbox, handle, 'text/uri-list')
#Set up the redirect
stdre = Redir(textbox)
# Redirect stdout, stdout is where the standard messages are ouput
sys.stdout = stdre
# Redirect stderr, stderr is where the errors are printed too!
sys.stderr = stdre
# Print hello so we can see the redirect is working!
print "hello"
# Start the application mainloop
root.mainloop()
Examples are: python drag and drop explorer files to tkinter entry widget
And also the example provided kindly by Noelkd.
In order for this code to work you must create the wrapper from first example. Also currently code just displays dragged file in window, however variable is in place to be passed onto the script which runs behind the gui interface.
If you want to use Tkinter:
from Tkinter import *
import tkFileDialog
class Redir(object):
# This is what we're using for the redirect, it needs a text box
def __init__(self, textbox):
self.textbox = textbox
self.textbox.config(state=NORMAL)
self.fileno = sys.stdout.fileno
def write(self, message):
# When you set this up as redirect it needs a write method as the
# stdin/out will be looking to write to somewhere!
self.textbox.insert(END, str(message))
def askopenfilename():
""" Prints the selected files name """
# get filename, this is the bit that opens up the dialog box this will
# return a string of the file name you have clicked on.
filename = tkFileDialog.askopenfilename()
if filename:
# Will print the file name to the text box
print filename
if __name__ == '__main__':
# Make the root window
root = Tk()
# Make a button to get the file name
# The method the button executes is the askopenfilename from above
# You don't use askopenfilename() because you only want to bind the button
# to the function, then the button calls the function.
button = Button(root, text='GetFileName', command=askopenfilename)
# this puts the button at the top in the middle
button.grid(row=1, column=1)
# Make a scroll bar so we can follow the text if it goes off a single box
scrollbar = Scrollbar(root, orient=VERTICAL)
# This puts the scrollbar on the right handside
scrollbar.grid(row=2, column=3, sticky=N+S+E)
# Make a text box to hold the text
textbox = Text(root,font=("Helvetica",8),state=DISABLED, yscrollcommand=scrollbar.set, wrap=WORD)
# This puts the text box on the left hand side
textbox.grid(row=2, column=0, columnspan=3, sticky=N+S+W+E)
# Configure the scroll bar to stroll with the text box!
scrollbar.config(command=textbox.yview)
#Set up the redirect
stdre = Redir(textbox)
# Redirect stdout, stdout is where the standard messages are ouput
sys.stdout = stdre
# Redirect stderr, stderr is where the errors are printed too!
sys.stderr = stdre
# Print hello so we can see the redirect is working!
print "hello"
# Start the application mainloop
root.mainloop()
What this does is creates a window with a button and a text box, with stdout redirect.
Currently in Tkinter you can't drag and drop files on to the open tk window(you can if you use tkdnd), so I have included a different way of getting the path of a file.
The way I have included to select a file is the askopenfilename dialog from tkFileDialog, this opens up a file browser and the path file selected is returned as a string.
If you have any questions or this doesn't quite do what your looking for please leave a comment!
Have a look at GTK. It is a really powerful library. Not the simplest, that's a fact, but once you get to understand how things work, it becomes much easier.
Here's the official tutorial
If oyu are still using Python2, I think you should use PyGTK but it has been replaced by gl (which is described in the above tutorial). A good tutorial for PyGTK can be found here.
For a static interface, you can use glade which produces XML files that are then read by GTKBuilder to create the "real" interface. The first tutorial that I've found is available here

pyQt "flush" equivalent

Function that is connected to button opens and regexp replace some text:
def process(self):
# first do this!!
self.label.setText('Processing for 5-10 sec.....')
self.button.setEnabled(0)
# and only then heavy files operations
file = open(self.filename, mode='r', encoding='utf-8')
text = re.sub(r'^ (.+)\n ', r' \[\1\]\n ', file.read(), flags=re.MULTILINE)
newfile = open(self.filename+'temp', mode='w', encoding='utf-8')
file_new.write(text)
self.label.setText('Ready')
Python 3.1, PyQt 4.8.2
File is big enough, operation takes ~10sec.
I want, when button is pressed, fist replace some text in label and disable button. So user can see, that he should wait some time.
But nothing happens. System just hangs for 10 seconds, and then "ready" label appeared and button disabled.
How can I make Qt first do label change and button disabled, and only after this do file operation?
I can imagine two solutions.
First, try calling QCoreApplication.processEvents() after you set the label's text and disable the button.
Second, if the first solution doesn't work, split your method in two. Then, in the first method, set the label's text, disable the button, and use QTimer.singleShot() to call your second method. It will be called asynchronously, so Qt event processing loop will have a chance to update the GUI.
I think using a thread is the better way to do it.
However, you may try put
while self.button.isEnabled():
pass
after where you set it.

Categories