Is it possible to have a slider (Scale widget in tkinter) where the possible values that are displayed when manipulating the slider are discrete values read from a list?
The values in my list are not in even steps and are situation dependent.
From all the examples I've seen, you can specify a minimum value, a maximum value and a step value (n values at a time), but my list might look like this:
list=['0', '2000', '6400', '9200', '12100', '15060', '15080']
Just as an example. To reiterate, I want it go from for instance list[0] to list[1] or list[6] to list[5] when pulling the slider.
If anyone has any other suggestion for easily being able to pick a value from hundreds of items in a list, I'm all ears. I tried the OptionMenu widget but it gets to extensive and hard get a view of.
Edit you could set the command of the slider to a callback, have that callback compare the current value to your list and then jump to the nearest by calling set() on the slider
so:
slider = Slider(parent, from_=0, to=100000, command=callback)
and:
def callback(event):
current = event.widget.get()
#compare value here and select nearest
event.widget.set(newvalue)
Edit:
to show a complete (but simple example)
try:
import tkinter as tk
except ImportError:
import Tkinter as tk
valuelist = [0,10,30,60,100,150,210,270]
def valuecheck(value):
newvalue = min(valuelist, key=lambda x:abs(x-float(value)))
slider.set(newvalue)
root = tk.Tk()
slider = tk.Scale(root, from_=min(valuelist), to=max(valuelist), command=valuecheck, orient="horizontal")
slider.pack()
root.mainloop()
i've tested this in python 2.7.6 and 3.3.2, even when dragging the slider this jumps to the nearest value to where the mouse is currently as opposed to only jumping when you let go of the slider.
Related
I wrote the below code to bind event and do operations on individual listbox items.
import tkinter as tk
root = tk.Tk()
custom_list = tk.Listbox(root)
custom_list.grid(row=0, column=0, sticky="news")
def onselect_listitem(event):
w = event.widget
index = int(w.curselection()[0])
value = w.get(index)
print(index, value, " color : ",custom_list.itemcget(index,'background'))
custom_list.itemconfig(index, fg='gray', selectforeground="gray")
custom_list.bind('<Double-Button-1>', onselect_listitem)
for k in range(20):
custom_list.insert(k, " --------- " + str(k))
root.mainloop()
I am having trouble using itemcget to get the background properties while itemconfig works properly. Everything else is working. Can someone tell me if there is something wrong? I am trying to obtain the current item background color via index of the item in the listbox. The part with custom_list.itemcget doesn't print anything.
Thanks
From the New Mexico tech Tkinter reference:
.itemcget(index, option)
Retrieves one of the option values for a specific line in the listbox. For option values, see itemconfig below. If the given option has not been set for the given line, the returned value will be an empty string.
So since you haven't set the background option, itemcget returns an empty string. You can see this working by changing the print to custom_list.itemcget(index,'fg'). The first time you doubleclick you get an empty sting because you haven't set it, the second time it prints gray.
Thanks for looking into my question. I'll try to give you a big and small picture of what I'm trying to do here.
Big Picture:
So, basically I'm trying to make a simple mindmapping program where, after the first entry, every text I input unto the Entry widget is linked to the previous text via a line widget. So like this: Hello----There, and then Hello----There----Yo. Actually, I'm hoping for more modifications in the future like being able to rearrange the links via some metric I have yet explored, but this is basically it.
Small/Specific Picture:
I realize that in order to do this, I will have to find a way to acquire all the xy coordinates of every text drawn on the canvas (text I drew on the canvas by using the random function). I need the coordinates of the first text and the coordinates of the second text so I can use those to draw the line to visually link the two texts. I thought of using an array to list down all inputted text, but I realize that only stores the text and not the location of the text on the canvas. I have explored using tags, or using the coords functions or using the bbox function, but to no avail. Any clues on how to go about this? I would highly appreciate it, Thanks. :)
import Tkinter
import random
a = Tkinter.Tk()
b = Tkinter.Canvas(a, width=1000, height=500)
b.pack()
def c(event):
b.create_text(random.randint(50,940), random.randint(50,480), anchor="center", text=d.get())
f.append(d.get())
d.delete(0, 'end')
print f
#this function creates a randomly located text taken from the entry widget below and, at the same time, appends the text in the list known as f''
d = Tkinter.Entry(a)
d.pack()
d.bind("<Return>", c)
d.focus()
b.create_line(300, 250, 600, 290)
#this is my very early attempt at linking text inputted and drawn on the Canvas
f = []
a.mainloop()
Simply assign random values to variables before you use it to create text on canvas, and keep on list with object ID and text.
x = random.randint(...)
y = random.randint(...)
obj_id = b.create_text(x, y, ...)
f.append([x, y, obj_id, d.get()])
BTW: if you have obj_id then you can also do
x,y = b.coords(obj_id)
In this example, if a user selects any option in any of the drop downs, then clicks on another drop down, the previously selected item displayed a check mark beside it. Even though that choice was picked in a different menu.
from Tkinter import *
from ttk import *
choices = ['1st Choice', '2nd Choice', '3rd Choice', '4th Choice']
root = Tk()
for each in range(10):
OptionMenu(root, StringVar(), choices[0], *choices).pack()
root.mainloop()
This happens on both Python 2.7 and 3.5.
I've even moved the choices list into the loop so it generates on each iteration and the problem still occurs. I'm assuming since the elements of the list are the same objects tkinter isn't differentiating between which OptionMenu they are in. It would seem I need a unique list every time through the loop.
Is there any way to limit the check mark to displaying only on the OptionMenu that the user has interacted with?
This is a bug in the ttk implementation of OptionMenu. It isn't assigning a unique variable for the radiobuttons in each OptionMenu.
You can fix this with a little bit of code. Basically, you have to loop over every item in the menu and set the variable attribute.
Here's an example:
def optionmenu_patch(om, var):
menu = om['menu']
last = menu.index("end")
for i in range(0, last+1):
menu.entryconfig(i, variable=var)
...
for each in range(10):
sv = StringVar()
om = OptionMenu(root, sv, choices[0], *choices)
om.pack()
optionmenu_patch(om, sv)
Bug tracker issue: http://bugs.python.org/issue25684
I have two listboxes that are side by side. They use the lstBox.grid() method to order them in the window. I then have a dropdown option that changes the items displayed in one of the listboxes. The ultimate aim here is to be able to "add" items from one box to the other, and remove ones from the other. My issue is that I don't know how to expand the list box width to be that of the largest item it contains. I have a method that is used for handling when the dropdown is changed, and determining its value to change the items as appropriate, but te listbox doesn't change width.
I am aware that by using lstBox.pack(fill=X,expand=YES), the listbox will resize, but i'm using the .grid() geometry.
Any suggestions?
You can list all your items to find the biggest (if there aren't too much it should be fine). For instance with strings you count their length with 'len(item)'.
Then, when you create your listbox (not when you grid it) your set its width with 'width = "The size you want" ', if the size you put in there is well defined with regard to the length of the biggest item, you shouldn't have any problem.(I think I remember, the listbox's width's unity is given by the size of the text in it, but it needs to be checked)
I don't know grid that much, so that I don't know if there is any faster option to do it.
It should look something like this:
len_max = 0
list_items = ["item2", "item2", "item3+a few characters for the size"]
for m in list_items:
if len(m) > len_max:
len_max = len(m)
import tkinter
master = Tk()
my_listbox1 = Listbox(master, width = len_max)
my_listbox1.grid(row = 0, column = 0)
my_listbox2 = Listbox(master, width = len_max)
my_listbox2.grid(row = 0, column = 1)
my_listbox1.insert(END, list_items[0])
my_listbox2.insert(END, list_items[1])
my_listbox2.insert(END, list_items[2])
master.mainloop()
I am writing a Tkinter program for the first time and have a question on radio buttons. What I am trying to do is this:
open a set of images (one at a time).
When an image is opened, annotate a value using the radio button.
Collect this value in a list
So, in this example I have 2 compounds and the list would have 2 annotations.
The problem I have is, if by mistake the user clicks radiobutton 2 instead of one, and then corrects him/herself, the list will have 4 items (3 for the first image, 1 for the second). How do I handle this, so that the list will have only 2 values?
import Tkinter as tk
from PIL import ImageTk, Image
from tkFileDialog import askopenfilename
cmp_list = ["VU435DR","VU684DR"]
li = []
li_final = []
def sel():
selection = str(var.get())
if selection == "1":
li.append("Antagonist")
elif selection == "2":
li.append("Agonist")
for i in range(len(cmp_list)):
root = tk.Tk()
var = tk.IntVar()
ig = str(cmp_list[i] + '.png')
img = ImageTk.PhotoImage(Image.open(ig))
panel = tk.Label(root,image=img)
panel.pack(side = "top",fill="none",expand="no")
#w = tk.Text(height=2,width=50)
#w.pack(side='right")
q = tk.Radiobutton(root,text="Antagonist",command=sel,value=1,variable=var)
q.pack()
r = tk.Radiobutton(root,text="Agonist",command=sel,value=2,variable=var)
r.pack()
root.mainloop()
print li
Your code is creating more than one instance of tk.Tk(). This is not how Tkinter was designed to work, and it will yield unpredictable behavior. A proper Tkinter program always has exactly one instance of tk.Tk().
If you need more than one window, for the second and subsequent windows you should create an instance of tk.Toplevel.
To answer your specific question about how to handle someone first hitting one radiobutton and then the other -- the problem is that you are unconditionally appending to your list each time they click on a radiobutton. The solution is to use some sort of flag or indicator to know whether one of the radiobuttons has been clicked, or change your code so that it doesn't matter.
Let's look at that second option - make it so it doesn't matter. When you open up a new image you can automatically append a value to your list. In this case, set it to None to say that nothing has been picked yet. Then, in sel, you would always replace the last element rather than append a new element, since you know that the last element always refers to the current compound.