Tkinter ~ how to space out elements WITHIN a grid box - python

I am trying to space the following widgets out correctly so that they don't stack on top of eachother, but I don't know how to do it having them all within the same .grid() box. An example of the problem can be seen in the problem above.
To answer the question of why I can't just stick the widgets in the next row down: On the left, you can see that the listbox takes up significant vertical space. If I place the text widgets on the next row down, there will be a huge space inbetween the elements.
Here is the relevant code. For context, I am broadly working with receipts.
test.py
receipt_number_input = Label(text="Receipt Number: ")
receipt_number_input.grid(row=0,column=3)
receipt_number_entry = Entry()
receipt_number_entry.grid(row=0,column=4)
order_total_input = Label(text="Order Total: ")
order_total_input.grid(row=0,column=3)
order_total_entry = Entry()
order_total_entry.grid(row=0,column=4)
date_input = Label(text="Date: ")
date_input.grid(row=0,column=3)
date_entry = Entry()
date_entry.grid(row=0,column=4)
I have looked into padding and sticky to shift the text down and into a more usable format, but none of them in my experience have fixed the problem.
What would I have to pass into .grid() to shift these boxes into their correct positions?

Related

Fix Text widget size

I have made an application and part of it involves entering a question and answer. I have this code:
import tkinter as tk
root = tk.Tk()
root.geometry("500x250")
#Main question/answer frame
createFrm = tk.Frame(root)
createFrm.pack(expand = True) #To centre the contents in the window
#Create question entry area
cnqFrm = tk.Frame(createFrm)
cnqFrm.pack()
cnqFrm.pack_propagate(False)
#Question entry
cnqLabQ = tk.Label(cnqFrm, text = "Question")
cnqLabQ.grid(column = 0, row = 0)
#Frame for question Text
cnqTxtQFrm = tk.Frame(cnqFrm, height = 100, width = 100)
cnqTxtQFrm.grid(column = 0, row = 1)
cnqTxtQFrm.grid_propagate(False)
#Question Text
cnqTxtQ = tk.Text(cnqTxtQFrm)
cnqTxtQ.pack()
cnqTxtQ.pack_propagate(False)
#Answer entry
cnqLabA = tk.Label(cnqFrm, text = "Answer")
cnqLabA.grid(column = 1, row = 0)
#Frame for answer text
cnqTxtAFrm = tk.Frame(cnqFrm, height = 100, width = 100)
cnqTxtAFrm.grid(column = 1, row = 1)
cnqTxtAFrm.grid_propagate(False)
#Answer Text
cnqTxtA = tk.Text(cnqTxtAFrm)
cnqTxtA.pack()
cnqTxtA.pack_propagate(False)
Despite the fact the Text widget is in a Frame with grid_propagate(False) and a fixed height and width, and the Text widget itself has pack_propagate(False), it still expands to far larger than it should be. Why is this and how can I fix it?
You don't give the text widget an explicit size, so it defaults to 40x80 average-sized characters. The most common way to force it to a specific size that is determined by its parent is to give it a size that is smaller than the containing widget, and then let grid or pack expand it to fit the space given to it. So, start by giving the text widget a width and height of 1 (one).
Next, in this specific case you are calling grid_propagate(False) on the containing frame, but you are using pack to manage the window. You should call pack_propagate if you're using pack. You also need to tell pack to expand the text widget to fill its frame.
Finally, there's no point in calling cnqTxtQ.pack_propagate(False) since that only affects children of the text widget and you've given it no children.
All of that being said, I strongly encourage you to not use grid_propagate(False) and pack_propagate(False). Tkinter is really good at arranging widgets. Instead of trying to force the text widget to a specific pixel size, set the text widget to the desired size in lines and characters, and let tkinter intelligently arrange everything else to line up with them.

Python tkinter main window improper size when .grid() widgets

I have a game board which is rows x columns list.
Min size is 2x2 and max 10x10, with unequal rows:columns being okay (e.g. 2x3, 4x9).
Main window object has no predetermines geometry size setting, and widgets (buttons) are being .grid() in it for each list element in a was that creates a 2D map.
Ideally, given the method used this would lead to a nice, edge=to-edge map inside the main window.
Unfortunately, testing has shown that while this is true for maps with columns count > 3, when columns <= 3 then the window seems to default to a certain X-size, where this ugly free space is present at the right of the window.
This is not the case for Y-axis, which is defined by rows.
Note that buttons placed are fixed 32x32 px (determined by image inside).
def createMap (): #creates rows x columns 2D list - a map
global rowsEntryVar, columnsEntryVar, mapList
mapList = []
for row in range(rowsEntryVar):
tempList = []
for column in range(columnsEntryVar):
tempList.append(Button(root, bd=0, bg=redMagenta, activebackground=redMagenta))
mapList.append(tempList)
and then:
def drawMap ():
global mapList
for row in range(len(mapList)):
for column in range(len(mapList[row])):
mapList[row][column].grid(row=row, column=column)
Image:
Image showing the problem
Please go easy on me, I'm quite new to programming. :)
This appears to be a platform-specific limitation. I can't duplicate the problem on my Mac, but I can on a windows VM. Apparently, Windows won't allow the width of the window to be smaller than the space required for the buttons and icon on the titlebar.
My advice is to give the rows and columns a positive weight so that they will grow to fit the window, and then use the sticky option to cause the buttons to fill the space given to them.
when columns <= 3 then the window seems to default to a certain X-size,
Tkinter defaults to the size of the widgets so you must be setting the geometry for "root" somewhere. The following works fine on my Slackware box (and using a function as a function eliminates the globals). If you are just starting, then it is good to form good habits, like conforming to the Python Style Guide https://www.python.org/dev/peps/pep-0008/ (variables and functions are all lower case with underlines).
from Tkinter import *
def create_map (rowsEntryVar, columnsEntryVar): #creates rows x columns 2D list - a map
mapList = []
for row in range(rowsEntryVar):
tempList = []
for column in range(columnsEntryVar):
tempList.append(Button(root, text="%s-%s" % (row, column),
bd=0, bg="magenta2", activebackground=r"magenta3"))
mapList.append(tempList)
return mapList
def draw_map(mapList):
for row in range(len(mapList)):
for column in range(len(mapList[row])):
mapList[row][column].grid(row=row, column=column)
root = Tk()
map_list=create_map(4, 3)
draw_map(map_list)
root.mainloop()

Make text in a tkinter Listbox fit inside?

I've looked everywhere for a fix to this. I stumbled across this:
How to fit Tkinter listbox to contents
But this question is asking the reverse of what I want. I want the box to remain the size I've set it to, but the text runs off of the side like in the screenshot from the above linked question. Is there anyway to force a \n to be added to the string once its character count reaches the length of the listbox?
Also, I apologize if something is wrong with the format of my question, I've never posted here before.
class Stars(Tk):
def __init__(self):
Tk.__init__(self)
self.feed = Listbox(self, width = 55 , height = 31, relief = SUNKEN, borderwidth = 3)
self.feed.grid(row = 1, column = 2, columnspan = 2)
def simulate(self):
self.mass = eval(self.massEntry.get())
self.feed.insert(END, 'Star to be created with mass of {} * 10^30 kg; {} solar masses.'.format(1.98855 * self.mass, self.mass))
self.feed.insert(END, '0 years: Protostar formed in an interstellar gas cloud, and begins to compress due to gravity.')
This is all of the relevant code (trying to make a stellar evolution simulation). This is what it looks like when run, with the problem circled in red:
http://imgur.com/dZCYe6s
No, there is no way to have a Listbox wrap the text. If you want to support wrapping, use a Text widget. If you want to select items like in a listbox, you can add some custom bindings to do that.

Generating Radiobutton grid menu from an Array in Tkinter, Python

Edit: Sorry I can't answer my own post since I'm new but I figured it out: If you remove the line "tki.Button(master,..." (2nd to last code line), then the code runs perfectly fine. I guess the grid and the button don't work the way I put it.
sorry to bother but I'm having a little trouble figuring out what's off here. Basically I have an array that I want to loop through and set each of the values as a radiobutton IN A GRID. Later I'm going to loop through several arrays to generate a larger grid menu, but I can probably figure that out once I get this first loop working.
Here is my code:
import Tkinter as tki
master = tki.Tk()
frm = tki.Frame(master, bd = 16, relief = "sunken")
frm.grid()
tType = tki.StringVar()
tColumn = tki.IntVar()
tRow = tki.IntVar()
compType = ["iMac ", "Mac Mini ", "Mac Pro ", "Macbook ", "Macbook Air ", "Macbook Pro "]
tColumn.set(0)
tRow.set(0)
def radioCreate(typeArray):
for t in typeArray:
b = tki.Radiobutton(frm, text = t, variable = tType)
b.config(indicatoron = 0, bd = 4, width = 16, value = t)
b.grid(row = tRow.get(), column = tColumn.get())
tRow.set((tRow.get() + 1)) #increment tRow for next run-through
def p():
print tType.get()
radioCreate(compType)
tki.Button(master, command = p, text = "Display").pack()
master.mainloop()
Now remember, I'm trying to get this working in a grid, because I'm going to populate other columns with other data from different arrays.
The problem is i. These two lines:
frm.grid()
...
tki.Button(...).pack()
While it's perfectly acceptable to use pack and grid in the same application, you can't use them on two widgets that share the same master.
Grid will potentially change the size of a widget or its master depending on its options. Pack will notice the change, and may itself try to resize one or more widgets and/or the master based on its options. Grid will notice the change, and may resize... Pack will notice the change and resize, ...

Unexpected label width value

i am reading the width of a label at three different times and only one of them is producing the correct output.. code:
from tkinter import *
def getwidth(string):
print(string+str(lbl1.winfo_width()))
root = Tk()
lbl1 = Checkbutton(root, text="test text")
lbl1.grid(row=0,rowspan=2)
print("first "+str(lbl1.winfo_width()))
getwidth("second ")
btn = Button(root, text="GO", command=lambda x="third ": getwidth(x))
btn.grid(row=2)
root.mainloop()
How can i read the correct width (69) during the first two outputs without having to rely on the button command? Thanks
current outputs are:
first 1
second 1
third 69
Well, unfortunately, you can't. The first two times are done before the window is loaded (which causes it to return the default value of 1 since the label isn't drawn yet). The third time is done after the window is loaded (the label is drawn), so it returns the correct number.
You have to remember that, until you call root.mainloop and load the window, the widgets are not placed on the screen. Sure, they exist behind the scenes (otherwise a NameError would be thrown), but they are not on the screen and taking up space yet. Thus, when you try to see how much space they are taking up, you get the default number of 1.

Categories