Selecting/Invoking Menubuttons in python Tkinter - python

I am trying to create a menu that calls an SQL database for life expectancy data across the world. I'm having trouble with menubutton, as I want to set the first value ("No region selected") as my initial value. There are three things that I want: how to set the value as the default (ideally the equivalent of invoked for normal radiobuttons), is there a better for what I want option than menubutton, and is there a decent web page that can help me (tutorialspoint got me in the initial direction, but unless I'm looking in the wrong places, I can't find how to select and invoke radiobuttons in the menubutton).
Ideally, I'd also like advise on how not to have the menuoptions stretch up everywhere too. A limit of say 20?
Here's a look of a somewhat stripped down piece of my code.
Edit: I've found a solution to my select/invoke issues. mb.menu.invoke(0)
from Tkinter import *
import tkMessageBox
import Tkinter
import sqlite3
def selectStuff():
selectionRegion = str(countryVar.get())
mb.config(text = selectionRegion)
sqlLocation = 'AllData/LifeExpectency.sqlite'
sqlDB = sqlite3.connect(sqlLocation)
cursor = sqlDB.cursor()
root = Tk()
frameCountry = Frame(root)
frameCountry.pack()
stringCountry= StringVar()
labelCountry = Label(frameCountry, textvariable=stringCountry)
stringCountry.set("Select Region")
labelCountry.pack()
'''-------------------------------'''
mb = Menubutton ( frameCountry, relief=RAISED, activebackground="white")
mb.grid()
mb.menu = Menu ( mb, tearoff = 0 )
mb["menu"] = mb.menu
sql1 = "SELECT Country FROM total_lifespan"
countryVar = StringVar()
mb.menu.add_radiobutton ( label="No Region Selected", variable =countryVar,
value = "No Region Selected", command =selectStuff)
try:
cursor.execute(sql1)
results = cursor.fetchall()
for row in results:
mb.menu.add_radiobutton ( label=row, variable =countryVar, value = row,
command =selectStuff)
except:
print "Error: unable to retrieve data."
mb.pack()
'''-------------------------------'''
#Edit:
mb.menu.invoke(0)
#/Edit:
root.mainloop()
sqlDB.close()

Related

How can I pass the 2nd item of sqlite 3 table of every item in a label in tkinter python module?

I have a problem, the problem is i want to create a post-it tkinter gui app and I want to store all the posts the user create so they can open it when they rerun the app, so i used sqlite 3 module to achieve this, but im stuck at the moment when the user opens the existing post-its bcs it opens the last content of the for loop
In case u dont get it here is the code:
"""
from tkinter import *
import sqlite3
conn = sqlite3.connect("post-it.db")
row = 1
cursor = conn.cursor()
posts = cursor.execute("SELECT rowid, * FROM postits")
postsFetch = posts.fetchall()
print(f"{postsFetch}")
def createPost():
pass
def openPost(name):
print(name)
post = Tk()
text = Label(post,text=name)
text.pack()
post.mainloop()
window = Tk()
window.geometry("400x400")
window.config(bg="blue")
createNew = Button(text="Create new Post-it",command=createPost)
createNew.grid(column=1,row=1)
createName = Entry()
createName.grid(column=1,row=2)
frame = Frame()
frame.grid(column=2)
#the problem is at this for loop it opens the last item text
for postit in postsFetch:
postitBtn = Button(frame,text=postit[1],command=lambda: openPost(postit[2]))
postitBtn.grid(column=8,row=row)
row += 1
conn.commit()
window.mainloop()
conn.close()
"""
if u know the answer please help
Firstly, don't use Tk more than once in a program - it can cause problems later on. For all other windows, use Toplevel. Replace post = Tk() with post = Toplevel().The reason your loop doesn't work is explained here. To fix it, change your lambda function to lambda postit = postit: openPost(postit[2]))

Looping through tkinter buttons not working well

I'm writing a new code to display sqlite search results on buttons using loop. The code works correctly except for 1 issue. I wrote a function to change the background color and play a sound when hovering over the buttons. The problem is that when I hover over any button of the result, the color changes over the last button only although the sound plays without problem. Here's the part of the code involved:
SearchSelection = SearchVar.get()
SearchParameter = txtSearch.get()
conn = sqlite3.connect("EasyClinic.db")
cursor = conn.cursor()
cursor.execute ("SELECT * FROM patients WHERE (%s) = (?)" %(SearchSelection),
(SearchParameter,))
results = cursor.fetchall()
conn.commit()
conn.close()
if len(results) == 0:
print ("No result found")
else:
for result in results:
ResultsButtons = tk.Button(canvasResult, text=result, bg=BackGroundColor, fg=ForeGroundColor, relief='flat', font=MyFont2, width=65, height=2)
ResultsButtons.pack(pady=5)
def on_enter_results(result):
ResultsButtons['background'] = Hover
winsound.PlaySound ('Hover.wav', winsound.SND_ASYNC)
def on_leave_results(result):
ResultsButtons['background'] = BackGroundColor
ResultsButtons.bind("<Enter>", on_enter_results)
ResultsButtons.bind("<Leave>", on_leave_results)
Please for you assistance
Thanks
You can change the two functions, on_enter_results() and on_leave_results() as below:
def on_enter_results(event):
# event.widget is the widget that triggers this event
event.widget['background'] = Hover
winsound.PlaySound ('Hover.wav', winsound.SND_ASYNC)
def on_leave_results(event):
event.widget['background'] = BackGroundColor
Actually you can move these two functions outside of for loop (i.e. before the for loop).
The problem is that python always overwrites the ResultsButtons variable with the latest button, and this gives a problem when calling the funciton on_enter_results or on_leave_results, because it uses the overwritten ResultsButtons variable. The solution is to more directly specify the button and pass it to your function with a lambda function. I can't check if this code works because I don't have the full example, but something like this should work:
if len(results) == 0:
print ("No result found")
else:
def on_enter_results(event_info, resbutton):
resbutton['background'] = Hover
winsound.PlaySound ('Hover.wav', winsound.SND_ASYNC)
def on_leave_results(event_info, resbutton):
resbutton['background'] = BackGroundColor
for result in results:
ResultsButton = tk.Button(canvasResult, text=result, bg=BackGroundColor, fg=ForeGroundColor, relief='flat', font=MyFont2, width=65, height=2)
ResultsButton.pack(pady=5)
ResultsButton.bind("<Enter>", lambda event_info, btn=ResultsButton: on_enter_results(btn))
ResultsButton.bind("<Leave>", lambda event_info, btn=ResultsButton: on_leave_results(btn))
For a better understanding of what acually is going on: Python FAQ
It was solved by acw1668. Thank you all for your support

how to generate multiple buttons with a loop?

I have programmed software that displays a "tuile".
Definition of a tuile:
A tuile is a Frame which contains a button which displays an image and an explanatory text.
I would like to display 3 tuiles with 3 different settings.
listes_icones = ["icone1.png","icone2.png","icone3.png"]
listes_relx = [".3",".4",".5"]
listes_text = ["SYSTEM", "USER", "GAME"]
for i in range(3):
gen_img = PhotoImage(file=listes_icones[i])
gen_cadre = Frame(home,width=100, height=100,bg=bg_root)
gen_cadre.place(anchor="c", relx=listes_relx[i], rely=.5)
gen_img_bouton = Button(gen_cadre, image=gen_img, relief="flat",bg=bg_root)
gen_img_bouton.pack()
gen_text = Label(gen_cadre, text=listes_text[i], bg=bg_root, fg=text_color,font="blocktastic 18")
gen_text.pack()
I manage to display the text but not the button and the image, the variable is overwritten. How to solve this problem?
The problem that you are facing is like you said, the variable is overwritten in your loop. To solve this you need to keep track of your generated images. A simple solution is to store them in a list and get them in the next step. Here is an exampel:
import tkinter as tk
import PIL
listes_icones = ["icone1.png","icone2.png","icone3.png"]
gen_icons = []
listes_relx = [".3",".4",".5"]
listes_text = ["SYSTEM", "USER", "GAME"]
home = tk.Tk()
for i in range(3):
gen_img = tk.PhotoImage(file=listes_icones[i])
gen_icons.append(gen_img)
gen_cadre = tk.Frame(home,width=100, height=100)
gen_cadre.place(anchor="c", relx=listes_relx[i], rely=.5)
gen_img_bouton = tk.Button(gen_cadre, image=gen_icons[i], relief="flat")
gen_img_bouton.pack()
gen_text = tk.Label(gen_cadre, text=listes_text[i], font="blocktastic 18")
gen_text.pack()
home.mainloop()

Change the color of a single word in a tk option menu?

So I'm grabbing links of events off a website and putting them into a drop down menu to be selected. My code for the menu:
import Tkinter as tk
from Tkinter import StringVar
selectMenu = tk.Tk()
# #-> this is what I have
# Followed by what you can use
#var = Vars()
#events = var.GetVars('Event')
events = " "
options = []
links = []
#forms = (driver.find_elements_by_class_name("with-cats")) #This is what I have
forms = ["Yolo ","Dad? Closed","Anotha One","Normies! Closed"] #This is so you can try it for yourself
for x in forms:
#info = x.text
info = x #Again, this is so you can try it for yourself
if events in info.lower():
links.append(x)
for link in range(0,len(links)):
#options.append(links[link].text)
options.append(links[link])
list(set(options))
selection = []
for link in range(0,len(options)):
selection.append(options[link])
select = StringVar(selectMenu)
select.set("--None Selected--")
menu = tk.OptionMenu(selectMenu, select, *(selection))
msg = "Which one would you like to attend?"
label = tk.Label(selectMenu, text=msg, font="Helvedica 14")
label.pack(side='top', pady=10)
menu.pack(side="top", pady=10)
selectMenu.attributes('-topmost', True)
selectMenu.mainloop()
So this works fine and dandy, but I would like to improve the look to make it more obvious which events are open. To clarify, an event found that is open and put into the menu may look like "This is a cool event", but one that is closed would be read as "This is a cool event Closed". My aim is to be able to make the foreground red of either just the word Closed or the string containing Closed, whichever is possible if any (And I'm not sure if it's possible because menus and buttons on osx are usually defaulted to system settings, maybe there is a way around this?).
Current: Desired:
According to the documentation for OptionMenu here and here I don't think there is a way to set the color of text.
You might be able to get something close to what you want by using a listBox instead. See post here for the listBox example.
Found a solution! Using a Menu inside of a MenuButton the same way Tkinter creates MenuOptions, I was able to create a custom MenuOption. If you want to add more options, you can use the menbutton.configure() option to edit the button, and menbutton.menu to edit the menu items.
import Tkinter as tk
from Tkinter import Menu, Menubutton
class Vars():
global vari
vari = {}
def GetVars(self, var):
return vari.get(str(var))
def SendVars(self, var, val):
vari[str(var)] = val
class App():
def buttselect(self, link, menbutton, selectMenu):
var = Vars()
var.SendVars("Selection", link) # Store selected event
menbutton.configure(text=link) # Set menu text to the selected event
def prnt(self, link):
var = Vars()
print var.GetVars("Selection") # Print event
def __init__(self, selectMenu):
events = " "
options = []
links = []
forms = ["Yolo ","Dad? Closed","Anotha One","Normies! Closed"] #This is so you can try it for yourself
menbutton = Menubutton (selectMenu, text="--None Selected--", relief="raised")
menbutton.grid()
menbutton.menu = Menu (menbutton, tearoff=0)
menbutton["menu"] = menbutton.menu
#Get a list of event names
for x in forms:
info = x #Again, this is so you can try it for yourself
#If desired event keyword is in an event name, add it to the correct links
if events in info.lower():
links.append(x)
#Remove duplicates
for link in range(0,len(links)):
options.append(links[link])
list(set(options))
#Final list of event names turned into menu commands
for link in options:
if "Closed" in link:
menbutton.menu.add_command( label= link, command= lambda link=link: self.buttselect(link, menbutton, selectMenu), foreground='red')
else:
menbutton.menu.add_command( label= link, command= lambda link=link: self.buttselect(link, menbutton, selectMenu))
b = tk.Button(selectMenu, text="Selection", command= lambda link=link: self.prnt(link)) #Print selected event
b.pack()
msg = "Which one would you like to attend?"
label = tk.Label(selectMenu, text=msg, font="Helvedica 14")
label.pack(side='top', pady=10)
menbutton.pack(side="top", pady=10)
selectMenu = tk.Tk()
selectMenu.attributes('-topmost', True)
app = App(selectMenu)
selectMenu.mainloop()
This results in exactly the result desired:
I found a way!
Let's say x is an optionmenu with options:
options=['Red','Blue','Green']
defopt=tk.StringVar(options[0]) #StringVariable to hold the selected option.
x=tk.OptionMenu(self.optmenuframe,defopt,*options)
Now, get the menu object from the optionmenu and use entryconfig method. That's it!
x.children['menu'].entryconfig(0,foreground='red')
x.children['menu'].entryconfig(1,foreground='blue')
x.children['menu'].entryconfig(2,foreground='green')
#0 is the index of the option you want to apply the configurations to.

Cannot get LabelFrame widget to display on ttk notebook (python 3.5.1)

I have an application in which the (tkinter) LabelFrame widget on the tab of a ttk Notebook will not update itself. In a much simplified version of that program (code extracts below) the widget will not even appear on the GUI.
Everything else on the GUI works properly, including updating the tab titles, the application title (and icon) and updating the Label, Checkbutton and Radiobutton widgets on all notebook tabs. Using the ttk versions (e.g. ttk.LabelFrame) to create those widgets did not fix the issue. I also tried using “update_idletasks” (some think this as a kludge) immediately after updating the widget attributes without success.
In the real application the text of all GUI widgets change according to the state of a “choose language” widget on the same GUI (for details, see: Need Python/tkinter GUI to dynamically update itself (for a multi-lingual GUI)). It’s known that all GUI text values (the WidgetName["text”] attribute), including the missing LabelFrame, are being correctly updated to match the state of that widget.
Is there something “special” about LabelFrame widgets on Notebook tabs? What (probably simple) thing am I overlooking?
Also, any confirmation/denial by others will help determine if the problem is unique to my system - a distinct possiblity since my machine is administered by corporate IM (who don't have the best record when handling the needs of unusual users like me).
Thanks
The following is a complete example of the problem. When run, the LabelFrame widget (which should appear at (0, 0) of Tab 1) does not appear. Clicking on the "language" widget causes everything else to display text in the language selected by the "language" widget.
From “LanguageInUse.py” the code that switches languages:
import sys
c_cedille_lower = "\u00E7" # UTF 8/16 (code page 863?) French character
e_circumflex_lower = "\u00EA"
English = 'English' # string shown on GUI
Francais = 'Fran' + c_cedille_lower + 'ais' # string shown on GUI
DefaultLanguage = Francais
DefaultLanguage = English # comment out this line to use French
user_language = DefaultLanguage # set up language shown on GUI
# Define all language-dependent GUI strings (as "Application-Level" globals)
ComplianceMessage = None
ReportTab1Title = None
ReportTab2Title = None
XCheckbuttonMessage = None
XLabelFrameMessage = None
XLabelMessage = None
XRadioButtonMessage = None
'''=========================================================================='''
def SetGuiLanguage( user_language ) :
global ComplianceMessage, LanguagePrompt
global ReportTab1Title, ReportTab2Title
global XLabelFrameMessage, XCheckbuttonMessage, XLabelMessage, XRadioButtonMessage
if ( user_language == English ):
LanguagePrompt = "Language"
ReportTab1Title = "Message Counts"
ReportTab2Title = "Communications Compliance"
XLabelFrameMessage = "LabelFrame"
XCheckbuttonMessage = "Checkbox"
XLabelMessage = "Label"
XRadioButtonMessage = 'Radiobutton'
ComplianceMessage = "Compliance (engish)"
elif ( user_language == Francais ):
LanguagePrompt = "Langage"
ReportTab1Title = "Comtes de message"
ReportTab2Title = "Compliance Communications"
XLabelFrameMessage = "LabelFrame en " + Francais
XCheckbuttonMessage = "Checkbox en " + Francais
XLabelMessage = "Label en " + Francais
XRadioButtonMessage = "Radiobutton en " + Francais
ComplianceMessage = "Compliance - " + Francais
else:
print (' An unknown user language was specified' )
sys.exit()
return
'''=========================================================================='''
SetGuiLanguage( user_language ) # initialize all tkinter strings at startup
'''========================== End of File ================================'''
From “SelectReports.py”) the code that builds the notebook:
from tkinter import Checkbutton, Radiobutton, Label, LabelFrame, Frame
from tkinter import ttk
import LanguageInUse
# Locally defined entities importable by other modules (often
# Tkinter Application level objects whose language can be changed)
ComplianceMessageText = None
NotebookFrame = None
XCheckbutton = None
XLabel = None
XLabelFrame = None # NOT APPEARING ON THE GUI
XRadiobutton = None
'''=========================================================================='''
def TabbedReports( ParentFrame ) :
global ComplianceMessageText, NotebookFrame
global SelectReportFrame, UplinkFileWarningText
global XCheckbutton, XLabel, XLabelFrame, XRadiobutton
# Builds the notebook and it's widgits
NotebookFrame = ttk.Notebook( ParentFrame )
NotebookFrame.grid( row = 0, column = 1 )
Tab1Frame = Frame( NotebookFrame )
Tab2Frame = Frame( NotebookFrame )
NotebookFrame.add( Tab1Frame )
NotebookFrame.add( Tab2Frame )
# Create widgets on Tab 1
XLabelFrame = LabelFrame( Tab1Frame ) # NOT APPEARING ON GUI
XCheckbutton = Checkbutton( Tab1Frame )
XLabel = Label( Tab1Frame )
XRadiobutton = Radiobutton( Tab1Frame )
XLabelFrame.grid( row = 1, column = 0 ) # NOT ON GUI
XCheckbutton.grid( row = 1, column = 1 )
XLabel.grid( row = 2, column = 0 )
XRadiobutton.grid( row = 2, column = 1 )
XLabelFrame.configure( text = LanguageInUse.XLabelFrameMessage ) # NOT ON GUI
XCheckbutton.configure( text = LanguageInUse.XCheckbuttonMessage )
XLabel.configure( text = LanguageInUse.XLabelMessage )
XRadiobutton.configure( text = LanguageInUse.XRadioButtonMessage )
# .tab() gives same effect as .configure() for other widget types
NotebookFrame.tab( 0 , text = LanguageInUse.ReportTab1Title )
NotebookFrame.tab( 1 , text = LanguageInUse.ReportTab2Title )
# Create the only widget on Tab 2 (uses other method to specify text)
ComplianceMessageText = Label( Tab2Frame )
ComplianceMessageText.grid( row = 0, column = 0 )
ComplianceMessageText['text'] = LanguageInUse.ComplianceMessage
return
From “ChangeLanguageOnGui.py” the code that updates all notebook widgets:
import sys, os
from tkinter import StringVar, Radiobutton, PhotoImage
#from TkinterRoot import root
import LanguageInUse
import SelectReports
'''=========================================================================='''
def ChangeLanguageOnGui() :
SelectReports.XLabelFrame.configure( text = LanguageInUse.XLabelFrameMessage ) # NOT ON GUI
SelectReports.XCheckbutton.configure( text = LanguageInUse.XCheckbuttonMessage )
SelectReports.XLabel.configure( text = LanguageInUse.XLabelMessage )
SelectReports.XRadiobutton.configure( text = LanguageInUse.XRadioButtonMessage )
# .tab() gives the same effect as .configure() for other widget types
SelectReports.NotebookFrame.tab( 0 , text = LanguageInUse.ReportTab1Title )
SelectReports.NotebookFrame.tab( 1 , text = LanguageInUse.ReportTab2Title )
SelectReports.ComplianceMessageText['text'] = LanguageInUse.ComplianceMessage
'''=========================================================================='''
def SetUpGuiLanguage( LanguageFrame ) :
GUI_Language = StringVar( value = LanguageInUse.user_language )
#---------------------------------------------------------------------------
def switchLanguage():
LanguageInUse.user_language = GUI_Language.get()
LanguageInUse.SetGuiLanguage( LanguageInUse.user_language )
ChangeLanguageOnGui()
return
#---------------------------------------------------------------------------
UsingEnglish = Radiobutton( LanguageFrame, indicatoron = False,
variable = GUI_Language,
command = lambda: switchLanguage(),
value = LanguageInUse.English )
UsingFrancais = Radiobutton( LanguageFrame, indicatoron = False,
variable = GUI_Language,
command = lambda: switchLanguage(),
value = LanguageInUse.Francais )
UsingEnglish.grid( row = 0, column = 0 )
UsingFrancais.grid( row = 1, column = 0 )
UsingEnglish.configure( text = LanguageInUse.English )
UsingFrancais.configure( text = LanguageInUse.Francais )
From "TkinterRoot.py" the code that makes root importable everywhere (explictly importing this avoided problems such as IntVar() being unavailable during the intitialization phase of other modules):
from tkinter import Tk # possibly the worlds shortest useful python module
root = Tk() # makes root an importable "Application Level" global
And finally "A.py", the mainline file:
from TkinterRoot import root
from tkinter import LabelFrame
from tkinter import ttk
import ChangeLanguageOnGui, LanguageInUse, SelectReports, sys
LanguageFrame = None
if __name__ == "__main__":
LanguageChoice = LanguageInUse.English
if ( LanguageChoice == LanguageInUse.English ) :
LanguageInUse.user_language = LanguageChoice
elif ( LanguageChoice == 'Francais' ) :
LanguageInUse.user_language = LanguageInUse.Francais
else :
print( "Unknown Language: " + LanguageChoice )
sys.exit()
mainframe = ttk.Frame( root )
mainframe.grid( row = 0, column = 0 )
LanguageFrame = LabelFrame( mainframe, text = LanguageInUse.LanguagePrompt )
LanguageFrame.grid( row = 0, column = 0 )
ChangeLanguageOnGui.SetUpGuiLanguage( LanguageFrame )
SelectReports.TabbedReports( mainframe )
try:
root.mainloop()
except:
print( 'Exception Occurred' )
sys.exit()
The Environment is 64-bit Python 3.5.1, 64-bit Win 7 Enterprise SP 1, 64-bit Eclipse Mars 2 (the Java EE IDE edition) running 64-bit PyDev 5.1.2.201606231256. The "one user" (no admin rights) version of Pydev was used, this required Microsoft patch KB2999226 (part of Win10) to run on Win7. Eventual target distribution is a 32-bit Windows app (so it can run on 32 & 64 bit Windows) - and if/when time permits - Linux.
There’s one minor complication to keep in mind: In the real program several packages are being used. Inside each package each function is isolated inside its own file. All objects (e.g. the tkinter widgets) that must be externally visible (or need be shared amongst the package’s files) are declared in the package’s _ _ init.py _ _ file. Functions that must be externally visible are explicitly imported into _ _ init.py _ _ by using relative imports (e.g. “from .Function1 import Function1” ).
Your title is misleading because you place the LabelFrame in a Frame, not directly on a Notebook tab. Your problem is that the labelframe does not appear in its parent frame. The Notebook is irrelevant and all associated code should have been deleted before posting.
Even the frame is irrelevant in that the same problem arises when putting the labelframe directly in root. Here is minimal code that demonstrate both the problem and a solution.
from tkinter import Tk
from tkinter import ttk
root = Tk()
lf1 = ttk.LabelFrame(root, text='labelframe 1')
lf2 = ttk.LabelFrame(root, text='labelframe 2', width=200, height=100)
lf1.pack()
lf2.pack()
I ran this on Win 10 with 3.6a2, which comes with tk 8.6.4. Only lf2 is visible because the default size of a labelframe, as with a frame, is 0 x 0. A non-default size arises either from explicit sizing or from contents. Somewhat surprisingly, the label does not count as content and does not force a non-default size. I reproduced the same result with the labelframe in a frame (your situation) and on a tab.

Categories