IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

PyQt Python Discussion :

Création et intégration de widgets personnalisés avec QtDesigner


Sujet :

PyQt Python

  1. #1
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Juillet 2009
    Messages
    51
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Juillet 2009
    Messages : 51
    Par défaut Création et intégration de widgets personnalisés avec QtDesigner
    Bonjour,

    J'aimerai créer mes widegets personnalisés dans QtDesigner et pouvoir les réutiliser pour créer mon application principale.

    Prenons un exemple simple :
    Je crée un LineEdit assosié à un Label. J'appel ça un LabeledLineEdit > LabeledLineEdit.ui
    Comment feriez-vous pour réutiliser ce LabeledLineEdit dans la création d'une MainWindow par exemple ?

    Pour l'instant, j'ai eu l'idée de transformer le LabeledLineEdit.ui en ui_LabeledLineEdit.py grâce à pyuic :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    pyuic4 -o ui_LabeledLineEdit.py -wx LabeledLineEdit.ui
    Ensuite, je créer un plugin pour QtDesigner comme dans les exemples fournis avec PyQt LabeledLineEditPlugin.py.
    Enfin, je démare QtDesigner et je devrai voir apparaitre mon LabelLineEdit dans la liste des widgets mais rien n'apparait...

    Pour vérrifier que ça peut marcher, j'ai copier/coller un des exemples (widget+plugin) PyQt dans les mêmes répertoires que ui_LabeledLineEdit.py et LabeledLineEditPlugin.py. Et là ça marche !

    Merci

    ui_configViewer.py :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    from PyQt4 import QtCore, QtGui
     
    class Ui_configViewer(QtGui.QWidget):
        def setupUi(self, configViewer):
            configViewer.setObjectName("configViewer")
            configViewer.resize(400, 346)
            self.cVGridLayout = QtGui.QGridLayout(configViewer)
            self.cVGridLayout.setObjectName("cVGridLayout")
            self.configViewerToolBox = QtGui.QToolBox(configViewer)
            self.configViewerToolBox.setObjectName("configViewerToolBox")
            self.configViewerToolBoxPage = Ui_configViewerToolBoxPage()
            self.configViewerToolBoxPage.setGeometry(QtCore.QRect(0, 0, 382, 264))
            self.configViewerToolBoxPage.setObjectName("configViewerToolBoxPage")
            self.configViewerToolBoPageGridLayout = QtGui.QGridLayout(self.configViewerToolBoxPage)
            self.configViewerToolBoPageGridLayout.setObjectName("configViewerToolBoPageGridLayout")
            self.configViewerToolBox.addItem(self.configViewerToolBoxPage, "")
            self.cVGridLayout.addWidget(self.configViewerToolBox, 1, 0, 1, 2)
            self.confAsCurrentPushButton = QtGui.QPushButton(configViewer)
            self.confAsCurrentPushButton.setObjectName("confAsCurrentPushButton")
            self.cVGridLayout.addWidget(self.confAsCurrentPushButton, 0, 0, 1, 1)
            spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
            self.cVGridLayout.addItem(spacerItem, 0, 1, 1, 1)
     
            self.retranslateUi(configViewer)
            self.configViewerToolBox.setCurrentIndex(0)
            QtCore.QMetaObject.connectSlotsByName(configViewer)
     
        def retranslateUi(self, configViewer):
            configViewer.setWindowTitle(QtGui.QApplication.translate("configViewer", "Form", None, QtGui.QApplication.UnicodeUTF8))
            self.configViewerToolBox.setItemText(self.configViewerToolBox.indexOf(self.configViewerToolBoxPage), QtGui.QApplication.translate("configViewer", "Page 1", None, QtGui.QApplication.UnicodeUTF8))
            self.confAsCurrentPushButton.setText(QtGui.QApplication.translate("configViewer", "Exporter comme configuration courante", None, QtGui.QApplication.UnicodeUTF8))
     
    from ui_configViewerToolBoxPage import Ui_configViewerToolBoxPage
     
    class configViewer(QtGui.QWidget, Ui_configViewer):
        def __init__(self, parent=None, f=QtCore.Qt.WindowFlags()):
            QtGui.QWidget.__init__(self, parent, f)
     
            self.setupUi(self)
     
     
    if __name__ == "__main__":
        import sys
        app = QtGui.QApplication(sys.argv)
        configViewer = QtGui.QWidget()
        ui = Ui_configViewer()
        ui.setupUi(configViewer)
        configViewer.show()
        sys.exit(app.exec_())
    configViewerPlugin.py :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    #!/usr/bin/env python
     
    from PyQt4 import QtGui, QtDesigner
    from Interface.widgets.ui_configViewer import *
     
    class ConfigViewerPlugin(QtDesigner.QPyDesignerCustomWidgetPlugin, configViewer):
     
            # The __init__() method is only used to set up the plugin and define its
        # initialized variable.
        def __init__(self, parent=None):
     
            super(ConfigViewerPlugin, self).__init__(parent)
     
            self.initialized = False
     
        # The initialize() and isInitialized() methods allow the plugin to set up
        # any required resources, ensuring that this can only happen once for each
        # plugin.
        def initialize(self, core):
     
            if self.initialized:
                return
     
            self.initialized = True
     
        def isInitialized(self):
     
            return self.initialized
     
        # This factory method creates new instances of our custom widget with the
        # appropriate parent.
        def createWidget(self, parent):
            return configViewer(parent)
     
        # This method returns the name of the custom widget class that is provided
        # by this plugin.
        def name(self):
            return "ConfigViewer"
     
        # Returns the name of the group in Qt Designer's widget box that this
        # widget belongs to.
        def group(self):
            return "PyQt Examples"
     
     
        # Returns a short description of the custom widget for use in a tool tip.
        def toolTip(self):
            return ""
     
        # Returns a short description of the custom widget for use in a "What's
        # This?" help message for the widget.
        def whatsThis(self):
            return ""
     
        # Returns True if the custom widget acts as a container for other widgets;
        # otherwise returns False. Note that plugins for custom containers also
        # need to provide an implementation of the QDesignerContainerExtension
        # interface if they need to add custom editing support to Qt Designer.
        def isContainer(self):
            return False
     
        # Returns an XML description of a custom widget instance that describes
        # default values for its properties. Each custom widget created by this
        # plugin will be configured using this description.
        def domXml(self):
            return '<widget class="configViewer" name="ConfigViewer">\n' \
                   ' <property name="toolTip">\n' \
                   '  <string>Le template d\' une config</string>\n' \
                   ' </property>\n' \
                   ' <property name="whatsThis">\n' \
                   '  <string>Le widget pour afficher une config</string>\n' \
                   ' </property>\n' \
                   '</widget>\n'
     
        # Returns the module containing the custom widget class. It may include
        # a module path.
        def includeFile(self):
            return "Interface.widgets.ui_configViewer"

  2. #2
    Expert confirmé
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 486
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 486
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Comme j'avais absolument besoin de ça, j'ai creusé un peu le sujet, et tu trouveras ce que je fais ici:

    http://python.jpvweb.com/mesrecettes...uveaux_widgets

    Ça a l'air un peu compliqué, comme ça, mais une fois qu'on a compris, ça marche très bien!

  3. #3
    Expert confirmé

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    4 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2008
    Messages : 4 307
    Par défaut
    Pour que ton widget apparaisse dans le Designer, il faut qu'il soit placé au bon endroit et dans une catégorie.

    Exemple Linux, tu adaptes le début des chemins à ta situation, avec les fichiers:
    LabeledLineEdit.py -> le widget
    LabeledLineEditplugin.py -> le plugin
    et le groupe MyWidgets

    Dans le dossier:
    /usr/lib/pythonx.y/dist-packages/MyWidgets/
    (ou sa variante site-packages)
    tu places le fichier LabeledLineEdit.py

    Dans le dossier:
    /usr/lib/qt4/plugins/designer/python/MyWidgets/
    (variante, /usr/lib/x86_64-linux-gnu/qt4/...)
    tu replaces le fichier LabeledLineEdit.py

    Dans le dossier:
    /usr/lib/qt4/plugins/designer/python/
    (variante, /usr/lib/x86_64-linux-gnu/qt4/...)
    tu places le fichier LabeledLineEditplugin.py

    Dans le fichier plugin, tu devras adapter avant tout:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
        def group(self):
            return "MyWidgets"
     
    ...
     
        def includeFile(self):
            return "MyWidgets.LabeledLineEdit"
    Normalement ton widget apparaîtra dans sa catégorie dans le Designer.
    Si ce n'est pas le cas, lance-le dans un terminal pour voir si ce n'est pas le script du widget qui est rejeté
    Sous Linux:

    D'autres exemples:
    http://bazaar.launchpad.net/~vincent...y-1.0/VWidgets

  4. #4
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Juillet 2009
    Messages
    51
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Juillet 2009
    Messages : 51
    Par défaut
    Citation Envoyé par tyrtamos Voir le message
    Bonjour,

    Comme j'avais absolument besoin de ça, j'ai creusé un peu le sujet, et tu trouveras ce que je fais ici:

    http://python.jpvweb.com/mesrecettes...uveaux_widgets

    Ça a l'air un peu compliqué, comme ça, mais une fois qu'on a compris, ça marche très bien!
    tyrtamos : Je me suis inspiré exactement des même sources que toi et mis en place quasiment la même solution à une différence prêt : la tienne marche !
    Je ne pense pas avoir de problème au niveu de l'environnement parce que j'ai copié/collé l'exemple "analogClock" de PyQt dans mes répertoires plugins et widgets et il marche très bien (il s'affiche dans QtDesigner) !
    J'en déduit que les erreurs ne peuvent apparaitre qu'à cause de mon code soit du plugin, soit du widget.
    Le code du widget est généré directement avec pyuic4. Il n'a donc pas de slot ni properties ect...

    VinsS: Comme j'ai répondu plus haut, je ne pense pas avoir de souci d'environnement car un exemple tourne sur mon arboressance. Merci pour tes exemples.


    Comme je n'arrive pas à voir le configViewer généré avec pyuic, j'ai essayé de coder un petit widget perso codé à la main. Il n'apparait toujours pas !

    Mes dossiers :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    runQtDesigner.py
    plugins/
    |____ configViewerPagePlugin.py
    |____ analogclockplugin.py
    widgets/
    |____ configViewerPage.py
    |____ analogclock.py
    configViewerPagePlugin.py

    configViewerPage.py :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    from PyQt4 import QtCore, QtGui
     
     
    class ConfigViewerPage(QtGui.QWidget):
     
        def __init__(self, parent=None):
            super(ConfigViewerPage, self).__init__(parent)
     
            self.gridLayout = QtGui.QGridLayout()
            self.myPushButton = QtGui.QPushButton("Mon bouton !")
            self.groupsVLayout = QtGui.QVBoxLayout()
            self.label1 = QtGui.QLabel("hyalaaaaa !!!")
            self.label2 = QtGui.QLabel("hyalaaaaa22222 !!!")
            self.groupsVLayout.addWidget(self.label1)
            self.groupsVLayout.addWidget(self.label2)        
            self.gridLayout.addLayout(self.groupsVLayout, 0,0)
            self.gridLayout.addWidget(self.myPushButton, 1,0)
            self.setLayout(self.gridLayout)
     
     
     
    if __name__ == "__main__":
        import sys
        app = QtGui.QApplication(sys.argv)
        widget = ConfigViewerPage()
        widget.show()
        sys.exit(app.exec_())

    configViewerPagePlugin.py :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    from PyQt4 import QtGui, QtDesigner
    from Interface.widgets.configViewerPage import ConfigViewerPage
     
    class ConfigViewerPagePlugin(QtDesigner.QPyDesignerCustomWidgetPlugin):
     
        def __init__(self, parent=None):
            super(ConfigViewerPagePlugin, self).__init__(parent)
     
            self.initialized = False
     
        def initialize(self, core):
            if self.initialized:
                return
            self.initialized = True
     
        def isInitialized(self):
            return self.initialized
     
        def createWidget(self, parent):
            return ConfigViewerPage(parent)
     
        def name(self):
            return 'configViewerPage'
     
        def group(self):
            return 'Diam widget'
     
        def domXml(self):
            return '<widget class="ConfigViewerPage" name="configViewerPage" />\n'
     
        def includeFile(self):
            return 'configViewerPage'
    Du coup j'ai essayé de reprendre exactement l'exemple sur le widget de géolocalisation que tyrtamos présente mais sans plus de résultat !

    J'ai regardé dans la rubrique Aide/"A propos de plugins" dans QtDesigner et il ne charge que des librairies en .so. Je ne sais pas si c'est normal mais il n'y a aucun widget écrit en python qui apparait dans la rubrique.

    Merci pour vos réponses

  5. #5
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Juillet 2009
    Messages
    51
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Juillet 2009
    Messages : 51
    Par défaut
    J'ai mis trop d'informations sur mon précédent poste.
    La réflexion est :
    Mon QtDesigner ne voit que les librairies .so est-ce normal ? Si oui, comment génère-t-on ces librairies à partir des plugins écrit en python ?

    On gagne en clarté mais excusez moi pour ce double post.
    Merci

  6. #6
    Expert confirmé

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    4 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2008
    Messages : 4 307
    Par défaut
    Pourrais-tu préciser ton arborescence ?

    Qu'est-ce que c'est que ce runQtDesigner.py ?

    Exemple chez moi:

    /usr/lib/i386-linux-gnu/qt4/plugins/designer/libpythonplugin.so
    /usr/lib/i386-linux-gnu/qt4/plugins/designer/python/vlineeditplugin.py
    /usr/lib/i386-linux-gnu/qt4/plugins/designer/python/VWidgets/vlineedit.py


    Le premier fichier libpythonplugin.so est indispensable, si il n'est pas présent, c'est un problème au niveau de l'installation de PyQt qui est censé l'installer.


    Ensuite je vois que tu as défini un groupe
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
        def group(self):
            return 'Diam widget'
     
     
        def includeFile(self):
            return 'configViewerPage'
    C'est le nom de la catégorie tel qu'il apparaît dans le panneau des widgets. Après ceux de Qt et, chez moi, avant ceux de KDE.
    Selon moi tes widgets doivent se trouver dans un dossier de ce nom. Donc quelque chose comme ça:
    .../qt4/plugins/designer/libpythonplugin.so
    .../qt4/plugins/designer/python/configViewerPagePlugin.py
    .../qt4/plugins/designer/python/Diam_widget/configViewerPage.py

    et changer le code du plugin:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
        def group(self):
            return 'Diam_widget'
     
     
        def includeFile(self):
            return 'Diam_widget.configViewerPage'

  7. #7
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Juillet 2009
    Messages
    51
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Juillet 2009
    Messages : 51
    Par défaut
    Citation Envoyé par VinsS Voir le message
    Pourrais-tu préciser ton arborescence ?

    Qu'est-ce que c'est que ce runQtDesigner.py ?
    oui bien sûr

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    racineProjet/
    |____main.py  #fichier principal de l'appli
    |____Interface/  #package qui gêre l'affichage GUI
    |____|____runQtDesigner.py   #script qui lance QtDesigner avec les variable renseigées PYTHONPATH et PYTHONDESIGNERPATH
    |____|____|____plugins/ #package des plugins QtDesigner
    |____|____|____|____ configViewerPagePlugin.py # ne s'affiche pas
    |____|____|____|____ analogclockplugin.py # s'affiche 
    |____|____|____widgets/  #package des widgets perso
    |____|____|____|____ configViewerPage.py
    |____|____|____|____ analogclock.py
    |____|____|____ui/  #package des fichiers .ui de QtDesigner
    |____|____|____|____ configViewerPage.ui

    Citation Envoyé par VinsS Voir le message
    Ensuite je vois que tu as défini un groupe
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
        def group(self):
            return 'Diam widget'
     
     
        def includeFile(self):
            return 'configViewerPage'
    et changer le code du plugin:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
        def group(self):
            return 'Diam_widget'
     
     
        def includeFile(self):
            return 'Diam_widget.configViewerPage'
    oui j'avoue que je ne sais pas trop comment indiquer le fichier à inclure et surtout par rapport à quoi (La racine de mon projet, le script runQtDesigner.py, du fichier plugin, ??) !

    Je ne peut pas inclure directement mes fichiers dans les répertoires python et qt car je n'ai pas les accès root sur ma machine.

    Merci

  8. #8
    Expert confirmé

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    4 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2008
    Messages : 4 307
    Par défaut
    Citation Envoyé par Dr Rodney Mckay Voir le message
    Je ne peut pas inclure directement mes fichiers dans les répertoires python et qt car je n'ai pas les accès root sur ma machine.
    O.k. tout est dit.

    Il est impératif que le plugin libpythonplugin.so trouve les fichiers dans le dossier python se trouvant dans le même dossier que lui-même.

    Une solution serait de demander que l'on te crée un lien vers ton dossier de travail.
    donc :
    .../qt4/plugins/designer/python/ pointant vers .../racineProjet/Interface/plugins/python

    Et dans ce dossier python tu places comme ceci:
    /python/configViewerPagePlugin.py
    /python/widgets/configViewerPage.py

    Tout ceci n'est nécessaire que pour faire apparaître ton widget dans le designer, bien sur, ton application embarquant, elle, ton widget comme n'importe quel autre module.

  9. #9
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Juillet 2009
    Messages
    51
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Juillet 2009
    Messages : 51
    Par défaut
    Bonjour,

    J'ai fait le nécessaire pour le lien symbolique. Il est en place mais mes widgets ne sont toujours pas reconnus par QtDesigner ! Le plugin de "référence" des exemples PyQt ne marche pas non plus

    Par contre, quand je le lance le script de tyrtamos ça marche !
    Je suis arrivé à afficher son plugin (geolocationplugin.py) en changeant le from import du widget et la méthode includeFile du plugin.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    from Interface.widgets.geolocationwidget import GeoLocationWidget
     
    def includeFile(self):
            """retourne le nom du fichier qui définit le nouveau widget"""
            return "geolocationwidget"
    Je me suis donc dit "c'est gagué !". Et bien non, j'ai appliqué la même recette à mes widgets perso sans résultat !

    J'ai comparer les codes du plugin de Tyrtamos et le mien. C'est son copié/collé ! Seul les noms et chemins d'accès change pour s'adapter...

    Si vous n'êtes toujours pas à court d'idée, je prends !
    Merci

  10. #10
    Expert confirmé

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    4 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2008
    Messages : 4 307
    Par défaut
    Première chose qui me vient à l'esprit, le plugin est refusé par le Designer.

    Lors de l'import du plugin, il semble que le Designer l'exécute ou, à tout le moins, il l'initialise.
    À plusieurs reprises j'ai eu un custom widget refusé pour cause d'erreur dans le scrip lui-même ou dans le script plugin associé.

    Donc lorsque je teste un nouveau widget, je lance toujours le designer dans un terminal
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    /usr/bin/designer-qt4
    Les erreurs éventuelles apparaîtrons. Ce sont, dailleurs, ces erreurs qui me font dire que Designer exécute le script lors de son import.

    Comme tu sembles avoir un fichier Python pour le lancement du Designer, faudra peut-être voir son contenu pour lancer le programme.

  11. #11
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Juillet 2009
    Messages
    51
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Juillet 2009
    Messages : 51
    Par défaut
    Le script que j'utilise est grandement inspiré de celui de tyrtamos :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    #! /usr/bin/python
    # -*- coding: utf-8 -*-
    # Python v2.7
     
    import os, sys
    from PyQt4 import QtCore, QtGui
     
    # définition du séparateur de chemins dans les variables d'environnement
    if sys.platform == "win32":
        sepchemin = ';'
    else:
        sepchemin = ':'
     
    # lancement du système graphique
    app = QtGui.QApplication(sys.argv)
     
    # ===> lecture du dictionnaire des variables d'environnement
    env = os.environ.copy()
     
    # ===> ajout du chemin des widgets s'il n'existe pas déjà
    cheminwidgets = os.path.abspath("widgets")
    if env.has_key('PYTHONPATH'):
        chemins = env['PYTHONPATH']
        if cheminwidgets not in chemins.split(sepchemin):
            env['PYTHONPATH'] = chemins + sepchemin + cheminwidgets
    else:
        env['PYTHONPATH'] = cheminwidgets        
     
    # ===> ajout du chemin des plugins s'il n'existe pas déjà
    cheminplugins = os.path.abspath("plugins")
    if env.has_key('PYQTDESIGNERPATH'):
        chemins = env['PYQTDESIGNERPATH']
        if cheminplugins not in chemins.split(sepchemin):
            env['PYQTDESIGNERPATH'] = chemins + sepchemin + cheminplugins
    else:
        env['PYQTDESIGNERPATH'] = cheminplugins        
     
    # ===> création liste des variables d'environnement à partir du dictionnaire
    lstenv = ['%s=%s' % (name, value) for name, value in env.items()]
     
    # ===> Lancement du designer
    designer = QtCore.QProcess()
    designer.setEnvironment(lstenv)
     
    designer_bin = QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.BinariesPath)
     
    if sys.platform == 'darwin':
        designer_bin += '/Designer.app/Contents/MacOS/Designer'
    else:
        designer_bin += '/designer'
     
    designer.start(designer_bin)
    designer.waitForFinished(-1)
     
    sys.exit(designer.exitCode())

  12. #12
    Expert confirmé
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 486
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 486
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Quelques idées.

    Puisque tu fais des importations en utilisant la syntaxe des packages, il devrait y avoir des "__init__", même vides, à tous les étages: est-ce le cas?

    Est-ce que ton widget perso fonctionne quand tu l'importes normalement dans un script Python (sans QtDesigner)?

    Dans le runQtDesigner, vérifie avec quelques "print" que les adresses sont correctes.

    Le plugin doit importer le widget: es-tu sûr que cette importation fonctionne?


    Pour mémoire, il y a quelque chose qu'il faut éviter dans les codes de ton 1er message: à ma connaissance, PyQt accepte les héritages multiples, mais à condition qu'il n'y ait pas plus d'un objet Qt dépendant de QObject dans les ancêtres.

  13. #13
    Expert confirmé

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    4 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2008
    Messages : 4 307
    Par défaut
    C'est le fichier runQtDesigner.py que tu nous montres là ?

    Maintenant que je l'ai lu, je me demande à quoi sert-il ?

    Si tu rentre ceci dans un terminal:
    ou bien
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    /usr/bin/designer-qt4
    Obtiens tu le même résultat que moi:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
     
    vincent@tiemoko:~$ designer-qt4
    Error while reparenting!
    QMetaProperty::read: Unable to handle unregistered datatype 'QList<QColor>' for property 'KColorCombo::colors'
    Connecting to deprecated signal QDBusConnectionInterface::serviceOwnerChanged(QString,QString,QString)
    QDBusConnection: session D-Bus connection created before QCoreApplication. Application may misbehave.
    QDBusConnection: session D-Bus connection created before QCoreApplication. Application may misbehave.
    kbuildsycoca4 running...
    kbuildsycoca4(2339) KBuildSycoca::checkTimestamps: checking file timestamps
    kbuildsycoca4(2339) KBuildSycoca::checkTimestamps: timestamps check ok
    kbuildsycoca4(2339) kdemain: Emitting notifyDatabaseChanged ()
    "/usr/bin/designer-qt4(2325)" Soprano: "QLocalSocket::connectToServer*: Nom invalide"
    "/usr/bin/designer-qt4(2325)" Soprano: "QLocalSocket::connectToServer*: Nom invalide"
    QMetaProperty::read: Unable to handle unregistered datatype 'SelectionBehavior' for property 'QTabBar::selectionBehaviorOnRemove'
    QMetaProperty::read: Unable to handle unregistered datatype 'KUrl' for property 'KUrlRequester::url'
    QMetaProperty::read: Unable to handle unregistered datatype 'QList<QChar>' for property 'KCharSelect::displayedChars'
    QMetaProperty::read: Unable to handle unregistered datatype 'KUrl' for property 'KUrlRequester::url'
    Enchant dict for "en_US" 0x8f475c0 
    QMetaProperty::read: Unable to handle unregistered datatype 'QList<QColor>' for property 'KColorCombo::colors'
    The property "colors" of type 0 (invalid)  is not supported yet!
    QMetaProperty::read: Unable to handle unregistered datatype 'QList<QColor>' for property 'KColorCombo::colors'
    The property "colors" of type 0 (invalid)  is not supported yet!
    QMetaProperty::read: Unable to handle unregistered datatype 'SelectionBehavior' for property 'QTabBar::selectionBehaviorOnRemove'
    The property "selectionBehaviorOnRemove" of type 0 (invalid)  is not supported yet!
    ... avec, comme on peut le voir, des erreurs dans les widgets de KDE.

  14. #14
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Juillet 2009
    Messages
    51
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Juillet 2009
    Messages : 51
    Par défaut
    Bonjour,


    @tyrtamos :
    Citation Envoyé par tyrtamos Voir le message
    Puisque tu fais des importations en utilisant la syntaxe des packages, il devrait y avoir des "__init__", même vides, à tous les étages: est-ce le cas?
    Je programme sur éclipse. Il ne reconnait pas mes packages quand je n'ai pas de "__init__"

    Citation Envoyé par tyrtamos Voir le message
    Est-ce que ton widget perso fonctionne quand tu l'importes normalement dans un script Python (sans QtDesigner)?
    oui, très bien.


    Citation Envoyé par tyrtamos Voir le message
    Dans le runQtDesigner, vérifie avec quelques "print" que les adresses sont correctes.
    J'ai fait afficher l'environnement passé au lancement de QtDesigner :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    # ===> création liste des variables d'environnement à partir du dictionnaire
    lstenv = ['%s=%s' % (name, value) for name, value in env.items()]
    print(lstenv)
    # ===> Lancement du designer
    designer = QtCore.QProcess()
    designer.setEnvironment(lstenv)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    PYQTDESIGNERPATH=/monHome/examples/designer/plugins:/monHome/Programation/DiamAnalyse/Interface/plugins
    Ce sont les bons répertoires car le dossier Interface correspond au package du même nom que j'ai évoqué plus haut.

    Citation Envoyé par tyrtamos Voir le message
    Le plugin doit importer le widget: es-tu sûr que cette importation fonctionne?
    Pour ton plugin ça marche (qui est plassé au même endroit que le mien). J'ai donc appliqué le même linkage pour mon module.

    Citation Envoyé par tyrtamos Voir le message
    Pour mémoire, il y a quelque chose qu'il faut éviter dans les codes de ton 1er message: à ma connaissance, PyQt accepte les héritages multiples, mais à condition qu'il n'y ait pas plus d'un objet Qt dépendant de QObject dans les ancêtres.
    Bonne précision mais je ne suis pas (ou plus) dans ce cas là. J'ai créé un widget tout bête pour tester

    le widget :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import os, sys
    from PyQt4 import QtCore, QtGui
     
     
    class ConfigViewerPage(QtGui.QWidget):
     
        def __init__(self, parent=None):
            super(ConfigViewerPage, self).__init__(parent)
     
            self.gridLayout = QtGui.QGridLayout()
            self.myPushButton = QtGui.QPushButton("Mon bouton !")
            self.groupsVLayout = QtGui.QVBoxLayout()
            self.label1 = QtGui.QLabel("hyalaaaaa !!!")
            self.label2 = QtGui.QLabel("hyalaaaaa22222 !!!")
            self.groupsVLayout.addWidget(self.label1)
            self.groupsVLayout.addWidget(self.label2)        
            self.gridLayout.addLayout(self.groupsVLayout, 0,0)
            self.gridLayout.addWidget(self.myPushButton, 1,0)
            self.setLayout(self.gridLayout)
    le plugin correspondant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    import os, sys
    from PyQt4 import QtGui, QtCore, QtDesigner
    from Interface.widgets.configViewerPage import ConfigViewerPage
     
    class ConfigViewerPagePlugin(QtDesigner.QPyDesignerCustomWidgetPlugin):
     
        def __init__(self, parent=None):
            super(ConfigViewerPagePlugin, self).__init__(parent)
            self.initialized = False
     
        def initialize(self, core):
            if self.initialized:
                return
            self.initialized = True
     
        def isInitialized(self):
            return self.initialized
     
        def createWidget(self, parent):
            return ConfigViewerPage(parent)
     
        def name(self):
            return "ConfigViewerPage"
     
        def group(self):
            return "Les widgets Diam"
     
        def isContainer(self):
            return False
     
        def includeFile(self):
            return "configViewerPage"

    @VinsS : oui c'est mon fichier runQtDesigner.py. Désolé, je vais éditer mon message.

    Ma commant QtDesigner () n'est pas aussi bavarde que la tienne. Elle ne me renvoie strictement rien !
    Je ne suis pas sur kde mais sur une scientific-linux basée sur RedHat.

  15. #15
    Expert confirmé

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    4 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2008
    Messages : 4 307
    Par défaut
    Je suis en ce moment en train de faire la même chose avec Qt5 et donc PyQt5.

    Et ici, je n'ai, moi non plus, aucun retour dans le terminal. Mais, une fois le Designer ouvert, il y a un menu Aide >> À propos des plugins où une boîte de dialogue m'informe que l'import de mon widget à échoué. Malheureusement la raison n'est pas clairement donnée.

    À suivre...

  16. #16
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Juillet 2009
    Messages
    51
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Juillet 2009
    Messages : 51
    Par défaut
    Citation Envoyé par VinsS Voir le message
    Et ici, je n'ai, moi non plus, aucun retour dans le terminal. Mais, une fois le Designer ouvert, il y a un menu Aide >> À propos des plugins où une boîte de dialogue m'informe que l'import de mon widget à échoué. Malheureusement la raison n'est pas clairement donnée.
    J'ai également accès à ce panneau mais il ne m'indique rien sur les tentatives de chargement de mes widgets perso...

  17. #17
    Expert confirmé
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 486
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 486
    Billets dans le blog
    6
    Par défaut
    Bonjour VinsS,

    Citation Envoyé par VinsS Voir le message
    C'est le fichier runQtDesigner.py que tu nous montres là ?

    Maintenant que je l'ai lu, je me demande à quoi sert-il ?
    Quand on ajoute des widgets perso à utiliser avec QtDesigner, la 1ère solution à laquelle on pense, c'est: "j'ajoute mes widgets au système, et tous mes projets vont pouvoir en profiter comme n'importe quel autre widget d'origine".

    Mais il y a 3 problèmes:

    - si je donne mon code à quelqu'un d'autre sur un autre PC, il ne fonctionnera que s'il a intégré mes widgets perso dans son propre système.

    - si je sauvegarde mon projet, il ne faut pas oublier que je sauvegarde aussi mes widgets perso du système (alors que je n'ai pas besoin de sauvegarder les autres widgets système).

    - si je fais évoluer mes widgets perso dans le temps, iil faudra que je gère la compatibilité de ces évolutions avec les précédents projets qui les utilisent.

    La solution que j'ai trouvé, est donc celle-ci:

    1- je développe mes widgets perso quelque part en tant que projets (projets Pydev sous Eclipse).

    2- dans tous les cas, une fois mis au point, j'intègre ces widgets perso au projet qui les utilise pour que ce projet soit complet: il fonctionnera donc tel quel sur n'importe quel autre machine qui a Python + PyQt, sans autre préparation.

    Ce dernier point résoud les problèmes que je citais plus haut.

    La conséquence, c'est qu'il faut intégrer un script, qui fait partie du projet, pour lancer QtDesigner, afin qu'il reconnaisse les widgets perso intégrés à ce projet pour les utiliser.

    Mais si tu as une autre solution pour résoudre ce problème, je suis intéressé!

    Cependant, même si mes projets sont multiplateformes (Windows-Linux-Mac OS X), y compris pour les widgets perso, je ne développe que sous Windows. Aussi, je n'ai jamais essayé le runQtDesigner sous Linux. Peut-être y a-t-il une subtilité d'OS qui m'a échappé: je vais essayer (j'ai Linux Mint issu d'Ubuntu)!

    [edit] un premier résultat sous Linux Mint 14: en console, la commande "designer-qt4" lance QtDesigner sans autre message. Et c'est aussi la ligne d'instruction intégrée à l'icône du bureau (/usr/bin/designer-qt4).

  18. #18
    Expert confirmé
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 486
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 486
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    J'ai trouvé pourquoi ça ne marche pas!

    Dans la doc, il y a une petite phrase importante que je traduis en français:: "le nom du fichier du plugin doit se terminer par 'plugin', sinon, il est ignoré".

    Et il s'agit bien de 'plugin' et non 'Plugin'. Il suffit donc de renommer le fichier du plugin par "configViewerPageplugin.py"!

    Je redonne ici les fichiers avec lesquels j'ai fait l'essai sous Linux:

    fichier configViewerPage.py:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    #! /usr/bin/python
    # -*- coding: utf-8 -*-
    # Python v2.7
     
    import sys
     
    from PyQt4 import QtCore, QtGui
     
    class ConfigViewerPage(QtGui.QWidget):
     
        def __init__(self, parent=None):
            super(ConfigViewerPage, self).__init__(parent)
     
            self.gridLayout = QtGui.QGridLayout()
            self.myPushButton = QtGui.QPushButton("Mon bouton !")
            self.groupsVLayout = QtGui.QVBoxLayout()
            self.label1 = QtGui.QLabel("hyalaaaaa !!!")
            self.label2 = QtGui.QLabel("hyalaaaaa22222 !!!")
            self.groupsVLayout.addWidget(self.label1)
            self.groupsVLayout.addWidget(self.label2)        
            self.gridLayout.addLayout(self.groupsVLayout, 0,0)
            self.gridLayout.addWidget(self.myPushButton, 1,0)
            self.setLayout(self.gridLayout)
     
     
    if __name__ == "__main__":
        app = QtGui.QApplication(sys.argv)
        widget = ConfigViewerPage()
        widget.show()
        sys.exit(app.exec_())
    fichier configViewerPageplugin.py:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    #! /usr/bin/python
    # -*- coding: utf-8 -*-
    # Python v2.7
     
    from PyQt4 import QtCore, QtGui, QtDesigner
    from configViewerPage import ConfigViewerPage
     
    class ConfigViewerPagePlugin(QtDesigner.QPyDesignerCustomWidgetPlugin):
     
        def __init__(self, parent=None):
            super(ConfigViewerPagePlugin, self).__init__(parent)
     
            self.initialized = False
     
        def initialize(self, core):
            if self.initialized:
                return
            self.initialized = True
     
        def isInitialized(self):
            return self.initialized
     
        def createWidget(self, parent):
            return ConfigViewerPage(parent)
     
        def name(self):
            return 'ConfigViewerPage'
     
        def group(self):
            return 'Diam widget'
     
        def isContainer(self):
            """dit si le nouveau widget est un conteneur ou pas"""
            return False
     
        def domXml(self):
            return '<widget class="ConfigViewerPage" name="configViewerPage" />\n'
     
        def includeFile(self):
            return 'configViewerPage'
    et enfin le runQtDesigner.py:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    #! /usr/bin/python
    # -*- coding: utf-8 -*-
    # Python v2.7
     
    import os, sys
    from PyQt4 import QtCore, QtGui
     
    # définition du séparateur de chemins dans les variables d'environnement
    if sys.platform == "win32":
        sepchemin = ';'
    else:
        sepchemin = ':'
     
    # lancement du système graphique
    app = QtGui.QApplication(sys.argv)
     
    # ===> lecture du dictionnaire des variables d'environnement
    env = os.environ.copy()
     
    # ===> ajout du chemin des widgets s'il n'existe pas déjà
    cheminwidgets = os.path.abspath("widgets")
    if env.has_key('PYTHONPATH'):
        chemins = env['PYTHONPATH']
        if cheminwidgets not in chemins.split(sepchemin):
            env['PYTHONPATH'] = chemins + sepchemin + cheminwidgets
    else:
        env['PYTHONPATH'] = cheminwidgets        
     
    # ===> ajout du chemin des plugins s'il n'existe pas déjà
    cheminplugins = os.path.abspath("plugins")
    if env.has_key('PYQTDESIGNERPATH'):
        chemins = env['PYQTDESIGNERPATH']
        if cheminplugins not in chemins.split(sepchemin):
            env['PYQTDESIGNERPATH'] = chemins + sepchemin + cheminplugins
    else:
        env['PYQTDESIGNERPATH'] = cheminplugins        
     
    print env['PYTHONPATH']
    print env['PYQTDESIGNERPATH']
     
    # ===> création liste des variables d'environnement à partir du dictionnaire
    lstenv = ['%s=%s' % (name, value) for name, value in env.items()]
     
    print lstenv
     
    # ===> Lancement du designer
    designer = QtCore.QProcess()
    designer.setEnvironment(lstenv)
     
    designer_bin = QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.BinariesPath)
     
    if sys.platform == 'darwin':
        designer_bin += '/Designer.app/Contents/MacOS/Designer'
    else:
        designer_bin += '/designer'
     
    print designer_bin
     
    designer.start(designer_bin)
    designer.waitForFinished(-1)
     
    sys.exit(designer.exitCode())
    La structure est celle fournie (j'ai omis les __init__):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    interface
        plugins
            configViewerPageplugin.py
        widgets
            configViewerPage.py
        runQtDesigner.py

  19. #19
    Expert confirmé

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    4 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2008
    Messages : 4 307
    Par défaut
    Ah oui d'accord, j'avais pensé que ce fichier était dû à la situation de l'OP qui n'a pas les droits root sur sa machine, mais ça n'a rien à voir.

    En ce qui me concerne, mes deux applis sont distribuées avec leurs widgets personnalisés. Ceux-ci sont développés avec Oqapy et sont disponibles dans la même forge (Launchpad) le dossier contient une documentation développeur expliquant comment les installer pour les rendre disponibles dans le Designer.

    C'est clair que pour moi toute modification d'un widget nécessite une mise à jour dans le dossier plugin du designer et dans le dist-package de chaque version de Python que j'utilise, mais bon, j'ai un script pour faire ça bien sur. L'utilisateur du programme, lui, ne doit rien faire.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 3
    Dernier message: 22/02/2010, 10h40
  2. Création d'un livre d'or avec dreamweaver
    Par stanley dans le forum Dreamweaver
    Réponses: 2
    Dernier message: 24/10/2008, 17h07
  3. Kdevelop avec Qtdesigner
    Par Moonshadow dans le forum Linux
    Réponses: 5
    Dernier message: 10/03/2006, 10h35
  4. Création d'une variable de session avec un ID
    Par PrinceMaster77 dans le forum ASP
    Réponses: 4
    Dernier message: 18/10/2004, 10h28

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo