Une zone graphique avec Qt

À l'image de GIMP

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Bonjour,

J'ai besoin, avec Qt - PyQt en l'occurrence -, de permettre à l'utilisateur d'ouvrir des images et de créer des régions rectangulaires dessus - autrement dit, permettre à l'utilisateur d'encadrer des choses et de modifier ces cadres.

Pour cela, j'utilise une QGraphicsView et j'ajoute des rectangles à la scène à la fin du mousedown/mousemove/mouseup. Seulement, ce widget, situé au centre de ma QMainWindow occupe toute la place, même si mon image est minuscule. Je pensais donc placer la QGraphicsView dans un QWidget, de la centrer et de la mettre de la taille de l'image - avec au plus celle du conteneur. Mais je ne parviens pas à la redimensionner.

La démarche est-il bonne ? Le cas échéant, comment puis-je remédier à mon souci ?

Merci !

Édité par Vayel

+0 -0
Auteur du sujet

Du coup, j'opte toujours pour une QGraphicsView. Je parviens à la dimensionner et la positionner correctement sauf que je passe par setFixedSize()

 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
# -*- coding: utf-8 -*-
# Python 2 

from PyQt4 import QtCore, QtGui

class CanvasView(QtGui.QGraphicsView):
  def __init__(self):
    super(CanvasView, self).__init__()

    self.setMouseTracking(True)
    self.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)

  def init(self, bg_pixmap=None):
    self.scene_ = QtGui.QGraphicsScene(self)
    if bg_pixmap is None:
      bg_pixmap = QtGui.QPixmap()
    self.background = QtGui.QGraphicsPixmapItem(bg_pixmap)
    self.background.setPos(0, 0)

    self.scene_.addItem(self.background)
    self.setScene(self.scene_)

  def setBackground(self, filename):
    pixmap = QtGui.QPixmap(bg_filename)
    self.background.setPixmap(pixmap)

class BillCanvasView(QtGui.QWidget):
  def __init__(self, app):
    super(BillCanvasView, self).__init__()
    self.app_ = app

    self.canvas = CanvasView()
    self.initGui()

  def initGui(self):
    layout = QtGui.QHBoxLayout()
    layout.setAlignment(QtCore.Qt.AlignCenter)
    layout.addWidget(self.getCanvas())

    self.setLayout(layout)

  def init(self, bg_pixmap=None):
    if bg_pixmap is None:
      bg_pixmap = QtGui.QPixmap()

    self.getCanvas().init(bg_pixmap)

    w = min(bg_pixmap.width(), self.app_.getGui().centralWidget().width())
    h = min(bg_pixmap.height(), self.app_.getGui().centralWidget().height())
    self.getCanvas().setFixedSize(w, h)

  def getCanvas(self): return self.canvas

Et donc je ne peux pas redimensionner ma zone quand mon widget central l'est - enfin du coup il ne peut plus l'être, sauf en agrandissant.

Un autre souci mineur est que la scrollbar apparait sur mon QGraphicsView alors que je fixe sa taille à celle de l'image - donc à celle du sceneRect de la QGraphicsScene -. Y a-t-il des marges par défaut importunes ?

Merci !

Édité par Vayel

+0 -0

(je connais Qt en C++, mais pas en python) Tu as d'autres widgets dans ta fenêtre ? Tu n'utilises pas de layout ? J'ai du mal à voir pourquoi tu veux dimensionner ta vue en fonction de son contenu. Si c'est vraiment ce que tu veux faire, il faut faire un slot qui redimensionne la vue en fonction de l'image et le zoom appliqué

+0 -0
Auteur du sujet

(Pas de souci, PyQt reprends exactement les mêmes méthodes - à quelques exceptions près)

Je viens de noter qu'apparemment je n'ai pas précisé que j'utilisais une QMainWindow.

J'ai donc des docks et au centre - centralWidget() - j'ai un QStackedWidget comportant, entre autre, une instance de BillCanvasView. Si je veux redimensionner ma vue, c'est pour avoir ça au lieu de ça.

À la base, la fenêtre est affichée via showMaximized(). Quand je charge une image et que j'appelle la méthode init de BillCanvasView - ligne 42 -, elle s'affiche correctement - ou presque puisque les scrollbars apparaissent alors que pourtant je fixe la taille de ma QGraphicsView à exactement celle de l'image, donc de la scène (cf : premier lien). Seulement, pour avoir ma QGraphicsView de la bonne taille, je passe par setFixedSize() : du coup, je ne peux plus redimensionner le widget central autrement qu'en l'agrandissant - en changeant la taille de la fenêtre ou bien des docks. J'ai aussi pensé à employer des signaux, mais vais-je devoir en créer un pour chaque dock et pour la fenêtre entière ?

Merci !

Édité par Vayel

+0 -0
Auteur du sujet

C'est fait ! Ligne 50, j'ai changé setFixedSize par setMaximumSize.

Par contre, j'ai toujours un souci avec les scrollbars : elles s'affichent alors que je fixe la taille de ma QGraphicsView à celle de la scène - d'ailleurs, lignes 48/49, j'ai changé bg_pixmap par self.getCanvas().scene() donc j'ai bien les dimensions de la scène, même si ce sont les mêmes que celles de l'image.

Édité par Vayel

+0 -0

Cette réponse a aidé l'auteur du sujet

Oui, c'est normal : la viewport est deux pixels plus petite que la QGraphicsView. Il te suffit donc soit d'ajouter deux pixels en largeur et hauteur. Soit de carrément masquer les scrollbars en réglant la Qt::ScrollBarPolicy sur Qt::ScrollBarAlwaysOff.

Shave the whales! | Thistle

+0 -0
Auteur du sujet

Merci !

Je ne vais pas ajouter les deux pixels puisqu'il me faut obtenir avec précision la position du curseur afin de créer mes régions : le coin inférieur droit perçu par l'utilisateur sera deux pixels en dessous de celui de l'image.

Par contre, malgré le changement de ScrollBarPolicy, je peux toujours scroller - via la molette ou les flèches directionnelles -, c'est juste que les scrollbars ne s'affichent pas… Is it normal ? Apparemment, oui. Et ça peut se résoudre ainsi.

 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
# -*- coding: utf-8 -*-
# Python 2 

from PyQt4 import QtCore, QtGui

from views.canvas import CanvasView # Ma QGraphicsView

class BillCanvasView(QtGui.QWidget):
  def __init__(self, app):
    super(BillCanvasView, self).__init__()
    self.app_ = app

    self.canvas = CanvasView()
    self.initGui()

  def initGui(self):
    layout = QtGui.QHBoxLayout()
    layout.setAlignment(QtCore.Qt.AlignCenter)
    layout.addWidget(self.getCanvas())

    self.setLayout(layout)

  def init(self, bg_pixmap=None):
    if bg_pixmap is None:
      bg_pixmap = QtGui.QPixmap()

    self.getCanvas().init(bg_pixmap)

    if self.getCanvas().scene().width() > self.app_.getGui().centralWidget().width():
      w = self.app_.getGui().centralWidget().width()
      h_scroll_policy = QtCore.Qt.ScrollBarAsNeeded
    else:
      w = self.getCanvas().scene().width()
      h_scroll_policy = QtCore.Qt.ScrollBarAlwaysOff

    if self.getCanvas().scene().height() > self.app_.getGui().centralWidget().height():
      h = self.app_.getGui().centralWidget().height()
      v_scroll_policy = QtCore.Qt.ScrollBarAsNeeded
    else:
      h = self.getCanvas().scene().height()
      v_scroll_policy = QtCore.Qt.ScrollBarAlwaysOff

    self.getCanvas().setMaximumSize(w, h)
    self.getCanvas().setHorizontalScrollBarPolicy(h_scroll_policy)
    self.getCanvas().setVerticalScrollBarPolicy(v_scroll_policy)

  def getCanvas(self): return self.canvas

Édité par Vayel

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte