I've discovered urwid recently and started to play around with it. I've seen a Menu like tutorial example (which works!) and I've tried to modify the code a bit so I can get back and forth in the menu for starters - without doing anything else.
However I got stuck - and Im at a loss on why its happening.
Running this code:
import urwid
choices = u'Tank_1 Tank_2 Tank_3 Solarkreis Exit'.split()
def menu(title, choices):
body = [urwid.Text(title), urwid.Divider()]
for c in choices:
button = urwid.Button(c)
if c == "Exit":
urwid.connect_signal(button, 'click', exit_program)
else:
urwid.connect_signal(button, 'click', item_chosen, c)
body.append(urwid.AttrMap(button, None, focus_map='reversed'))
return urwid.ListBox(urwid.SimpleFocusListWalker(body))
def item_chosen(button, choice):
response = urwid.Text([u'Uebersicht ', choice, u'\n'])
done = urwid.Button(u'Ok')
urwid.connect_signal(done, 'click', menu(u'Menu', choices))
main.original_widget = urwid.Filler(urwid.Pile([response,
urwid.AttrMap(done, None, focus_map='reversed')]))
def exit_program(button):
raise urwid.ExitMainLoop()
main = urwid.Padding(menu(u'Menu', choices), left=2, right=2)
top = urwid.Overlay(main, urwid.SolidFill(u'\N{MEDIUM SHADE}'),
align='center', width=('relative', 60),
valign='middle', height=('relative', 60),
min_width=20, min_height=9)
urwid.MainLoop(top, palette=[('reversed', 'standout', '')]).run()
Running it I get
TypeError: 'ListBox' object is not callable
And I done quite understand how and why.. Help is much appreciated!
Edit: Forgot to add, the error pops up when I try to go back from the sub - menu to the original menu.
line 22, the third parameter is a ListBox object not a callable function.
urwid.connect_signal(done, 'click', menu(u'Menu', choices))
Related
I have this very easy program which I want to display one random line from a file each time I click on the Button.
Problem is a new line is display at startup of the program, but nothing happens when I click the button, can someone explain me why ?
from random import randrange
from tkinter import *
def entree():
n=randrange(251)
fs = open('lexique','r')
liste = fs.readlines()
return liste[n]
fen = Tk()
fen.title("lexique anglais politique")
defi = StringVar()
defi.set(entree())
lab = Label(fen, textvariable=defi).pack()
Button(fen, text='Beste Bat', command=entree).pack()
fen.mainloop()
As stated in one of the comments (by #matszwecja), your entree() function doesn't really do anything appart from returning a value.
Nothing in your code updates the actual label. Try something like this :
from random import randrange
from tkinter import *
def entree():
n=randrange(251)
fs = open('lexique','r')
liste = fs.readlines()
return liste[n]
def update_label():
lab.config(text=entree())
fen = Tk()
fen.title("lexique anglais politique")
lab = Label(fen, text=entree())
lab.pack()
Button(fen, text='Beste Bat', command=update_label).pack()
fen.mainloop()
In this example, the entree() function is used to go get a line from your file, and the update_label() function is used to actually update the label.
Also, if you want to be able to update a label, you'll have to pack it after assigning it to a variable.
On a side note, it could be worth noting that hardcoding values that could change in the future is generally considered bad practice. In that regard, I think coding the entree() function this way might be a better idea :
def entree():
fs = open('lexique','r')
liste = fs.readlines()
n=randrange(len(liste))
return liste[n]
This way, if you ever add or remove lines to your "lexique" file, you will not have to change the code.
Trying to convert a simple tool that selects all hard edges of a mesh in Maya. Basically I'm trying to convert this Mel script to Python and give it a UI just for a bit of practise with Python scripting for Maya. I'm running into an issue where I'm feeding a function into a UI button but when I run the code it returns:
Error: TypeError: file line 18: Invalid arguments for flag 'c'. Expected string or function, got NoneType #
Here's the code:
import pymel.core as pm
def createWindow():
title = 'Hard Edge Selection'
if cmds.window(title, exists=True):
cmds.deleteUI(title, window=True)
else:
cmds.window(t = title)
cmds.rowColumnLayout(nc=2)
lowAngle = cmds.intSliderGrp( field=True, label='Low Angle', minValue=0, maxValue=30, value=30 )
highAngle = cmds.intSliderGrp( field=True, label='High Angle', minValue=90, maxValue=150, value=150 )
lowAngleSel = cmds.intSliderGrp( lowAngle, q=True, value=True)
highAngleSel = cmds.intSliderGrp( highAngle, q=True, value=True)
#_findEdges = findEdges("{0},{1}").format(lowAngleSel, highAngleSel)
cmds.button(l='Find Edges', c=findEdges(lowAngleSel, highAngleSel))
cmds.showWindow()
cmds.selectMode(o=True)
def findEdges(x, y):
currentSelection = cmds.ls(sl=True)
pm.mel.doMenuComponentSelectionExt(currentSelection[0], "edge", 0)
#edgecount = pm.polyEvaluate(currentSelection[0], e=1)
#pm.select(currentSelection[0] + ".e [0:" + str((edgecount[0] - 1)) + "]")
#Constrain that selection to only edges of a certain Angle
pm.polySelectConstraint(a=True, ab=(x, y), m=3, t=0x8000)
#Remove the selection constraint so everything back to normal
pm.polySelectConstraint(m=0)
print ("Hard edges between " + str(x) + " and " + str(y) + " degrees have been selected")
createWindow()
Your issue is with this line:
cmds.button(l='Find Edges', c=findEdges(lowAngleSel, highAngleSel))
findEdges(...) returns None, which is then attempted to bind to the command-callback for your button.
What you need to be doing is binding your callback to the actual function, like this:
cmds.button(l='Find Edges', c=findEdges)
However, that presents the issue of how to send your function parameters. Personally, I would suggest using a class for this and storing your slider groups on the class instance, but if you want to proceed down your current path you should look at lambda and partial implementations.
PS: I believe the button callback sends the button's name as an argument to the callback function, which you would need to capture (and probably ignore):
cmds.button(l='Find Edges', c=lambda x: findEdges(lowAngleSel, highAngleSel))
PPS: I'm not entirely sure about the scope of the lambda arguments above, it may be that they come up as undefined at callback time. In which case, you could fall back to partial (which acts as a factory, instead of anonymous method).
Some more information on Maya button callbacks: https://riptutorial.com/maya/example/25096/callback-functions
I'm writing a new code to display sqlite search results on buttons using loop. The code works correctly except for 1 issue. I wrote a function to change the background color and play a sound when hovering over the buttons. The problem is that when I hover over any button of the result, the color changes over the last button only although the sound plays without problem. Here's the part of the code involved:
SearchSelection = SearchVar.get()
SearchParameter = txtSearch.get()
conn = sqlite3.connect("EasyClinic.db")
cursor = conn.cursor()
cursor.execute ("SELECT * FROM patients WHERE (%s) = (?)" %(SearchSelection),
(SearchParameter,))
results = cursor.fetchall()
conn.commit()
conn.close()
if len(results) == 0:
print ("No result found")
else:
for result in results:
ResultsButtons = tk.Button(canvasResult, text=result, bg=BackGroundColor, fg=ForeGroundColor, relief='flat', font=MyFont2, width=65, height=2)
ResultsButtons.pack(pady=5)
def on_enter_results(result):
ResultsButtons['background'] = Hover
winsound.PlaySound ('Hover.wav', winsound.SND_ASYNC)
def on_leave_results(result):
ResultsButtons['background'] = BackGroundColor
ResultsButtons.bind("<Enter>", on_enter_results)
ResultsButtons.bind("<Leave>", on_leave_results)
Please for you assistance
Thanks
You can change the two functions, on_enter_results() and on_leave_results() as below:
def on_enter_results(event):
# event.widget is the widget that triggers this event
event.widget['background'] = Hover
winsound.PlaySound ('Hover.wav', winsound.SND_ASYNC)
def on_leave_results(event):
event.widget['background'] = BackGroundColor
Actually you can move these two functions outside of for loop (i.e. before the for loop).
The problem is that python always overwrites the ResultsButtons variable with the latest button, and this gives a problem when calling the funciton on_enter_results or on_leave_results, because it uses the overwritten ResultsButtons variable. The solution is to more directly specify the button and pass it to your function with a lambda function. I can't check if this code works because I don't have the full example, but something like this should work:
if len(results) == 0:
print ("No result found")
else:
def on_enter_results(event_info, resbutton):
resbutton['background'] = Hover
winsound.PlaySound ('Hover.wav', winsound.SND_ASYNC)
def on_leave_results(event_info, resbutton):
resbutton['background'] = BackGroundColor
for result in results:
ResultsButton = tk.Button(canvasResult, text=result, bg=BackGroundColor, fg=ForeGroundColor, relief='flat', font=MyFont2, width=65, height=2)
ResultsButton.pack(pady=5)
ResultsButton.bind("<Enter>", lambda event_info, btn=ResultsButton: on_enter_results(btn))
ResultsButton.bind("<Leave>", lambda event_info, btn=ResultsButton: on_leave_results(btn))
For a better understanding of what acually is going on: Python FAQ
It was solved by acw1668. Thank you all for your support
I have the following block of code.
class ChooseBook():
bookprice=2
def getqty(self, sv):
if self.txtbookqty.get()=="":
self.qty=0
else:
self.qty=int(self.sv.get())
self.txtbookprice.delete(0,END)
#print(self.qty)
self.txtbookprice.insert(0,self.qty*self.bookprice)
def on_CPOname_change(self,value,op,op2):
stname=self.comboBookname.get()
name=stname[1:-1]
book_data=Fun_Select("SELECT book_price FROM book_record WHERE book_name='"+name+"'")
#print(book_data)
self.bookprice=int(book_data[0][0])
def on_date_change(self,day):
if self.txtdaysborrowed.get()=="":
self.dayadd=0
else:
self.dayadd=int(self.day.get())
date=self.txtborrowdate.get()
self.dayindate=int(date[8:10])
self.yearindate=int(date[0:4])
self.monthindate=int(date[5:7])
if self.dayindate+self.dayadd > 31:
self.monthindate=self.monthindate+1
if self.monthindate > 12:
self.yearindate+=1
self.txtrd.insert(0,self.dayindate+'-'+self.monthindate+'-'+self.yearindate)
def __init__(self):
today_date=datetime.date.today()
win=Tk()
win.title("Choose book type")
win.geometry("600x800")
v=StringVar()
d=StringVar()
v.trace('w', self.on_CPOname_change)
self.day=StringVar()
self.day.trace('w',lambda name, index, mode, day=self.day: self.on_date_change(day))
self.sv = StringVar()
self.sv.trace("w", lambda name, index, mode, sv=self.sv: self.getqty(sv))
Label(win,text="Choose Book Name").grid(row=0,column=0,padx="1.5c",pady="1c")
Label(win,text="Enter Book Quantity").grid(row=1,column=0,padx="1.5c",pady="1c")
Label(win,text="Total Book Price").grid(row=2,column=0,padx="1.5c",pady="1c")
Label(win,text="Borrowed Date").grid(row=3,column=0,padx="1.5c",pady="1c")
Label(win,text="Days borrowed").grid(row=4,column=0,padx="1.5c",pady="1c")
Label(win,text="Return Date").grid(row=5,column=0,padx="1.5c",pady="1c")
Label(win,text="Choose Employee Name").grid(row=6,column=0,padx="1.5c",pady="1c")
Label(win,text="Choose Customer Name").grid(row=7,column=0,padx="1.5c",pady="1c")
#bookname
self.comboBookname=ttk.Combobox(win, textvar=v)
self.comboBookname["values"]=Fun_Select("SELECT book_name FROM book_record")
self.comboBookname.grid(row=0,column=1,pady="1c")
#bookqty
self.txtbookqty=Entry(win,textvariable=self.sv)
self.txtbookqty.grid(row=1,column=1,pady="1c")
#bookprice
self.txtbookprice=Entry(win)
self.txtbookprice.grid(row=2,column=1,pady="1c")
#borrowdate
self.txtborrowdate=Entry(win,textvariable=d,state=DISABLED)
d.set(today_date)
self.txtborrowdate.grid(row=3,column=1,pady="1c")
#daysborrowed
self.txtdaysborrowed=Entry(win,textvariable=self.day)
self.day.set(0)
self.txtdaysborrowed.grid(row=4,column=1,pady="1c")
#returndate
self.txtrd=Entry(win)
self.txtrd.grid(row=5,column=1,pady="1c")
#employeename
self.comboEmployeename=ttk.Combobox(win)
self.comboEmployeename["values"]=Fun_Select("SELECT employee_name FROM employees")
self.comboEmployeename.grid(row=6,column=1,pady="1c")
#customername
self.comboCustomername=ttk.Combobox(win)
self.comboCustomername["values"]=Fun_Select("SELECT customer_name FROM customers")
self.comboCustomername.grid(row=7,column=1,pady="1c")
Button(win,text="Exit",width=10,command=win.destroy).grid(row=8,column=0,padx="1.5c",pady="1c")
Button(win,text="Save",width=10,command=None).grid(row=8,column=1,padx="1.5c",pady="1c")
win.mainloop()
ChooseBook()
But I get always error which says: AttributeError: 'ChooseBook' object has no attribute 'txtrd'. It seems that problem is somewhere at self.txtrd.insert(0,self.dayindate+'-'+self.monthindate+'-'+self.yearindate)
Also i'm quite new so my code might be very messy.
Here is a quick fix to THIS problem, just move the declaration of self.txtrd above self.day.set(0).
def __init__(self):
....#same bunch of code
self.txtrd=Entry(win)
self.txtrd.grid(row=5,column=1,pady="1c")
....#same bunch of code
self.day.set(0)
....#same bunch of code
You are using trace with self.day which means whenever you change the value of StringVar() it triggers the function, and later in code you are saying self.day.set(0)(value changing) so the trace becomes active and the function is called, before the self.txtrd is defined and hence the error.
Though after solving this error you will get a ValueError at:
self.txtrd.insert(0,self.dayindate+'-'+self.monthindate+'-'+self.yearindate)
To fix that do simple type casting like:
self.txtrd.insert(0,str(self.dayindate)+'-'+str(self.monthindate)+'-'+str(self.yearindate))
Though I highly doubt if you might get another error(unexpected behavior) over setting the values for the Combobox, anyway that can be asked as a new post, as one post should only focus around one problem.
I'm building a toolbox UI using python in Maya, and I keep on getting a Nonetype error when I call one of the imported functions. This is the script for the toolbox:
class Toolbox():
import maya.cmds as cmds
def __init__(self):
self.window_name = "mlToolbox"
def create(self):
self.delete()
self.window_name = cmds.window(self.window_name)
self.m_column = cmds.columnLayout(p = self.window_name, adj = True)
cmds.button(p=self.m_column,label = 'MyButton', c=lambda *arg: cmds.polySphere(r = 2))
cmds.button(p=self.m_column, label = 'Make_Control', command = lambda *args: self.ControlBTN())
cmds.button(p=self.m_column, label = 'Find Center of All Selected', command = lambda *args: self.CenterBTN())
cmds.button(p=self.m_column, label = 'Find Center of Each Selected Object', command = lambda *args: self.IndiCenterBTN())
self.colorname = cmds.textField(placeholderText = 'Enter color name...')
cmds.button(p=self.m_column, label = 'ChangeColor', command = lambda *args: self.colorBtn())
self.MinAndMax = cmds.textField()
cmds.button(p=self.m_column, label = 'Random Scatter', command = lambda *args: self.ScatterBTN())
cmds.showWindow(self.window_name)
cmds.button(p=self.m_column, label = 'Select Everything', command = lambda *args: self.selectBTN())
def CenterBTN(self):
import CenterSelected
CenterSelected.Locator()
def ScatterBTN(self):
import Scatter
value = cmds.textField(self.MinAndMax, q=True)
Scatter.RandomScatter(value)
cmds.intField(self.moveMin, self.moveMax, self.rotMin, self.rotMax, self.scaleMin, self.scaleMax, e=True, text='')
def IndiCenterBTN(self):
import ManySelected
ManySelected.LocatorMany()
def colorBtn(self):
import ColorControl
value = cmds.textField(self.colorname, q=True, text = True)
ColorControl.colorControl(value)
cmds.textField(self.colorname, e=True, text='')
def selectBTN(self):
import tools
tools.selectAll()
def delete(self):
if cmds.window(self.window_name, exists=True):
cmds.deleteUI(self.window_name)
def ControlBTN(self):
import CreateControl
CreateControl.createControl()
myTool = Toolbox()
myTool.create()
And this is the function that I'm having trouble with:
def RandomScatter(MinAndMax):
import random
import maya.cmds as cmds
Stuff = cmds.ls(sl=True)
i=0
for i in range(random.randint(1,100)):
Stuff.append(cmds.duplicate(Stuff))
cmds.move( (random.randint(MinAndMax[0], MinAndMax[1])), (random.randint(MinAndMax[0], MinAndMax[1])), (random.randint(MinAndMax[0], MinAndMax[1])), Stuff[i], absolute=True )
cmds.rotate( (random.randint(MinAndMax[2], MinAndMax[3])), (random.randint(MinAndMax[2], MinAndMax[3])), (random.randint(MinAndMax[2], MinAndMax[3])), Stuff[i], absolute=True )
cmds.scale( (random.randint(MinAndMax[4], MinAndMax[5])), (random.randint(MinAndMax[4], MinAndMax[5])), (random.randint(MinAndMax[4], MinAndMax[5])), Stuff[i], absolute=True )
i = i+1
RandomScatter() works fine as long as I call it on it's own using a RandomScatter([a, b, c, d, e, f]) format, but when I try to call it Toolbox(), I get "Scatter.py line 21: 'NoneType' object has no attribute 'getitem'" as an error. It also happens when I try to use the intField() command instead of textField(). The UI window builds just fine; the error only happens after I enter input into the text field and press the button that's supposed to call RandomScatter(). It seems like the input isn't making it to the MinAndMax list, so when it reaches "cmds.move( (random.randint(MinAndMax[0]," it can't find anything to put in the MinAndMax[0] slot, or any of the slots after that, but I can't figure out why. Does anyone have any advice?
I didn't test your code and didn't read it totally, but I can already say that your strange "lambda" usage doesn't make any sens.
lambda *args: self.ControlBTN()
this lambda execute self.ControlBTN during the cmds.button definition and provide to it a function which return None.
that like doing that:
self.ControlBTN()
function = def func(): return None
cmds.button(p=self.m_column, label = 'Make_Control', command=function)
I advise you to reread the documentation about the "python lambda".
replace this by:
cmds.button(p=self.m_column, label = 'Make_Control', command=self.ControlBTN)
...
def ControlBTN(self, *args)
...
That should help
good luck
As written self.MinAndMax is a text field; even if you get the value from it it'll be a string and you won't be able to index into it to get the individual items -- and your indexing will be thrown off if any of the numbers are negative or have decimals. The lazy solution is to use a FloatFieldGrp which lets you have 2-4 numberic inputs. It's a bit annoying to get at all of the values at once (see the way it's done below) but it will avoid many issues with trying to parse the text field.
Also, this line doesn't make sense in context:
cmds.intField(self.moveMin, self.moveMax, self.rotMin, self.rotMax, self.scaleMin, self.scaleMax, e=True, text='')
You're not seeing the error because it's failing in the previous line, but the first argument ought to be the name of an existing intField.
In any case, I'd refactor this a bit to keep the argument parsing out of the scatter function, so you can separate out the working logic from the UI parsing logic. It'll make it much easier to spot where things have gone off the rails:
import random
import maya.cmds as cmds
class TestGUI(object):
def __init__(self):
self.window = cmds.window()
self.layout = cmds.rowLayout(nc=3)
self.min_xyz = cmds.floatFieldGrp( numberOfFields=3, label='min', value1=-10, value2=-10, value3=-10 )
self.max_xyz= cmds.floatFieldGrp( numberOfFields=3, label='min', value1=10, value2=10, value3=10 )
cmds.button(label='scatter', c = self.scatter)
cmds.showWindow(self.window)
def scatter(self, *_):
selected = cmds.ls(sl=True)
if not selected:
cmds.warning("select something")
return
min_xyz = (
cmds.floatFieldGrp(self.min_xyz, q=True, v1=True),
cmds.floatFieldGrp(self.min_xyz, q=True, v2=True),
cmds.floatFieldGrp(self.min_xyz, q=True, v3=True)
)
max_xyz = (
cmds.floatFieldGrp(self.max_xyz, q=True, v1=True),
cmds.floatFieldGrp(self.max_xyz, q=True, v2=True),
cmds.floatFieldGrp(self.max_xyz, q=True, v3=True)
)
print "scatter settings:", min_xyz, max_xyz
rand_scatter(selected, min_xyz, max_xyz)
def rand_scatter(selection, min_xyz, max_xyz):
dupe_count = random.randint(1, 10)
duplicates = [cmds.duplicate(selection) for n in range(dupe_count)]
for dupe in duplicates:
destination = [random.randint(min_xyz[k], max_xyz[k]) for k in range(3)]
cmds.xform(dupe, t=destination, absolute=True)
print (dupe, destination)
t = TestGUI() # shows the window; hit 'scatter' to test
My version changes your logic a bit -- you were duplicating your selection list, which would cause the number of items to grow exponentially (as each duplication would also duplicate the previous dupes). I'm not sure if that's inline with your intention or not.
You can avoid the extra lambdas by including a *_ in the actual button callback; it's a minor maya irritant that buttons always have that useless first argument.
As an aside, I'd try not to do imports inside of function bodies. If the imported module is not available, it's better to know at the time this file is imported rather than only when the user clicks a button -- it's much easier to spot a missing module if you do all your imports in a block at the top.