This happened after adding dropdown boxes and spinners. The text is a little off, on some too far too the left, on others the vertical space isn't uniform. Anyone have a similar issue or solution?
Note: The text on the left side is to show that it doesn't line up, but because off my work, it's purpously cut off.
Here's the code
test[iii].spinner = tki.Spinbox(frame, from_=0, to=test[iii].maxVal)
test[iii].spinner.grid(row=iii, column = 1)
test[iii].spinner.delete(0,"end")
test[iii].spinner.insert(0,test[iii].minVal)
tki.Label(frame,text=test[iii].label).grid(sticky = "W", row=iii, column = 0)
tki.Label(frame,text=" Mission Number: ").grid(sticky = "W", row=iii, column = 2)
test[iii].spinner = tki.Spinbox(frame, from_=0, to=999999999)
test[iii].spinner.grid(row=iii, column = 3)
tki.Label(frame,text=test[iii].label).grid(sticky = "W", row=iii, column = 0)
tki.Label(frame,text=test[iii].comment).grid(sticky = "W", row=iii, column = 2)
I'm assuming you are using .grid(). I have not tested this yet, however if you use
.grid(row=row, column=column, sticky=W)
it should work.
It might be because the text size is smaller than the cell and it is positioned at a non desired location inside it.
You could try fixing it with ipadx and ipady, two .grid() and .pack() attributes that do horizontal and vertical internal padding. These attributes can position the cell content in a different location inside it.
Related
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?
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.
I'm trying to create the some sample application.
Which first row is One label then input entry box then submit button.
Then second row has the another entry box.
My problem is when I increase width of the entry box in second row it affect the first row style. I don't know what is the problem.
import Tkinter
tk_obj = Tkinter.Tk()
tk_geo = tk_obj.geometry("1200x800")
Tkinter.Label(tk_obj, text='Enter query ').grid(row=1,column=1)
def callback():
print "hi"
E1 = Tkinter.Entry(tk_obj,bd=3,width=120)
E1.grid(row=1, column=2,ipady=3)
b = Tkinter.Button(tk_obj, text="Check", command=callback)
b.grid(row=1,column=3)
E2 = Tkinter.Entry(tk_obj,bd=3,width=100)
E2.grid(row=2,column=1,ipady=100)
tk_obj.mainloop()
The grid method places widgets in the center of the cell they inhabit. When you have two widgets of different sizes sharing a row or column, this means that there will be blank space around the smaller widget. To make the second Entry widget span the first two columns, use columnspan=2 when you grid() it. To left-align it within those two columns, use sticky='W':
E2.grid(row=2,column=1,ipady=100, columnspan=2, sticky='W')
You can then adjust that Entry widget's width attribute until it looks the way you want it to.
I'm working on a GUI in Python using tkinter. I'm reading a text file in and creating GUI elements dynamically based on the lines in the text file. I have an array for each of my element types, which include labels, radiobutton variables (StringVars), and colored circles (drawn with create_oval). My goal is that when the user changes a radiobutton from "not assigned" to "in" or "out", the colored circle on that line will change from yellow to green. Here's how the GUI looks after the text file has been read in:
Item 1: (o) in () out () not assigned (G)
Item 2: () in () out (o) not assigned (Y)
Currently, I have a trace on the radiobutton StringVars so that I can call a method whenever one of the buttons is changed. My problem is figuring out which radiobutton was changed so that I can change the color of the circle on that line...
I'm currently going the route of duplicating the whole radiobutton StringVar array into a temp global array. When the trace function is called, I compare the temp array with what's currently in the array to figure out where the change is. I duplicate the array with: temp_radiobutton_vars = list(radiobutton_vars), but I'm not sure if this is the right route. My temp list and the current list always show the same results when I get() the StringVar, even after I changed the button. Any ideas on how to fix this, or maybe there's a better method to do what I'm looking to do...
Sorry for the long and not great explanation. If anyone needs more info or snippets of code, let me know. Thanks!
There are many ways to solve this problem. Since you are already using variable traces, perhaps the simplest solution is to pass the index of the canvas item to the callback. You can use lambda or functools.partial for this task. You could also not use variable traces, but instead, associate a command with each radiobutton. In both cases you simply need to tell the callback which index to operate on.
In the following example, the callback takes a reference to a variable and the index to the canvas item. It fetches the value, looks up the color in a table, and then configures the canvas item:
def on_radiobutton(var, index):
value = var.get()
color = {"in": "green", "out": "red", "unassigned": "yellow"}
self.canvas.itemconfigure(index, fill=color[value])
This is how the trace is set up using lambda (note that name1, name2 and op are automatically sent by tkinter for every trace):
var = tk.StringVar()
rb0 = tk.Radiobutton(..., variable=var, value="in", text="in")
rb1 = tk.Radiobutton(..., variable=var, value="out", text="out")
rb2 = tk.Radiobutton(..., variable=var, value="unassigned", text="not assigned")
var.trace("w", lambda name1, name2, op, index=i, var=var:
on_radiobutton(var, index))
It sounds like you have the wrong idea with Radiobuttons. All "connected" Radiobuttons should have the same variable value; in this way, you can call theVariable.get() and compare that with the value of each Radiobutton; you shouldn't need a reference to every Radiobutton; nor should you have a StringVar for each Radiobutton, only each line.
Edit: I've expanded my example to show how this would work for more than one line. All that changed is now I check which line I have passed in my callback, and using that I know which line to update (in your case, which canvas to color). It's just some 2D list processing to check which Radiobutton is selected based upon which line is issuing the callback.
from Tkinter import *
root = Tk()
root.geometry("300x200+500+400")
lines = [StringVar(), StringVar()]
strings = [["Hello", "Stack", "Overflow"], ["Whats", "Going", "On"]]
buttons = [[],[]]
l1 = Label(root, text = "Selection: ", justify = LEFT)
l1.grid(column = 0, row = 0, sticky = NW, padx = (0, 250))
l1.grid_propagate(False)
l2 = Label(root, text = "Selection: ", justify = LEFT)
l2.grid(column = 0, row = 4, sticky = NW, padx = (0, 250))
l2.grid_propagate(False)
def process(line):
global l1, l2, strings, lines
if line == lines[0]:
# Since lines[0] was passed in to the callback, we know to update line 0;
# take that line's label (or canvas in your case)
updateLine = 0
updateLabel = l1
else:
# Otherwise take the other line
updateLine = 1
updateLabel = l2
# These operations are performed within if/elif/else to show how you coul
# choose a different process for each Radiobutton: example, coloring a canvas differently
if lines[updateLine].get() == strings[updateLine][0]:
# This means the first button of whatever line was selected
updateLabel.config(text = "Selection: %s" %strings[updateLine][0])
elif lines[updateLine].get() == strings[updateLine][1]:
# This means the second button of whatever line was selected
updateLabel.config(text = "Selection: %s" %strings[updateLine][1])
else:
# You get the idea
updateLabel.config(text = "Selection: Bet you thought I'd say %s" %strings[updateLine][2])
# Must have a seperate row number because with multiple lines, we can't simply use 'i' or 'j'
rowNum = 1
for i in range(len(lines)):
for j in range(len(strings[i])):
buttons[i].append(Radiobutton(root, text = strings[i][j], variable = lines[i], value = strings[i][j], command = lambda line = lines[i]: process(line)))
buttons[i][j].grid(column = 0, row = rowNum, sticky = NW)
rowNum +=1
rowNum += 2
root.mainloop()
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, ...