Trying my best at learning Tkinter and failing miserably every step of the way.
I'm trying to write a GUI that creates me either a drop-down list, either a button grid, based on row entries in a .csv file that my script imports.
Thing is, both attempts went up in flames, as I am new to this.
Can anyone link me to a resource/ give me the steps for achieving what I want?
Here is the solution. Next time please paste some code you've written so we know what exactly you've tried.
import tkinter as tk
dataMatrix = [] #this will be 2d list containing csv table elements as strings
with open("filename.csv") as file:
for row in file:
dataMatrix.append(row[:-1].split(";"))
mainWindow = tk.Tk()
######## EITHER THIS
optionMenus = []
strVars = []
for i in range(len(dataMatrix)):
strVars.append(tk.StringVar(mainWindow, dataMatrix[i][0]))
#variable containing currently selected value in i-th row, initialised to first element
optionMenus.append(tk.OptionMenu(mainWindow, strVars[-1], *dataMatrix[i])) #drop-down list for i-th row
optionMenus[-1].grid(row = 0, column = i) #placing i-th OptionMenu in window
######## OR THIS
for r in range(len(dataMatrix)):
for c in range(len(dataMatrix[r])):
b = tk.Button(mainWindow, text = dataMatrix[r][c], width = 10)
#creating button of width 10 characters
b.grid(row = r, column = c)
OptionMenu
Control Variables
Generally infohost.nmt.edu and effbot.org are the best resources for learning tkinter.
Related
I'm trying to change the color of text in a text widget similar to how a VS code changes the code colors:
Like this:
To do this I have a Text widget and I have a function handleCodeEditor bound to <KeyRelease> that will split the text widget's contents by row and then conditionally modify text colors. To make the process less exhaustive I would like to only modify the current row, but I am not aware of any method that would give me the row number.
Any ideas?
def getrow(event):
index = textbox.index(INSERT)
row = index.split(".")[0]
print(row)
root = Tk()
textbox = Text(root)
textbox.bind("<KeyRelease>",getrow)
textbox.pack()
root.mainloop()
This function will print the current line number whenever a key is released.
Hope this is what you're after.
I have a table (grid) I'm creating, constituted of labels.
These labels are showing elements I'm adding to a list, therefore, when I add a new obj to the list, the table will grow, showing more labels.
My intent is that I can click a label and have it print the row of the table that that label is in.
import tkinter as tk
phrasesdb = []
def debug(event):
#this is where I'd have it print the row
#but how do I get it?
#for example, it the label I pressed was in the row 2, it'd print 2
print( ??? )
#Add obj to list
def addline():
##This creates new obj with values from the input fields, and inserts it in the list
newobj = {"text": newtext.get()} #This is getting the text from an Entery
phrasesdb.append(newobj)
##This shows new obj on the table
newesttext = tk.Label(tableframe, text=newobj["text"])
newesttext.grid(row=len(phrasesdb), column=1, sticky=tk.W)
newesttext.bind("<Double-Button-1>", debug)
I'm already able to show them in the table, and to have it recognize I'm pressing the correct label (tested with a simple print("yup, this is it") ), but I'm not being able to figure out how to access the row of the label I'm clicking.
I'm kinda new to python and especially tkinter, so sorry if this is a really easy question, but I'm not finding the answer anywhere else...
You can use the grid_info method which will return a dictionary of the item's grid attributes.
def debug(event):
widget = event.widget
info = widget.grid_info()
row = info['row']
If I understand your problem correctly using .grid_info()['row'] on the label you already received after clicking should return the result you need.
I am creating a memory game and am running into a problem with the buttons. I create the buttons using a for loop and place them into an array, but I have no idea how to call a definition OnButtonClick for each one of them. Each button should have a random picture, chosen from eight options, with no more than two duplicates.
from Tkinter import *
import Tkinter
import random
root = Tkinter.Tk()
poop=PhotoImage(file="poop.gif")
apple=PhotoImage(file="apple.gif")
earth=PhotoImage(file="earth.gif")
fish=PhotoImage(file="fish.gif")
frowny=PhotoImage(file="frowny.gif")
heart=PhotoImage(file="heart.gif")
smiley=PhotoImage(file="images.gif")
water=PhotoImage(file="water.gif")
back=PhotoImage(file="card back.gif")
images = [poop,apple,earth,fish,frowny,heart,smiley,water]
row = 1
column = 0
buttonList=[]
def OnButtonClick():
self.Button.config(image=random.choice(images))
for i in range(16):
buttonList.append(Button(root, image=back, width=150,height=250,command=OnButtonClick()))
buttonList[i].grid(row = row,column = column)
column += 1
if column == 4:
column = 0
row += 1
root.mainloop()
How would I go about changing the picture when the buttons are pressed?
I didn't check if your code is working properly but if it works than you have to do something like this:
def OnButtonClick(number):
buttonList[number].config(image=random.choice(images))
for i in range(16):
buttonList.append(Button(root, image=back,width=150,height=250,command=lambda e=i: OnButtonClick(e)))
buttonList[i].grid(row=row, column=column)
I am beginning GUI in Python 3.5, and I am trying to setup a simple qwerty keyboard. Based on the examples, I tried the following code
from tkinter import Tk, Label, RAISED, Button, Entry
self.window = Tk()
#Keyboard
labels = [['q','w','e','r','t','y','u','i','o','p'],
['a','s','d','f','g','h','j','k','l'],
['z','x','c','v','b','n','m','<']]
n = 10
for r in range(3):
for c in range(n):
n -= 1
label = Label(self.window,
relief=RAISED,
text=labels[r][c])
label.grid(row=r,column=c)
continue
This gives me the first row, but it does not return anything else. I tried simply using 10 as the range, which created the first two rows of the keyboard, but it still did not continue onto the last row.
Your issue is in the line n -= 1. Every time a label is created, you make n one less- after the first whole row, n==0, and thus the range is 0>0, and ranges never include the high bound- for c in range(0) will just drop from the loop (as it has looped through all the nonexistent contents).
A better solution involves iterating through the lists instead of through the indexes- for loops take any iterable (list, dictionary, range, generator, set, &c.);
for lyst in labels:
# lyst is each list in labels
for char in lyst:
# char is the character in that list
label = Label(... text=char) # everything else in the Label() looks good.
label.grid(...) # You could use counters for this or use ennumerate()-ask if you need.
# The continue here was entirely irrelevant.
Is this what you want it to do? Let me know if you need me to explain it further but basically what I'm doing is first filling the columns in each row. So row remains 0 and then as I loop through the column (the inner list) I fill in each of the keys, then on to the next row and etc.
from tkinter import Tk, Label, RAISED, Button, Entry
window = Tk()
#Keyboard
labels = [['q','w','e','r','t','y','u','i','o','p'],
['a','s','d','f','g','h','j','k','l'],
['z','x','c','v','b','n','m','<']]
for r in labels:
for c in r:
label = Label(window, relief=RAISED, text=str(c))
label.grid(row=labels.index(r), column=r.index(c))
window.mainloop()
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()