How can I add new ellipse while retaining the previous ellipse? - python

Currently, my code can paint an ellipse but every time I click the mouse for a new ellipse the previous ellipse will not retain .
I am used the **QGraphicsView** Framework because I am using its pantool and zoom function but I have a hard time adding the paint function.
import sys
from PyQt5.QtCore import Qt, QRectF, QPointF
from PyQt5.QtGui import QPixmap, QTransform, QBrush, QColor, QPen, QPainterPath
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QGraphicsView,
QGraphicsScene, QGraphicsPixmapItem, QSizePolicy, QSpacerItem, QGraphicsObject
class MouseBrushObject(QGraphicsObject):
def __init__(self):
QGraphicsObject.__init__(self)
self._size = 10
self._x = 0
self._y = 0
self._pen = None
self._brush = None
self._color = None
self.setColor(QColor(255, 0, 0, 255))
def paint(self, painter, option, widget):
rect = self.boundingRect()
painter.setPen(self._pen)
painter.setBrush(self._brush)
painter.drawEllipse(QPointF(self._x,self._y), 5,5)
def boundingRect(self):
return QRectF(self._x, self._y, self._size, self._size)
def setColor(self, color):
self._color = color
self._pen = QPen(self._color, 1)
self._brush = QBrush(QColor(self._color.red(), self._color.green(), self._color.blue(), 40))
def setSize(self, size):
self._size = size
def setPosition(self, pos):
self._x = pos.x() - pos.x()/2
self._y = pos.y() - pos.y()/2
self.setPos(QPointF(self._x, self._y))
class View(QGraphicsView):
def __init__(self, parent=None):
QGraphicsView.__init__(self, parent=parent)
self.setMouseTracking(True)
self.scene = QGraphicsScene(self)
self.setScene(self.scene)
pixmap = QPixmap(300, 300)
self.scene.addItem(QGraphicsPixmapItem(pixmap))
#self.setTransform(QTransform().scale(1, 1).rotate(0))
self.scene.setBackgroundBrush(QBrush(Qt.lightGray))
self._brushItem = MouseBrushObject()
self.path = QPainterPath()
def mousePressEvent(self, event):
pos = event.pos()
self._brushItem.setPosition(pos)
def enterEvent(self, event):
self.scene.addItem(self._brushItem)
return super(View, self).enterEvent(event)
def leaveEvent(self, event):
self.scene.removeItem(self._brushItem)
return super(View, self).leaveEvent(event)
class Viewer(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent=parent)
layout = QVBoxLayout()
self.view = View(self)
self.setLayout(layout)
layout.addWidget(self.view)
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.viewer = Viewer(self)
layout = QVBoxLayout()
layout.addWidget(self.viewer)
centralwidget = QWidget(self)
centralwidget.setLayout(layout)
self.setCentralWidget(centralwidget)
if __name__ == '__main__':
app = QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
Every time the mousePressEvent is trigger, it will paint an ellipse but the problem is that it will not retain the previous ellipse will adding the new ellipse.
My goal is for this code is two paint an ellipse when the mouse is clicked as it will serve as a point. Then The two points will be automatically connect using path. I have tried it will the qpainter function and it works but since I can't used qpaintEvent in QgraphicsView, I have a hard time executing the code.

Related

How can I connect two created ellipses?

I have a code that will paint an ellipse whenever the mouse is clicked.I want to connect the previous ellipse with the new painted ellipse with a line like in the picture here
My code is
import sys
from turtle import pos
from PyQt5.QtCore import Qt, QRectF, QPointF
from PyQt5.QtGui import QPixmap, QTransform, QBrush, QColor, QPen, QPainterPath
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QGraphicsView, QGraphicsScene, QGraphicsPixmapItem, QSizePolicy, QSpacerItem, QGraphicsObject
class MouseBrushObject(QGraphicsObject):
def __init__(self):
QGraphicsObject.__init__(self)
self._size = 10
self._x = 0
self._y = 0
self._pen = QPen(Qt.red, 5)
self._brush = QBrush(Qt.red, 5)
self._px = 0
self._py = 0
def paint(self, painter, option, widget):
#rect = self.boundingRect()
painter.setPen(self._pen)
painter.setBrush(self._brush)
painter.drawEllipse(QPointF(self._x,self._y), 10,10)
self._px = self._x
self._py = self._y
print(QPointF(self._x,self._y))
print(self._x)
print(self._px)
#painter.drawLine(self._x,self._y, 5, 5)
def boundingRect(self):
return QRectF(self._x, self._y, self._size, self._size)
def setSize(self, size):
self._size = size
def setPosition(self, pos):
self._x = pos.x() - pos.x()/2
self._y = pos.y() - pos.y()/2
self.setPos(QPointF(self._x, self._y))
class View(QGraphicsView):
def __init__(self, parent=None):
QGraphicsView.__init__(self, parent=parent)
self.setMouseTracking(True)
self._scene = QGraphicsScene(self)
self.setScene(self._scene)
pixmap = QPixmap(300, 300)
self._scene.addItem(QGraphicsPixmapItem(pixmap))
self._scene.setBackgroundBrush(QBrush(Qt.lightGray))
self.objects = []
def mousePressEvent(self, event):
pos = event.pos()
obj = MouseBrushObject()
obj.setPosition(pos)
self.objects.append(obj)
self._scene.addItem(obj)
class Viewer(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent=parent)
layout = QVBoxLayout()
self.view = View(self)
self.setLayout(layout)
layout.addWidget(self.view)
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.viewer = Viewer(self)
layout = QVBoxLayout()
layout.addWidget(self.viewer)
centralwidget = QWidget(self)
centralwidget.setLayout(layout)
self.setCentralWidget(centralwidget)
if __name__ == '__main__':
app = QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
I am thinking of adding drawLine in the paint function, but I can't retrieve the previous x and y because I don't know how to save them. Also, I am thinking of using the qpainterpath, but I don't know how to code it.
(Update)I have already found a way to drawLine using the previous and present ellipse. The points are correct since I have printed it:
but the problem is will not perfectly fit the two ellipses like in this picture:
.
I know that the points from the previous and present points are correct, but the line is not corretly drawn.
import sys
from turtle import pos
from PyQt5.QtCore import Qt, QRectF, QPointF
from PyQt5.QtGui import QPixmap, QTransform, QBrush, QColor, QPen, QPainterPath
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QGraphicsView, QGraphicsScene, QGraphicsPixmapItem, QSizePolicy, QSpacerItem, QGraphicsObject
class MouseBrushObject(QGraphicsObject):
def __init__(self):
QGraphicsObject.__init__(self)
self._size = 10
self._x = 0
self._y = 0
self._pen = QPen(Qt.red, 5)
self._brush = QBrush(Qt.red, 5)
def paint(self, painter, option, widget):
#rect = self.boundingRect()
painter.setPen(self._pen)
painter.setBrush(self._brush)
painter.drawEllipse(QPointF(self._x,self._y), 10,10)
#print("hello")
#print(QPointF(self._x,self._y))
#print(self._x)
#print(self._px)
if not self.prevpos:
print("current xy: "+"("+str(self._x)+","+str(self._y)+")")
else:
print("previous xy: "+"("+str(self._px)+","+str(self._py)+")")
print("new current xy: "+"("+str(self._x)+","+str(self._y)+")")
painter.drawLines(QPointF(self._px,self._py),QPointF(self._x,self._y))
#painter.drawLine(self._px,self._py,self._x,self._y)
def boundingRect(self):
return QRectF(self._x, self._y, self._size, self._size)
def setSize(self, size):
self._size = size
def setPosition(self, pos,prevpos):
self.prevpos = prevpos
#print("meow")
#print(prevpos)
if not prevpos:
self._x = pos.x() - pos.x()/2
self._y = pos.y() - pos.y()/2
#self.setPos(QPointF(self._x, self._y))
else:
self._x = pos.x() - pos.x()/2
self._y = pos.y() - pos.y()/2
self._px = prevpos.x() - prevpos.x()/2
self._py = prevpos.y() - prevpos.y()/2
self.setPos(QPointF(self._x, self._y))
class View(QGraphicsView):
def __init__(self, parent=None):
QGraphicsView.__init__(self, parent=parent)
self.setMouseTracking(True)
self._scene = QGraphicsScene(self)
self.setScene(self._scene)
pixmap = QPixmap(300, 300)
self._scene.addItem(QGraphicsPixmapItem(pixmap))
#self.setTransform(QTransform().scale(1, 1).rotate(0))
self._scene.setBackgroundBrush(QBrush(Qt.lightGray))
#self._brushItem = MouseBrushObject()
self.objects = []
self.posprev = ""
def mousePressEvent(self, event):
pos = event.pos()
if not self.posprev:
obj = MouseBrushObject()
obj.setPosition(pos,self.posprev)
self.objects.append(obj)
self._scene.addItem(obj)
self.posprev = pos
#print(pos)
else:
#print(self.posprev)
#print(pos)
obj = MouseBrushObject()
obj.setPosition(pos,self.posprev)
self.objects.append(obj)
self._scene.addItem(obj)
self.posprev =pos
class Viewer(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent=parent)
layout = QVBoxLayout()
self.view = View(self)
self.setLayout(layout)
layout.addWidget(self.view)
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.viewer = Viewer(self)
layout = QVBoxLayout()
layout.addWidget(self.viewer)
centralwidget = QWidget(self)
centralwidget.setLayout(layout)
self.setCentralWidget(centralwidget)
if __name__ == '__main__':
app = QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())

pyqt5 drawing lines with a button on a QGraphicsScene

I've a problem with pyqt5. I've create a windows with a scene that has a background image, re-implementing drawBackground. I've also a button that allow me to add a line in a position on the scene. The problem is that if i click the button to draw the line, then this line is drawn in a separate scene with it's own background, instead of into the scene i have. Seems like it create a new scene to draw the line on. Here is my code:
import sys
from PyQt5 import QtGui
from PyQt5.QtGui import QImage
from PyQt5.QtWidgets import (QMainWindow, QGraphicsView, QPushButton,
QHBoxLayout, QVBoxLayout, QWidget, QApplication, QGraphicsScene)
class GraphicsScene(QGraphicsScene):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._image = QImage()
#property
def image(self):
return self._image
#image.setter
def image(self, img):
self._image = img
self.update()
def drawBackground(self, painter, rect):
if self.image.isNull():
super().drawBackground(painter, rect)
else:
painter.drawImage(rect, self._image)
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.title = "parcelDeliveryIta";
self.top = 100
self.left = 100
self.width = 1500
self.height = 900
self.initUI()
def initUI(self):
self.scene = GraphicsScene(self)
self.scene._image = QImage('Italy.png')
view = QGraphicsView(self.scene, self)
self.scene.setSceneRect(0, 0, view.width(), view.height())
addLine = QPushButton('AddLine')
addLine.clicked.connect(self.addLine)
hbox = QHBoxLayout(self)
hbox.addWidget(view)
vbox = QVBoxLayout(self)
vbox.addWidget(addLine)
hbox.addLayout(vbox)
self.setWindowTitle(self.title)
self.setGeometry(self.top, self.left, self.width, self.height)
self.setFixedSize(self.width, self.height)
self.setLayout(hbox)
self.show()
def addLine(self):
self.scene.addLine(0, 0, 100, 100)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())
and this is the result when clicking the button:
As it is possible to see, the line is drawn with its own backgroung, that is image I've setted as background to the scene (the image above is cropped to show better the line)
Thanks for helping.
Explanation:
It seems that my previous solution is not suitable for your case since as the docs points out:
void QGraphicsScene::drawBackground(QPainter *painter, const
QRectF &rect) Draws the background of the scene using painter,
before any items and the foreground are drawn. Reimplement this
function to provide a custom background for the scene.
All painting is done in scene coordinates. The rect parameter is the
exposed rectangle.
If all you want is to define a color, texture, or gradient for the
background, you can call setBackgroundBrush() instead.
See also drawForeground() and drawItems().
(emphasis mine)
That paint will also be used to paint the base of the items and therefore caused that behavior.
Solution:
So you will have to resort to another solution, for example to use a QGraphicsPixmapItem as a base and readjust the size of the window with that information:
import sys
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import (
QGraphicsView,
QPushButton,
QHBoxLayout,
QVBoxLayout,
QWidget,
QApplication,
QGraphicsScene,
)
class GraphicsView(QGraphicsView):
def __init__(self, parent=None):
super().__init__(parent)
scene = QGraphicsScene(self)
self.setScene(scene)
self._pixmap_item = self.scene().addPixmap(QPixmap())
self._pixmap_item.setZValue(-1)
#property
def pixmap(self):
return self._pixmap_item.pixmap()
#pixmap.setter
def pixmap(self, pixmap):
self._pixmap_item.setPixmap(pixmap)
self.scene().setSceneRect(self._pixmap_item.boundingRect())
def resizeEvent(self, event):
if not self._pixmap_item.pixmap().isNull():
self.fitInView(self._pixmap_item)
super().resizeEvent(event)
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.title = "parcelDeliveryIta"
self.top = 100
self.left = 100
self.width = 1500
self.height = 900
self.initUI()
def initUI(self):
self.view = GraphicsView(self)
self.view.pixmap = QPixmap("Italy.png")
addLine = QPushButton("AddLine")
addLine.clicked.connect(self.addLine)
hbox = QHBoxLayout(self)
hbox.addWidget(self.view)
vbox = QVBoxLayout()
vbox.addWidget(addLine)
hbox.addLayout(vbox)
self.setWindowTitle(self.title)
self.setGeometry(self.top, self.left, self.width, self.height)
self.setFixedSize(self.width, self.height)
self.show()
def addLine(self):
self.view.scene().addLine(0, 0, 100, 100)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())

PyQt - Custom scrolling with QListWidget

I am trying to figure out a way to customize the scrollbars for QListWidget to have the scrollbars above and below the QListWidget instead of the normal vertical and horizontal scrollbars.
Please check out my example below if you don't understand what I mean.
In the example below I use QPushButtons with QTimers controlling the scrolling in place of the scrollbars but what I am looking for are scrollbars like the ones in QMenu when menu scrolling is enabled.
If that is not an option, I am wondering if there is a scrollbar signal or something that I could try to use to know when the scrollbars are normally activated? That way I can show/hide the buttons as needed. Thanks.
import sys
from PyQt5.QtCore import pyqtSignal, QTimer, Qt
from PyQt5.QtGui import QPainter
from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout, \
QApplication, QStyle, QListWidget, QStyleOptionButton, QListWidgetItem
class UpBtn(QPushButton):
mouseHover = pyqtSignal()
def __init__(self):
QPushButton.__init__(self)
self.setMouseTracking(True)
self.timer = QTimer()
def paintEvent(self, event):
painter = QPainter()
painter.begin(self)
opt = QStyleOptionButton()
self.initStyleOption(opt)
self.style().drawControl(QStyle.CE_ScrollBarSubLine, opt, painter, self)
painter.end()
def startScroll(self):
self.mouseHover.emit()
def enterEvent(self, event):
self.timer.timeout.connect(self.startScroll)
self.timer.start(120)
def leaveEvent(self, event):
self.timer.stop()
class DwnBtn(QPushButton):
mouseHover = pyqtSignal()
def __init__(self):
QPushButton.__init__(self)
self.setMouseTracking(True)
self.timer = QTimer()
def paintEvent(self, event):
painter = QPainter()
painter.begin(self)
opt = QStyleOptionButton()
self.initStyleOption(opt)
self.style().drawControl(QStyle.CE_ScrollBarAddLine, opt, painter, self)
painter.end()
def startScroll(self):
self.mouseHover.emit()
def enterEvent(self, event):
self.timer.timeout.connect(self.startScroll)
self.timer.start(120)
def leaveEvent(self, event):
self.timer.stop()
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.layout = QVBoxLayout()
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.setSpacing(0)
self.upBtn = UpBtn()
self.upBtn.setFixedWidth(230)
self.layout.addWidget(self.upBtn)
self.listWidget = QListWidget()
self.listWidget.setFixedWidth(230)
self.listWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.listWidget.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.layout.addWidget(self.listWidget)
self.downBtn = DwnBtn()
self.downBtn.setFixedWidth(230)
self.layout.addWidget(self.downBtn)
self.setLayout(self.layout)
self.upBtn.clicked.connect(self.upBtnClicked)
self.upBtn.mouseHover.connect(self.upBtnClicked)
self.downBtn.clicked.connect(self.downBtnClicked)
self.downBtn.mouseHover.connect(self.downBtnClicked)
for i in range(100):
item = QListWidgetItem()
item.setText("list item " + str(i))
self.listWidget.addItem(item)
def upBtnClicked(self):
cur = self.listWidget.currentRow()
self.listWidget.setCurrentRow(cur - 1)
def downBtnClicked(self):
cur = self.listWidget.currentRow()
self.listWidget.setCurrentRow(cur + 1)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
EDIT:
Here is an example image for what I am talking about. This is a scrollable QMenu.
EDIT:
Scrollable QMenu code.
Uncomment the commented parts to get a fixed size like in the image. Normally Qmenu scrolling only works when the menu items exceed the screen height. I am just looking for the top and bottom hover style scrolling but to be used in QListWidget.
import sys
from PyQt5.QtCore import QPoint, QEvent
from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout, \
QApplication, QAction, QMenu, QProxyStyle, QStyle
class MyMenu(QMenu):
def event(self, event):
if event.type() == QEvent.Show:
self.move(self.parent().mapToGlobal(QPoint(-108, 0)))
return super(MyMenu, self).event(event)
# class CustomStyle(QProxyStyle):
# def pixelMetric(self, QStyle_PixelMetric, option=None, widget=None):
# if QStyle_PixelMetric == QStyle.PM_MenuScrollerHeight:
# return 15
# if QStyle_PixelMetric == QStyle.PM_MenuDesktopFrameWidth:
# return 290
# else:
# return QProxyStyle.pixelMetric(self, QStyle_PixelMetric, option, widget)
class MainWindow(QWidget):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.layout = QVBoxLayout()
self.btn = QPushButton("Button")
self.btn.setFixedHeight(30)
self.btn.setFixedWidth(100)
self.myMenu = MyMenu("Menu", self.btn)
self.btn.setMenu(self.myMenu)
self.layout.addWidget(self.btn)
self.setLayout(self.layout)
menus = []
for _ in range(5):
myMenus = QMenu("Menu"+str(_+1), self.btn)
# myMenus.setFixedHeight(120)
myMenus.setStyleSheet("QMenu{menu-scrollable: 1; }")
menus.append(myMenus)
for i in menus:
self.btn.menu().addMenu(i)
for item in range(100):
action = QAction("item" + str(item), self)
i.addAction(action)
if __name__ == '__main__':
app = QApplication(sys.argv)
# app.setStyle(CustomStyle())
w = MainWindow()
w.show()
app.exec_()
The idea is to obtain the row of the upper and lower element that will decide whether the buttons are hidden or not, for that we use the method itemAt () that returns the item given the geometrical coordinates. On the other hand I have improved this calculation has to do every time they change the number of items in the QListView for that we use the signals of the internal model.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Button(QtWidgets.QPushButton):
moveSignal = QtCore.pyqtSignal()
def __init__(self, *args, **kwargs):
super(Button, self).__init__(*args, **kwargs)
self.m_timer = QtCore.QTimer(self, interval=120)
self.m_timer.timeout.connect(self.moveSignal)
self.setMouseTracking(True)
self.setFixedHeight(20)
def mouseReleaseEvent(self, e):
super(Button, self).mousePressEvent(e)
self.setDown(True)
def enterEvent(self, e):
self.setDown(True)
self.m_timer.start()
super(Button, self).enterEvent(e)
def leaveEvent(self, e):
self.setDown(False)
self.m_timer.stop()
super(Button, self).leaveEvent(e)
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
self.setFixedWidth(230)
icon = self.style().standardIcon(QtWidgets.QStyle.SP_ArrowUp)
self.upBtn = Button(icon=icon)
self.upBtn.moveSignal.connect(self.moveUp)
icon = self.style().standardIcon(QtWidgets.QStyle.SP_ArrowDown)
self.downBtn = Button(icon=icon)
self.downBtn.moveSignal.connect(self.moveDown)
self.listWidget = QtWidgets.QListWidget()
self.listWidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.listWidget.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
layout = QtWidgets.QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
layout.addWidget(self.upBtn)
layout.addWidget(self.listWidget)
layout.addWidget(self.downBtn)
self.adjust_buttons()
self.create_connections()
def create_connections(self):
self.listWidget.currentItemChanged.connect(self.adjust_buttons)
model = self.listWidget.model()
model.rowsInserted.connect(self.adjust_buttons)
model.rowsRemoved.connect(self.adjust_buttons)
model.rowsMoved.connect(self.adjust_buttons)
model.modelReset.connect(self.adjust_buttons)
model.layoutChanged.connect(self.adjust_buttons)
#QtCore.pyqtSlot()
def adjust_buttons(self):
first = self.listWidget.itemAt(QtCore.QPoint())
r = self.listWidget.row(first)
self.upBtn.setVisible(r != 0 and r!= -1)
last = self.listWidget.itemAt(self.listWidget.viewport().rect().bottomRight())
r = self.listWidget.row(last)
self.downBtn.setVisible( r != (self.listWidget.count() -1) and r != -1)
#QtCore.pyqtSlot()
def moveUp(self):
ix = self.listWidget.moveCursor(QtWidgets.QAbstractItemView.MoveUp, QtCore.Qt.NoModifier)
self.listWidget.setCurrentIndex(ix)
#QtCore.pyqtSlot()
def moveDown(self):
ix = self.listWidget.moveCursor(QtWidgets.QAbstractItemView.MoveDown, QtCore.Qt.NoModifier)
self.listWidget.setCurrentIndex(ix)
#QtCore.pyqtSlot(str)
def add_item(self, text):
item = QtWidgets.QListWidgetItem(text)
self.listWidget.addItem(item)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
for i in range(100):
window.add_item("item {}".format(i))
window.show()
sys.exit(app.exec_())

How to add a menubar(QMainWindow) alongside QGraphicsView in PyQt?

A picture is displayed with scrollbars on a window. We can draw on it. I want to add a menubar to the same window. I tried the following, it didn't work. Nothing is shown on the window when I run this.
#!/usr/bin/env python
from PyQt5.QtCore import (QLineF, QPointF, QRectF, Qt)
from PyQt5.QtGui import (QIcon, QBrush, QColor, QPainter, QPixmap)
from PyQt5.QtWidgets import (QAction, QMainWindow, QApplication, QGraphicsView, QGraphicsScene, QGraphicsItem,
QGridLayout, QVBoxLayout, QHBoxLayout,
QLabel, QLineEdit, QPushButton)
class TicTacToe(QGraphicsItem):
def __init__(self):
super(TicTacToe, self).__init__()
def paint(self, painter, option, widget):
painter.setPen(Qt.black)
painter.drawLine(0,100,300,100)
def boundingRect(self):
return QRectF(0,0,300,300)
def mousePressEvent(self, event):
pos = event.pos()
self.select(int(pos.x()/100), int(pos.y()/100))
self.update()
super(TicTacToe, self).mousePressEvent(event)
class MyGraphicsView(QGraphicsView):
def __init__(self):
super(MyGraphicsView, self).__init__()
scene = QGraphicsScene(self)
self.tic_tac_toe = TicTacToe()
scene.addItem(self.tic_tac_toe)
scene.addPixmap(QPixmap("exit.png"))
self.setScene(scene)
self.setCacheMode(QGraphicsView.CacheBackground)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
def keyPressEvent(self, event):
key = event.key()
if key == Qt.Key_R:
self.tic_tac_toe.reset()
super(MyGraphicsView, self).keyPressEvent(event)
class Example(QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.y = MyGraphicsView()
self.initUI()
def initUI(self):
menubar = self.menuBar()
menu = menubar.addMenu('File')
db_action = menu.addAction("Open file")
self.setGeometry(30, 30, 30, 20)
self.setWindowTitle('Menubar')
self.show()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
mainWindow = Example()
mainWindow.showFullScreen()
sys.exit(app.exec_())
A widget is shown in a window if it is a child of some component of the window, in your case self.y is not the child of Example, but only an attribute, a possible solution is to set it as centralWidget:
class Example(QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.y = MyGraphicsView()
self.setCentralWidget(self.y)
self.initUI()
def initUI(self):
menubar = self.menuBar()
menu = menubar.addMenu('File')
db_action = menu.addAction("Open file")
self.setGeometry(30, 30, 30, 20)
self.setWindowTitle('Menubar')
self.show()

PyQt5: painting using events

I am new on PyQt I am working on a project on which I should implement a feature that make the user able to draw a digit using the mouse (digit recognition system). So what I want is when the mouse button is pressed the app will start to draw till the button is released. I made this source code but it is still not working (I think I am struggling with sending a signal to PaintEvent()).
import sys
from PyQt5 import QtCore
from PyQt5 import QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QWidget, QInputDialog, QLineEdit, QFileDialog,QGraphicsView,QGraphicsScene,QVBoxLayout
from PyQt5.QtWidgets import (QApplication, QLabel, QWidget)
from PyQt5.QtGui import QPainter, QColor, QPen
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPainter
from PyQt5.QtCore import pyqtSignal, QObject
from PyQt5.QtWidgets import QMainWindow, QApplication
class Communicate(QObject):
drawApp = pyqtSignal()
class MyWidget(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 300, 220)
self.setWindowTitle('Simple')
self.setMouseTracking(True)
self.label = QLabel(self)
self.label.resize(500, 40)
self.c = Communicate()
self.c.drawApp.connect(self.PaintEvent())
self.show()
def mousePressEvent(self, event):
self.c.drawApp.emit()
self.startingposx = event.x()
self.startingposy = event.y()
#super().mousePressEvent(event)
print ("mouse pressed")
def mouseMoveEvent(self, event):
self.label.setText('Coordinates: ( %d : %d )' % (event.x(), event.y()) )
self.y = event.y()
self.x=event.x()
def PaintEvent(self,event):
qp = QPainter()
qp.begin(self)
#qp.setPen(Qt.red)
qp.drawPoints(self,qp)
qp.end()
self.update()
def mouseReleaseEvent(self,event):
self.endingposx = event.x()
self.endingposy = event.y()
super().mouseReleaseEvent(event)
print("starting point was",self.startingposx)
print("starting point y was ",self.startingposy)
print("ending point was ",self.endingposx)
print("ending point was y ",self.endingposy)
print("released")
def drawPoints(self,qp):
qp.setPen(Qt.red)
size = self.size()
x=self.x
y=self.y
qp.drawPoint(x,y)
app = QApplication(sys.argv)
widget = MyWidget()
widget.show()
app.exec_()
Python is sensitive to uppercase and lowercase so be more careful, the method is called paintEvent.
Also you should not call paintEvent directly, you must use the function update(), this method will internally call paintEvent().
But even correcting that error your problem is not solved, if you want to draw a path it is advisable to use QPainterPath as this stores the strokes.
class Drawer(QWidget):
newPoint = pyqtSignal(QPoint)
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.path = QPainterPath()
def paintEvent(self, event):
painter = QPainter(self)
painter.drawPath(self.path)
def mousePressEvent(self, event):
self.path.moveTo(event.pos())
self.update()
def mouseMoveEvent(self, event):
self.path.lineTo(event.pos())
self.newPoint.emit(event.pos())
self.update()
def sizeHint(self):
return QSize(400, 400)
class MyWidget(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setLayout(QVBoxLayout())
label = QLabel(self)
drawer = Drawer(self)
drawer.newPoint.connect(lambda p: label.setText('Coordinates: ( %d : %d )' % (p.x(), p.y())))
self.layout().addWidget(label)
self.layout().addWidget(drawer)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWidget()
w.show()
sys.exit(app.exec_())
Screenshot:
I don't have the reputation to reply to eyllanesc's solution, but in case others are having issues, the imports were not complete. Here's is eyllanesc's solution with the imports. It will execute without error:
import sys
from PyQt5.QtWidgets import QVBoxLayout
from PyQt5.QtWidgets import (QLabel, QWidget)
from PyQt5.QtGui import QPainter, QPainterPath
from PyQt5.QtCore import pyqtSignal, QPoint, QSize
from PyQt5.QtWidgets import QApplication
class Drawer(QWidget):
newPoint = pyqtSignal(QPoint)
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.path = QPainterPath()
def paintEvent(self, event):
painter = QPainter(self)
painter.drawPath(self.path)
def mousePressEvent(self, event):
self.path.moveTo(event.pos())
self.update()
def mouseMoveEvent(self, event):
self.path.lineTo(event.pos())
self.newPoint.emit(event.pos())
self.update()
def sizeHint(self):
return QSize(400, 400)
class MyWidget(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setLayout(QVBoxLayout())
label = QLabel(self)
drawer = Drawer(self)
drawer.newPoint.connect(lambda p: label.setText('Coordinates: ( %d : %d )' % (p.x(), p.y())))
self.layout().addWidget(label)
self.layout().addWidget(drawer)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWidget()
w.show()
sys.exit(app.exec_())

Categories

Resources