Tôt ou tard, il fallait bien que je mette les doigts dans le Python!
Je remercie donc au passage gr4ph0s pour ces tutos sur le langage Python.
Les tutos en Français et destinés aux débutants étant pour ainsi dire quasi-inexistants je salue cette initiative qui m'a permis d'entre apercevoir le coté obscure de C4D.
Comme premier exercice, je me suis intéressé au menu filtre (d'affichage) dans le viewport qui pour moi est archaïque : menu déroulant qui disparait dès qu'on a coché un élément… impossibilité de transformer le menu en palette dockable… impossibilité d'affecter un raccourci clavier à chaque filtre!
J'en suis donc venu à écrire ceci :
Ce qui m'amène donc à plusieurs questions:Code PHP:
import c4d
bd = doc.GetActiveBaseDraw()
displayFilter = bd.GetDisplayFilter()
if displayFilter & c4d.DISPLAYFILTER_GRID:
bd[c4d.BASEDRAW_DISPLAYFILTER_GRID] = False
print "Desactive le filtre grille"
else:
bd[c4d.BASEDRAW_DISPLAYFILTER_GRID] = True
print "Active le filtre grille"
bd.Message(c4d.MSG_CHANGE)
c4d.EventAdd()
1- mon code est-il correct ou est-ce une catastrophe?
2- une fois mon script Python enregistré, une icône et un raccourci créé comme sur l'image ci-dessous; est-il possible de modifier l'icône en fonction de l'état du filtre? c'est à dire comme sur l'icône de la fonction déplacement par exemple : fond gris : outil inactif, fond bleu : outil actif.
3- question subsidiaire, dans le gestionnaire de script en COFFEE il y a un champ pour le nom du script et un champ pour les infos-bulles ( informations )? En Python je ne trouve rien et la bulle d'aide m'affiche le nom du script, le raccourci, mais reprend le nom du script pour la description.
Bref, pour l'instant, me concernant, c'est un peu comme demander à un dindon de retrouver une Clé dynamométrique dans une usine à boulons. L'usine, c'est le SDK… la clé, c'est la fonction que je cherche ( sans savoir qu'elle existe ni quel nom elle porte ) … et le didon, ben c'est moi
Si une âme charitable passe dans le coin pour éclairer ma lanterne je la remercie d'avance.
Je comprends vite, mais faut m'expliquer longtemps…
Tout d'abord merci il faudrait que je reprenne d'ailleurs
1) ton code est niquel, alors chose que j'ai apprise dernierement et que je n'utilisais pas auparavant dans mes codes c'est le nommage des variables aux style pep8 (une convention, et c'est toujours bon de suivre les conventions même si maxon ne le fait pas ^^)
Donc au lieux de displayFilter c'est display_filter je t'invite à lire ces articles.
http://sametmax.com/le-pep8-en-resume/
http://sametmax.com/bien-nommer-ses-...les-en-python/
2) alors oui c'est possible. Pour faire simple on recup le basebitmap actuel, on va créer un basebitmap avec la nouvelle image et on copie le nouveau basebitmap (les données de celui-ci) vers l'ancien qui lui est en read-only (c'est à dire que tu ne peux pas le changer, c4d pointera toujours vers cet objet basebitmap) donc la seule option que tu as c'est de modifier le contenu de ce basebitmap par celui de ta nouvelle image
Tu as un exemple ici http://www.plugincafe.com/forum/foru...ID=36785#36785
C'est assez simple à comprendre. Et même si tu ne comprends pas la fonctions set_icon elle prend juste deux arguments le pluginID, tu pourras retrouver l'id correspondant as ton script dans le panneau Customize Command cherche ton script et en bas à droite tu as l'id donc au lieu de c4d.OCube (qui est l'ID du cube) tu mets celui de ton script. Et en deuxième arguments il prend un chemin d'un bitmap et ça fait tout le boulot pour toi
Donc après tu n'as plus qu'a faire tes deux icon 1 normal et un activé.
3)Oui alors ce n'est pas dynamique tu ne peux pas le changer en cours d’exécution (enfin je crois) mais en rajoutant ceci au début de ton code
Ou Name-LangageCode = Titre et Description-LangageCode = la description complèteCode:""" Name-US: My Title Description-US: Something Name-FR: Le Titre De mon Script Description-FR: Bidule Chouette """ import c4d .....
Voili voilou et si tu as des question n'hésite pas
SDK Specialist
MAXON Computer GmbH
Merci. Tu m'a donné de quoi m'occuper ce week-end.
Je comprends vite, mais faut m'expliquer longtemps…
Ah un nouvel utilisateur de Python ! ^^
J'ai rien à redire de plus, si ce n'est de mettre ton code dans une fonction et de l'appeler si on est bien dans __main__ :
En gros ça sert à éviter que ton script ne s'exécute directement si jamais il est importé ailleurs (via import).Code PHP:
import c4d
def maFonction():
bd = doc.GetActiveBaseDraw()
displayFilter = bd.GetDisplayFilter()
if displayFilter & c4d.DISPLAYFILTER_GRID:
bd[c4d.BASEDRAW_DISPLAYFILTER_GRID] = False
print "Desactive le filtre grille"
else:
bd[c4d.BASEDRAW_DISPLAYFILTER_GRID] = True
print "Active le filtre grille"
bd.Message(c4d.MSG_CHANGE)
c4d.EventAdd()
if __name__ == '__main__':
maFonction()
Moi perso je préfère nommer mes variables comme t'as fait, c'est à dire displayFilter, ou encore mieux : filtreDAffichage.
Peu importe vraiment la convention que tu suis, du moment que t'y restes fidèle dans tout ton code.
Merci. J'ai de quoi me triturer le cerveau pour deux week-end.
Edit.: un petit lien ici, en Français et destiné aux débutants. De quoi s'occuper pour un bout de temps.
Dernière modification par gaff ; 05/11/2016 à 11h25.
Je comprends vite, mais faut m'expliquer longtemps…
Bon, pour l'instant j'en suis là :
Cela me semble un peu plus propre.
Code PHP:
"""Name-FR: Filtre d'affichage Grille
Description-FR: Active / Desactive l'affichage de la grille dans la vue
"""
import c4d
def Filtre_d_affichage():
bd = doc.GetActiveBaseDraw()
Filtre_d_affichage = bd.GetDisplayFilter()
if Filtre_d_affichage & c4d.DISPLAYFILTER_GRID:
bd[c4d.BASEDRAW_DISPLAYFILTER_GRID] = False
print "Désactive le filtre grille"
else:
bd[c4d.BASEDRAW_DISPLAYFILTER_GRID] = True
print "Active le filtre grille"
bd.Message(c4d.MSG_CHANGE)
c4d.EventAdd()
if __name__ == '__main__':
Filtre_d_affichage()
Par contre, pour assigner une icône lorsque la fonction est active et une autre lorsqu'elle ne l'est pas; là, je sèche
Avec le script ci-dessous, j'arrive à changer l'icône correspondant à mon ID de script, mais il faut à chaque fois que j'aille chercher l'icône que je souhaite assigner.
Sur le lien de gr4ph0s ici, il y a un exemple pour changer à la volée l'icône. Mais il ne s'agit pas d'un script, plutôt d'un plugin; ce qui permet d'avoir un dossier " res " avec les icônes souhaitées… C'est là où je patauge : comment faire en sorte que mon script soit un .pyp avec un dossier res?Code PHP:
import c4d
def set_icon(pluginid, bmp, overwrite_alpha=True):
icon = c4d.gui.GetIcon(pluginid)
if not icon:
return c4d.gui.RegisterIcon(pluginid, bmp)
ref = icon['bmp']
w, h = icon['w'], icon['h']
temp = c4d.bitmaps.BaseBitmap()
if temp.Init(w, h, bmp.GetBt()) != c4d.IMAGERESULT_OK:
return False
bmp.ScaleIt(temp, 256, True, True)
a1 = a2 = None
if overwrite_alpha:
a1 = ref.GetInternalChannel()
a2 = temp.GetInternalChannel()
for x in xrange(w):
rx = x + icon['x']
for y in xrange(h):
ry = y + icon['y']
ref[rx, ry] = temp[x, y]
if a1:
if a2:
alpha = temp.GetAlphaPixel(a2, x, y)
else:
alpha = 255
ref.SetAlphaPixel(a1, rx, ry, alpha)
return True
def main():
fn = c4d.storage.LoadDialog()
if not fn: return
bmp = c4d.bitmaps.BaseBitmap()
res = bmp.InitWith(fn)[0]
if res != c4d.IMAGERESULT_OK:
print "Icône non valide", res
return
set_icon(600000043, bmp) # ID de mon script
c4d.gui.GeUpdateUI()
c4d.EventAdd()
main()
Je comprends vite, mais faut m'expliquer longtemps…
Justement si tu regarde son plugin dans la fonction get_res_bmp tu as vu qu'il recupérait l'image qui ce situe dans le dossier res.
Du coup pour toi la manière la plus propre serais de faire un dossier dans le dossier de script avec ton script et tes deux images.
Ensuite grâce au module os (qui lui fait partie de python) tu peux récupérer le dossier courant et faire tout sorte de chose avec.
Donc ce qui donnerais pour toi quelque chose comme
et après tu n'as plus qu'a faire ceci dans ton mainCode PHP:
def get_res_bitmap(filename):
current_dir = os.path.dirname(__file__)
"""
__file__ est une variable qui correspond au chemin de ton fichier actuel.
dirname te permet de recupéré justement le chemin du dossier parent.
Au passage quand tu vois une variable qui commence par __ cela veux dire que c'est une variable privé
tu peux la lire mais il est déconseillé de stocker quoi que ce sois dedans
"""
full_path = os.path.join(current_dir, filename)
"""
os.path.join est unefonction qui te permet de rajouter des dossiers / fichier au path.
L'avantage c'est que ça gère pour toi les differents système (windows / mac /unix)
Cette fonction na pas de nombre d'argument fixe c'est à dire que tu peux lui passer 1 argument comme tu peux lui en passer 50.
Au final il fera toujours chemin_d'avant + argument d'apres + arguments d'apres et ainsi de suite'
"""
bmp = c4d.bitmaps.BaseBitmap()
result = bmp.InitWith(full_path)
if not result or result[0] != c4d.IMAGERESULT_OK:
return None
return bmp
Code PHP:
bmp_a = get_res_bitmap('activer.png')
bmp_b = get_res_bitmap('desactiver.png')
if bmp_a is None:
return True
set_icon(pluginid, bmp_a) # ou bien set_icon(pluginid, bmp_b)
return True
Dernière modification par gr4ph0s ; 05/11/2016 à 22h14.
SDK Specialist
MAXON Computer GmbH
Merci pour ces explications, mais cela dépasse mes compétences. Je reviendrai donc plus tard sur cette histoire de changement d'icône à la volée.
Avant de courir, il faut savoir marcher…
je suis donc reparti sur quelque chose de beaucoup plus simple, à savoir une simple extrusion de poly.
En suivant tes tutos, j'arrive assez facilement à sortir ceci :
Par contre, je souhaiterais que la fonction ne s'exécute que lorsqu'un ( ou plusieurs polygones ) est sélectionné. Car si je lance le script sans avoir sélectionné un polygone ( ou que je sois dans un autre mode ), tous les polys sont extrudés, ce qui provoque un résultat non voulu. L'idéal étant d'avoir un message d'alerte en cas de mauvaise manip.Code PHP:
import c4d
settings = c4d.BaseContainer() # Paramètres
settings[c4d.MDATA_EXTRUDE_ANGLE] = 1.5882496193148399 # Angle 91°
settings[c4d.MDATA_EXTRUDE_OFFSET] = 100.0 # Longueur de l'extrusion
settings[c4d.MDATA_EXTRUDE_CREATECAPS] = 0 # Sans couvecle
settings[c4d.MDATA_EXTRUDE_PRESERVEGROUPS] = 1 # Préserve les groupes
res = c4d.utils.SendModelingCommand(command = c4d.ID_MODELING_EXTRUDE_TOOL,
list = [op],
mode = c4d.MODELINGCOMMANDMODE_POLYGONSELECTION,
bc = settings,
doc = doc,
flags = c4d.MODELINGCOMMANDFLAGS_CREATEUNDO
)
c4d.EventAdd()
if res is False:
print "Fonction non exécutée, Quelque-chose s'est mal passé."
elif res is True:
print "Extrusion(s) de polygone(s) réusssie ( 100 cm )."
c4d.CallCommand(12298) # Mode Modélisation
c4d.EventAdd()
Pour le message d'alerte, cela me semble simple :
Par contre, je n'arrive pas à insérer le second code dans le premier pour que ça s'exécute dans le cas où je ne sois pas en mode polygone. ça doit être tout bête, mais là, je coince.Code PHP:
import c4d
def Message_d_alerte():
c4d.gui.MessageDialog("Erreur, veillez sélectionner un ou plusieurs polygones")
return
dlg = MonDlg(imgs)
dlg.Open(c4d.DLG_TYPE_MODAL)
return
if __name__=='__main__':
Message_d_alerte()
edit : premier code affiné pour ajouter l'angle d'extrusion, ne pas avoir de couvercle et préserver les groupes.
Dernière modification par gaff ; 13/11/2016 à 12h43.
Je comprends vite, mais faut m'expliquer longtemps…
Pour savoir en quel mode tu es il te suffit d'utiliser GetMod du document en cours. Regarde ici https://developers.maxon.net/docs/Ci...cument.GetMode
Du coup en faisant un
Tu t'assure d'être en mode polyCode PHP:
if doc.GetMode() == c4d.Mpolygons:
Après pour le nombre de poly sélectionné via GetPolygonS https://developers.maxon.net/docs/Ci...ct.GetPolygonS Tu obtiens un objet de type BaseSelect. Ensuite il te suffit de faire un GetCount et voila
Code PHP:
obj = doc.GetActiveObject()
selection = obj.GetPolygonS()
nb_selected = selection.GetCount()
Pour ton premier projet je ferais une vidéo dessus car c'est un bon exercice pour commencer et expliquer justement le système de path. Mais quand j'aurais une petite heure de libre donc ne compte pas avant 1 a 2 semaines ^^'
SDK Specialist
MAXON Computer GmbH
Merci pour ces précisions. Je regarderai tout ça dès que j'ai un petit moment.
Pour cette histoire d'icône, oui une vidéo serait la bien venue . 1 ou 2 semaine d'attente : aucun problème, c'est au moins le temps qu'il me faut pour me remettre de la boite d'aspirine que j'ai avalé " cul sec " sans arriver à quoi que ce soit .
Je comprends vite, mais faut m'expliquer longtemps…
Bon j'avance tout doucement. Pour l'instant ça fonctionne presque correctement.
J'arrive à avoir les messages d'avertissements dans la boite de dialogue, ainsi que dans la console. Le script ne s'exécute que lorsque je suis en mode Polygone, c'est ok.
Par contre, il me reste le problème de la sélection de polygone: lorsque je n'ai pas de polygone sélectionné. Là le script extrude tous les poly. C'est là que tes trois lignes de code interviennent, mais je ne sais pas comment les placer dans mon script.Code PHP:
"""Name-FR: Extrusion 100 cm
Description-FR: Exrude le ou les polygones selectionnes de 100 cm
"""
import c4d
if doc.GetMode() == c4d.Mmodel:
print "Fonction non exécutée, Veillez être en mode Polygone."
c4d.gui.MessageDialog("Erreur, Vous êtes en mode Modèle!")
if doc.GetMode() == c4d.Mpoints:
print "Fonction non exécutée, Veillez être en mode Polygone."
c4d.gui.MessageDialog("Erreur, vous êtes en mode Points, veillez sélectionner un ou plusieurs polygones")
if doc.GetMode() == c4d.Medges:
print "Fonction non exécutée, Veillez être en mode Polygone."
c4d.gui.MessageDialog("Erreur, vous êtes en mode Arêtes, veillez sélectionner un ou plusieurs polygones"
if doc.GetMode() == c4d.Mpolygons:
def Extrud_100():
settings = c4d.BaseContainer() # Paramètres
settings[c4d.MDATA_EXTRUDE_ANGLE] = 1.5882496193148399 # Angle 91°
settings[c4d.MDATA_EXTRUDE_OFFSET] = 100.0 # Longueur de l'extrusion
settings[c4d.MDATA_EXTRUDE_CREATECAPS] = 0 # Sans couvecle
settings[c4d.MDATA_EXTRUDE_PRESERVEGROUPS] = 1 # Préserve les groupes
res = c4d.utils.SendModelingCommand(command = c4d.ID_MODELING_EXTRUDE_TOOL,
list = [op],
mode = c4d.MODELINGCOMMANDMODE_POLYGONSELECTION,
bc = settings,
doc = doc,
flags = c4d.MODELINGCOMMANDFLAGS_CREATEUNDO
)
c4d.EventAdd()
if res is True:
print "Extrusion(s) de polygone(s) réusssie(s) = 100 cm."
c4d.CallCommand(12298) # Mode Modélisation
c4d.EventAdd()
if __name__=='__main__':
Extrud_100()
Je comprends vite, mais faut m'expliquer longtemps…
Y a un petit souci dans ton script, Gaff, le if __name__ == '__main__' doit être la toute première instruction, sinon ça n'a pas d'intérêt.
Tu peux aussi factoriser le mode et faire des elif, ainsi que vérifier que l'objet sélectionné existe et soit bien de type polygonal
Et quelques autres petits trucs, j'ai ajouté la vérification des polygones sélectionnés, je te laisse regarder, j'ai commenté.
Code PHP:
"""Name-FR: Extrusion 100 cm
Description-FR: Exrude le ou les polygones selectionnes de 100 cm
"""
import c4d
def Extrud_100():
# Si pas d'objet sélectionné ou pas de type polygonal, quitter
if not op or not op.CheckType(c4d.Opolygon) :
c4d.gui.MessageDialog("Erreur : veuillez sélectionner un objet polygonal.")
return
bs = op.GetPolygonS()
nbsel = bs.GetCount()
# Si pas de polygones sélectionnés, quitter
if not nbsel :
return
settings = c4d.BaseContainer() # Paramètres
settings[c4d.MDATA_EXTRUDE_ANGLE] = 1.5882496193148399 # Angle 91°
settings[c4d.MDATA_EXTRUDE_OFFSET] = 100.0 # Longueur de l'extrusion
settings[c4d.MDATA_EXTRUDE_CREATECAPS] = 0 # Sans couvecle
settings[c4d.MDATA_EXTRUDE_PRESERVEGROUPS] = 1 # Préserve les groupes
res = c4d.utils.SendModelingCommand(command = c4d.ID_MODELING_EXTRUDE_TOOL,
list = [op],
mode = c4d.MODELINGCOMMANDMODE_POLYGONSELECTION,
bc = settings,
doc = doc,
flags = c4d.MODELINGCOMMANDFLAGS_CREATEUNDO
)
if res is True:
print "Extrusion(s) de polygone(s) réusssie(s) = 100 cm."
c4d.CallCommand(12298) # Mode Modélisation
c4d.EventAdd()
def main():
mode = doc.GetMode()
if mode == c4d.Mmodel:
print "Fonction non exécutée, Veillez être en mode Polygone."
c4d.gui.MessageDialog("Erreur, Vous êtes en mode Modèle!")
elif mode == c4d.Mpoints:
print "Fonction non exécutée, Veillez être en mode Polygone."
c4d.gui.MessageDialog("Erreur, vous êtes en mode Points, veillez sélectionner un ou plusieurs polygones")
elif mode == c4d.Medges:
print "Fonction non exécutée, Veillez être en mode Polygone."
c4d.gui.MessageDialog("Erreur, vous êtes en mode Arêtes, veillez sélectionner un ou plusieurs polygones")
elif mode == c4d.Mpolygons:
Extrud_100()
if __name__=='__main__':
main()
Dernière modification par César Vonc ; 13/11/2016 à 21h07.
Super. Merci César! Je vais pouvoir décortiquer ça pour comprendre un peu comment ça fonctionne.
Et merci pour les commentaires, c'est une bonne aide à la compréhension des lignes de code.
Je comprends vite, mais faut m'expliquer longtemps…
C'est parfait. Merci à César et Gr4ph0s, vous êtes des chefs
Pour la version finale, je me suis juste rajouté un avertissement dans le cas où il n'y a pas de polygone sélectionné.
Ben oui, je suis un peu tête en l'air et j'aime bien quand C4D m'avertit que j'aie fait une boulette
Code PHP:
"""Name-FR: Extrusion 100 cm
Description-FR: Exrude le ou les polygones selectionnes de 100 cm
"""
import c4d
def Extrud_100():
# Si pas d'objet sélectionné ou pas de type polygonal, quitter
if not op or not op.CheckType(c4d.Opolygon) :
c4d.gui.MessageDialog("Erreur : veuillez sélectionner un objet polygonal.")
return
bs = op.GetPolygonS()
nbsel = bs.GetCount()
# Si pas de polygones sélectionnés, quitter
if not nbsel :
c4d.gui.MessageDialog("Erreur : veuillez sélectionner au moins un polygone.")
print "Fonction non exécutée, veuillez sélectionner au moins un polygone."
return
settings = c4d.BaseContainer() # Paramètres
settings[c4d.MDATA_EXTRUDE_ANGLE] = 1.5882496193148399 # Angle 91°
settings[c4d.MDATA_EXTRUDE_OFFSET] = 100.0 # Longueur de l'extrusion
settings[c4d.MDATA_EXTRUDE_CREATECAPS] = 0 # Sans couvecle
settings[c4d.MDATA_EXTRUDE_PRESERVEGROUPS] = 1 # Préserve les groupes
res = c4d.utils.SendModelingCommand(command = c4d.ID_MODELING_EXTRUDE_TOOL,
list = [op],
mode = c4d.MODELINGCOMMANDMODE_POLYGONSELECTION,
bc = settings,
doc = doc,
flags = c4d.MODELINGCOMMANDFLAGS_CREATEUNDO
)
if res is True:
print "Extrusion(s) de polygone(s) réusssie(s) = 100 cm."
c4d.CallCommand(12298) # Mode Modélisation
c4d.EventAdd()
def main():
mode = doc.GetMode()
if mode == c4d.Mmodel:
print "Fonction non exécutée, Veillez être en mode Polygone."
c4d.gui.MessageDialog("Erreur, Vous êtes en mode Modèle!")
elif mode == c4d.Mpoints:
print "Fonction non exécutée, Veillez être en mode Polygone."
c4d.gui.MessageDialog("Erreur, vous êtes en mode Points, veillez sélectionner un ou plusieurs polygones")
elif mode == c4d.Medges:
print "Fonction non exécutée, Veillez être en mode Polygone."
c4d.gui.MessageDialog("Erreur, vous êtes en mode Arêtes, veillez sélectionner un ou plusieurs polygones")
elif mode == c4d.Mpolygons:
Extrud_100()
if __name__=='__main__':
main()
Juste un petit truc qui me dérange : les accents ne fonctionnent pas dans la description des bulles d'aides. C'est étrange car dans la console, ça ne pose aucun souci.
c'est un Bug / coquille parce que je suis sur Mac ou bien c'est propre au Python et il n'est pas possible d'utiliser des accents?
Je comprends vite, mais faut m'expliquer longtemps…
Bien joué !
C'est propre à C4D, c'est pas vraiment un problème, c'est juste que par souci d'encodage, les caractères spéciaux doivent être écrits sous la forme unicode \u[code] (du moins, tous ceux à partir de 00A0, je pense)
Le é devient ainsi \u00E9
Pour les autres, je te réfère à ce site : http://www.utf8-chartable.de/
Code PHP:
"""Name-FR: Extrusion 100 cm
Description-FR: Exrude le ou les polygones s\u00E9lectionn\u00E9s de 100 cm
"""
Dernière modification par César Vonc ; 14/11/2016 à 10h07.
Décidément tu as toujours une solution pour contourner les obstacles.
Merci, comme je suis un peu tatillon, je vais rectifier de suite.
Je comprends vite, mais faut m'expliquer longtemps…
Voila la vidéo est en ligne pour ceux qui veulent !
SDK Specialist
MAXON Computer GmbH
Merci gr4ph0s. Après de multiples copier/coller de lignes de code, ça fonctionne…
Je me répète un peu mais tes vidéos sont un vrai régal
En Pj. : le script.
Filtre_Grille.zip
Je comprends vite, mais faut m'expliquer longtemps…
Avec plaisir que ça plaise !
Le seul petit problème de ton script c'est que l'id du tool n'est pas remplis automatiquement ce qui peux être un peu chiant pour d'autre personne à installé (ils ne savent pas forcément comment récupérer l'id)
Pour combler ceci tu peux passer par un plugin comme ça tu auras un ID fix (et pas un idéfix).
Bref ceci sera une bonne base pour justement faire une vidéo sur les plugins
SDK Specialist
MAXON Computer GmbH
Je comprends vite, mais faut m'expliquer longtemps…