Python Tkinter Help Menu - python

I have a paragraph of help information that I would like to display in a window with an "ok" button at the bottom. My issue is formatting...I want to be able to simply set the paragraph equal to a variable and then send that variable to a message box widget. By default, it formats laterally and in a very ugly manner. Any advice?
def aboutF():
win = Toplevel()
win.title("About")
about = "Top/bottom 3 - Reports only the top/bottom 3 rows for a param you will later specify.\
Set noise threshold - Filters results with deltas below the specified noise threshold in ps.\
Sort output - Sorts by test,pre,post,unit,delta,abs(delta).\
Top 2 IDD2P/IDD6 registers - Reports only the top 2 IDD2P/IDD6 registers.\
Only critical registers - Reports only critical registers.\
Use tilda output format - Converts the output file from csv to tilda.\
Use html output format - Converts the output file from csv to html."
Label(win, text=about, width=100, height=10).pack()
Button(win, text='OK', command=win.destroy).pack()

Use a text widget with word wrapping, and either define your string more accurately or do a little post-processing to remove all that extra whitespace. Using the code from this answer makes it easy to use multiple colors, fonts, etc.
For example:
import Tkinter as tk
import re
class CustomText(tk.Text):
'''A text widget with a new method, HighlightPattern
example:
text = CustomText()
text.tag_configure("red",foreground="#ff0000")
text.HighlightPattern("this should be red", "red")
The HighlightPattern method is a simplified python
version of the tcl code at http://wiki.tcl.tk/3246
'''
def __init__(self, *args, **kwargs):
tk.Text.__init__(self, *args, **kwargs)
def HighlightPattern(self, pattern, tag, start="1.0", end="end", regexp=True):
'''Apply the given tag to all text that matches the given pattern'''
start = self.index(start)
end = self.index(end)
self.mark_set("matchStart",start)
self.mark_set("matchEnd",end)
self.mark_set("searchLimit", end)
count = tk.IntVar()
while True:
index = self.search(pattern, "matchEnd","searchLimit",count=count, regexp=regexp)
if index == "": break
self.mark_set("matchStart", index)
self.mark_set("matchEnd", "%s+%sc" % (index,count.get()))
self.tag_add(tag, "matchStart","matchEnd")
def aboutF():
win = tk.Toplevel()
win.title("About")
about = '''Top/bottom 3 - Reports only the top/bottom 3 rows for a param you will later specify.
Set noise threshold - Filters results with deltas below the specified noise threshold in ps.
Sort output - Sorts by test,pre,post,unit,delta,abs(delta).
Top 2 IDD2P/IDD6 registers - Reports only the top 2 IDD2P/IDD6 registers.
Only critical registers - Reports only critical registers.
Use tilda output format - Converts the output file from csv to tilda.
Use html output format - Converts the output file from csv to html.'''
about = re.sub("\n\s*", "\n", about) # remove leading whitespace from each line
t=CustomText(win, wrap="word", width=100, height=10, borderwidth=0)
t.tag_configure("blue", foreground="blue")
t.pack(sid="top",fill="both",expand=True)
t.insert("1.0", about)
t.HighlightPattern("^.*? - ", "blue")
tk.Button(win, text='OK', command=win.destroy).pack()
root=tk.Tk()
aboutF()
root.mainloop()

Related

How to update variables from a text file in a GUI in real time? Python and tkinter

I have a script that continuously updates numbers on a text file in the same directory. I made a GUI using tkinter to display these numbers. I'm having a hard time getting the GUI to update the numbers in real time. It will display the numbers as they were when the GUI first started, but will not update the display as the text file changes. Here's some basic code to demonstrate:
def read_files(file, line):
old = open(f'{file}.txt', 'r').readlines()[line]
new = old.replace('\n', '')
return new
number = read_files('Statistics', 0)
number_label = Label(frame1, text=f'{number}')
number_label.grid(row=0, column=0)
The above code shows the number from the text file as it was when the GUI first opened. However, it does not update the number as its value in the text file changes. I did some reading around and also tried the following:
def read_files(file, line):
old = open(f'{file}.txt', 'r').readlines()[line]
new = old.replace('\n', '')
return new
number = read_files('Statistics', 0)
number_label = StringVar()
number_label.set(number)
number_display = Label(frame1, text=f'{number_label.get()}')
number_display.grid(row=0, column=0)
This has the same effect. It shows the value retrieved from the text file at the moment the GUI was opened, but does not update it as the text file is updated. Any help is appreciated.
Since there is no complete code, take a look at this example:
from tkinter import *
root = Tk()
def update_gui():
number = read_files('Statistics', 0) # Call the function
number_display.config(text=number) # Change the text of the label, also same as text=f'{number}'
root.after(1000,update_gui) # Repeat this function every 1 second
def read_files(file, line):
old = open(f'{file}.txt', 'r').readlines()[line]
new = old.replace('\n', '')
return new
number_display = Label(root) # Empty label to update text later
number_display.grid(row=0, column=0)
update_gui() # Initial call to the function
root.mainloop()
Here the label is created outside the function but the text is not given, but inside the function we are repeating the function every 1 second, with after, and changing the text of the label with config method. I have avoided the use of StringVar() as it's of no use here, you can just store it in a normal variable and use it.
Here is a plagiarized look at how after should be used:
def after(self, ms, func=None, *args):
"""Call function once after given time.
MS specifies the time in milliseconds. FUNC gives the
function which shall be called. Additional parameters
are given as parameters to the function call. Return
identifier to cancel scheduling with after_cancel."""

Python text editor with "programatically" typing capabilities

I need a text editor which, as a feature, must have the capability of changing some of the information it shows on the screen, while open.
For example, I open the text file using that text editor mentioned above, and I can see on the screen:
|--------------------------------------------------------|
| My Text Editor (C:\myfile.txt) [Button] |
|--------------------------------------------------------|
|Name: John |
|Age: 32 |
|Gender: Male |
| |
| |
Then, for example, if I hit the button [Button], I want that the Age 32 change for, say, 30, while the text file is open.
But I want to do that without using keyboard and mouse automation...
Is that possible? Will Tkinter be enough for that task?
Here is a contrived example that both has a button to change the age, and also will update the time every second.
It does this with a context manager which preserves the insertion cursor and then inserts or deletes any text you want. This isn't particularly good coding style, but it shows what tkinter can do with its text widget.
import tkinter as tk
from datetime import datetime
from contextlib import contextmanager
#contextmanager
def preserve_insert_cursor(text):
"""Performs an action without changing the insertion cursor location"""
saved_insert = text.index("insert")
yield
text.mark_set("insert", saved_insert)
def change_age():
"""Change the age on line 3"""
with preserve_insert_cursor(text):
text.delete("3.5", "3.0 lineend")
text.insert("3.5", "30")
def update_time():
with preserve_insert_cursor(text):
# find all ranges of text tagged with "time" and replace
# them with the current time
now = datetime.now()
timestring = now.strftime("%H:%M:%S")
ranges = list(text.tag_ranges("time"))
while ranges:
start = ranges.pop(0)
end = ranges.pop(0)
text.delete(start, end)
text.insert(start, timestring, "time")
# call this function again in a second
text.after(1000, update_time)
root = tk.Tk()
header = tk.Frame(root, bd=1, relief="raised")
text = tk.Text(root)
header.pack(side="top", fill="x")
text.pack(fill="both", expand=True)
button = tk.Button(header, text="Button", command=change_age)
button.pack(side="right", padx=10)
# insert "Time:" with no tags, "<time>" with the tag "time",
# and then a newline with no tags
text.insert("end", "Time: ", "", "<time>", "time", "\n")
text.insert("end", "Name: John\n")
text.insert("end", "Age: 32\n")
text.insert("end", "Gender: Male\n")
update_time()
root.mainloop()
You can't tell from a static screenshot, but if you run the code you'll see that the time updates in real time even while you're typing.
I think, the best way to achieve this is to take a simple TextEditor example for PyQT and add a button to do what you want.
In a quick search I found for example this one:
https://www.learnpyqt.com/examples/no2pads-simple-notepad-clone/

How to store the input from the text box in python?

I want to make a program that show me the divisors of the number introduced in a text box using python tkinter GUI and store the results into a plain text file.
I don't how to get the value from the text box. I understood that is something linked with get() , I read something but I still don't get it.
Here is the code:
from tkinter import *
def create_file():
file_object = open("C:/Users/valis/Desktop/Divisors.txt","w+")
def evaluate():
show_after= Label(text= "Check your Desktop !")
show_after.pack(pady=2, anchor=SW)
create_file()
#Windows size and Title
window = Tk()
window.title("Show Divisors")
window.geometry("300x100")
message = Label(window, text="Enter a number : ")
message.pack(pady=2, anchor=NW)
textbox_input = Text(window,height=1, width=11)
textbox_input.pack(padx= 2,pady= 2, anchor=NW)
window.mainloop()
The code isn't complete, so what should I do ?
As you said, you will use the get() function but with some additional attributes.
If we have a text box textbox_input, then you can return its input using this line:
test_input = textbox_input.get("1.0",END)
The first part, "1.0" means that the input should be read from line one, character zero (ie: the very first character). END is an imported constant which is set to the string "end". The END part means to read until the end of the text box is reached.
Reference: This answer.

How does adding tags change a text file - Tkinter?

I am a beginner in Tkinter and I am currently trying to create an interface, which runs a program which checks for the 'correctness' of formal specifications. What this program does is checks whether a written specification meets certain criteria - checks whether all terms used are declared, etc.
Anyway, I am trying to add a search button, which changes the color of a key words, when they are found in the text - I use tags and it works ok. The problem is, after I have used my search function (i.e. have added tags and then removed them, when clicking on save) the program which checks for the 'correctness' of the specification doesn't run properly, even on files which have been checked and shown correct. I get the following error:
File "C:\Python27\lib\lib-tk\Tkinter.py", line 1193, in _configure
self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
TclError: invalid command name ".43965456"
I have no idea what this might mean. I was just wondering whether adding tags changes a text file in some way (apart from changing the color of key words), which might mess up with the program, which checks for 'correctness'.
Here is my Search function:
def searchWindowButton(self,event):
self.top = Toplevel()
self.top.title("Search")
Label(self.top,text='Text to find:').pack(side=LEFT)
self.edit = Entry(self.top)
self.edit.pack(side=LEFT, fill=BOTH, expand=1)
self.edit.focus_set()
button = Button(self.top, text="Search", command = self.on_button)
button.pack(side=LEFT)
def find(self):
self.txt.tag_remove('found', '1.0', END)
s = self.edit.get()
if s:
idx = '1.0'
while 1:
idx = self.txt.search(s, idx, nocase=1, stopindex=END)
if not idx: break
lastidx = '%s+%dc' % (idx, len(s))
self.txt.tag_add('found', idx, lastidx)
idx = lastidx
self.txt.tag_config('found', background='green', foreground='black')
self.edit.focus_set()
def on_button(self):
self.find()
self.top.destroy()
And this is my Save function:
def saveMenu(self):
try:
self.txt.tag_remove("found", "1.0", 'end')
fileName=self.fl
fl = open(fileName, 'w')
textoutput = self.txt.get(0.0, END)
fl.write(textoutput)
except:
self.save_asMenu()
I was just wondering whether adding tags changes a text file in some
way (apart from changing the color of key words)
No, adding tags has absolutely zero effect on a text file.
TclError: invalid command name ".43965456" means that a widget with the internal id of .43965456 has been destroyed prior to you calling whatever function triggers the error.

How can i make the browsed file the target for process, instead of it being hard coded in?

Instead of "mbox = ?????????" in the def start_processing(self) section, how do i make it the file that has been uploaded. This was originaly hard coded however have changed it to a file upload? Thanks
class App:
def __init__(self, master):
self.master = master
# call start to initialize to create the UI elemets
self.start()
def start(self):
self.master.title("Extract Email Headers")
self.now = datetime.datetime.now()
# CREATE A TEXT/LABEL
# create a variable with text
label01 = "Please select the .mbox file you would like to analyse"
# put "label01" in "self.master" which is the window/frame
# then, put in the first row (row=0) and in the 2nd column (column=1),
# align it to "West"/"W"
tkinter.Label(
self.master, text=label01).grid(row=0, column=0, sticky=tkinter.W)
# CREATE A TEXTBOX
self.filelocation = tkinter.Entry(self.master)
self.filelocation["width"] = 60
self.filelocation.focus_set()
self.filelocation.grid(row=1, column=0)
# CREATE A BUTTON WITH "ASK TO OPEN A FILE"
# see: def browse_file(self)
self.open_file = tkinter.Button(
self.master, text="Browse...", command=self.browse_file)
# put it beside the filelocation textbox
self.open_file.grid(row=1, column=1)
# now for a button
self.submit = tkinter.Button(
self.master, text="Execute!", command=self.start_processing,
fg="red")
self.submit.grid(row=3, column=0)
def start_processing(self):
date1= "Tue, 18 Jan 2015 15:00:37"
date2="Wed, 23 Jan 2015 15:00:37"
date1 = parser.parse(date1)
date2 = parser.parse(date2)
f = open("results.txt","w")
mbox = ????????????????????
count = 0
for msg in mbox:
pprint.pprint(msg._headers, stream = f)
tempdate = parser.parse(msg['Date'])
print(tempdate)
f.close()
print(count)
pass
def browse_file(self):
# put the result in self.filename
self.filename = filedialog.askopenfilename(title="Open a file...")
# this will set the text of the self.filelocation
self.filelocation.insert(0, self.filename)
I'm assuming you want to store the file path in a StringVar. TK uses special control variables to provide functionality for Entry objects. You can create a string control variable by calling the function tk.StringVar().
You want to create the variable when you initialize your UI, so in your start() method:
# CREATE A TEXTBOX
self.filepath = tkinter.StringVar() # This will hold the value of self.filelocation
# We set it to the "textvariable" option of the new entry
self.filelocation = tkinter.Entry(self.master, textvariable=self.filepath)
self.filelocation["width"] = 60
self.filelocation.focus_set()
self.filelocation.grid(row=1, column=0)
Now when we want to retrieve the value of it we use the get() method. In your start_processing() method:
# Here it opens the file, but you may want to do something else
mbox = open(self.filepath.get(),'r')
The way you set the value in your browse_file() method can be updated to use the control variable quite easily. Instead of inserting into the entry box directly, we'll set the value of our control variable and it will automatically update in the text entry field. In browse_file():
# this will set the text of the self.filelocation
self.filepath.set( self.filename )
Now you can properly set and retrieve the value of self.filelocation the intended way. You can change the name of self.filepath to whatever you want, of course.
For more information:
Tkinter 8.5 Reference - Control Variables
TkDocs - Tk Tutorial - Basic Widgets - Entry
I don't know for certain if mbox is supposed to be an open file, a list, a tuple, or some custom object. I'm going to assume it's an open file, since you have a function to pick a filename.
If that's the case, all you need to do is call the get method of the entry widget to get whatever the user typed:
mbox_name = self.filelocation.get()
mbox = open(mbox_name, "r")
for msg in mbox:
...

Categories