I'm trying to make a custom script that replicates/imitates the Maya Sculpt Geometry Tool. Basically I have 2 radio buttons, Push and Relax[which imitates the push and relax from the sculpt parameters obviously], a value slider[replicates the max displacement slider]. The radio and reset button works perfectly however I'm having problems with coding the slider. Any help for this one? Thanks in advance.
Please see images for further clarification.
Click here for image
Here is my code:
import maya.cmds as cmds
import maya.mel as mel
if cmds.window("cusWin", exists = True):
cmds.deleteUI("cusWin")
customwindow = cmds.window("cusWin",t= "Push/Relax", wh = (200, 117), s= False, mnb= False, mxb= False)
cmds.frameLayout( label='Push/Relax Modifier', borderStyle='in', cll= False)
cmds.columnLayout(adj = True, columnOffset= ("both", 3))
cmds.radioCollection()
cmds.radioButton(l = "Push", onc= "mel.eval('artUpdatePuttyOperation artPuttyCtx push ;')")
cmds.radioButton(l = "Relax", onc= "mel.eval('artUpdatePuttyOperation artPuttyCtx relax ;')")
cmds.separator(style= "none", h= 3)
DynFsgCol1 = 30
DynFsgCol2 = 50
DynFsgCol3 = 100
valSlider = cmds.floatSliderGrp(l = "Value", field = True, min = 0, max= 5, precision = 4, cw3= (DynFsgCol1, DynFsgCol2, DynFsgCol3 ))
cmds.separator(style= "none", h= 3)
cmds.rowColumnLayout(numberOfColumns=2, columnWidth=[(1,98),(2,100)], columnOffset=[(1,'left',1),(2,'right',95)])
cmds.button(l = "Apply", w= 92, c= 'slider()')
cmds.button(l = "Reset", w= 91, c= 'resetButton()')
cmds.showWindow( customwindow )
def slider():
valueSlider = cmds.floatSliderGrp(valSlider, q= True, value= True)
mel.eval('artPuttyCtx -e -maxdisp valueSlider `currentCtx`;')
def resetButton():
mel.eval('resetTool artPuttyContext;')
There's two different things going on here.
First, by using the string form of the callbacks you lose control over the scope of your functions. It's better to pass the python objects directly. This version does what it looks like your's is intended to do using the callbacks:
import maya.cmds as cmds
import maya.mel as mel
if cmds.window("cusWin", exists = True):
cmds.deleteUI("cusWin")
customwindow = cmds.window("cusWin",t= "Push/Relax", wh = (200, 117), s= False, mnb= False, mxb= False)
cmds.frameLayout( label='Push/Relax Modifier', cll= False)
cmds.columnLayout(adj = True, columnOffset= ("both", 3))
cmds.radioCollection()
push = lambda _: cmds.artPuttyCtx(cmds.currentCtx(), e = True, mtm='push')
relax = lambda _: cmds.artPuttyCtx(cmds.currentCtx(), e = True, mtm='relax')
cmds.radioButton(l = "Push", onc= push)
cmds.radioButton(l = "Relax", onc=relax)
cmds.separator(style= "none", h= 3)
DynFsgCol1 = 30
DynFsgCol2 = 50
DynFsgCol3 = 100
valSlider = cmds.floatSliderGrp(l = "Value", field = True, min = 0, max= 5, precision = 4, cw3= (DynFsgCol1, DynFsgCol2, DynFsgCol3 ))
cmds.separator(style= "none", h= 3)
cmds.rowColumnLayout(numberOfColumns=2, columnWidth=[(1,98),(2,100)], columnOffset=[(1,'left',1),(2,'right',95)])
# put the defs here, where the names of the slider are known
def slider(*_):
valueSlider = cmds.floatSliderGrp(valSlider, q = True, value = True)
cmds.artPuttyCtx(cmds.currentCtx(), e = True, maxdisp = valueSlider)
def resetButton(*_):
cmds.resetTool(cmds.currentCtx())
cmds.button(l = "Apply", w= 92, c= slider)
cmds.button(l = "Reset", w= 91, c= resetButton)
cmds.showWindow( customwindow )
The thing to note is that order in which things are defined allows you to reference the names of controls you've made. (As an aside, you need to handle the nonsense arguments Maya passes on the button and slider callbacks). It's also a good idea to clean up dangling Mel scripts where you can, if you want to make this tool more complex in future it's much harder if you have to work in two languages at once. In this case artUpdatePuttyOperation looks like it's defined when the sculpt tool UI opens, so if you ran this script without the UI I don't think it would work correctly.
The second issue is that you're not explicitly setting an artPuttyCtx with setTool, so this won't work unless the user has already created the right context and activated it.
Related
I got 2 functions to get the values of the HDD drives. I am using tkinter and I can access the graphics with the buttons. But, after I click on one, I can't seem to remove the previous.
The objective is: if I click the "C Drive" button, I erase the E graph, and if I click on the "E" drive, I erase the C graph.
#Disk E Storage
def hdd_e():
usage_e=shutil.disk_usage("E:\\")
total_space_e = usage_e[0]
used_space_e = usage_e[1]
free_space_e =usage_e[2]
fig_e = matplotlib.figure.Figure(figsize=(50, 5), facecolor="#F0F0F0")
canvas_e = FigureCanvasTkAgg(fig_e, master=tab3)
ax_e = fig_e.add_subplot(111)
ax_e.pie([total_space_e, used_space_e, free_space_e])
ax_e.legend(["Total", "Used", "Free"])
circle_e = matplotlib.patches.Circle((0, 0), 0)
ax_e.add_artist(circle_e)
canvas_e.get_tk_widget().pack()
canvas_e.draw()
#Disk C Storage
def hdd_c():
usage_c=shutil.disk_usage("C:\\")
total_space_c = usage_c[0]
used_space_c = usage_c[1]
free_space_c =usage_c[2]
fig_c = matplotlib.figure.Figure(figsize=(50, 5), facecolor="#F0F0F0")
canvas_c = FigureCanvasTkAgg(fig_c, master=tab3)
ax_c = fig_c.add_subplot(111)
ax_c.pie([total_space_c, used_space_c, free_space_c ])
ax_c.legend(["Total", "Used", "Free"])
circle_c = matplotlib.patches.Circle((0, 0), 0)
ax_c.add_artist(circle_c)
canvas_c.get_tk_widget().pack()
canvas_c.draw()
Let me start by posting some little helper functions I'll use to formulate my questions:
import textwrap
import sys
from pathlib import Path
from PyQt5.Qsci import QsciScintilla
from PyQt5.Qt import * # noqa
def set_style(sci):
# Set default font
sci.font = QFont()
sci.font.setFamily('Consolas')
sci.font.setFixedPitch(True)
sci.font.setPointSize(8)
sci.font.setBold(True)
sci.setFont(sci.font)
sci.setMarginsFont(sci.font)
sci.setUtf8(True)
# Set paper
sci.setPaper(QColor(39, 40, 34))
# Set margin defaults
fontmetrics = QFontMetrics(sci.font)
sci.setMarginsFont(sci.font)
sci.setMarginWidth(0, fontmetrics.width("000") + 6)
sci.setMarginLineNumbers(0, True)
sci.setMarginsForegroundColor(QColor(128, 128, 128))
sci.setMarginsBackgroundColor(QColor(39, 40, 34))
sci.setMarginType(1, sci.SymbolMargin)
sci.setMarginWidth(1, 12)
# Set indentation defaults
sci.setIndentationsUseTabs(False)
sci.setIndentationWidth(4)
sci.setBackspaceUnindents(True)
sci.setIndentationGuides(True)
sci.setFoldMarginColors(QColor(39, 40, 34), QColor(39, 40, 34))
# Set caret defaults
sci.setCaretForegroundColor(QColor(247, 247, 241))
sci.setCaretWidth(2)
# Set edge defaults
sci.setEdgeColumn(80)
sci.setEdgeColor(QColor(221, 221, 221))
sci.setEdgeMode(sci.EdgeLine)
# Set folding defaults (http://www.scintilla.org/ScintillaDoc.html#Folding)
sci.setFolding(QsciScintilla.CircledFoldStyle)
# Set wrapping
sci.setWrapMode(sci.WrapNone)
# Set selection color defaults
sci.setSelectionBackgroundColor(QColor(61, 61, 52))
sci.resetSelectionForegroundColor()
# Set scrollwidth defaults
sci.SendScintilla(QsciScintilla.SCI_SETSCROLLWIDTHTRACKING, 1)
# Current line visible with special background color
sci.setCaretLineBackgroundColor(QColor(255, 255, 224))
# Set multiselection defaults
sci.SendScintilla(QsciScintilla.SCI_SETMULTIPLESELECTION, True)
sci.SendScintilla(QsciScintilla.SCI_SETMULTIPASTE, 1)
sci.SendScintilla(QsciScintilla.SCI_SETADDITIONALSELECTIONTYPING, True)
def set_state1(sci):
sci.clear_selections()
base = "line{} state1"
view.setText("\n".join([base.format(i) for i in range(10)]))
for i in range(0, 10, 2):
region = (len(base) * i, len(base) * (i + 1) - 1)
if i == 0:
view.set_selection(region)
else:
view.add_selection(region)
def set_state2(sci):
base = "line{} state2"
view.setText("\n".join([base.format(i) for i in range(10)]))
for i in range(1, 10, 2):
region = (len(base) * i, len(base) * (i + 1) - 1)
if i == 1:
view.set_selection(region)
else:
view.add_selection(region)
class Editor(QsciScintilla):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
set_style(self)
def clear_selections(self):
sci = self
sci.SendScintilla(sci.SCI_CLEARSELECTIONS)
def set_selection(self, r):
sci = self
sci.SendScintilla(sci.SCI_SETSELECTION, r[1], r[0])
def add_selection(self, r):
sci = self
sci.SendScintilla(sci.SCI_ADDSELECTION, r[1], r[0])
def sel(self):
sci = self
regions = []
for i in range(sci.SendScintilla(sci.SCI_GETSELECTIONS)):
regions.append(
sci.SendScintilla(sci.SCI_GETSELECTIONNSTART, i),
sci.SendScintilla(sci.SCI_GETSELECTIONNEND, i)
)
return sorted(regions)
I've got a couple of questions actually:
Question 1)
if __name__ == '__main__':
app = QApplication(sys.argv)
view = Editor()
set_state1(view)
view.move(1000, 100)
view.resize(800, 300)
view.show()
app.exec_()
I'll get this (you can see the question in the below snapshot):
Question 2)
if __name__ == '__main__':
app = QApplication(sys.argv)
view = Editor()
set_state1(view)
set_state2(view)
view.move(1000, 100)
view.resize(800, 300)
view.show()
app.exec_()
How can I modify the code so I'll be able to restore state1 when pressing ctrl+z?
Right now when using ctrl+z you won't be able to get state1:
mainly because how setText behaves:
Replaces all of the current text with text. Note that the undo/redo
history is cleared by this function.
I've already tried some of the functions posted in the undo and redo docs but no luck so far.
For instance, one of my attempts has been first selecting all text and then using replaceSelectedText and finally restoring the selections from the previous state manually, the result was ugly (i don't want the editor scrolling messing up when undoing/redoing)... Basically, I'd like to get the same feeling than SublimeText.
Btw, this is a little minimal example but in the real-case I'll be accumulating a bunch of operations without committing to scintilla very often... that's why I'm interested to figure out how to rollback to a previous state when using the undoable setText... Said otherwise, i'd like to avoid using Scintilla functions such as insertAt, replaceSelectedText or similars... as I'm using python string builtin functions to modify the buffer internally.
EDIT:
I'm pretty sure beginUndoAction & endUndoAction won't help me to answer question2 but... what about SCI_ADDUNDOACTION? Although the docs are pretty confusing though... :/
Question 1:
The last selection added is automatically set as the Main selection. To remove it, add line sci.SendScintilla(sci.SCI_SETMAINSELECTION, -1) at the end of the set_state1 function.
Question 2:
The way you described it by storing the selections, using the replaceSelectedText, and then using setCursorPosition / reselecting all selections and setFirstVisibleLine to restore the scroll position is one way to go.
Looking at the C++ source of the setText function:
// Set the given text.
void QsciScintilla::setText(const QString &text)
{
bool ro = ensureRW();
SendScintilla(SCI_SETTEXT, ScintillaBytesConstData(textAsBytes(text)));
SendScintilla(SCI_EMPTYUNDOBUFFER);
setReadOnly(ro);
}
You could try setting the text using sci.SendScintilla(sci.SCI_SETTEXT, b"some text"), which doesn't reset the undo/redo buffer.
(New Coder) I'm trying it create a basic GUI which allows the user to create a sphere and choose the radius and rotation. I managed to do the radius however i am a bit stuck with the rotation; the xform command. Like i said the radius is working but not the rotation.
Heres the GUI at the moment: https://gyazo.com/5f3626443334da1abdecaea434fe4e03
Any help appreciated:)
Heres my code:
import maya.cmds as cmds
def shapeTool ():
ram = 'RenamerWin'
if cmds.window(ram, q = True, exists =True):
cmds.deleteUI(ram)
ram = cmds.window("RenamerWin",t = "Shape Tool", w=300, h=300)
cmds.columnLayout(adj = True)
cmds.separator(h=20)
cmds.text("Welcome to the Shape Creator")
cmds.separator(h=20)
sphR = cmds.intSliderGrp(l = "Radius", min =0, max = 10, field = True)
sphA = cmds.intSliderGrp(l = "Rotate", field = True)
def mySphere(_):
mySphereRadius = cmds.intSliderGrp(sphR , q= True,value =True)
mySphereRotate = cmds.intSliderGrp(sphA , q= True,value =True)
finalSphere = cmds.polySphere(r=mySphereRadius, n = "mySphere")
finalSphere = cmds.xform( r=True, ro=(0, 90, 0) )
cmds.button(l = "Create a Sphere",c=mySphere)
cmds.showWindow(ram)
shapeTool()
Your rotate function will always rotate the new sphere 90 degrees in Y. Is that the intent? it ignores the rotate slider.
As written, it will rotate the sphere you create -- but for clarity and to understand what's really going on you might want to do this:
finalSphere, finalShape = cmds.polySphere(r=mySphereRadius, n = "mySphere")
cmds.xform(finalSphere, r=True, ro=(0, mySphereRotate, 0))
I have written up a simple UI that requires user to select something from a drop-down list, then using that selection, the code will executes the rest of the stuff
Right now, I am having 2 issues..
1. The 'value' is not exactly returning, as soon as a Format is selected and the OK button is hit... Am I missing something?
2. How can I make my UI closes upon a OK button has been selected?
import maya.cmds as cmds
def mainCode():
...
...
print "UI popping up"
showUI()
print "A format has been selected"
cmds.optionMenu('filmbackMenu', edit = True, value = xxx ) # <-- I want to grab the value from the menu selection and input into this 'value' flag
...
...
def showUI():
if cmds.window("UI_MainWindow", exists = True):
cmds.deleteUI("UI_MainWindow")
cmds.window("UI_MainWindow", title = "User Interface Test", w = 500, h = 700, mnb = False, mxb = False, sizeable = False)
cmds.columnLayout("UI_MainLayout", w = 300, h =500)
cmds.optionMenu("UI_FormatMenu", w = 250, label = "Select a Format")
list01 = ['itemA-01', 'itemB-02', 'itemC-02', 'itemD-01', 'itemE-01', 'itemF-03']
for x in list01:
cmds.menuItem(label = str(x))
cmds.button("UI_SelectButton", label = "OK", w = 200, command=ObjectSelection)
cmds.showWindow("UI_MainWindow") #shows window
def ObjectSelection(*args):
currentFormat = cmds.optionMenu("UI_FormatMenu", query=True, value=True)
print currentFormat
return currentFormat
Use python dictionnaries or class to pass data in your script.
I really don't understand what the problem is.
When you say : "The 'value' is not exactly returning", what do you mean ? Can you tell us what do you get and what do you expect ?
def mainCode():
...
...
showUI()
cmds.optionMenu('filmbackMenu', edit = True, value = xxx ) # <-- I want to grab the value from the menu selection and input into this 'value' flag
...
Here the value selection from the "input value", I guess it is :
cmds.optionMenu('filmbackMenu', edit = True, value = ObjectSelection())
But as there is not filmbackMenu in your code, I'm not sure.
Your second question has been answered on google groups by Justin. You just have to do :
def ObjectSelection(*args):
currentFormat = cmds.optionMenu("UI_FormatMenu", query=True, value=True)
cmds.deleteUI("UI_MainWindow")#close the UI
print currentFormat
return currentFormat
Or maybe "upon a OK button has been selected?" doesn't mean "OK button pressed" ?
If you want to see how use dictionnaries, you can read this other post where I have answered : Maya Python - Using data from UI
You are in a good path to using partial, I recommend you to read about it : Calling back user input values inside maya UI
--- EDIT ---
I tried to create a fully functionnal example :
import maya.cmds as cmds
uiDic = {}
uiDic['this']= 1
def ui_refresh(*args):
uiDic['this'] = cmds.optionMenu("UI_FormatMenu", query=True, value=True)
return uiDic['this']
def showUI():
if cmds.window("UI_MainWindow", exists = True):
cmds.deleteUI("UI_MainWindow")
cmds.window("UI_MainWindow", title = "User Interface Test", w = 500, h = 700, mnb = False, mxb = False, sizeable = False)
cmds.columnLayout("UI_MainLayout", w = 300, h =500)
cmds.optionMenu("UI_FormatMenu", w = 250, label = "Select a Format")
list01 = ['itemA-01', 'itemB-02', 'itemC-02', 'itemD-01', 'itemE-01', 'itemF-03']
for x in list01:
cmds.menuItem(label = str(x))
cmds.button("UI_SelectButton", label = "OK", w = 200, command=ObjectSelection)
uiDic['om_filmback'] = cmds.optionMenu('filmbackMenu' )
list01 = ['itemA-01', 'itemB-02', 'itemC-02', 'itemD-01', 'itemE-01', 'itemF-03']
for x in list01:
cmds.menuItem(label = str(x))
cmds.showWindow("UI_MainWindow") #shows window
def ObjectSelection(*args):
cmds.optionMenu(uiDic['om_filmback'], edit = True, value=ui_refresh())
showUI()
I've only just started coding, so I thought I would try and make something simple, however, I can't select the objects from my ls, I know the error is in my def attrLockT and was wondering if anyone could help me to fix this issue and understand what I am doing wrong?
import maya.cmds as cmds
#Selects the attributes
sat = ['.tx', '.ty', '.tz']
sar = ['.rx', '.ry', '.rz']
sas = ['.sx', '.sy', '.sz']
#Creates the list of currently selected objects
myList = cmds.ls(sl = True)
#Lock the translate attributes of the selected objects
def attrLockT(*args):
checkAttr=cmds.getAttr (myList[0] + sat)
if (checkAttr == 0):
cmds.setAttr(myList[0] + sat, lock = 1)
#Delete window if it is already open
if cmds.window('animationCtrl', exists=True):
cmds.deleteUI('animationCtrl', window=True)
#Setup the window
cmds.window(
'animationCtrl',
title = "Animation Controls",
widthHeight = (300, 500),
s = False)
form = cmds.formLayout()
tabs = cmds.tabLayout(innerMarginWidth=5, innerMarginHeight=5)
cmds.formLayout(
form,
edit=True,
attachForm=(
(tabs, 'top', 0),
(tabs, 'left', 0),
(tabs, 'bottom', 0),
(tabs, 'right', 0)))
#Layout for the first tab
child1 = cmds.gridLayout( numberOfRowsColumns=(4, 3) , cwh = (100, 50))
cmds.text(label = "")
cmds.text(label = "Lock", align = "center", h = 20, w = 250)
cmds.text(label = "")
cmds.button(label = "Translate", h = 300, w = 250, c = attrLockT)
cmds.button(label = "Rotate", h = 50, w = 250)
cmds.button(label = "Scale", h = 50, w = 250)
cmds.text(label = "")
cmds.text(label = "Unlock", align = "center", h = 20, w = 250)
cmds.text(label = "")
cmds.button(label = "Translate", h = 50, w = 250)
cmds.button(label = "Rotate", h = 50, w = 250)
cmds.button(label = "Scale", h = 50, w = 250)
cmds.setParent( '..' )
#Layout for the second tab
child2 = cmds.rowColumnLayout(numberOfColumns=3)
cmds.button()
cmds.button()
cmds.button()
cmds.setParent( '..' )
cmds.tabLayout(
tabs,
edit=True,
tabLabel=((child1, 'Lock/Unlock'), (child2, 'Keyable/Unkeyable')))
cmds.showWindow('animationCtrl')
The error that is thrown is
# Error: coercing to Unicode: need string or buffer, list found
# Traceback (most recent call last):
# File "<maya console>", line 16, in attrLockT
# TypeError: coercing to Unicode: need string or buffer, list found
Does this work?
myList[0] + sat
Is myList[0] type of list ? Because the sat variable is certainly list.
If myList is just a list of string then myList[0] will be just one element of type string and it will produce an error.
Simplify your program, just leave only locking routine and window with button to see what will happen, be sure that the right name of an object + attribute is passed to getAttr - just string like 'obj.attrib'.
Some python specific clues for your function
If you need to sum two lists:
[ objName + attName for objName, attName in zip(myList, sat) ]
that will result, for example, in ['obj1.tx', 'obj2.ty', 'obj3.tz']
If you need apply a list of attributes to an object:
[ myList[0] + a for a in sat ]
that will result in ['obj1.tx', 'obj1.ty', 'obj1.tz']
If you need apply the same list of attributes to all objects:
[ objName + attName for objName in myList for attName in sat ]
will result in ['obj1.tx', 'obj1.ty', 'obj1.tz', 'obj2.tx', ..., 'obj3.tz']
Then you can call your locking function over that result list:
def locker(a):
checkAttr = cmds.getAttr(a)
if (checkAttr == 0):
cmds.setAttr(a, lock = 1)
and finally it should look:
def attrLockT(*args):
atrlist = [ ..... ]
for a in atrlist:
locker(a)
Two issues:
first, you want to loop through the individual attributes and concatenate them with object names:
def lockTranslate(*args):
for obj in mylist:
for attr in ['.tx', '.ty', '.tz']:
cmds.setAttr(obj + "." + attr, l=True)
second, and maybe more important, you will likely have problems the scope of your functions. In the form you have it typed in, variables like myList and sat, etc are accessible to the function through closure - it will work if you execute all of this in the listener, but if you break the closure (for example, if this goes into a function that gets called by another function) things wont work -- myList will be stuck pointing at whatever was selected when the function was defined.
In this particular case you probably want to just operate on selection instead of inheriting myList:
def lockTranslate(*args):
for obj in cmds.ls(sl=True, type = 'transform'):
for attr in ['.tx', '.ty', '.tz']:
cmds.setAttr(obj + "." + attr, l=True)