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])
Related
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.
I want to change (kind of dynamically) the NumberOfColumns of a rowColumnLayout inside a Maya Window. Depending on the value given by 'SLiBThumbSizeComboBox' i want to change the count for the columns.
Here is the problem: when running my 2 functions inside the Script Editor everything is working fine. I execute the first one - the ScrollLayout is created. I execute the second and the rowColumnLayout is created with the right count of columns.
But when I try run it only by the first function, meaning the second function is called at the end of the first one - it's not working anymore?! I get the error message, that NumberOfCollumns has to be greater than 0 ?!
def FunctionA():
if cmds.scrollLayout('SLiBScrollLayout', query = True, exists = True):
cmds.deleteUI('SLiBScrollLayout', layout = True)
cmds.scrollLayout('SLiBScrollLayout', p="SLiB_thumbsframe")
def FunctionB():
iconLayout = cmds.optionMenu('SLiBThumbSizeComboBox', query = True, value = True)
iconSize = iconLayout.split('x')
iconDiv = int(iconSize[0])
n = int(cmds.scrollLayout("SLiBScrollLayout", query=True, saw=1)/iconDiv)
cmds.rowColumnLayout('Icons', numberOfColumns=n, p="SLiBScrollLayout")
Thanks in advance
Daniel
I'm not surprised the rowColumnlayout complains if you give it zero columns: you'll always need 1. Depending on how the are setting up the gui , your query on saw might return 0, which would explain your problem.
Here's a very basic example of what it looks like your trying to do:
w = cmds.window()
c = cmds.columnLayout(adj=True)
v_slider = cmds.intSlider(min = 1, max =10, value=2)
h_slider = cmds.intSlider(min = 1, max =10, value=2)
scroll = cmds.scrollLayout()
form = cmds.formLayout()
def update_row(*_):
# get the row and column counts
rows = cmds.intSlider(v_slider, q=True, v= True)
columns = cmds.intSlider(h_slider, q=True, v= True)
# delete the old layout and rebuild.
# the 'or []` below lets you loop even if there are no children....
for n in cmds.formLayout(form, q=True, ca=True) or []:
cmds.deleteUI(n)
cmds.setParent(form)
# make a new rowColumn
new_row = cmds.rowColumnLayout(nc = columns)
for n in range(rows * columns):
cmds.button(label="button_%i" % n)
cmds.formLayout(form, e=True, af = [(new_row,'top',0), (new_row, 'bottom', 0 ), (new_row, 'left', 0 ), (new_row, 'right', 0)])
# both sliders call the update function
cmds.intSlider(h_slider, e=True, cc =update_row)
cmds.intSlider(v_slider, e=True, cc =update_row)
update_row() # do it once to get started
cmds.showWindow(w)
The key here is the order in which it's declared: the function knows what v_slider, h_slider and form are because it's declared after them and doesn't need to do extra work to find them (you can get a more controlled version of the same effect with a class. You'll notice I don't use the names at all: names are unreliable if there's an old layout lying around, it's simpler to use variables.
I've also set the minimum values for the row and columns to 1 so there's no zero division problem.
To start off, this is the code I wanted to use to present my problem in a simplified way, but turns out it actually works as I initially expected the thing to work:
list_a = []
list_b = []
for i in range(2):
list_a = [i, i+1]
list_b.append(list_a)
Now list_b is [[0, 1], [1, 2]], as I want it to be.
I'm trying to learn programming by making a simple Tetris clone with pygame, and I wanted to use a similar approach to parse a text file into a list containing rotations/frames of a single block (2d lists). I fill out a 2d frame, append it to frames which is the list of all frames, and then reuse frame. However, unlike in the above example, it seems that when frame is appended to frames, it's not the contents of frame, but a reference to frame (though that's probably not the correct term?). What is different about this that it behaves differently, and how do I make it work? Thanks in advance!
class Block:
object = {}
def __init__(self, block_type):
frame = [[0 for i in range(4)] for i in range(4)]
frames = []
frames_file = open('files\\%s.txt' % block_type, 'r')
for i, line in enumerate(frames_file):
if line.strip() == '----':
frames.append(frame)
print(frame)
print(frames)
print('Append!\n')
else:
for x, char in enumerate(line.strip()):
y = i % 5
if char == '#':
frame[x][y] = 1
elif char == '.':
frame[x][y] = 0
self.frame = frames
Block.object['T'] = Block('T')
Now all frames contain the same thing:
>>> Block.object['T'].frame[0] == Block.object['T'].frame[1]
True
Just for the record, files\T.txt is:
....
###.
.#..
....
----
.#..
##..
.#..
....
----
.#..
###.
....
....
----
.#..
.##.
.#..
....
----
After some digging around and a helpful (although incomplete) hint from a user who already deleted his answer, this is essentially a duplicate of Copying 2D lists in python
The problem here was twofold:
frames.append(frame) indeed only references frame in frames, I didn't attempt to copy it at any point
frames.append(frame[:] and frames.append(list(frame)), which are the usual ways to copy a list, don't work for nested lists in that form
The proper code is: frames.append(list(list(i) for i in frame))
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.
I would like to get the content of a list box thanks to python and ctypes.
item_count = ctypes.windll.user32.SendMessageA(hwnd, win32con.LB_GETCOUNT, 0, 0)
items = []
for i in xrange(item_count):
text_len = ctypes.windll.user32.SendMessageA(hwnd, win32con.LB_GETTEXTLEN, i, 0)
buffer = ctypes.create_string_buffer("", text_len+1)
ctypes.windll.user32.SendMessageA(hwnd, win32con.LB_GETTEXT, i, buffer)
items.append(buffer.value)
print items
The number of items is correct but the text is wrong. All text_len are 4 and the text values are something like '0\xd9\xee\x02\x90'
I have tried to use a unicode buffer with a similar result.
I don't find my error. Any idea?
If the list box in question is owner-drawn, this passage from the LB_GETTEXT documentation may be relevant:
If you create the list box with an owner-drawn style but without the LBS_HASSTRINGS style, the buffer pointed to by the lParam parameter will receive the value associated with the item (the item data).
The four bytes you received certainly look like they may be a pointer, which is a typical value to store in the per-item data.
It looks like you need to use a packed structure for the result. I found an example online, perhaps this will assist you:
http://www.brunningonline.net/simon/blog/archives/winGuiAuto.py.html
# Programmer : Simon Brunning - simon#brunningonline.net
# Date : 25 June 2003
def _getMultipleWindowValues(hwnd, getCountMessage, getValueMessage):
'''A common pattern in the Win32 API is that in order to retrieve a
series of values, you use one message to get a count of available
items, and another to retrieve them. This internal utility function
performs the common processing for this pattern.
Arguments:
hwnd Window handle for the window for which items should be
retrieved.
getCountMessage Item count message.
getValueMessage Value retrieval message.
Returns: Retrieved items.'''
result = []
VALUE_LENGTH = 256
bufferlength_int = struct.pack('i', VALUE_LENGTH) # This is a C style int.
valuecount = win32gui.SendMessage(hwnd, getCountMessage, 0, 0)
for itemIndex in range(valuecount):
valuebuffer = array.array('c',
bufferlength_int +
" " * (VALUE_LENGTH - len(bufferlength_int)))
valueLength = win32gui.SendMessage(hwnd,
getValueMessage,
itemIndex,
valuebuffer)
result.append(valuebuffer.tostring()[:valueLength])
return result
def getListboxItems(hwnd):
'''Returns the items in a list box control.
Arguments:
hwnd Window handle for the list box.
Returns: List box items.
Usage example: TODO
'''
return _getMultipleWindowValues(hwnd,
getCountMessage=win32con.LB_GETCOUNT,
getValueMessage=win32con.LB_GETTEXT)