Adding a check next to the selected item in tkinter OptionMenu - user-interface

How could I add a check sign next to the currently selected item (or highlight it) in a OptionMenu in a tkinter GUI? The idea is that when I click again to select another item, I can see easily which one is selected (similar to the following picture)
I just added a new example:
from tkinter import *
OptionList = [
"Aries",
"Taurus",
"Gemini",
"Cancer"
]
app = Tk()
app.geometry('100x200')
variable = StringVar(app)
variable.set(OptionList[0])
opt = OptionMenu(app, variable, *OptionList)
opt.config(width=90, font=('Helvetica', 12))
opt.pack(side="top")
labelTest = Label(text="", font=('Helvetica', 12), fg='red')
labelTest.pack(side="top")
def callback(*args):
labelTest.configure(text="The selected item is {}".format(variable.get()))
variable.trace("w", callback)
app.mainloop()

Just use ttk widgets for this modern looking style, try saying something like:
from tkinter import ttk
....
#arguments - master variable default *values
opt = ttk.Optionmenu(app, variable, OptionList[0], *OptionList)
The effect given by this is pretty similar or maybe identical to what your trying to achieve.
You might notice an additional third positional argument here, it is actually default=OptionList[0] argument specified here(specific to just ttk.Optionmenu), it is just the default value that the optionmenu will display, ignoring this might lead to some bugs in the looks of optionmenu, like this.
And also keep in mind, it does not have a font option too. To overcome this, check this out
Hope this was of some help to you, do let me know if any errors or doubts.
Cheers

You can get similar effect using tk.OptionMenu:
from tkinter import *
OptionList = [
"Aries",
"Taurus",
"Gemini",
"Cancer"
]
app = Tk()
app.geometry('300x200')
variable = StringVar(app)
variable.set(OptionList[0])
opt = OptionMenu(app, variable, None) # need to supply at least one menu item
opt.config(width=90, font=('Helvetica', 12))
opt.pack(side="top")
# populate the menu items
menu = opt['menu']
menu.delete(0) # remove the None item
for item in OptionList:
menu.add_radiobutton(label=item, variable=variable)
labelTest = Label(text="", font=('Helvetica', 12), fg='red')
labelTest.pack(side="top")
def callback(*args):
labelTest.configure(text="The selected item is {}".format(variable.get()))
variable.trace("w", callback)
app.mainloop()

Related

My toplevel window in tkinter is no longer being destroyed. It was working fine until I tried changing other aspects of my function

I'm trying to get a popup window to display random text and a picture every time a button is pressed in tkinter. My original code was going to use an if/elif statement to do this. It worked as intended but I thought it might be easier to pair the data in a dictionary since there would be 50 elif statements otherwise (is it frowned upon to use so many? I actually found it easier to read).I was able to get this working but now the toplevel window in tkinter is not being destroyed like it was in the original function. A new Label is just being created on top of it and I can't figure out why. The function code is below. Thanks in advance, any help would be appreciated!
def Add_Gemstone2():
global Addstone
#destroy the previous window if there is one.
try:
AddStone.destroy()
except(AttributeError, NameError):
pass
#create the window.
AddStone=Toplevel()
AddStone.configure(bg='White', height=200, width=325)
AddStone.geometry('325x180+10+100')
# add gemstones to list from file.
gem_stones = open('gemstones.txt')
all_gem_stones = gem_stones.readlines()
gemstones = []
for i in all_gem_stones:
gemstones.append(i.rstrip())
# Add pictures to list.
path = r'C:\Users\Slack\Desktop\PYTHON WORKS\PYTHON GUI PROJECT\gems'
gempictures = []
# r=root, d=directories, f = files
for r,d,f in os.walk(path):
for file in f:
if '.gif' in file:
gempictures.append(os.path.join(r, file))
#create dictionary from lists.
gemdiction = dict(zip(gemstones, gempictures))
key, val = random.choice(list(gemdiction.items()))
# create the labels.
glbl1 = Label(AddStone, text=key, bg='gold', wraplength=300)
glbl1.pack()
image = ImageTk.PhotoImage(Image.open(val))
glbl2 = Label(AddStone, image=image)
glbl2.image = image
glbl2.pack()

Updating a kivy label on a screen when dynamically generating it through a list?

Firstly, disclaimer: I am terribly new to programming and am trying to build my understanding with this project. Additionally, let me say I have searched the forums and found similar posts to mine, but none have the issue of updating a label that has been dynamically generated through a list.
My question is in my code, commented out, but to summarize: I generate buttons and labels for each item in a list. Then the buttons should add and subtract from the linked value in a dictionary. Currently the code does this, but the labels on screen don't update to reflect the new values. Can someone please assist with updating the value for "ordlayout.add_widget(ordlayout.lbl[str(i)])" when calling to updateup and updatedown?
import kivy
kivy.require('1.10.0')
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.gridlayout import GridLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.textinput import TextInput
from kivy.lang import Builder
from kivy.uix.dropdown import DropDown
from kivy.base import runTouchApp
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from functools import partial
#The first screen the app opens to. Contains all other screen branches.
class MainScreen(Screen):
pass
#NewOrder will be the screen used for choosing which
#items/and how many of each the customer wants added.
class NewOrder(Screen):
def __init__(self, **kwargs):
super(NewOrder, self).__init__(**kwargs)
#This will eventually read/create a list of strings from a user-modified file.
self.foods = ["Puppy", "Cat", "Fish"]
#I create a dictionary linking a number to each item.
self.countfoods = {}
for i in self.foods:
self.countfoods[i] = 0
#Now I create a grid layout to put on the screen.
ordlayout = GridLayout()
ordlayout.cols = 8
ordlayout.row_default_height=20
ordlayout.buttons={}
ordlayout.btns1 = {}
ordlayout.lbl = {}
#The items I want on the screen are 1.)First item from list. 2.) Minus button.
#3.) Current number of the item. 4.) Plus button.
#I want these four buttons for each item.
for i in self.countfoods:
#Adds text for first item.
ordlayout.add_widget(Label(text=i))
#Adds a button for minus, linked to a unique dict value.
ordlayout.buttons[str(i)] = Button(text="-")
ordlayout.lbl[str(i)] = Label(text=str((self.countfoods[i])))
#The below assigns the specific object location of each label
#to a variable for passing to ocuntup and countdown.
tempPlacement = str(ordlayout.lbl[str(i)])
ordlayout.buttons[str(i)].bind(on_press=partial(self.updatedown, i))
ordlayout.add_widget(ordlayout.buttons[str(i)])
#Add the value that I want to update.
ordlayout.add_widget(ordlayout.lbl[str(i)])
#Adds a button for addition, but doesn't properly link it to a specific value.
ordlayout.btns1[str(i)] = Button(text="+")
ordlayout.btns1[str(i)].bind(on_press=partial(self.updateup, i))
ordlayout.add_widget(ordlayout.btns1[str(i)])
#Add that grid wit
h values to the screen.
self.add_widget(ordlayout)
#Function used to change value down by one.
def updatedown(self, event, i):
self.countfoods[event] -= 1
print (self.countfoods)
#Function used to change value up by one.
def updateup(self, event, i):
self.countfoods[event] += 1
print (self.countfoods)
#AdminOpt will be the screen used for
class AdminOpt(Screen):
def __init__(self, **kwargs):
super(AdminOpt, self).__init__(**kwargs)
#Will allow for opening and checking of created orders.
class OrdHist(Screen):
pass
#This is purely the class used for managing the other screens.
class ScreenManagement(ScreenManager):
pass
Main = Builder.load_file("Order Handler2.kv")
class Customer:
def __init__(self, name, pricelist):
self.name = name
self.pricelist = pricelist
class SimpleKivy(App):
def build(self):
return Main
if __name__== "__main__":
SimpleKivy().run()
Haven't been able to test this (your question is missing your kv file), but something like this might work:
#Function used to change value down by one.
def updatedown(self, i, button):
self.countfoods[i] -= 1
self.ordlayout.lbl[str(i)].text = str(self.countfoods[i])
print (self.countfoods)
#Function used to change value up by one.
def updateup(self, i, button):
self.countfoods[i] += 1
self.ordlayout.lbl[str(i)].text = str(self.countfoods[i])
print (self.countfoods)
You will also need to replace every occurence of ordlayout with self.ordlayout in the __init__() method.
As an aside, you don't need to do str(i) for your dictionary keys. In fact, you can use lists instead of dictionaries, if you prefer.

How can I align items in menu bar from right to left with using python 3 and tkinter module?

I'm using python 3 and tkinter module for creating a GUI. I need to create a menu bar and align items on it from right to left (I want to use that with Persian language so I need to align my menu items from right to left), now I have used from this code but it didn't aligned file and edit items from right to left and they still align from left to right in the menubar. How can I deal with it?
from tkinter import *
from tkinter import ttk
root = Tk()
root.title('Create Menu bar and items')
root.geometry('300x300+100+50')
root.option_add('*tearOff', False)
menubar = Menu(root)
root.config(menu = menubar)
file = Menu(menubar)
edit = Menu(menubar)
menubar.add_cascade(menu = file, label = 'فایل',compound = RIGHT)
menubar.add_cascade(menu = edit, label = 'ویرایش',compound = RIGHT)
Use a simple trick, create a blank menu between the itens ;-)
blankmenu = Menu(menubar, tearoff=0)
menubar.add_cascade(label="".ljust(130), menu=blankmenu)
I don't believe you can do what you want. I've not used Tkinter with a right-to-left language so I can't say for sure, but you're limited to what the OS supports for menubars. Tkinter gives you no control over where items are placed on a menubar, other than the relative order.

Pyside - Change entire GUI when button is pressed

I'm totally new to pyside and I'm having a problem with my little program (and pyside layouts in general).
What I have is an UI with some QlineEdits, comboboxes and a button. After I have filled out the Qlines and press the button I want to either to open a new window with a completely new layout or preferably clear out the open window and fill it with a new layout based on the input from the qlines. Perhaps this is super basic but I can't get it to work. The reason is that I can't grasp how I would be able to replace or add new stuff to my gui when it's already set and shown.
Let's say I have a script like this:
import sys
import os
from PySide import QtCore, QtGui
class BasicGui(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.projectNameLbl1 = QtGui.QLabel('Label1')
self.projectNameLbl2 = QtGui.QLabel('Label2')
self.nextBtn = QtGui.QPushButton("Next")
self.projectNameEdit = QtGui.QLineEdit(self)
self.projectNameEdit2 = QtGui.QLineEdit(self)
grid = QtGui.QGridLayout()
grid.setSpacing(10)
grid.addWidget(self.projectNameLbl1, 2, 0)
grid.addWidget(self.projectNameEdit, 2, 1)
grid.addWidget(self.projectNameLbl2, 3, 0)
grid.addWidget(self.projectNameEdit2, 3, 1)
grid.addWidget(self.nextBtn, 4, 1)
self.setLayout(grid)
self.setGeometry(300, 300, 350, 300)
self.setWindowTitle('projectCreator')
self.show()
self.nextBtn.clicked.connect(self.nextPressed)
def nextPressed(self):
self.msgBox = QtGui.QMessageBox()
self.msgBox.setText("When this button is pressed I want to generate a new layout")
self.msgBox.exec_()
def main():
app = QtGui.QApplication(sys.argv)
ex = BasicGui()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Say that I enter 10 in the line next to label1 and 2 in the other and press Next.Now I want to clear everything out and create 2 new columns with 10 qlines in each (or something like that).
Excuse me if I'm being either to vague or if I'm just repeating myself. I'm tired and irritated and English is not my first language.
I would deeply appreciate any help I could get or a push in the right direction.
Edit: If it's easier to accomplish this with some other widgetype with tabs or something that's fine. All i want to do is generate new widgets after i have recieved input from the user.
What you'll want to do is used a QStackedLayout[1].
Create a QWidget.
Create your layout.
Call setLayout() on the widget with your layout as the argument.
Push the new widget onto the QStackedLayout.
Use QStackedLayout's setCurrentIndex() or setCurrentWidget() functions to set the current layout.
I did something similar in a project of mine. See https://github.com/shanet/Cryptully/blob/master/cryptully/qt/qChatTab.py for a more complete example. Also see Pyside Changing Layouts for a similar problem.
[1] http://qt-project.org/doc/qt-4.8/qstackedlayout.html

Python 3.1 Tkinter layout help. I am close, please help me finish this

I am using Python 3.1 by the way.
I am trying to build a simple GUI using Tkinter - label, text entry field, button on the first row and editable text area with scrollbar to the right and on the bottom of it - on the second row. Please help me fix up the layout. What I have below does not quite work. If I have to use a grid, I will. I wish to keep the code very simple - I want to "sell" Python to some of my coworkers. So, I want to get a somewhat decent look and feel. Suggest better padding if you do not mind. Also, if my variable names, etc. seem weird, then please make a note.
At the same time I want to pretend that this is a throw-away script which I have not spent much time on. Since I am asking for your help, it ain't so, but they do not need to know ;). So, I do not want to introduce fancy code to create nice borders, etc. I just want something that is visually appealing, clean and simple. If I do not, then my presentation will not achieve its goal.
Thank you, my code is below:
class App:
def __init__(self, parent):
frame = Frame(parent)
self.__setup_gui(frame) # Call Helper
frame.pack(padx=15, pady=15)
parent.title('To be changed')
def __setup_gui(self, frame):
# First Row
self.cs_label = Label(frame, text='Change Set: ')
self.cs_label.pack(side=LEFT, padx=10, pady=10)
self.cs_val = Entry(frame, width=10)
self.cs_val.pack(side=LEFT, padx=10, pady=10)
self.get_button = Button(frame, text='Get', command=self.get_content)
self.get_button.pack(side=LEFT, padx=10, pady=10)
# Text area and scrollbar
self.text_area = Text(frame, height=10, width=50, background='white')
# Put a scroll bar in the frame
scroll = Scrollbar(frame)
self.text_area.configure(yscrollcommand=scroll.set)
self.text_area.pack(side=TOP)
scroll.pack(side=RIGHT,fill=Y)
self.clipboard_var = IntVar()
self.notepad_var = IntVar()
def get_content(self):
print(self.clipboard_var.get())
print(self.notepad_var.get())
###################################################################################################
if __name__ == '__main__':
root = Tk()
app = App(root)
root.mainloop()
You definitely want the grid manager -- Pack only works for a vertical or horizontal stackup by itself. You can use multiple frames to work around it, but I find it's easier to expand a GUI if you just do it with Grid to start.
Here's what I've worked up real quick based what you said and the code. I reduced/removed the padding -- it looked huge for me -- and I set up two scrollbars, in a subframe to make the padding work out more easily. Note that to make the horizontal scrollbar useful your Text area needs to have wrap=NONE; otherwise you might as well use the easy 'ScrolledText' widget from tkinter.scrolledtext and skip the horizontal scroll bar.
I've now reframed things a bit to allow for resize, with a minimum size that shows the top buttons -- see the uses of minsize and row/columnconfigure.
BTW, it looks like your variables aren't being pulled from anywhere -- is that intentional?
from tkinter import *
class App:
def __init__(self, parent):
self.__setup_gui(parent) # Call Helper
parent.title('To be changed')
def __setup_gui(self, parent):
# First Row
self.rowframe = Frame(parent)
self.rowframe.grid()
self.cs_label = Label(self.rowframe, text='Change Set: ')
self.cs_label.grid(row=0, column=0, padx=2, pady=2)
self.cs_val = Entry(self.rowframe, width=10)
self.cs_val.grid(row=0, column=1, padx=2, pady=2)
self.get_button = Button(self.rowframe, text='Get', command=self.get_content)
self.get_button.grid(row=0, column=2, padx=2, pady=2)
parent.update_idletasks()
parent.minsize(width=self.rowframe.winfo_width(), height=self.rowframe.winfo_height())
# Text area and scrollbars
self.textframe = Frame(parent)
self.textframe.grid(row=1, columnspan=2, padx=2, pady=2, sticky=N+S+E+W)
self.hscroll = Scrollbar(self.textframe, orient=HORIZONTAL)
self.vscroll = Scrollbar(self.textframe)
self.text_area = Text(self.textframe, height=10, width=50, wrap=NONE, background='white', yscrollcommand=self.vscroll.set, xscrollcommand=self.hscroll.set)
self.text_area.grid(row=0, column=0, sticky=N+S+E+W)
self.hscroll.config(command=self.text_area.xview)
self.hscroll.grid(row=1, column=0, sticky=E+W)
self.vscroll.config(command=self.text_area.yview)
self.vscroll.grid(row=0, column=1, sticky=N+S)
# Row 0 defaults to 0
parent.rowconfigure(1, weight=1)
parent.columnconfigure(1, weight=1)
# Textarea setup
self.textframe.rowconfigure(0, weight=1)
self.textframe.columnconfigure(0, weight=1)
self.clipboard_var = IntVar()
self.notepad_var = IntVar()
def get_content(self):
print(self.clipboard_var.get())
print(self.notepad_var.get())
###################################################################################################
if __name__ == '__main__':
root = Tk()
app = App(root)
root.mainloop()
Now, all that said...you might get more visual appeal with PyGTK, PyQt, or wxPython, though tkinter coming "standard" is a nice feature.

Resources