Is there a way to get way to get an integer to a valid Tkinter text widget position. For example, 0 would get changed to '1.0' and if there were no characters on line 1, 1 would go to '2.0'. I am aware of the widget's index method however only to get the cursor position (in case this is the answer).
Many thanks
EDIT
I believe (having now done more research on the matter) that the Tk Text widget lazy loads the text (for performance). Therefore, without you getting the entire text contents and performing string actions on that, I don't think its possible (the Tk Text only has a minor understanding of what is above and below the currently viewed text - hence why a horizontal scrollbar will constantly adjust when scrolling vertically).
The text widget supports many modifiers to an index. For example, you can take an existing index and compute a new index by adding or subtracting characters or lines. For example, if you want to get the first 10 characters on line 2 you can use "2.0 + 10 chars" (or the more compact "2.0+10c"). If you want the 100th character in the text widget you can use "1.0+100c", etc.
In addition to characters, you can add or subtract lines (eg: "end-1line"), and you can get the start or end of a word or line (eg: "end-1c linestart" represents the first character of the last line in the widget).
You can read about all of the forms an index can take in the canonical tcl/tk documentation: http://tcl.tk/man/tcl8.5/TkCmd/text.htm#M7
Related
Re: wraplength for tkinter.Label, from the NM Tech docs by John Shipman: "You can limit the number of characters in each line by setting this option to the desired number. The default value, 0, means that lines will be broken only at newlines." Other sources agree that the units for wraplength are characters.
The code below seems to be breaking the line as if the units of wraplength were pixels, not characters. If I set wraplength to 10, for example, the label displays a column of text one or two characters wide. If I set wraplength to 20, the lines are 3 or 4 characters long.
In my application, the user will be creating his own simple widgets for custom forms, and it would be better if the wraplength option used character count for units instead of whatever it is doing. Since the NMTech docs are for Tkinter 8.5, but 8.6 is what comes with my Python 3.5, maybe that explains the difference, but I don't see docs for 8.6.
But ideally, lines should wrap at the nearest space between words with wraplength just used as a maximum line length. So if the user has to type in his own \n anyway, wraplength seems useless to me.
Summary: in a GUI for users who will be inputing option values for their own simple widgets, 1) is there a way to get Tkinter to accept characters for wraplength units and 2) can I get the line to break at the nearest space short of the wraplength with wraplength used as a maximum line length only, instead of an absolute line length?
Thanks for any solutions or suggestions.
import tkinter as tk
root = tk.Tk()
sib = tk.Label(root, text='Give em hell Harry', wraplength=10)
sib.grid()
root.mainloop()
Is there a way to get Tkinter to accept characters for wraplength units
No, there is not. If you need wrapping to happen at word boundaries you can use a text widget rather than a label.
Unless specified otherwise, the units are in pixels. From the canonical tk documentation:
wraplength For widgets that can perform word-wrapping, this option specifies the maximum line length. Lines that would exceed this length are wrapped onto the next line, so that no line is longer than the specified length. The value may be specified in any of the standard forms for screen distances. If this value is less than or equal to 0 then no wrapping is done: lines will break only at newline characters in the text.
In the above text, "any of the standard forms for screen distances" refers to the fact that you can use a suffix to specify the distance in printers points (eg: "72p"), centimeters (eg: "2.54c"), millimeters (eg: "1000m"), or inches (eg: "1i")
This answer is two years later, but if anyone that sees it and needs to wrap text in a label based on characters or words, you could use the textwrap module. It's very useful if you don't want words to get cut-off between lines but still want to keep them in a label widget.
I've been going through a tutorial on Python's Tkinter, I'm currently working with the Entry widget and just came across the insert method. I understand what the method does (makes a default string to appear in a text entry field), but what I don't get is the method's first parameter, which to the best of my understanding is the index at which the string should start. now here comes the confusion. When playing around with it, giving different values for the index parameter, every time I run it the text appears in the same spot (at the very beginning). So as best I can tell, I'm either doing something wrong or, I'm misunderstanding the documentation. this is a snippet from my code:
e1 = Entry(master)
e1.insert(0,"First Name")
When ever I run this, weather the index is 0, 10 or 100 the text "First Name" always appears at the very beginning of the text field
First, the statement "makes a default string appear" is not quite true. While it can be used to insert default text, it more correctly is described simply as inserting text, period. It can be default text, replacement text, additional text, whatever you want.
Any index that is before the first character is treated as 0 (zero). Any index that is after the last character is treated as being the end. When you insert something into an entry widget that is empty, every index is treated as 0. Thus, the index is mostly useful for inserting text somewhere into existing text.
For example:
e1 = Entry(master)
e1.insert(0, "hello")
e1.insert("end", "world")
e1.insert(5, ", ")
As an application programmer, you will almost never use the insert method with anything other than an index of 0 (zero) or "end", and perhaps "insert". However, when you try to add advanced functionality (eg: spell checking, auto-complete, etc) you will find the index to be highly useful.
In this line:
text.insert('1.0', 'here is my\ntext to insert')
The "1.0" here is the position where to insert the text, and can be read as "line 1, character 0". This refers to the first character of the first line. Historically, especially on Unix, programmers tend to think about line numbers as 1-based and character positions as 0-based.
The text to insert is just a string. Because the widget can hold multi-line text, the string we supply can be multi-line as well. To do this, simply embed \n (newline) characters in the string at the appropriate locations.
From the Tk docs https://tkdocs.com/tutorial/text.html
you use it when you use multiple insert() method
for example:
e = Entery(window)
e.pack()
e.insert(0, "Hi")
e.insert(2, " there")
Note : in " there" you should have a space behind it or else it will appear like this (Hithere)
There appears to be some concept of lines vs rows in terminal emulators, about which I'd like to know more.
Demonstration of what I mean by rows vs lines
The Python script below displays three lines of 'a' and waits, then with three lines of 'b'.
import sys, struct, fcntl, termios
write = sys.stdout.write
def clear_screen(): write('\x1b[2J')
def move_cursor(row, col): write('\x1b['+str(row)+';'+str(col)+'H')
def current_width(): #taken from blessings so this example doesn't have dependencies
return struct.unpack('hhhh', fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, '\000' * 8))[1]
clear_screen()
for c in 'ab':
#clear_screen between loops changes this behavior
width = current_width()
move_cursor(5, 1)
write(c*width+'\n')
move_cursor(6, 1)
write(c*width+'\n')
move_cursor(7, 1)
write(c*width+'\n')
sys.stdout.flush()
try: input() # pause and wait for ENTER in python 2 and 3
except: pass
If you narrow the terminal window width by one character during this break, you see
That seems pretty reasonable - each line has been separately wrapped. When we hit enter again to print bs,
Everything works as expected. I've used absolute cursor positioning, and written to the same rows I wrote to previously - which of course doesn't overwrite all of the a's, because many of them are on other rows.
However, when we narrow the window by one more character, the wrapping works differently:
Why did the second and third rows of b wrap together, and why did last line of a's merge with the first line of b's? A hint of why is in the top visible row above - we see two a's because theyse two rows are still linked - of course if we move the window again, that one line will continue to wrap the same way. This seems to be happening even for lines which we replaced a whole row of.
It turns out that the rows that had wrapped before are now linked to their corresponding parent rows; it's more obvious that they belong to the same logical line once we widen the terminal a lot:
My question
Practically, my question is how to prevent or predict this rows-being-combined-into-lines. Clearing the whole screen eliminates the behavior, but it would be nice to do this only for individual lines that need it if possible so I can keep the caching by line that is significantly speeding up my application. Clearing to the end of a row unlinks that row from the row below it, but clearing to the beginning of a row does not unlink that row from the one above it.
I'm curious - what are these line things? Where can I read about them? Can I find out which rows are part of the same line?
I've observed this behavior with terminal.app and iterm, with and w/o tmux. I imagine source-diving into any of these would yield an answer even if there's no spec - but I imagine there's a spec somewhere!
Background: I'd like to make a terminal user interface that can predict the way terminal wrapping will occur if the user decreases the window width. I'm aware of things like fullscreen mode (tput smcup, or python -c 'print "\x1b[?1049h"', which are what ncurses uses) which would work for preventing line wrap, but don't want to use it here.
Edit: made it more clear that I understand the overwriting behavior of the script already and want an explanation of the wrapping behavior.
OK. Let's start with the causes for the behavior you are seeing:
I tested your code and noticed that it only happened when you resized the window. When the window was left alone, it would write out the a's, and upon pressing enter would over-write them with b's (I assume that's the intended behavior).
What appears to be happening is that when you resize the window partway through, the line indices change, so that on your next iteration, you can't trust the same coordinates when you call move_cursor().
Interestingly, when you resize the window, the word wrapping pushes the text before the cursor upwards. I assume this is part of the terminal emulator's code (since we almost always want to retain focus on the cursor and if the cursor is at the bottom of the screen, resizing might obscure it beyond the window's height if the word-wrapping pushed it downwards).
You'll notice that after a resize when you hit enter, only two lines of a's remain visible (and not all 3). Here's what appears to be happening:
First we begin with the initial output. (line numbers added for clarity)
1
2
3
4
5 aaaaaaaaaaaaaaa\n
6 aaaaaaaaaaaaaaa\n
7 aaaaaaaaaaaaaaa\n
8
Note that there is a new line character at the end of each of these lines (which is why your cursor appears below the last despite your not having moved the cursor again)
When you shrink the window by one character, this happens:
1
2 aaaaaaaaaaaaaa
3 a\n
4 aaaaaaaaaaaaaa
5 a\n
6 aaaaaaaaaaaaaa
7 a\n
8
You'll notice what I mean by "pushing the text upwards"
Now when you hit enter and your loop reiterates, the cursor is sent to row 5 col 1 (as specified by your code) and is placed directly over the last a of the second line. When it starts writing b's it overwrites the last a of the second line with b's and the subsequent line as well.
1
2 aaaaaaaaaaaaaa
3 a\n
4 aaaaaaaaaaaaaa
5 bbbbbbbbbbbbbb\n
6 bbbbbbbbbbbbbb
7 bbbbbbbbbbbbbb\n
8
Importantly, this also overwrites the new-line character at the end of the second line of a's. This means that there is now no new-line dividing the second line of a's and the first line of b's, so when you expand the window: they appear as a single line.
1
2
3
4
5 aaaaaaaaaaaaaaa\n
6 aaaaaaaaaaaaaabbbbbbbbbbbbbb\n
7 bbbbbbbbbbbbbbbbbbbbbbbbbbbb\n
8
I'm not totally sure why this second line of b's also gets put together but it appears to likely have something to do with the fact that the line of a's which the first one overwrites is now missing it's own new-line termination. However, that's just a guess.
The reason why you get two characters of line-wrap if you try to shrink the window by yet another character is because now you are shrinking two halves of the same line of text, which means that one pushes on the other, causing two characters instead of one at the end.
For example: in these test windows I've shown, the width begins at 15 characters, I then shrink it to 14 and print out the b's. There is still one line of a's which is 15 chars long, and now a line of 14 a's & 14 b's which is line-wrapped at 14 chars. The same (for some reason) is true of the last two rows of b's (they are one line of 28 chars, wrapped at 14). So when you shrink the window by one more character (down to 13): the first line of 15 a's now has two trailing characters (15 - 13 = 2); the next line of 28 chars now has to fit in a 13 character-wide window (28 / 13 = 2 R2), and the same applies to the last b's as well.
0 aaaaaaaaaaaaa
1 aa\n
2 aaaaaaaaaaaaa
3 abbbbbbbbbbbb
4 bb\n
5 bbbbbbbbbbbbb
6 bbbbbbbbbbbbb
7 bb\n
8
Why does it work like this?:
This sort of stuff is the difficulty you run into when you are trying to run your program within another program that has the power to reposition the text as it sees fit. In the event of a resize your indices become unreliable. Your terminal emulator is trying to handle the realignment for you and is pushing the text before your prompt (which is fixed at row 8) up and down in the scroll-back to ensure you can always see your active prompt.
Rows and columns are something defined by the terminal/terminal emulator and it is up to it to interpret their location accordingly. When the appropriate control sequences are given it is the terminal which interprets them accordingly for proper display.
Note that SOME terminals do behave differently and in an emulated terminal there is often a setting to change what sort of terminal it is emulating, which may also affect how certain escape sequences respond. This is why a UNIX environment usually has a setting or environment variable ($TERM) which tells it which type of terminal it is communicating with so that it knows what control sequences to send.
Most Terminals use standard ANSI compliant control sequences, or systems based on the DEC VT series of Hardware Terminals.
In the Terminal.app preferences under Preferences->Settings->Advanced you can actually see (or change) which type of Terminal is being emulated by your window in the drop-down menu next to "Declare terminal as:"
How to overcome this:
You might be able to mitigate this by storing the last known width and checking to see if there has been a change. In which case you can change your cursor logic to compensate for the changes.
Alternately you might consider using escape sequences designed for relative cursor movement (as opposed to absolute) to avoid accidentally overwriting previous lines after a resize. There is also the ability to save and restore specific cursor locations using only escape sequences.
Esc[<value>A Up
Esc[<value>B Down
Esc[<value>C Forward
Esc[<value>D Backward
Esc[s Save Current Position
Esc[u Restore Last Saved Position
Esc[K Erase from cursor position to end of line
However you have no real guarantee that all Terminal emulators will deal with window resizes the same way (that's not really part of any terminal standard, AFAIK), or that it won't change in the future. If you are hoping to make a true terminal emulator, I suggest first getting your GUI window setup so that you can be in control of all resizing logic.
However if you want to run in a terminal-emulator window and deal with mitigating window resizes for a given command-line utility that you're writing. I'd suggest looking at the curses library for python. This is the sort of functionality used by all window-resize aware programs that I know of off the top of my head (vim, yum, irssi), and can deal with this sort of changes. Though I don't personally have any experience using it.
It's available for python via the curses module.
(and please, if you plan on redistributing your program, consider writing it in Python3. Do it for the children :D)
Resources:
These links might be helpful:
ANSI Escape Sequences
VT100 Escape Sequences
I hope that helps!
As 0x783czar pointed out, the key difference is whether an explicit newline was printed which caused the terminal to begin a new row, or there was an implicit overflow because there was no room left on the right to print the desired characters.
It's important to remember this at the end of each line for copy-pasting purposes (whether there'll be a newline character in the buffer or not), for triple-click highlight behavior in many terminals, and for rewrapping the contents when the window is resized (in those terminals that support it).
Applications running inside terminals hardly ever care about this difference, and they use the words "line" and "row" interchangeably. Hence, when we implemented rewrapping the contents on resize in gnome-terminal, we preferred the words "row" or "line" for one single visual line of the terminal, and the word "paragraph" for the contents between two adjacent newline characters. A paragraph wraps into multiple lines if it's wider than the terminal. (This is not by any means an official terminology, but IMO is quite reasonable and helps talk about these concepts.)
I am trying to create a statement which checks whether a TextBox is empty, if that is not the case and the TextBox is not empty, then I want the textBox to refresh itself.
I have tried the following:
if (len(self.txtBox.get() != 0)):
self.txtBox.update()
print "Textbox was not empty"
However I am given the following error, 'Type Error: get() takes at least 2 arguments, 1 given '. I know the error indicates that I should pass an argument in the get function, however I have seen code snippets using the get() function without passing any arguments, and either way I do not know what argument I should pass.
Any help would be greatly appreciated.
There is no widget called a "TextBox", so I don't know if you're talking about an Entry widget or a Text widget. The get method of the entry widget can be called without parameters, but the get method of the text widget requires two parameters. The two parameters are the starting and ending points of a region.
To get everything in a text widget, you should do it like this:
self.txtBox.get("1.0", "end-1c")
The "1.0" represents the first character, and "end-1c" represents the last character ("end") minus one character ("-1c") which will ignore the trailing newline that is always added by tkinter itself.
This old message from the python-tutor list might help. The two parameters are bizarre (to my mind: I am not a Tk expert) pointers, similar to string slicing but with the "pointers" being decimal numbers where the integer portion specifies the line and the decimal places specify the character numbers.
This is an assignment, i have put good effort since i am new to python programming:
I am running the following function which takes in image and phrase (spaces will be removed so just text) as arguments, i have already been given all the import and preprocessing code, i just need to implement this function. I can only use getpixel, putpixel, load, and save. That is why coding this has been a hard task for me.
def InsertoImage(srcImage, phrase):
pix = srcImage.load()
for index,value in enumerate(phrase):
pix[10+index,15] = phrase[index]
srcImage.save()
pass
This code is giving "system error" which says that "new style getargs format but argument is not tuple"
Edit:
C:\Users\Nave\Desktop\a1>a1_template.py lolmini.jpg Hi
Traceback (most recent call last):
File "C:\Users\Nave\Desktop\a1\a1_template.py", line 31, in <module>
doLOLImage(srcImage, phrase)
File "C:\Users\Nave\Desktop\a1\a1_template.py", line 23, in doLOLImage
pix[10+index,15] = phrase[index]
SystemError: new style getargs format but argument is not a tuple
Edit:
Ok Thanks, i understood and now posting code but i am getting error for the if statement not sure why the if statement is not working, here is full code sorry for not adding it entirely before:
from future import division
letters, numbers, and punctation are dictionaries mapping (uppercase)
characters to Images representing that character
NOTE: There is no space character stored!
from imageproc import letters, numbers, punctuation, preProcess
This is the function to implement
def InserttoImage(srcImage, phrase):
pix = srcImage.load()
for index,value in enumerate(phrase):
if value in letters:
pix[10+index, 15] = letters[value]
elif value in numbers:
pix[10+index, 15] = numbers[value]
elif value in punctuation:
pix[10+index, 15] = punctuation[value]
srcImage.save()
pass
This code is performed when this script is called from the command line via:
'python .py'
if name == 'main':
srcImage, phrase = preProcess()
InserttoImage(srcImage, phrase)
Thanks, letter, numbers, and punctuation are dictionaries which see the key element and open the image (font).
But still there is an issue with pix[10+index, 15] as it is giving error:
pix[10+index, 15] = letters[value]
SystemError: new style getargs format but argument is not a tuple
You seem to be confusing two very different concepts. Following from the sample code you posted, let's assume that:
srcImage = A Python Image Library image, generated from lolmini.jpg.
phrase = A string, 'Hi'.
You're trying to get phrase to appear as text written on top of srcImage. Your current code shows that you plan on doing this by accessing the individual pixels of the image, and assigning a letter to them.
This doesn't work for a few reasons. The primary two are that:
You're working with single pixels. A pixel is a picture element. It only ever displays one colour at a time. You cannot represent a letter with a single pixel. The pixel is just a dot. You need multiple pixels together, to form a coherent shape that we recognize as a letter.
What does your text of Hi actually look like? When you envision it being written on top of the image, are the letters thin? Do they vary in their size? Are they thick and chunky? Italic? Do they look handwritten? These are all attributes of a font face. Currently, your program has no idea what those letters should look like. You need to give your program the name of a font, so that it knows how to draw the letters from phrase onto the image.
The Python Imaging Library comes with a module specifically for helping you draw fonts. The documentation for it is here:
The ImageFont Module
Your code shows that you have the general idea correct — loop through each letter, place it in the image, and increment the x value so that the next letter doesn't overlap it. Instead of working with the image's pixels, though, you need to load in a font and use the methods shown in the above-linked library to draw them onto the image.
If you take a look at the draw.text() function in the linked documentation, you'll see that you can in fact skip the need to loop through each letter, instead passing the entire string to be used on the image.
I could've added sample code, but as this is a homework assignment I've intentionally left any out. With the linked documentation and your existing code, you hopefully shouldn't have any troubles seeing this through to completion.
Edit:
Just read your comment to another answer, indicating that you are only allowed to use getpixel() and putpixel() for drawing onto the source image. If this is indeed the case, your workload has just increased exponentially.
My comments above stand — a single pixel will not be able to represent a letter. Assuming you're not allowed any outside source code, you will need to create data structures that contain the locations of multiple pixels, which are then all drawn in a specific location in order to represent a particular letter.
You will then need to do this for every letter you want to support.
If you could include the text of the assignment verbatim, I think it would help those here to better understand all of your constraints.
Actually, upon further reading, I think the problem is that you are trying to assign a character value to a pixel. You have to figure out some kind of way to actually draw the characters on the image (and within the images boundaries).
Also as a side note since you are using
for index,value in enumerate(phrase):
You could use value instead of phrase[index]
My suggestion to the general problem is to create an image that contains all of the characters, at known coordinates (top, bottom, left, right) and then transfer the appropriate parts of the character image into the new output image.
Just try this:
pix[10+index:15] = letters[value]
Use ":" instead of ","