Salut tout le monde, je rencontre un petit problème bien bête.
Comment gère t'on le focus sur les différents éléments de notre UI?
Mon but étant de changer l'image lors du passage de la souris dessus.
Voila mon code pour le moment qui ne fonctionne pas bien sur ^^
Merci d'avanceCode PHP:
import c4d
MY_BITMAP_BUTTON = 1004
class MyDialog(c4d.gui.GeDialog):
def CreateLayout(self):
self.SetTitle("Ye test des choses")
self.GroupBegin(0, c4d.BFH_SCALEFIT|c4d.BFH_SCALEFIT, 1, 1, "bitmap Button",0)
bc = c4d.BaseContainer()
bc.SetLong(c4d.BITMAPBUTTON_BORDER,c4d.BORDER_NONE)
self.myBitButton=self.AddCustomGui(MY_BITMAP_BUTTON, c4d.CUSTOMGUI_BITMAPBUTTON, "Button", c4d.BFH_CENTER | c4d.BFV_CENTER, 50, 50, bc)
self.myBitButton.SetImage('C:/Users/gr4ph0s/Desktop/image1.jpg')
self.GroupEnd()
return True
def Command(self, id, msg):
if id==MY_BITMAP_BUTTON:
print "Hello"
elif msg==c4d.BFM_INPUT and id==MY_BITMAP_BUTTON
if msg[c4d.BFM_INPUT_DEVICE]==BFM_INPUT_MOUSE: & id ==MY_BITMAP_BUTTON
print "test"
return True
if __name__=='__main__':
dlg = MyDialog()
dlg.Open(c4d.DLG_TYPE_ASYNC, defaultw=100, defaulth=100)
Dernière modification par gr4ph0s ; 02/07/2015 à 13h44.
SDK Specialist
MAXON Computer GmbH
Je suis pas certain que ça soit possible aussi directement et je vois pas d'exemple dans cinema4D.
Les boutons sont tous réactif au mouseOver mais je crois pas qu'on y ai accès.
Tu voulais faire quoi au juste ?
Salut,
Tu peux avoir accès au mouseOver sur une GeUserArea (GeUserArea.Message). Le problème étant que tu n'as pas accès au mouseOut.
Il faut lancer un Timer lors du mouseOver et vérifier régulièrement (toutes les x millisecondes) si la souris n'a pas quittée la zone (et désactiver le Timer si c'est le cas).
Un exemple :
Code PHP:
import c4d
from c4d import gui, bitmaps
MY_BITMAP_BUTTON = 1004
class BitmapGui(gui.GeUserArea):
def __init__(self, image_out, image_over):
super(BitmapGui, self).__init__()
self.__bitmap_out = bitmaps.BaseBitmap()
self.__bitmap_out.InitWith(image_out)
self.__bitmap_over = bitmaps.BaseBitmap()
self.__bitmap_over.InitWith(image_over)
self.__hover = False
@staticmethod
def create(dialog, gadget_id, image_out, image_over):
bitmap = BitmapGui(image_out, image_over)
dialog.AddUserArea(gadget_id, c4d.BFH_SCALEFIT)
dialog.AttachUserArea(bitmap, gadget_id)
return bitmap
def InputEvent(self, msg):
device = msg.GetLong(c4d.BFM_INPUT_DEVICE)
channel = msg.GetLong(c4d.BFM_INPUT_CHANNEL)
if (device == c4d.BFM_INPUT_MOUSE and
channel == c4d.BFM_INPUT_MOUSELEFT):
print "clicked"
return True
def Message(self, msg, result):
if msg.GetId() == c4d.BFM_GETCURSORINFO:
if not self.__hover:
print "over"
self.__hover = True
self.Redraw()
self.SetTimer(100)
return super(BitmapGui, self).Message(msg, result)
def Timer(self, msg):
base = self.Local2Global()
bc = c4d.BaseContainer()
res = self.GetInputState(c4d.BFM_INPUT_MOUSE, c4d.BFM_INPUT_MOUSELEFT, bc)
x = bc.GetLong(c4d.BFM_INPUT_X) - base['x']
y = bc.GetLong(c4d.BFM_INPUT_Y) - base['y']
if x > self.GetWidth() or x < 0 or y > self.GetHeight() or y < 0:
print "out"
self.__hover = False
self.SetTimer(0)
self.Redraw()
def GetMinSize(self):
x1, y1 = self.__bitmap_out.GetSize()
x2, y2 = self.__bitmap_over.GetSize()
return (max(x1, x2), max(y1, y2))
def DrawMsg(self, x1, y1, x2, y2, msg):
bitmap = self.__bitmap_over if self.__hover else self.__bitmap_out
self.DrawBitmap(bitmap, 0, 0, x2, y2, 0, 0, x2, y2, c4d.BMP_NORMAL)
class MyDialog(c4d.gui.GeDialog):
def CreateLayout(self):
self.SetTitle("Ye test des choses")
self.GroupBegin(0, c4d.BFH_SCALEFIT | c4d.BFH_SCALEFIT)
self.bitmap = BitmapGui.create(self, MY_BITMAP_BUTTON,
'/Users/xs_yann/Desktop/out.jpg',
'/Users/xs_yann/Desktop/over.png')
self.GroupEnd()
return True
if __name__ == '__main__':
dlg = MyDialog()
dlg.Open(c4d.DLG_TYPE_ASYNC, defaultw=100, defaulth=100)
ha ben j'ai essayé hier de passer par un userarea et j'ai pas réussi :p
j'avais l'idée c'est déjà pas mal T.T
Afin d'apprendre la lib c4d j'ai voulu commencé par un projet simple, un outil pour gérer le point de pivot des objets.
Bon le main du programme est déjà codé et ce fut assez simple mais je pense que je vais galérer un peu plus sur l'UI car je compte faire un truc comme l'image si dessous et de cliquer sur les différentes bouboules pour changer simplement le point de pivot.
(Une fois fini il sera open source )
D'ailleurs je n'ai pas fait de recherche,ni même lu la doc encore donc vraiment si vous avez du temps de libre. Mais y'a moyen de superposer 2 images dans une UI? Ou alors d'afficher différents layer d'un fichier photoshop/tif et de créer des zone clickable?
Mais a vrai dire je ne suis pas sur qu'il s'agisse de la bonne option si sa ne serait pas plus simple de dessiner l'ui directement mais je n'es pas trouver de lib dans C4d proposant ça.
C4d supporterai un appel PyGDI ou PyQt(ayant déjà fait du Qt en C++ sa pourrais être cool) :3
Dernière modification par gr4ph0s ; 01/07/2015 à 21h37.
SDK Specialist
MAXON Computer GmbH
waw, tu m'impressionnes Maxime, accompagné par les zouzous du dessus, tu risques de vite progresser et j'envie ton binôme qui t'aura près de lui sur le projet d'animation à l'école
kenavo !! // Pinterest KAMIGAZ®
Y'a deux solutions qui me viennent en tête, toujours avec les GeUserArea :
1. Tu créés des zones cliquables en forme de cercle au niveau de tes boules : tu te créer un tableau avec la position de chacune de tes boules sur ton image, et un rayon correspondant à leur taille. A chaque clic, si la distance entre le point cliqué et le centre d'un cercle est inférieure au rayon de ce cercle alors tu es dans la zone.
2. Tu copies ton image pour créer une sorte de masque, tu mets un fond uni et à la place de chaque boule, un rond d'une couleur ou d'un niveau de gris bien précis. Ensuite tu associes chaque couleur ou niveau de gris à la boule correspondante et à chaque clic tu reportes la position sur ton image masque pour récupérer la couleur.
Bon après recherche j'ai rien trouvé de bien concluant sur la superposition d'image mais je pense et surtout si tu le dit aussi qu'avec les UserArea y'a moyen de faire ce que je veux. Merci en tout cas
Après test c'est parfait xs_yann exactement ce que je demandais ^^.
Cependant vu que copier coller un code n'as pas vraiment de but pour moi.
Pourquoi tu définis create en static? et de ne pas passer par un appel standard? Juste pour un confort ou y'a une réelle utilitée?
Un truc dont je ne suis pas trop sur mais qui me semble logique msg et result sont des variables global set par C4D un peu comme self dans une classe ?
super(BitmapGui, self).__init__() Ceci ne créé pas une boucle infini? ^^'
Hahaha merci Aurety, je ne sais pas si c'est un cadeau de m'avoir, mais en tout cas merci, mais c'est pas dût à notre prof de C4D qui a vraiment été minable toute l'année, en plus il nous poussait jamais à découvrir des choses et à s'amuser en 3D, vraiment pas un type bien
Dernière modification par gr4ph0s ; 01/07/2015 à 22h09.
SDK Specialist
MAXON Computer GmbH
Arfff, retourne au code, au lieu de dire des des conneries
kenavo !! // Pinterest KAMIGAZ®
Tiens, tu codes toujours pour C4D, Yann ? ^^
J'aime bien la dernière mise à jour sur ton site qui indique une compatibilité à la toute nouvelle R15.
pourquoi static alors aucune idée ^^
msg et result sont juste des arguments des fonctions (en général des BaseContainer pour ces deux noms)
Si j'ai bien compris, pour l'appel à la fonction super, c'est la fonction de la classe parente pas celle que tu crées.
Quand maxon a construit la class GeDialog par exemple, ils ont peut être inclus la gestion de certains messages.
Donc dans ta nouvelle class, tu surcharges la fonction Message pour gérer ce que tu veux, il faut appeler la fonction "original" pour gérer le reste.
Dans la documentation les fonctions sont notées "ne pas oublier d'appeler la fonction super"
Je fais confiance à Yann pour me reprendre si j'ai dis une connerie.
Gnagnagna, qu'est-ce que tu fais sur mon site toi, tu sais pas coder tes plugins tout seul ?
Je travaille sur la compatibilité R17, vu qu'elle est pas sortie ça prend du temps, c'est pour ça.
Plus sérieusement, avec mon école (que j'ai bientôt finie) et depuis qu'on a instancié une classe fille avec Madame xs, j'ai un peu lâché les plugins c4d mais je suis bientôt de retour (à plein temps sur C4D si tout se passe bien).
@gr4ph0s :
Une UserArea se créer comme ceci dans un dialogue :
Ca m'ennuie un peu de devoir écrire trois lignes identiques à chaque fois que je veux créer un BitmapGui. Ce code doit être encapsulé quelque part afin d'être en mesure de l'appeler plusieurs fois sans le répéter (principe DRY).Code PHP:
def CreateLayout(self):
user_area = BitmapGui('img1.jpg', 'img2.png')
self.AddUserArea(1001, c4d.BFH_SCALEFIT)
self.AttachUserArea(user_area, 1001)
C'est possible de mettre ce bout de code dans le constructeur de BitmapGui et de faire :
Mais, à mon sens, ce code n'a rien à faire dans le constructeur de la classe. Devoir passer le dialogue en argument du constructeur c'est un peu comme si pour construire une voiture il fallait connaître le conducteur, c'est pas logique.Code PHP:
self.bitmap = BitmapGui(self, MY_BITMAP_BUTTON, 'img1.jpg', 'img2.png')
De plus, tu peux avoir besoin de dissocier l'initialisation de la création dans le dialogue si tu veux utiliser la même instance, même après ouverture / fermeture du dialogue.
Tu peux donc faire comme ceci (en créant une méthode attach dans la classe BitmapGui) :Envoyé par SDK
La méthode attach n'étant pas statique, elle a besoin d'une instance de la classe (un objet) pour être appelée. Pour s'affranchir de cette contrainte, on créé une méthode statique qui se charge de l'instanciation (appel au constructeur) et de la création (attachement au dialogue).Code PHP:
def CreateLayout(self):
self.bitmap = BitmapGui('img1.jpg', 'img2.png')
self.bitmap.attach(self, MY_BITMAP_BUTTON)
D'une manière générale, les méthode statiques sont utilisées pour grouper des fonctions qui ont une connexion logique avec la classe, dans la classe.Code PHP:
def CreateLayout(self):
self.bitmap = BitmapGui.create(self, MY_BITMAP_BUTTON, 'img1.jpg', 'img2.png')
Pour msg et result, comme l'a dit val, ce sont des arguments.
Quand tu as fais dialog.AttachUserArea(bitmap, MY_BITMAP_BUTTON), tu as fourni une référence de ton UserArea (l'objet 'bitmap') à ton dialogue. Ton dialogue stocke ça dans une liste interne.
A chaque clic, ton dialogue itère à travers la liste et pour chaque UserArea appelle la méthode Message.
Quelque chose du genre :
Ta classe hérite de GeUserArea, ton dialogue sais donc que ta classe aura forcément les méthodes Message, Init, GetMinSize, Timer, etc.Code PHP:
bc = BaseContainer(c4d.BFM_GETCURSORINFO)
bc.SetLong(c4d.BFM_INPUT_DEVICE, c4d.BFM_INPUT_MOUSE)
for user_area in self.ua_list:
user_area.Message(bc, BaseContainer())
Ton dialogue ne sais pas ce qu'il y a dans la méthode Message de ta classe BitmapGui mais il sais qu'elle existe et quels type d'arguments elle prend en paramètre.
Tu sais donc que à chaque clic, la méthode Message de ta classe sera appelée par le dialogue.
A toi de mettre le code que tu veux dedans (sans oublier d'appeler le code de la méthode Message du parent à la fin), c'est ce que l'on appelle surcharger (override).
self n'est pas une variable globale, c'est l'instance avec laquelle tu appelles la méthode qui est passée en premier paramètre. Un exemple qui aide à comprendre :
Lorsque tu appelles instance.foo() c'est traduit par Toto.foo(instance) (la méthode foo de la classe Toto appelée avec instance en premier argument).Code PHP:
class Toto:
def foo(self):
print "bar"
def main():
instance = Toto()
instance.foo() # equivalent
Toto.foo(instance) # equivalent
if __name__=='__main__':
main()
Pour compléter ce qu'a dis val sur la fonction super voilà un exemple :
en sortie :Code PHP:
class Toto(object):
def __init__(self):
print "Je construis des choses"
self.value = 10
class Tata(Toto):
def __init__(self):
super(Tata, self).__init__()
print "Je construis d'autres choses"
print self.value
def main():
tata = Tata()
Si tu retire la ligne "super(Tata, self).__init__()", le constructeur de Toto n'est plus appelé (étant donné qu'il est surchargé), tu as donc en sortie :Je construis des chose
Je construis d'autres choses
10
En espérant que ça t'aide, bon courage.Je construis d'autres choses
AttributeError: 'Tata' object has no attribute 'value'
Fiouuuu merci pour toutes ces explications tres precises je ne pouvais attendre mieux
Une autre question de débutant et surtout vu que sa sera open source histoire davoir le truc le plus propre possible. Y'a t'il une convention a propos des ID des differents controle?
Car dans tout les exemples que jai regardé les id commence a 1000.
SDK Specialist
MAXON Computer GmbH
Par sécurité on commence à 1000 car les id inférieur peuvent être déjà utilisés (je ne saurais dire par quoi exactement).
https://developers.maxon.net/docs/Ci..._resource.html
Et bien merci et rendez vous a la sortie du plugin
SDK Specialist
MAXON Computer GmbH
et j'ai pas dis de trop grosses conneries
Félicitations, si elle hérite de la classe père ça risque de faire mal d'ici deux ou trois ans, vu le niveau de précocité génétique ! Vous êtes tous du groupe sanguin C++ dans la famille ?Envoyé par xs_yann
C'est toujours un bonheur de te lire, j’apprends toujours des trucs ! Ça commence à faire tes études, on a besoin de toi ici !
Bon vient un autre problème, je ne sais pas si j'ouvre un nouveau post ou pas...
Mais une fois convertie en plugin, je n'ai pu accès à la class op au sein de mon code, ça faisait pareil pour la class doc, mais celle-ci pouvais passer en argument de Execute du coup je récup son instance et la passe à mes différentes class qui en ont besoin. Cependant aucun moyen de faire la même chose pour la classe op.
Voila mon code qui pose problème
le plus troublant c'est que j'ai regardé dans un plugin de cesar vonc et lui il se sert justement de Init mais moi impossible de faire appeler cette méthode ^^'Code PHP:
class LunchUI(c4d.plugins.CommandData):
PLUGIN_ID = 10000090 # TEST ID
op = None
dlg = None
ua = None
document = None
ICON = None
def Init(self, op) :
print "salut"
print op
return True
def Execute(self, doc):
if not self.dlg: self.dlg = MyDialog()
self.dlg.setData(doc)
self.dlg.Open(c4d.DLG_TYPE_ASYNC, self.PLUGIN_ID, -1, -1, 220, 310)
return True
def RestoreLayout(self, subid):
if not self.dlg:
self.dlg = MyDialog()
return self.dlg.Restore(self.PLUGIN_ID, subid)
@classmethod
def Register(AxisMaster):
data = {
"id": AxisMaster.PLUGIN_ID,
"icon": AxisMaster.ICON,
"str": "AxisMaster",
"help": "Click on the circle",
"info": c4d.PLUGINFLAG_COMMAND_HOTKEY,
"dat": AxisMaster(),
}
c4d.plugins.RegisterCommandPlugin(**data)
if __name__ == "__main__":
LunchUI.Register()
PS: voila mon code complet https://github.com/gr4ph0s/C4D-PivotMaster je sais que c'est pas clean notamment au niveau updateAxi ou je créé des variables inutiles, c'est juste que je n'ai pas fini d’adapter mon script python que j'ai fait sans class.Code PHP:
def Init(self, op) :
donnees = op.GetDataInstance()
self.InitAttr(op, int, [VONC_SUBDINV_TYPE])
self.InitAttr(op, int, [VONC_SUBDINV_SUBD])
self.InitAttr(op, int, [VONC_SUBDINV_DEPA])
self.InitAttr(op, bool, [VONC_SUBDINV_SELP])
self.InitAttr(op, bool, [VONC_SUBDINV_INVN])
op[VONC_SUBDINV_TYPE] = self.type
op[VONC_SUBDINV_SUBD] = self.subd
op[VONC_SUBDINV_DEPA] = self.depa
op[VONC_SUBDINV_SELP] = self.selp
op[VONC_SUBDINV_INVN] = self.invn
return True
Dernière modification par gr4ph0s ; 04/07/2015 à 21h33.
SDK Specialist
MAXON Computer GmbH
En regardant ton code, vite fait, il y a quelques erreurs, comme à la ligne 356 :
Ça ne peut pas de marcher sans mettre le self derrière :Code PHP:
def InitValues(self):
doc = None
ua = None
return True
sef.doc = None
self.ua = None
Pareil ton LunchUI, tu ne peux définir le op de ta classe uniquement si tu lui mets un self derrière dans ta fonction :
Du genre :
Code PHP:
def Init(self, op) :
print "salut"
self.op = op
Init est exécuté à chaque initialisation de l'outil, soit une seule fois quand tu le sélectionnes après avoir lancé C4D, si je ne me goure pas.
PS : op, doc sont des variables, pas des classes. ^^
Dernière modification par César Vonc ; 04/07/2015 à 23h15.
Dernière modification par gr4ph0s ; 04/07/2015 à 23h23.
SDK Specialist
MAXON Computer GmbH
Bah c'est juste une question de nomenclature, je veux pas dire de bêtise mais je dirais que doc est une variable qui a pour classe c4d.documents.BaseDocument.
Pour créer un doc de cette classe, il faudrait faire doc = c4d.documents.BaseDocument(), avec les parenthèses, qui appelle automatiquement le __init__ de la classe, c'est propre au Python.
Init et InitValues sont inventées par Maxon, on les appelle (ou sont appelées) quand on le veut pour initialiser certaines fonctionnalités qui demandent certains paramètres.
Par exemple, quand tu créés un Neighbor, tu fais un :
n = c4d.utils.Neighbor()
Puis tu l'initialises avec Init qui demande, dans ce cas là, un PolygonObjet :
n.Init(op)
Après, suffit de lire la doc pour savoir quand la fonction est appelée par C4D :
GeDialog.InitValues(self)
Called when the dialog is initialized by the GUI.
Ici, quand le menu est initialisé, il appelle InitValues qui détermine les valeurs par défaut des paramètres. C'est ici qu'on va définir ces valeurs par défaut.
Merci beaucoup effectivement je n'avais pas penser a regarder la doc pour GeDialog et donc InitValue.
J'ai fait ce projet pour comprendre comment python fonctionnait avec C4D et bien je suis pas déçu ! un ENORME merci à vous !
SDK Specialist
MAXON Computer GmbH