Using Python 2.6.1 or 2.7.9 Tkinter's Entry widget entry.insert seems to destroy left justification. How do I get the text to stay left justified? Specifically, after entry.get() provides what I type into the widget, I insert additional text automatically at the beginning and redisplay it. When redisplayed, the justification ~sometime~ changes.
Specifically, when I enter a string of 20 characters or less and the entry.insert makes it longer, all works okay with some of the string not visible off the right side of the entry widget. When I enter a string of 21 characters or more and the entry.insert makes it longer, it displays
as right justified so only the end of the string is visible and "THANK YOU" is never seen off the left side.
Why does this happen and how do I get it to stop? I've considered Entry Widget Justification - text longer than widget but in his case, the OP wasn't doing an insert and the vague question didn't get a good answer.
The default displayed text size for an Enter widget is 20 characters, but the same phenomena happens for whatever Entry width I specify. Phenomena also happens on Raspberry Pi 3 and Mac OSX.
# demo of broken justification 1/14/2017
import Tkinter as tk
master = tk.Tk()
def clear_entry():
entry.delete(0, tk.END)
entry.config(bg='white')
def process_butninput():
if entry.get()=="done":
master.destroy()
else:
entry.insert(0,"THANK YOU ")
datarecord = entry.get()
print( "%s" % datarecord) # debugging only
entry.config(bg='green')
entry.after(1500, clear_entry)
label = tk.Label( master, text="Enter text: ")
label.grid(row=0, padx=20, pady=30)
entry = tk.Entry( master)
entry.grid( row=0, column=1)
button = tk.Button( master, text='Enter', command=process_butninput)
button.grid(row=0, column=3, padx=20, pady=30)
master.mainloop()
Text justify is only relevant for under-sized text strings. If the text string is longer than the Entry width, then justify does nothing and you have to use xview.
I was basing my expectations on how, for example, Word and Excel display justified text, but that is not how Tkinter displays text in the Entry widget.
I don't fully understand what you are asking, but it sounds like you simply need to scroll the contents of the entry widget after inserting "THANK YOU " so that the character at position 0 (zero) is visible.
entry.insert(0,"THANK YOU ")
entry.xview(0)
Related
Background
I have created a tk.Text widget within a frame and used grid to place successfully within my tool. I use methods to run read commands and display the results within the tk.Text widget. I only want the tk.Text to display the results nothing else and this works as expected.
resultsbox = tk.Text(centerframe)
resultsbox.config(state='disabled', bd='10', height=38, width=92, bg='black', fg='green', font='fixedsys')
resultsbox.grid(row=0, column=1, sticky="e")
I use the following commands on each method to enable my write results and then disable the user from typing into the tk.Text widget:
Example
resultsbox.config(state='normal')
resultsbox.insert(tk.INSERT, RESULT1, 'standard', "\n\n")
resultsbox.config(state='disabled')
This again works as expected.
Issue
The problem i have is if a user clicks within the tk.Text widget even though it is disabled the next result that the tool writes to the widget will place it at the point where the user last clicked causing a very unreadable result.
Example
This Screenshot shows if a user has clicked within the widget and then the next result is written.
Summary
Is it possible to stop the user pressing within the tk.Text widget?
You don't have to disable your mouse keys. It is very simple. You just replace tk.INSERT with tk.END. This tells where to insert. Either at the marked position or at the end. Check out my example. I created 2 buttons, one with tk.INSERT and one with tk.END.
import tkinter as tk
def insert_END():
RESULT1 = '---OUTPUT---'
resultsbox.config(state='normal')
resultsbox.insert(tk.END, RESULT1, 'standard', "\n\n")
resultsbox.config(state='disabled')
def insert_INSERT():
RESULT1 = '---OUTPUT---'
resultsbox.config(state='normal')
resultsbox.insert(tk.INSERT, RESULT1, 'standard', "\n\n")
resultsbox.config(state='disabled')
root = tk.Tk()
root.geometry("1000x1000")
frame1 = tk.Frame(root).grid(row=1, column=1)
resultsbox = tk.Text(frame1)
resultsbox.config(state='disabled', bd='10', height=38, width=92, bg='black', fg='green', font='fixedsys')
resultsbox.grid(row=0, rowspan=2, column=1, sticky="e")
btn1 = tk.Button(master=frame1, text='END', command=insert_END)
btn1.grid(row=0, column=0, padx=30)
btn2 = tk.Button(master=frame1, text='INSERT', command=insert_INSERT)
btn2.grid(row=1, column=0, padx=30)
root.mainloop()
The state 'disampled' is an editing property and means that the text cannot be modified. I do not know if that is linked with the cursor position issue you describe (as I get it).
If I understand it correctly the issue is that even when the textbox is disabled and you click in it, the position of the click is remembered and the next time you enable the textbox again it inserts the new text at the last position. If this is the issue, I would try to place the cursor at the end of the actual text (e.g. Text.insert(tk.END,...) before the disable command. When you change the status to 'enable' you can then add the new text to the end of the previous string. Alternatively I would try each time I enable the Text widget to check where the cursor is and move it to the end of the previous string.
Aspects of this question have most certainly been asked before, but not altogether.
[using Python 3.7.1]
Most of it works fine, but the display of scrolling textboxes is just not working... they expand southwards, and the scrollbar sliders never appear.
Never mind that the boxes and labels are too spaced apart.
Whilst it would probably be better to start again, with classes and use pack(), for the sake of understanding better, I would like to see if there's a way to make this work with grid() in a procedural way.
I can't quite get it over the line...
rtmiss = StringVar()
...
# COMMENT: Scrolled Text boxes
scrolledtext1_box=ScrolledText(framemain,width=30,height=10)
scrolledtext1_box.grid(sticky='n',row=5,column=0,rowspan=1,columnspan=1)
ttk.Label(scrolledtext1_box, anchor='w', width=20, text='placeholder', style='TLabel').grid(column=1, row=0, padx=0, pady=0)
tdiff_label = ttk.Entry(scrolledtext1_box, width=20, textvariable=tdiff)
scrolledtext1_box.insert(1.0, chars="anything")
tdiff_label.grid(column=0, row=5, columnspan=1, padx=1, pady=1)
no idea why this is so difficult. It's not the same as just inserting some free text into a textbox and adding a scrollbar.
tdiff_labelframe = ttk.LabelFrame(framemain, text='Title')
tdiff_labelframe.grid(column=0, row=5, padx=8, pady=4)
scrol_w = 30
scrol_h = 3
scrolledtext1_box = scrolledtext.ScrolledText(tdiff_labelframe, width=scrol_w, height=scrol_h, wrap=tk.WORD)
scrolledtext1_box.grid(sticky='nw',row=5,column=0,rowspan=1,columnspan=1)
tdiff_labelframe = ttk.Entry(framemain, width=20, textvariable=tdiff)
tdiff_entry = ttk.Entry(scrolledtext1_box, width=12, textvariable=tdiff)
tdiff_entry.grid(column=0, row=5)
I looked inside the Class Entry(Widget, XView) stuff...
def insert(self, index, string):
"""Insert STRING at INDEX."""
You can insert a string, as most examples will show you, as "my string", and I am able to get the contents of a StringVar into the text box, but only as a contiguous string, with no scrolling.
In the form in the codesnippet above...
tk.Tk()= myframe(root)
my_stringvar = stringvar()
my_labelframe = ttk.LabelFrame(my_frame, text='My Text')
my_labelframe = Entry(my_frame, textvariable = my_stringvar)
The scrolled stuff seems to be on a different "layer". The two blocks of code seem to be unconnected: apart from the scrolledtext function variable referring to the labelframe, which just sticks it onto that layer.
my_scrollheight = 20
my_scrollwidth = 10
my_scrolledtext = scrolledtext.ScrolledText(my_labelframe, width=my_scrollwidth, height=my_scrollheight, wrap=tk.WORD)
my_scrolledtext.grid(row=0,column=0,sticky='news')
So is the StringVar just being inserted into the Frame, above the LabelFrame layer, and thus not being "seen" by the scrolledtext part?
I have a partial answer to this, that I happened upon myself, but it creates a new problem. Before I could print the filepath, but not get the file contents to print and scroll; now it's the other way round.
I started off with this code:
Button errors in Tkinter - Selecting files, adding their paths
...trimmed it down to bare bones and one of each thing; then added my own junk, and eventually I accidentally made it work.
The solution to getting the text into a scrolled text widget, as in the question "how to send StringVar() content to Scrolled Text box using grid() and using procedural code?" is this:
def forkscroll():
mylabelframe = tk.LabelFrame(myframe, text='My Long Text:').pack()
scroll_w = 30
scroll_h = 10
myscrolledtext = scrolledtext.ScrolledText(mylabelframe, width=scroll_w, height=scroll_h, wrap=tk.WORD)
myscrolledtext.vbar.config(width=20)
myscrolledtext.grid(sticky='news', row=6, column=0, rowspan=1, columnspan=1, padx=5, pady=5)
# Open a dialogue; select and load a text file; read the text file directly into the scrolledtext widget (box)
fork = open(filedialog.askopenfilename(), 'r') # it's the same as "infile" above; but it's not a string
myscrolledtext.insert(tk.END, fork.read()) ### This reads the content of "fork" into the scrolledtext widget
...mainly those bottom two lines.
I then go on to put the file path of the selected text file into an Entry widget, but I can't get it to work... the best I could do was to keep the browsefunc and open the file twice... once for the path, and once for the contents... which is obviously a rubbish bodge.
forkit = StringVar()
forkit.set(fork.get())
mynormaltext = Text(mylabelframe, width = 10, height = 1, wrap=tk.WORD)
mynormaltext.grid(sticky='news', row=5, column=0, rowspan=1, columnspan=1, padx=5, pady=5)
mynormaltext.insert(tk.END, forkit.get())
# Even though it does the same thing in one line as it does in three, the outcome is not the same for scrolledtext
# forkname = filedialog.askopenfilename()
# forkfile = open(forkname, 'r')
# fork = forkfile.read()
# above... "fork" is a textIO, but Text can read it...
The best I can do, is get the content of the text file to fill the scrolledtext widget, and to print "<_IO.TextWrapper", as in the attached image. That's a bit better than just printing "PYVAR4" or something.
I have tried converting the "TextIO" or "PathLike" file into a string to inset it; I've tried doing set()/get(), and read() and lambda and tried putting things in different functions, and inputting and returning things, all sorts to try and get the filepath out again, but not really getting anywhere.
At least I've become more familiar with what some things do, and how, but I think I've hit a wall with the total solution, which is:
present 2 labels with 2 buttons each with a field: one as a text widget field; and scrolledtext widget field.
to click a button to browse to a text file, and read it into memory, and print its path into the text widget field
to click the other button, and print the contents of the same file into the scrolledtext field, and have it present within the frame, with a nice long scrollbar.
So I made a text editor using tkinter and I used tag_config to highlight the syntax.
CODE
#Importing modules
from tkinter import *
#Main Window
Window = Tk()
Window.minsize(400, 550)
##Main Script
#Defs
#Main frame
main = Frame(Window)
#Main text widget
text = Text(main, bd=0, highlightthickness=0, borderwidth=0, bg="#323232", fg="white")
#Configs
text.config(width=55, height=35)
main.config(width=55, height=35)
#Tag config for coloring syntax
text.tag_configure("import", foreground="yellow")
Window.update()
#Packs and places
#main.place(anchor="c", rely=.5, relx=.5)
main.pack(expand=True, fill=BOTH, side="right")
text.pack(expand=True, fill=BOTH)
#Update window
Window.update()
#Window.mainloop()
Window.mainloop()
PROBLEM
The tag_configure is not working in line 23
text.tag_configure("import", foreground="yellow")
QUESTION
Is there a way to fix that? or Is there any way to highlighting text in tkinter?
EDIT
I add
def check_syntax(event):
text.tag_add('import', 1.0, END)
text.bind("<Return>", check_syntax)
on the code but there are 1 problem, when i run the code and type
import tkinter in the text widget for test and press enter the "tkinter" is highlighted too
How to fix that?
I believe the reason is that you need to actually add a tag first using the tag_add function. You should read this other question with a similar problem. I tried out the code myself and it works. (You'll have to press enter after completing a line)
How to use tkinter tag_config? Python 3.7.3
tag_configure only configures a tag, it doesn't apply the tag to a range of text. To use the tag you need to call tag_add and give it a start and end index. That, or add the tag when calling insert.
For example, this shows how to insert some text, and then apply highlighting to a few characters:
text.insert("end", "import foo\nimport bar\n")
text.tag_add("import", "1.0", "1.6")
text.tag_add("import", "2.0", "2.6")
what is this error and how can i fix it?
def check_number():
if (len(txtNum1)!=11):
error_number = "the number that you entered is wrong"
msg = tk.Message(frame, text = error_number , fg="red")
msg.pack()
title = Label(frame, text="enter your number", fg="gray")
title.pack()
txtNum1 = Text (frame, height=1, width=30)
txtNum1.pack(side=tk.TOP)
button = tk.Button(frame,
text="chek",
fg="green",
command=check_number)
button.pack(side=tk.BOTTOM)
root.mainloop()
i just test __len__method but its not working well.
One of the issues in your code is use use of the if statement. You are asking if the Text Object has a length instead of checking the content of the text object. This can be corrected with the use of get(). If you use get() on a Text Box you will need to specify the indices. .get(1.0, "end"). The problem with doing it this way is you will be getting a length that is 1 character longer than what has been typed so the easy fix to this is to just use an entry field here.
With an Entry() field you can use get() without indices and it will get a copy of the text in that field. Keep in mind if you have a space before or after the text it will count that as well. If you want to compensate for this you can add strip() after get() to delete the white-space on either side.
For a little clean up you will want to change how you are creating your message. With your code if you press the button multiple times then the program will add a new message with each button press. This will cause the messages to stack. To avoid this lets create the message label first and then just update it with our function using the .config() method.
The next bit of clean up lets remove the variable assignments to widgets that do not need them. Your first label and the button do not need to be assigned to a variable in this case.
The last bit of clean up is making sure you are consistent with your widgets. Right now (based of your example code) you are importing tkinter twice. Once with from tkinter import * and once with import tkinter as tk. You do not need both and should stick with the 2nd import method only. Using import tkinter as tk will help prevent you from overriding build in methods on accident.
Take a look at my below code:
import tkinter as tk
root = tk.Tk()
def check_number():
msg.config(text = "")
if len(txtNum1.get().strip()) != 11:
error_number = "the number that you entered is wrong"
msg.config(text = error_number)
tk.Label(root, text="enter your number", fg="gray").pack()
txtNum1 = tk.Entry(root, width=30)
txtNum1.pack(side=tk.TOP)
tk.Button(root, text="chek", fg="green", command=check_number).pack(side=tk.BOTTOM)
msg = tk.Message(root, text = "" , fg="red")
msg.pack()
root.mainloop()
Here's a python/tkinter program that puzzles me. The window displays a ttk.Entry that is readonly and a tkinter.Text that is disabled. It programmatically selects one character in the Entry box and never changes this selection. However the selection will change if I try to select text in the other box (the disabledText). This doesn't seem right.
Python 3.5.0 and tcl/tk 8.5.18 on OS X
When you run the program, you can see the "A" highlighted in the Entry (upper) box.
Push the "Write Data" button a few times; the print statement will display the "A" that's selected in the Entry box.
Sweep the mouse over some text in the Text (lower) box; it won't be highlighted, but the highlighting in the Entry will disappear.
Push the "Write Data" button; the print statement will display the characters you swept with the mouse.
Those characters came from selection_get() on the Entry! You can tell that it got them from the Text because the two boxes have no characters in common.
If somebody can explain this, I'd be most grateful.
import tkinter
from tkinter import ttk
class ButtonPanel(ttk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.data = ttk.Entry(self, width=27, takefocus=False)
self.data.insert(0, "ABCDEFG")
self.data.select_range(0, 1) # select the "A"
self.data.state(["readonly"])
self.data.bind('<ButtonPress>', lambda e: 'break') # ignore mouse clicks
button = ttk.Button(self, text="Write Data", command=self.master.write)
self.data.grid(column=0, row=0, padx=10)
button.grid(column=1, row=0, padx=10)
def get(self):
return self.data.selection_get() # should always be the "A"
class App(ttk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.bp = ButtonPanel(self)
self.display = tkinter.Text(self, width=50, height=10, wrap="char", takefocus="False")
self.display.insert('end', "HIJKLMNOPQRSTUV")
self.display.config(state="disabled")
self.bp.pack()
self.display.pack()
def write(self):
char = self.bp.get() # should always be the "A"
print("this should be just one character: ->{}<-".format(char))
if __name__ == "__main__":
root = tkinter.Tk()
root.title("What's up here?")
App(root).pack()
root.mainloop()
What you are observing is the default behavior. Both of those widgets (as well as the listbox) have an attribute named exportselection, with a default value of True. When True, the widget will export the selection to be the primary selection. On old unix systems (where tcl/tk and tkinter got its start), you could only have one "primary" selection at a time.
The simple solution is to set this option to False for the text widget. This will allow your application to have multiple items selected at once, but only the entry widget exports the selection to the clipboard (which is required for selection_get to work.
...
self.display = tkinter.Text(self, ..., exportselection=False)
...
The other issue is that on OSX the selection won't show for a disabled text widget. The text is being selected, you just can't see it. More accurately, the selection won't show except when the widget has focus, and by default, it is not given focus when you click on it.