Python Tkinter Spinbox seems to reset the variable value - python

I'm working with Python 2.6.5, and TKinter.TkVersion 8.5
Here is a MWE of the buggy behavior :
import Tkinter as tk
root = tk.Tk()
classic_var = tk.IntVar()
classic_var.set(2)
classic_spinbox = tk.Spinbox(root, from_=0, to=3,textvariable=classic_var)
classic_spinbox.pack()
values_var = tk.IntVar()
values_var.set(2)
values_spinbox = tk.Spinbox(root, values=(0, 1, 2, 3), textvariable=values_var)
values_spinbox.pack()
print classic_var.get() #expected : 2, actual : 2
print values_var.get() #expected : 2, actual : 0
root.mainloop()
I would expect for value_var to be "2" even after the Spinbox initialization.
Here you can see a screenshot of the result :
Is it a known bug ? Is there a workaround other than this ?
initial_value = 2
values_var = tk.IntVar()
values_spinbox = tk.Spinbox(root, ...
values_var.set(initial_value)

From testing it myself and from doing some research the best way is to do the workaround you suggested which would be to set the default value after it has initialised.
I found this answer from another user through a google search actually, haha: How can I establish a default String value on a Tkinter Spinbox?
This is intended behaviour actually due to how values is coded. The way values works is that it overrides other arguments in a sense, so it uses the first value you declare in values as the initial value and not your textvariable. I don't believe there is any way to fix this.
For reference: https://www.tcl.tk/man/tcl8.4/TkCmd/spinbox.htm#M22 then click on values and it will explain it.

Related

Python: how to pass a function as a variable command in Tkinter

This is my first time creating a GUI using Tkinter to simulate a DSP setup. I'm stuck.
This GUI requires multiple scales. Therefore I wrote a generic scale function.
# Overlap percent scale
def overlap_percent():
print("Testing")
# op=var.get()
# olive_features['overlap_perc']=op
# generic scale function
def create_scale(self,parent,xval,yval,start,end,resolution,orient,default,lng,clr,cmdtrig):
OHA_scale = tk.Scale(parent,resolution=resolution, from_=start, to=end, orient=orient,length=lng,fg=clr,command=cmdtrig)
OHA_scale.grid(row=xval, column=yval, sticky=tk.E + tk.W + tk.N + tk.S, padx=20, pady=4)
OHA_scale.set(default)
# Overlap %
yval=yval+2
xval=1
self.create_label(Oliveframe,xval,yval,"Overlap %")
xval=3
self.create_scale(Oliveframe,xval,yval,25,75,25,"vertical",75,90,'Black',"overlap_percent")
xval=1
yval=yval+1
self.create_arrow(Oliveframe,xval,yval)
Based on the code above, i figured it would print 'Testing' on the cmd line, when i adjust the Overlap scale. Nothing happened. To test the code, i replaced the command in generic scale to
def create_scale(self,parent,xval,yval,start,end,resolution,orient,default,lng,clr,cmdtrig):
OHA_scale = tk.Scale(parent,resolution=resolution, from_=start, to=end, orient=orient,length=lng,fg=clr,command=overlap_percent)
This resulted in an error.
OHA_scale = tk.Scale(parent,resolution=resolution, from_=start, to=end, orient=orient,length=lng,fg=clr,command=overlap_percent)
NameError: name 'overlap_percent' is not defined
I can't quite figure out what I'm doing wrong. How exactly can i use the generic scale function to control multiple scales?
on this line
OHA_scale = tk.Scale(parent,resolution=resolution, from_=start, to=end,
orient=orient,length=lng,fg=clr,command=cmdtrig)
command take function parameters but you send a string value
self.create_scale(Oliveframe,xval,yval,25,75,25,"vertical",75,90,'Black',overlap_percent)
If you do it will be alright

canvasObjects; found PartInstance, expecting tuple in Abaqus/CAE

I'm developening an Abaqus/CAE plug-in, in this plug-in i'm using the gui toolkit, and i have a button that uses the PickStep, on click the button i can select a PartInstance in the viewport.
Then i want to export the selected PartInstance to an .obj file but when i try it, abaqus displays an error.
This is an example of my PICK BUTTON:
# PICK BUTTON 1
pickHf = FXHorizontalFrame(p=col2, opts=0, x=0, y=0, w=0, h=0, pl=0, pr=0, pt=0, pb=0, hs=DEFAULT_SPACING,
vs=DEFAULT_SPACING)
# Note: Set the selector to indicate that this widget should not be
# colored differently from its parent when the 'Color layout managers'
# button is checked in the RSG Dialog Builder dialog.
pickHf.setSelector(99)
label1 = FXLabel(p=pickHf, text='' + ' (None)', ic=None, opts=LAYOUT_CENTER_Y | JUSTIFY_LEFT)
pickHandler1 = DBPickHandler(form, form.uper, 'Select a 3D, discrete and dependent meshed instance', INSTANCES,
1, label1)
icon = afxGetIcon('select', AFX_ICON_SMALL)
FXButton(p=pickHf, text='\tPick Items in Viewport', ic=icon, tgt=pickHandler1, sel=AFXMode.ID_ACTIVATE,
opts=BUTTON_NORMAL | LAYOUT_CENTER_Y, x=0, y=0, w=0, h=0, pl=2, pr=2, pt=1, pb=1)
I save the value in an ObjectKeyword:
self.uper = AFXObjectKeyword(self.cmd, 'uper', True, pickedDefault)
This is how i export the PartInstance to .obj:
print 'Uper - ' + uper[0].name
f.write('Uper - '+uper[0].name+'\n')
session.writeOBJFile(fileName='C:/temp/Uper.obj', canvasObjects=(uper[0]))
That displays and error, and i also tried this:
print 'Fixed - ' + fixed[0].name
f.write(fixed[0].name+'\n')
fixedobj = open('Fixed.obj', 'w')
pickle.dump(fixed[0], fixedobj)
fixedobj.close()
But that does not work either.
I get this error:
canvasObjects;found PartInstance, expecting tuple
This answer will help you. On your call to session.writeOBJFile you are trying to create a one element tuple for the canvasObjects argument. Simply wrapping the item in parentheses won't achieve that. You need to add a comma to make it a tuple:
session.writeOBJFile(fileName='C:/temp/Uper.obj', canvasObjects=(uper[0],))
The Abaqus documentation says this about canvasObjects:
canvasObjects
A sequence of canvas objects to export.
I'm not sure if PartInstance is considered a canvas object or not, but you may still have problems even after correcting the argument to be a tuple. If so, make sure the items of the tuple are proper canvas objects.

Create Optionmenu with for loop

I am pretty new to Python as well as coding in general.
I've got 42 Labels (Strings) stored in a list.
My aim is to: for each of the 42 labels, create a Tkinter Label displaying the String and a Dropdown List (Tkinter Optionmenu).
Two problems occur:
I get an index error for the list of variables (for the optionmenus), here is the output of my Console:
varDd[i] = StringVar(root)
IndexError: list assignment index out of range
The Canvas is Scrollable as I intended, but the content doesn't scroll with it
My complete Code:
https://codepaste.net/6jkuza
The essential part:
def createDdList():
del labelsDd[:]
del listsDd[:]
if len(labels) > 1:
varDd = [len(labels)]
for i,label in enumerate(labels):
# Erstelle Labels mit den Markerlabels im scrollbaren Canvas
labelsDd.append(Label(contentCanvas,text=label))
labelsDd[i].pack()
# Erstelle die Dropdown Listen im scrollbaren Canvas
varDd[i] = StringVar(root)
listsDd.append(OptionMenu(canvas,varDd[i],*labels))
listsDd[i].place(x=70,y=10+(30*i))
contentCanvas = Frame(canvas,bg='#FFFFFF')
canvas.create_window((0,375),window=contentCanvas)
The first issue arrises from this like:
varDd = [len(labels)]
While this syntax is used in other languages to mean "a list with this many elements in python it is just a list with one number:
>>> labels = range(4)
>>> v = [len(labels)]
>>> v
[4]
Instead you probably want something along the lines of this:
>>> varDb = [None]*len(labels)
>>> varDb
[None, None, None, None]
Then you can index it the same way as your other lists
The other issue is that using .place() inside a canvas places it on the screen but not on the canvas space itself, to do that you need to call canvas.create_window so this line:
listsDd[i].place(x=70,y=10+(30*i))
would translate to this:
canvas.create_window(70, 10+(30*i), window = listsDd[i])

TkInter ListBox and use of .format

I am using this command:
self.licenseBox.insert(END, "{:30}{:90}{:20}{:5}".format(item[0],
item[1], item[2], item[3]))
But .format will add the item then the column width. For example if item[0] = "foo", the first column is 33 wide and means the following parameters are off by three.
Any work around ?
But .format will add the item then the column width.
format() does no such thing:
print "1234567890" * 2
print "{:4}{:4}{:4}{:4}".format('aaaa', 'bbbb', 'cccc', 'dddd')
--output:--
12345678901234567890
aaaabbbbccccdddd
The total width of the output is 16 = 4 x 4.
You should explicitly specify the alignment:
lb.insert(tk.END, "{:<5}-{:<2}".format(123, 9))
The docs say:
'<' Forces the field to be left-aligned within the available space
(this is the default for most objects).
That 'most objects' language is what I think you may be running afoul of. Strings, numbers, etc. have a __format__() method that is called when they are asked to display themselves when you call the format() method on them. Check this out:
print "{:4}".format("a")
print "{:4}".format(9)
--output:--
a
9
Strings and numbers have different defaults for their justification. So I would not rely on the defaults--be explicit instead, and then you will know how the output will be justified.
Having said that, I have to use 17 for the min field width to actually get 10:
import Tkinter as tk
root = tk.Tk()
root.geometry("1000x200")
lb = tk.Listbox(root, width=150)
lb.insert("1", "{:4}{:4}".format("a", "b") )
lb.insert(tk.END, "1234567890" * 4)
lb.insert(tk.END, "{:<17}{:<10}".format(100, 200) )
lb.pack()
root.mainloop()
With that code, I see 200 starting in column 11. Okay, that alignment problem has to do with tkinter using a default font that is not fixed width, i.e. all characters do not occupy the same amount of space. If you are trying to align columns, you need to use a fixed width font. Try something like this:
import Tkinter as tk
import tkFont
root = tk.Tk()
my_font = tkFont.Font(family="Monaco", size=12) #Must come after the previous line.
root.geometry("1000x200")
lb = tk.Listbox(root, width=150, font=my_font)
lb.insert("1", "{:4}{:4}".format("a", "b") )
lb.insert(tk.END, "1234567890" * 4)
lb.insert(tk.END, "{:>10}{:>10}".format(100, 200) )
lb.pack()
root.mainloop()
Windows
listbox = Listbox(master, width=60, font='consolas')
I am on linux, where font='mono' works.

How to get the width of a string in pixels?

I am using wxPython's HyperTreeList and I want to set the column width exactly equal to length of the largest string in it.
To accomplish that, I'd like to to convert a python string size into pixels.
For Example: If we have a string like
str = "python"
len(str) = 6
How could I convert the above string length/size into pixels?
Is there another way?
You'll have to do something like (see the documentation of wxWidgets for more info)
f = window.GetFont()
dc = wx.WindowDC(window)
dc.SetFont(f)
width, height = dc.GetTextExtent("Text to measure")
It depends on how you are printing the text.
You may be interested by PIL ImageDraw which has a textsize method. See http://effbot.org/imagingbook/imagedraw.htm
Update: This was answering the original question. It may looks a little off-topic after question updates.
This is not my solution, I'm just passing it on as I found it works and
most useful
as in a program environment it reduces to only 3 lines. Python3.9 mac OS
from tkinter import *
from tkinter import font as W1
FW=Tk() ; FW.withdraw() # a must but don't need a window
Text ='1234567890'
def LENGTH(Text) :
W2 = W1.Font(family='Comicsans' , size = 20)
length = W2.measure(Text)
print(length)
LENGTH(Text)

Categories

Resources