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

GTK+ avec Python Discussion :

PyGTK, Drawing Area et rafraîchissement de fenêtre.


Sujet :

GTK+ avec Python

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Décembre 2011
    Messages
    65
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2011
    Messages : 65
    Par défaut PyGTK, Drawing Area et rafraîchissement de fenêtre.
    Bonjour à tous,

    Je suis en plein parcours initiatique de Python. Pour m'aider, j'utilise le livre de M.SWINNEN qui utilise la librairie Tkinter pour réaliser des interfaces graphiques. Moi, je préfère plutôt GTK et je me suis donc mis en tête de faire les exercices et de les adapter à GTK. Dans l'un des premiers exercices, un petite interface permet de dessiner des lignes dans une zone de dessin avec un bouton pour tracer et un bouton pour choisir une couleur aléatoire. Jusque là pas de problème, je m'en suis sorti (je n'utilise pas encore les classes car je n'ai pas encore abordé ce chapitre).

    J'ai toutefois une chose qui m'agace profondément. Lorsque je redimensionne ma fenêtre, tout mon dessin s’efface, idem si je la cache par une autre fenêtre.

    J'ai fais des recherches sur Internet et trouver une série de ligne de code qui devrait résoudre mon problème. Mais après plusieurs essais, je n'arrive à rien de concret. Mon niveau n'étant pas assez élevé. Quelqu'un pourrait-il m'expliquer comment y arriver. Notamment qu'elles sont les arguments à passe r et me donner quelques explications sur les signaux utilisées. Façon gros neuneu.

    - J'ai aussi entendu parler de double_buffering. Peut-t-on l'utiliser pour ça?
    - Par la suite,est-ce que je peux créer un module spécifique dédié au rafraîchissement de ma fenêtre?

    Merci d'avance pour votre aide. Je déjà fais une recherche sur le site et trouvé des exemples. Une des astuces utilisées et de rappeler la fonction qui a servi à dessiner l'objet. Mais cela ne fonctionne que dans le cas d'un dessin entièrement prédéfini.

    NOTA: Si vous avez d'autres petites astuces pour améliorer mon code, je suis preneur.

    Ci-dessous, le code que j'ai fais où je essayé d'ajouter la gestion de ma fenêtre. (J'ai fait des copier-coller, mais j'admets ne pas bien saisir à quoi tout cela correspond)

    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
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
     
    #!/usr/bin/python
     
    import gtk
    from random import randrange
     
    # Dessiner une ligne sur la zone de dessin   
    def expose(widget, data):
        global x1, x2, y1, y2, couleur
        cr = data.window.cairo_create()
        cr.set_source_rgb(couleur[0], couleur[1], couleur[2]) 
        cr.set_line_width(2)
     
        #w = data.allocation.width
        #h = data.allocation.height
        #print w, h
     
        cr.move_to(x1, y1)
        cr.line_to(x2, y2)
        cr.stroke()
     
        y2, y1 = y2+10, y1-10 
     
    # Choisir une couleur au hasard
    def color(widget):
        'test'
        global couleur
        i = 0
        while i<3:
            couleur[i] = randrange(0,255)/255.0
            i += 1
     
    ############ Gestion Pixmap 
    # Create a new backing pixmap of the appropriate size
    def configure_event(widget, event):
        global pixmap
     
        x, y, width, height = widget.get_allocation()
        pixmap = gtk.gdk.Pixmap(widget.window, width, height)
        pixmap.draw_rectangle(widget.get_style().white_gc,
                              True, 0, 0, width, height)
     
        return True
     
    # Redraw the screen from the backing pixmap
    def expose_event(widget, event):
        x , y, width, height = event.area
        widget.window.draw_drawable(widget.get_style().fg_gc[gtk.STATE_NORMAL],
                                    pixmap, x, y, x, y, width, height)
        return False
     
    def button_press_event(widget, event):
        if event.button == 1 and pixmap != None:
            expose(widget, event.x, event.y)
        return True
     
    def motion_notify_event(widget, event):
        if event.is_hint:
            x, y, state = event.window.get_pointer()
        else:
            x = event.x
            y = event.y
            state = event.state
     
        if state & gtk.gdk.BUTTON1_MASK and pixmap != None:
            expose(widget, x, y)
     
        return True
    #################
     
     
    # Initialisation des variables
    couleur = [0, 0, 0]
    x1, y1, x2, y2 = 30, 30, 570, 570
    # Backing pixmap for drawing area
    pixmap = None
     
     
    # Cr?ation de l'interface
    PyApp = gtk.Window()
    PyApp.set_title("Formes")
    PyApp.set_size_request(800, 600)
    PyApp.set_border_width(20)
    PyApp.set_position(gtk.WIN_POS_CENTER)
    PyApp.connect("destroy", gtk.main_quit)
     
    darea = gtk.DrawingArea()
     
    ###################### Copier-coller pour gestion de l'affichage
    # Signals used to handle backing pixmap
    darea.connect("expose_event", expose_event)
    darea.connect("configure_event", configure_event)
     
    # Event signals
    darea.connect("motion_notify_event", motion_notify_event)
    darea.connect("button_press_event", button_press_event)
     
    darea.set_events(gtk.gdk.EXPOSURE_MASK
                            | gtk.gdk.LEAVE_NOTIFY_MASK
                            | gtk.gdk.BUTTON_PRESS_MASK
                            | gtk.gdk.POINTER_MOTION_MASK
                            | gtk.gdk.POINTER_MOTION_HINT_MASK)
     
    ##################
     
     
    # Suite de l'interface
    button1 = gtk.Button(label="Tracer une ligne")
    button1.set_size_request(130, 35)
    button1.connect("clicked", expose, darea)
     
    button2 = gtk.Button(label="Changer de couleur")
    button2.set_size_request(130, 35)
    button2.connect("clicked", color)
     
    button3 = gtk.Button(label="Quitter")
    button3.set_size_request(130, 35)
    button3.connect("clicked", gtk.main_quit)
     
    vbox = gtk.VBox(False, 5)
    hbox = gtk.HBox(False, 3)
     
    vbox.pack_start(button1, False, True, 3)
    vbox.pack_start(button2, False, False, 3)
    vbox.pack_end(button3, False, False, 3)
     
    hbox.pack_start(darea, True, True)
    hbox.pack_end(vbox, False, False)
     
    PyApp.add(hbox)
    PyApp.show_all()
    gtk.main()

  2. #2
    Membre chevronné
    Inscrit en
    Janvier 2007
    Messages
    329
    Détails du profil
    Informations forums :
    Inscription : Janvier 2007
    Messages : 329
    Par défaut
    Salut,

    Citation Envoyé par austin57 Voir le message
    Lorsque je redimensionne ma fenêtre, tout mon dessin s’efface, idem si je la cache par une autre fenêtre.
    ...
    Je déjà fais une recherche sur le site et trouvé des exemples. Une des astuces utilisées et de rappeler la fonction qui a servi à dessiner l'objet. Mais cela ne fonctionne que dans le cas d'un dessin entièrement prédéfini.
    Effectivement, ca ne fonctionne pas du tout pareil entre pygtk et Tkinter.
    Sous PyGTK, tu dois mémoriser toutes tes lignes à tracer et les redessiner lorsque tu captes le signal 'expose-event'.


    Citation Envoyé par austin57 Voir le message
    - J'ai aussi entendu parler de double_buffering. Peut-t-on l'utiliser pour ça?
    - Par la suite,est-ce que je peux créer un module spécifique dédié au rafraîchissement de ma fenêtre?
    Le double-buffering, c'est uniquement utilisé par gtk en interne pour éviter le flickering. Il est désactivable pour éventuellement améliorer un peu les performances par rapport au rendu. Ça ne concerne en rien ton problème.

    Tu peux effectivement reprogrammer un module ou une classe qui implémente le canevas de Tkinter en GTK, à savoir qui mémorise la liste d'objets (des lignes colorées dans ton cas) et les redessine en cas de redimensionnement. C'est même une très bonne idée (et un bon exercice) !


    -

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Décembre 2011
    Messages
    65
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2011
    Messages : 65
    Par défaut
    @ monnomamoi
    Merci pour ces premiers éléments de réponse.
    Je vais effectivement me créer, pour cet exercice, un module pour redessiner et/ou redimensionner les lignes tracées. Je crois que ce sera un excellent entraînement.



    Toutefois, j'aimerai quand même trouver une solution plus générale, qui fonctionnerai quelque soit le type de dessin. Je pense que les lignes de codes récupérées de l'exemple PyGTK scribble.py et que j'ai intégré dans mon programme en sont la clé. Il me reste à savoir comment bien les intégrer et c'est là que j'aurai besoin de votre aide. Merci d'avance pour vos conseils.

  4. #4
    Membre chevronné
    Inscrit en
    Janvier 2007
    Messages
    329
    Détails du profil
    Informations forums :
    Inscription : Janvier 2007
    Messages : 329
    Par défaut
    Tu peux aussi t'inspirer de cet exemple :
    http://www.eurion.net/python-snippet...h%20Cairo.html

    Ça donne déjà un bon aperçu des bases.

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Décembre 2011
    Messages
    65
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2011
    Messages : 65
    Par défaut
    J'ai encore fouiné à travers le web et je pense avoir trouvé un exemple intéressant. Toujours en utilisant Cairo. 2 fichiers.
    Si j'ai bien compris, le premier fichier, à nommer framework.py crée un widget dérivé de 'drawing area' gérant le redimensionnement et le masquage de la fenêtre. L'autre fichier étant le programme lui-même faisant appel à framework.py

    Il me reste à trouver comment faire le lien entre mon programme et framework.py ?!! Et pour l'instant, je sèche, ça fait bien 1h que je me retourne l'esprit là-dessus.

    L'adresse du site où j'ai trouvéça : http://www.tortall.net/mu/wiki/PyGTKCairoTutorial

    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
    #! /usr/bin/env python
    import pygtk
    pygtk.require('2.0')
    import gtk, gobject, cairo
     
    # Create a GTK+ widget on which we will draw using Cairo
    class Screen(gtk.DrawingArea):
     
        # Draw in response to an expose-event
        __gsignals__ = { "expose-event": "override" }
     
        # Handle the expose-event by drawing
        def do_expose_event(self, event):
     
            # Create the cairo context
            cr = self.window.cairo_create()
     
            # Restrict Cairo to the exposed area; avoid extra work
            cr.rectangle(event.area.x, event.area.y,
                    event.area.width, event.area.height)
            cr.clip()
     
            self.draw(cr, *self.window.get_size())
     
        def draw(self, cr, width, height):
            # Fill the background with gray
            cr.set_source_rgb(0.5, 0.5, 0.5)
            cr.rectangle(0, 0, width, height)
            cr.fill()
     
    # GTK mumbo-jumbo to show the widget in a window and quit when it's closed
    def run(Widget):
        window = gtk.Window()
        window.connect("delete-event", gtk.main_quit)
        widget = Widget()
        widget.show()
        window.add(widget)
        window.present()
        gtk.main()
     
    if __name__ == "__main__":
        run(Screen)
    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
    #! /usr/bin/env python
    import framework
    from math import pi
     
    class Transform(framework.Screen):
        def draw(self, cr, width, height):
            cr.set_source_rgb(0.5, 0.5, 0.5)
            cr.rectangle(0, 0, width, height)
            cr.fill()
     
            # draw a rectangle
            cr.set_source_rgb(1.0, 1.0, 1.0)
            cr.rectangle(10, 10, width - 20, height - 20)
            cr.fill()
     
            # set up a transform so that (0,0) to (1,1)
            # maps to (20, 20) to (width - 40, height - 40)
            cr.translate(20, 20)
            cr.scale((width - 40) / 1.0, (height - 40) / 1.0)
     
            # draw lines
            cr.set_line_width(0.01)
            cr.set_source_rgb(0.0, 0.0, 0.8)
            cr.move_to(1 / 3.0, 1 / 3.0)
            cr.rel_line_to(0, 1 / 6.0)
            cr.move_to(2 / 3.0, 1 / 3.0)
            cr.rel_line_to(0, 1 / 6.0)
            cr.stroke()
     
            # and a circle
            cr.set_source_rgb(1.0, 0.0, 0.0)
            radius = 1
            cr.arc(0.5, 0.5, 0.5, 0, 2 * pi)
            cr.stroke()
            cr.arc(0.5, 0.5, 0.33, pi / 3, 2 * pi / 3)
            cr.stroke()
     
    framework.run(Transform)

  6. #6
    Membre chevronné
    Inscrit en
    Janvier 2007
    Messages
    329
    Détails du profil
    Informations forums :
    Inscription : Janvier 2007
    Messages : 329
    Par défaut
    Ton exemple fait la même chose que le mien, mais en plus compliqué puisqu'il coupe le programme en 2 fichiers.

    La seule astuce à retenir, c'est le "cr.scale()" qui permet de changer l'échelle du canevas (ou plutôt du cairo context) au lieu de changer les dimensions du dessin.

    Un petit exemple (vite fait mal fait) de canevas qui implémente la méthode "create_line" comme avec Tkinter :
    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
    import gtk
    import cairo
     
    class Canevas(gtk.DrawingArea):
        def __init__(self, w, h):
            gtk.DrawingArea.__init__(self)
            self.dim = (w, h)
            self.set_size_request(*self.dim)
            self.lines = []
            self.context = None
            self.connect('expose-event', self.expose)
            self.show()
     
        def expose(self, area, event):
            self.context = area.window.cairo_create()
            self.context.rectangle(event.area.x, event.area.y, event.area.width, event.area.height)
            self.context.clip()
            self.context.scale(event.area.width/float(self.dim[0]), event.area.height/float(self.dim[1]))
            self.context.set_line_width(2.0)
            for l in self.lines:
                self.draw_line(*l)
            return False
     
        def draw_line(self, x1, y1, x2, y2, c):
            if self.context is None:
                return
            self.context.set_source_rgb(*c)
            self.context.move_to(x1, y1)
            self.context.line_to(x2, y2)
            self.context.stroke()
            return
     
        def create_line(self, x1, y1, x2, y2, c):
            self.lines.append( (x1, y1, x2, y2, c) )
            self.draw_line(*self.lines[-1])
            return
     
     
     
    if __name__ == '__main__':
        win = gtk.Window()
        win.connect('destroy', gtk.main_quit)
        can = Canevas(400, 300)
        win.add(can)
        can.create_line(20, 50, 350, 300, [1.0,0.0,0.0])
        can.create_line(20, 20, 200, 200, [0.0,1.0,0.0])
        can.create_line(100, 0, 100, 200, [0.0,0.0,1.0])
        win.show()
        gtk.main()
    -

  7. #7
    Membre confirmé
    Profil pro
    Inscrit en
    Décembre 2011
    Messages
    65
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2011
    Messages : 65
    Par défaut
    Merci monnomamoi

    Je t'avoue ne pas avoir encore tout compris à 100%.
    Mais j'avance dans ma compréhension de PyGTK, notamment l'importance des signaux tel que "expose". Idem pour l'astuce Scale de Cairo qui est vraiment pratique et simplifie réellement les choses.

    Je vais essayer de réaliser mon propre petit module afin de tester cela.

    Cordialement,

Discussions similaires

  1. Pygtk: comment utiliser le widget Drawing Area
    Par leousch dans le forum GTK+ avec Python
    Réponses: 9
    Dernier message: 23/08/2009, 12h44
  2. Dessiner sur une drawing area
    Par Fibus dans le forum GTK+ avec C & C++
    Réponses: 8
    Dernier message: 19/12/2007, 16h57
  3. drawing area et itérations
    Par Rniamo dans le forum GTK+ avec C & C++
    Réponses: 7
    Dernier message: 07/11/2007, 19h00
  4. Drawing area
    Par Blackshade dans le forum GTK+ avec PHP
    Réponses: 4
    Dernier message: 06/06/2007, 16h51
  5. Image de fond dans un drawing area
    Par smux dans le forum GTK+ avec C & C++
    Réponses: 1
    Dernier message: 15/09/2006, 11h17

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