PyQt5: QPen
Torna all’indice degli appunti
La classe QPen definisce come un QPainter dovrebbe disegnare linee e contorni di figure.
Il costruttore QPen prende come argomenti il colore, la larghezza del tratto e lo stile dello stesso, es: QPen(Qt.black, 2, Qt.SolidLine), ma anche il Cap Style ed il Join Style.
E’ possibile però costruire l’oggetto senza parametri e poi settarli in un secondo momento.
Un oggetto QPen ha 5 metodi fondamentali che lo identificano:
style(): indica il tipo di linea utilizzata;
brush(): indica il tipo di riempimento dei tratti generati da QPen;
width(): indica la larghezza del tratto della penna in pixel;
capStyle: indica la terminazione della linea, la parte finale che interrompe il tratto;
joinStyle: indica come viene effettuata l’unione di 2 linee (join);
I corrispondenti setter, che modificano le caratteristiche dell’oggetto QPen, sono ovviamente setStyle(Qt.PenStyle), setBrush(QBrush), setWidth(int), setCapStyle(Qt.PenCapStyle) e setJoinStyle(Qt.PenJoinStyle).
Useremo questo codice per vedere le varie differenze nell’oggetto QPen:
from PyQt5.QtWidgets import QWidget, QApplication from PyQt5.QtWidgets import QMainWindow from PyQt5.QtGui import QPainter, QPen from PyQt5.QtCore import Qt import sys class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setWindowTitle("QPen Example") self.central_widget = FormWidget(self) self.setCentralWidget(self.central_widget) self.resize(200, 100) class FormWidget(QWidget): def __init__(self, parent): super(FormWidget, self).__init__(parent) self.painter = None def paintEvent(self, event): self.painter = QPainter() self.painter.begin(self) self.draw_line(10, 50, 180, 50) self.painter.end() def draw_line(self, start_x, start_y, end_x, end_y): pen = QPen(Qt.black, 2, Qt.SolidLine) self.painter.setPen(pen) self.painter.drawLine(start_x, start_y, end_x, end_y) if __name__ == '__main__': app = QApplication(sys.argv) main_window = MainWindow() main_window.show() sys.exit(app.exec_())
PEN STYLE
Per sapere lo stile di QPen, come già detto, si usa il metodo style(), invece setStyle(style) setta il tipo di linea utilizzata;
style è un enum di tipo Qt.PenStyle e può assumere i seguenti valori:
Costante | valore | descrizione |
---|---|---|
Qt.NoPen | 0 | Nessuna linea. Esempio: QPainter.drawRect() riempie ma non disegna nessuna linea di contorno |
Qt.SolidLine | 1 | Una linea continua |
Qt.DashLine | 2 | Linea tratteggiata |
Qt.DotLine | 3 | Linea punteggiata |
Qt.DashDotLine | 4 | Trattini alternati da punti |
Qt.DashDotDotLine | 5 | Un trattino e 2 punti alternati |
Qt.CustomDashLine | 6 | Linea con un pattern personalizzato definito usando il metodo QPainterPathStroker.setDashPattern() |
In caso di creazione dell’oggetto QPen senza parametri, di default lo stile sarà Qt.SolidLine:
>>> from PyQt5.QtGui import QPen >>> pen = QPen() >>> pen.style() 1
Nota:
Come si nota dal codice di esempio, per vedere QPen in azione, è necessario reimplementare il metodo paintEvent(), che chiama a sua volta un metodo designato a creare un oggetto QPen e a tracciare un linea con lo stile desiderato.
Volendo cambiare ad esempio lo stile del tratto, in Qt.DashLine, basterà modificare la linea di codice:
... def draw_line(self, start_x, start_y, end_x, end_y): pen = QPen(Qt.black, 2, Qt.SolidLine) self.painter.setPen(pen) self.painter.drawLine(start_x, start_y, end_x, end_y)
in
... def draw_line(self, start_x, start_y, end_x, end_y): pen = QPen(Qt.black, 2, Qt.DashLine) self.painter.setPen(pen) self.painter.drawLine(start_x, start_y, end_x, end_y)
oppure utilizzare il setter, che sovrascriverà le impostazioni di creazione:
... def draw_line(self, start_x, start_y, end_x, end_y): pen = QPen(Qt.red, 5, Qt.SolidLine) pen.setStyle(Qt.DashLine) self.painter.setPen(pen) self.painter.drawLine(start_x, start_y, end_x, end_y)
Le altre opzioni sono:
PEN WIDTH
Di default la larghezza del tratto di QPen equivale a 1 e si ottiene con il metodo width().
Con i setter setWidth(int), o setWidthF(float), in caso di numero decimale, si può modificare lo spessore del tratto:
... def draw_line(self, start_x, start_y, end_x, end_y): pen = QPen(Qt.red, 5, Qt.SolidLine) pen.setStyle(Qt.DashLine) pen.setWidth(1) self.painter.setPen(pen) self.painter.drawLine(start_x, start_y, end_x, end_y)
PEN BRUSH/COLOR
Con il metodo brush() si risale all’oggetto QBrush associato a QPen. QBrush è il responsabile della colorazione data alla linea tracciata dall’oggetto QPen.
Di default il colore associato a QPen è il nero, infatti:
>>> brush = pen.brush() >>> pen_color = brush.color() >>> pen_color.getRgb() (0, 0, 0, 255)
In realtà esiste una scorciatoia nell’oggetto QPen, data da color, per raggiungere direttamente l’oggetto QColor associato al QBrush di QPen:
>>> pen.color().getRgb() (0, 0, 0, 255)
Per settare una colorazione differente, utilizziamo il setter setBrush(QBrush) o direttamente la scorciatoia setColor(QColor).
I due metodi sono equivalenti:
>>> from PyQt5.QtGui import QColor >>> pen.setColor(QColor("#008000")) >>> pen.color().getRgb() (0, 128, 0, 255)
o con l’oggetto QBrush:
>>> from PyQt5.QtGui import QBrush >>> brush = QBrush(QColor("#008500")) >>> pen.setBrush(brush) >>> pen.color().getRgb() (0, 133, 0, 255)
Nel codice di esempio è sufficiente aggiungere una riga di codice con uno dei due setter appena visti:
... def draw_line(self, start_x, start_y, end_x, end_y): pen = QPen(Qt.red, 5, Qt.SolidLine) pen.setStyle(Qt.DashLine) pen.setWidth(5) pen.setColor(QColor("#008000")) self.painter.setPen(pen) self.painter.drawLine(start_x, start_y, end_x, end_y)
PEN CAPSTYLE
Il capstyle, decide come l’oggetto QPen deve disegnare il terminale della linea.
Tale terminale può essere di tre tipologie: Square, Flat e Round. Sono deinibili con i soliti enum di tipo Qt.PenCapStyle:
Costante | valore | descrizione |
---|---|---|
Qt.FlatCap | 0x00 | una linea “quadrata” che non copre il terminale della linea |
Qt.SquareCap | 0x10 | una linea “quadrata” che copre il terminale della linea e si estende oltre la metà dello spessore della linea |
Qt.RoundCap | 0x20 | il terminale della linea è arrotondato |
I metodi getter e setter, sono ovviamente capStyle() per ottenere il valore associato all’oggetto QPen, che di default è Qt.SquareCap e setCapStyle(Qt.PenCapStyle), per modificare il valore:
>>> pen.capStyle() 16 >>> from PyQt5.QtCore import Qt >>> pen.setCapStyle(Qt.RoundCap) >>> pen.capStyle() 32
Per vedere la differenza, settiamo una larghezza del tratto importante.
... def draw_line(self, start_x, start_y, end_x, end_y): pen = QPen(Qt.red, 20, Qt.SolidLine) pen.setCapStyle(Qt.SquareCap) self.painter.setPen(pen) self.painter.drawLine(start_x, start_y, end_x, end_y)
lo stile Flat invece:
pen.setCapStyle(Qt.FlatCap)
Anche se con difficoltà, si nota che la linea in realtà è più corta, perchè la modalità FLAT, non copre il terminale della linea come nella SQUARE, ma si ferma a metà.
infine il Round:
pen.setCapStyle(Qt.RoundCap)
PEN JOINSTYLE
Il joinstyle, decide come l’oggetto QPen deve raccordare più linee tra loro.
Tale raccordo può essere di tre tipologie: Bevel, Miter e Round. Sono definibili con gli enum di tipo Qt.PenJoinStyle:
Costante | valore | descrizione |
---|---|---|
Qt.MiterJoin | 0x00 | i bordi più esterni delle linee vengono prolungati fino ad incontrarsi. L’angolo creatosi crea un’area che viene riempita |
Qt.BevelJoin | 0x40 | il triangolo formato dalle linee viene riempito |
Qt.RoundJoin | 0x80 | Un arco circolare raccorda le due linee e viene riempito |
I metodi getter e setter, sono joinStyle() per ottenere il valore associato all’oggetto QPen, che di default è Qt.BevelJoin e setJoinStyle(Qt.PenJoinStyle), per modificare il valore:
>>> pen.joinStyle() 64 >>> pen.setJoinStyle(Qt.RoundJoin) >>> pen.joinStyle() 128
ecco un esempio:
... def paintEvent(self, event): self.painter = QPainter() self.painter.begin(self) self.draw_symbol() self.painter.end() def draw_symbol(self): pen = QPen(Qt.red, 30, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin) self.painter.setPen(pen) self.painter.drawLine(20, 40, 50, 40) self.painter.drawLine(70, 40, 90, 80) ...
In caso di Round invece:
pen = QPen(Qt.red, 30, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)
Nota:
Il Qt.RoundJoin funziona in coppia con il cap style Qt.RoundCap, ma bisogna anche raccordare le linee in modo che il join, sia ben fatto.
Correggendo le coordinate infatti si otterrà un effetto migliore:
self.painter.drawLine(20, 40, 55, 40) self.painter.drawLine(60, 40, 90, 80)
DRAWING EXAMPLE
Ecco un esempio di come può essere utilizzato un oggetto QPen.
Sfruttiamo gli event handlers mousePressEvent, mouseMoveEvent e mouseReleaseEvent, per disegnare su un widget che mostra semplicemente un Pixmap.
Semplicemente reimplementandoli, controlliamo che venga premuto il tasto sinistro del mouse (event.button() == Qt.LeftButton), in tal caso, quando il mouse si muove (mouseMoveEvent), se il tasto è ancora premuto, settiamo l’oggetto QPen e, tenendo traccia dell’ultima posizione del mouse, disegnamo la linea.
Quando il pulsante del mouse viene rilasciato (mouseReleaseEvent), l’oggetto QPainter smette di disegnare poichè il flag is_drawable è stato settato a False.
Il flag is_drawable serve quindi per non continuare a disegnare senza che il pulsante sia premuto, durante i movimenti del mouse.
from PyQt5.QtWidgets import QApplication, QWidget from PyQt5.QtWidgets import QMainWindow from PyQt5.QtGui import QPainter, QPen, QPixmap import sys from PyQt5.QtCore import Qt, QPoint class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setWindowTitle("Drawaing Example") self.image = QPixmap("sheet.png") self.central_widget = FormWidget(self) self.setCentralWidget(self.central_widget) self.setMouseTracking(True) self.resize(self.image.width(), self.image.height()) class FormWidget(QWidget): def __init__(self, parent): super(FormWidget, self).__init__(parent) self.last_mouse_pos = QPoint() self.is_drawable = False def paintEvent(self, event): painter = QPainter(self) painter.drawPixmap(self.rect(), self.parent().image) def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.is_drawable = True self.last_mouse_pos = event.pos() def mouseMoveEvent(self, event): if event.buttons() and Qt.LeftButton and self.is_drawable: painter = QPainter(self.parent().image) painter.setPen(QPen(Qt.red, 3, Qt.SolidLine)) painter.drawLine(self.last_mouse_pos, event.pos()) self.last_mouse_pos = event.pos() self.update() # it calls paintEvent def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: self.is_drawable = False if __name__ == '__main__': app = QApplication(sys.argv) main_window = MainWindow() main_window.show() sys.exit(app.exec_())
Commenti recenti