Drop-down menu is not showing in Combobox tkinter - python

I'm trying to make a drop-down menu for tkinter gui (Combobox). The code has no errors, but drop-down menu is not working. I'm using PyCharm, macOS. Please see the code below.
import tkinter as tk
from tkinter import ttk
import sys
class cpuMon(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.geometry('400x400+1+1')
self.attributes('-alpha', 1)
self.attributes('-topmost', True)
self.resizable(False, False)
self.title('CPU Monitor')
self.set_ui()
def set_ui(self):
exitButton = ttk.Button(self, text='Exit', command=self.exit_app)
exitButton.pack(fill=tk.X)
self.bar2 = ttk.LabelFrame(self, text='Manual')
self.bar2.pack(fill=tk.X)
ttk.Button(self.bar2, text='Move').pack(side=tk.LEFT)
ttk.Button(self.bar2, text='>>>').pack(side=tk.LEFT)
self.combo_win = ttk.Combobox(self.bar2, state='readonly', values=["hide", "don't hide", "min"])
self.combo_win.pack(side=tk.LEFT)

So, I had to fix a few things to get this to run (i.e., instantiating app = cpuMon() and calling app.mainloop(). Likewise, I replaced self.exit_app with self.quit since you didn't share the code for exit_app().
But without modifying the code otherwise, I see that the combo_win dropdown is populated with the values given.
If the issue you're having is that combo_win is empty by default (which it is, using the code given), you'll need to set the initial selection with:
self.combo_win.current(0) # set the combo box to 'hide' by default

Related

How do I get the Tkinter event-listener to work?

I'm using Tkinter for the GUI of a little tool I wrote with Python. Basically I just want a callback-method to be executed as soon as the contents of an entry widget have changed. This can be done with Tkinter's own variable classes (StringVar, BooleanVar, etc. - see documentation for details: http://effbot.org/tkinterbook/variable.htm).
So I couldn't get the mechanism to work and I found a snippet online, where it works perfectly fine. Now I'm trying to figure out why my version does not work.
As you can see in the two code examples the only difference is, that I'm using the event-listening functionality inside a class, whereas the snippet I found online only demonstrates it in a straight top-to-bottom manner.
Here's what I've already tried:
I instantiated the Tk instance directly in the constructor of my GUI class - same behaviour.
I inherited directly from the Tk class (instead of Frame) - same behaviour.
I placed the callback outside of the class - same behaviour.
The only idea I have is that the problem might be scope related, which I tried to verify.
Working code snippet:
from tkinter import *
import tkinter as tk
def text_changed(*args):
print("Text changed.")
top = tk.Tk()
string_listener = StringVar()
string_listener.set("Init Text")
string_listener.trace("w", text_changed)
entry_widget = tk.Entry(top, textvariable = string_listener)
entry_widget.pack()
top.mainloop()
Not working code snippet
from tkinter import *
import tkinter as tk
root = tk.Tk()
class GUI(tk.Frame):
def __init__(self, master=root):
super(GUI, self).__init__(master)
string_listener = StringVar()
string_listener.set("Init Text")
string_listener.trace("w", self.text_changed_callback)
entry_widget = tk.Entry(master, textvariable=string_listener)
entry_widget.pack()
def text_changed_callback(self, *args):
print("Text changed.")
gui = GUI()
gui.mainloop()
Like in the working example, my code ought to print Text changed., everytime a character is either deleted from or appended to the string in the extry-widget.
The problem is that string_listener is a local variable, and python is destroying the variable when __init__ finishes running. This doesn't happen in your original code since the variable is created in the global scope.
A simple solution is to save a reference as an attribute of the class:
import tkinter as tk
root = tk.Tk()
class GUI(tk.Frame):
def __init__(self, master=root):
super(GUI, self).__init__(master)
self.string_listener = tk.StringVar()
self.string_listener.set("Init Text")
self.string_listener.trace("w", self.text_changed_callback)
entry_widget = tk.Entry(master, textvariable=self.string_listener)
entry_widget.pack()
def text_changed_callback(self, *args):
print("Text changed.")
gui = GUI()
gui.mainloop()
note: I also changed StringVar to tk.StringVar so that I could remove the redundant wildcard import of tkinter.

how to call a function from another script inside an imported class

I am building my first larger Python application using tkinter, python 3.6.3 and window OS. The GUI for the application consists of a Notebook with several tabs. Each of the tabs in turn contains a Labelframe which in turn contains a number of other widgets.
When searching Stackflow, I found the idea to lets each labelFrame be a class. Thereafter import the class in the main.py and finally creating an instance of the class.
Now when pressing the button 'Start' in tab1 I would like to execute the 'printThis' function. Ideally I would like to use the function defined in the script main.py. It would also be interested in knowing how to call the 'printThis' method withing the Run_Test_Window class. Unfortunately I have not solved either problem.
Interestingly the program actually prints "Now successful" without that I do anything but when I press the 'Start'-button nothing happens.
Grateful for help! Thanks!
main.py
import tkinter as tk
import tkinter.ttk as ttk
import RunTestClass as RT
def printThis():
print('Successful')
root = tk.Tk()
note = ttk.Notebook(root)
note.grid()
tab1 = ttk.Label(note, width = -20)
note.add(tab1, text = " Run Test ")
window1 = RT.Run_Test_Window(tab1)
root.mainloop()
RunTestClass.py
import tkinter as tk
import tkinter.ttk as ttk
# from main import printThis
class Run_Test_Window:
def printThis(self):
print('Now successful!')
def __init__(self,tab1):
runTestLabelFrame = ttk.LabelFrame(text ="Run Test", padding =10)
runTestLabelFrame.grid(in_ = tab1, padx = 20, pady = 20)
self.startButton = ttk.Button(runTestLabelFrame, text="START",command=self.printThis())
self.startButton.grid(row=5, column=1, padx = 10)
If I'm correct you want the button to use the printThis() from main. This can be done by adding the following in your main:
rt = RT.Run_Test_Window(tab1)
rt.startButton.configure(command=printThis)
To call the printThis() defined in RunTestClass, use (also in main)
rt.printThis()
Note: leave the brackets when creating the button in the command argument. So change it to this:
self.startButton = ttk.Button(runTestLabelFrame, text="START",command=self.printThis)

TKInter checkbox variable is always 0

I'm using Python's TkInter module for a GUI. Below is a simple checkbox code.
def getCheckVal():
print cbVar.get()
windowTime=Tk.Tk()
cbVar = Tk.IntVar()
btnC = Tk.Checkbutton(windowTime, text="Save", variable = cbVar, command=getCheckVal)
btnC.grid()
windowTime.mainloop()
This code works fine. Each time I tick the checkbox, I get 1, else 0.
However, when I run the same code in a function that is called from another TkInter command (when a button is pressed), it stops working. I always get 0 as the value.
class GUIMainClass:
def __init__(self):
'''Create the main window'''
self.window = Tk.Tk()
def askUser(self):
def getCheckVal():
print cbVar.get()
windowTime=Tk.Tk()
cbVar = Tk.IntVar()
btnC = Tk.Checkbutton(windowTime, text="Save", variable = cbVar,
command=getCheckVal)
btnC.grid()
windowTime.mainloop()
def cmdWindow(self):
frameShow=Tk.Frame(self.window)
frameShow.grid()
btnSwitch = Tk.Button(frameShow, text='Show Plots', command=self.askUser)
btnSwitch.grid()
self.window.mainloop()
GUIObj=GUIMainClass()
GUIObj.cmdWindow()
This is very unusual. What could be going wrong?
EDIT: I've used 2 mainloops because I want a separate window (windowTime) to open up when I click "Show Plots" button. This new window should have the checkbox in it.
Your windowTime, cbVar, etc. variables are defined in the function's local scope. When askUser() completes execution, those values are thrown away. Prepend self. to them to save them as instance variables.
There should only be one mainloop() in your program, to run the main Tkinter root object. Try putting it as the very last line in the program. I recommend doing some reading on Effbot for how to set up a Tkinter application.
I'm not sure what all you're trying to do, but one problem is that the TK.IntVar called cbVar that you create in your askUser() method will be deleted when the function returns, so you need to attach it to something that will still exist after that happens. While you could make it a global variable, a better choice would be to make it an attribute of something more persistent and has a longer "lifespan".
Another likely issue is that generally there should only be one call to mainloop() in a single Tkinter application. It appears what you want to do is display what is commonly known as a Dialog Window, which Tkinter also supports. There's some standard ones built-in, plus some more generic classes to simplify creating custom ones. Here's some documentation I found which describes them in some detail. You may also find it helpful to look at their source code.
In Python 2 it's in the /Lib/lib-tk/tkSimpleDialog.py file and
in Python 3 the code's in a file named /Lib/tkinter/simpledialog.py.
Below is code that takes the latter approach and derives a custom dialog class named GUIButtonDialog from the generic one included the Tkinter library which is simply named Dialog.
try:
import Tkinter as Tk # Python 2
from tkSimpleDialog import Dialog
except ModuleNotFoundError:
import tkinter as Tk # Python 3
from tkinter.simpledialog import Dialog
class GUIButtonDialog(Dialog):
"""Custom one Button dialog box."""
def __init__(self, btnText, parent=None, title=None):
self.btnText = btnText
Dialog.__init__(self, parent, title)
def getCheckVal(self):
print(self.cbVar.get())
def body(self, master):
"""Create dialog body."""
self.cbVar = Tk.IntVar()
self.btnC = Tk.Checkbutton(master, text=self.btnText, variable=self.cbVar,
command=self.getCheckVal)
self.btnC.grid()
return self.btnC # Return the widget to get inital focus.
def buttonbox(self):
# Overridden to suppress default "OK" and "Cancel" buttons.
pass
class GUIMainClass:
def __init__(self):
"""Create the main window."""
self.window = Tk.Tk()
def askUser(self):
"""Display custom dialog window (until user closes it)."""
GUIButtonDialog("Save", parent=self.window)
def cmdWindow(self):
frameShow = Tk.Frame(self.window)
frameShow.grid()
btnSwitch = Tk.Button(frameShow, text='Show Plots', command=self.askUser)
btnSwitch.grid()
self.window.mainloop()
GUIObj = GUIMainClass()
GUIObj.cmdWindow()

Tkinter combobox - gracefully lose focus when click out of widget

I have a ComboBox written in Python Tkinter that makes the horrible system alert sound when you click off of it without selecting something.
For instance, when you hit the dropdown and select your item, it works fine. But if you hit the drop-down and then decide to click off, it will lose focus as expected, but it makes an alert sound. Can this be disabled in some way so it can gracefully lose focus without complaining? I'm on OSX 10.9 btw
UPDATE -
Minimally working code that produces the alert.
from Tkconstants import *
import ttk
import Tkinter
class PyPrecursor():
def __init__(self,root):
self.root = root
self.TabNotebook()
def TabNotebook(self):
self.main_notebook_frame = ttk.Notebook(self.root, name='main_notebook')
self.main_notebook_frame.enable_traversal()
self.OptionsF = ttk.Frame(self.main_notebook_frame, name='options')
self.length_options_frame = ttk.LabelFrame(
self.OptionsF, labelwidget=ttk.Label(font=('Arial', 15), text="Length Options:"))
self.hcdr3_length_label = ttk.Label(self.length_options_frame, text="HCDR3 Length")
self.HCDR3_Length = Tkinter.StringVar()
self.hcdr3_length_combo = ttk.Combobox(
self.length_options_frame, values=[i for i in xrange(16, 36)],
textvariable=self.HCDR3_Length)
self.hcdr3_length_combo.current(0)
self.length_options_frame.pack(side=TOP,fill=X,pady=5)
self.hcdr3_length_label.pack(side=LEFT)
self.hcdr3_length_combo.pack(side=LEFT,anchor=W)
self.main_notebook_frame.pack(side=TOP,expand=1,fill=BOTH,padx=10,pady=10)
self.main_notebook_frame.add(
self.OptionsF, text="Input Options", underline=0, padding=2)
self.main_notebook_frame.bind("<<NotebookTabChanged>>",self.update_)
def update_(self,event):
self.root.update()
def main():
root = Tkinter.Tk()
PyPrecursor(root)
root.mainloop()
root.update_idletasks()
if __name__ == '__main__':
main()
You might want to try this:
self.hcdr3_length_combo.bell(displayof=1)
Not sure if it should be 1 or 0 though...
If it doesn't work, maybe a containing widget throws the sound. Might want to apply it to the parent widget as well. I'm not familiar with python 2.7 and it doesn't throw a sound when I use python3 with slight modifications.
Usually when you can't find an option for a specific widget, you can find something in the general widget options. Just search "tkinter widget options" and you'll get some place like:
https://effbot.org/tkinterbook/widget.htm

Python Tkinter menu bars don't display

I'm trying to make a GUI using Tkinter and have come to implementing a menu bar. I've looked at a few tutorials and written some code for it, but a menu bar never seems to appear - just a blank frame with a white background. This doesn't just happen for my code though; on copying and pasting the code of one of the aforementioned tutorials into a new script, the same behaviour is exhibited.
I'd appreciate it if anyone could shed any light on what's causing this. My system is OS X 10.5, Python 2.7, Tk 8.4. Here's the code from the tutorial that doesn't appear to work:
#!/usr/local/bin/python2.7
from Tkinter import *
from ttk import *
class App(Frame):
def __init__(self):
Frame.__init__(self)
self.master.geometry('400x300')
self.master.title(__file__)
self.pack()
self.menu = Menu(tearoff=False)
self.master.config(menu = self.menu)
fm = self.file_menu = None
fm = Menu(self.menu, tearoff=False)
self.menu.add_cascade(label='File', menu = fm)
fm.add_command(label='Say Hello', command = self.say_hello)
fm.add_separator()
fm.add_command(label='Quit', command = self.quit)
self.mainloop()
def say_hello(self, *e):
self.label = Label(self.master, text='Hello there!')
self.label.pack(anchor=CENTER, fill=NONE, expand=YES, side=LEFT)
if __name__ == '__main__':
App()
and my code is here:
from Tkinter import *
class App(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
parent.title("Cluedo Solver 1.0")
menubar = Menu(root)
menubar.add_command(label="File")
menubar.add_command(label="Quit", command=root.quit())
root.config(menu=menubar)
root=Tk()
root.geometry("300x250+300+300")
app=App(root)
root.mainloop()
Based on some comments you made to one of the answers, you are apparently running this on a Macintosh. The code works fine, but the menu appears in the mac menubar rather than on the window like it does on Windows and Linux. So, there's nothing wrong with your code as far as the menubar is concerned.
Code with Explanation
From personal experience, I have found that it is usually easier to manage all widgets in a widgets method. That is what I did here, and it worked. Also, instead of parent, I used master. I will now walk you through the code step-by-step.
from Tkinter import *
We import Tkinter (GUI stuff)
class App(Frame):
We create a class called App, which is the Frame where widgets are held.
def __init__(self, master):
Frame.__init__(self, master)
self.grid()
self.widgets()
We create a method called __init__. This initializes the class, and runs another method called widgets.
def widgets(self):
menubar = Menu(root)
menubar.add_command(label="File")
menubar.add_command(label="Quit", command=root.quit())
root.config(menu=menubar)
We create the widgets method. This is where the widget, menubar is added. If we were to create anymore widgets, they would also be here.
root=Tk()
root.title("Menubar")
app=App(root)
root.mainloop()
Lastly, we give the entire window some properties. We give it a title, Menubar, and run the App class. lastly, we start the GUI's mainloop with root.mainloop.
I am trying the code as above, but all I get is "Python" on the macOS menubar and it's usual pulldown. tkinter just doesn't seem to work menus on macOS 10.14.1
I think what is happening is that there are mac specific fixups that are changing the event codes so certain menu items will end up under the Python menu item instead of where expected, I saw some of this in my own experiments. When I expanded my code and used some of the reserved FILE event codes instead of the standard ones, things worked better.
#!/Library/Frameworks/Python.framework/Versions/3.7/bin/python3.7
# -*- coding: utf-8 -*-# -*- coding: utf-8 -*-
from tkinter import *
class App(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.grid()
self.widgets()
def widgets(self):
menubar = Menu(root)
menubar.add_command(label = 'File')
menubar.add_command(label = 'quit', command = root.quit())
root.config(menu = menubar)
root = Tk()
root.title('Menubar')
app = App(root)
root.mainloop()
Check your mac menu bar if you are doing any GUI that involves menubar which you will like to view or test. Its subtle and you may think you code is not to working. Click on the app(in this case python window), it will show a drop down menubar.
guys to solve the problem
look the file part that's where the menubar for mac is located

Categories