Drawing a circle on a QWidget python GUI - python-3.x

I'm trying to draw a circle instead of dots
Here is the class where it paints red dots on pressing event at any place in the widget
I want to draw a circle (hollow circle) as an outline not a solid circle that hide part of my picture
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class Canvas(QWidget):
def __init__(self, photo, *args, **kwargs):
super().__init__(*
args, **kwargs)
self.image = QImage(photo)
self.setFixedSize(self.image.width(), self.image.height())
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
qp = QPainter(self.image)
qp.setRenderHint(QPainter.Antialiasing)
qp.setPen(QPen(Qt.red, 5))
qp.setBrush(Qt.red)
qp.drawPoint(event.pos())
self.update()
def paintEvent(self, event):
qp = QPainter(self)
rect = event.rect()
qp.drawImage(rect, self.image, rect)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
w = QWidget()
self.setCentralWidget(w)
grid = QGridLayout(w)
grid.addWidget(Canvas('photo.jpeg'))
if __name__ == '__main__':
app = QApplication(sys.argv)
gui = MainWindow()
gui.show()
sys.exit(app.exec_())

I believe this is what you're trying to do.
In which case you need to take advantage of which paint device to pass to QPainter. During mousePressEvent and mouseMoveEvent use QPainter(self) so anything painted will only last until the next update. Then in mouseReleaseEvent when satisfied with the size of the circle, you can paint on the QImage with QPainter(self.image) to permanently draw the circle.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class Canvas(QWidget):
def __init__(self, photo, *args, **kwargs):
super().__init__(*args, **kwargs)
self.image = QImage(photo)
self.setFixedSize(self.image.width(), self.image.height())
self.pressed = self.moving = False
self.revisions = []
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.pressed = True
self.center = event.pos()
self.update()
def mouseMoveEvent(self, event):
if event.buttons() & Qt.LeftButton:
self.moving = True
r = (event.pos().x() - self.center.x()) ** 2 + (event.pos().y() - self.center.y()) ** 2
self.radius = r ** 0.5
self.update()
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.revisions.append(self.image.copy())
qp = QPainter(self.image)
self.draw_circle(qp) if self.moving else self.draw_point(qp)
self.pressed = self.moving = False
self.update()
def paintEvent(self, event):
qp = QPainter(self)
rect = event.rect()
qp.drawImage(rect, self.image, rect)
if self.moving:
self.draw_circle(qp)
elif self.pressed:
self.draw_point(qp)
def draw_point(self, qp):
qp.setPen(QPen(Qt.black, 5))
qp.drawPoint(self.center)
def draw_circle(self, qp):
qp.setRenderHint(QPainter.Antialiasing)
qp.setPen(QPen(Qt.black, 3, Qt.DashLine))
qp.drawEllipse(self.center, self.radius, self.radius)
def undo(self):
if self.revisions:
self.image = self.revisions.pop()
self.update()
def reset(self):
if self.revisions:
self.image = self.revisions[0]
self.revisions.clear()
self.update()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
w = QWidget()
self.setCentralWidget(w)
canvas = Canvas('photo.png')
grid = QGridLayout(w)
grid.addWidget(canvas)
QShortcut(QKeySequence('Ctrl+Z'), self, canvas.undo)
QShortcut(QKeySequence('Ctrl+R'), self, canvas.reset)
if __name__ == '__main__':
app = QApplication(sys.argv)
gui = MainWindow()
gui.show()
sys.exit(app.exec_())

Related

How can i draw interactively a line in QGraphicScene in PyQt5

I'm trying to draw a line interactively but I can't figure out how to do it. Anyone can help me?
I'm using PyQt5. I create a QGraphicsScene, I override mouse press event but when I try to draw the draw the it draw nothing.
Finally, I have another question. How can I snap this line to the grid?
import math
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class neGraphicScene(QGraphicsScene):
_colorBackGround: str
_colorLightLines: str
_colorDarkLines: str
_penLight: QPen
_penDark: QPen
gridSize: int = 20
gridSquares: int = 5
isLineDrawing = False
def __init__(self, parent=None):
QGraphicsScene.__init__(self, QRectF(-1000, -1000, 2000, 2000), parent)
self.setSceneColor()
self.begin, self.destination = QPoint(), QPoint()
def setSceneColor(self, _lightLines="#2f2f2f", _darkLines="#292929", _background="#393939"):
self._colorBackGround = _background
self._colorLightLines = _lightLines
self._colorDarkLines = _darkLines
self._penLight = QPen(QColor(self._colorLightLines))
self._penLight.setWidth(1)
self._penDark = QPen(QColor(self._colorDarkLines))
self._penDark.setWidth(2)
self.setBackgroundBrush(QColor(self._colorBackGround))
def setGraphicScene(self, width, height):
self.setSceneRect(-width // 2, -height // 2, width, height)
def mouseDoubleClickEvent(self, event):
QGraphicsScene.mouseMoveEvent(self, event)
def mousePressEvent(self, event):
self.isLineDrawing = True
if event.buttons() & Qt.LeftButton:
print('Point 1')
self.begin = event.pos()
self.destination = self.begin
self.update()
def mouseMoveEvent(self, event):
if event.buttons() & Qt.LeftButton:
print('Point 2')
self.destination = event.pos()
self.update()
def mouseReleaseEvent(self, event):
self.isLineDrawing = False
print('Point 3')
def drawBackground(self, painter, rect):
super(neGraphicScene, self).drawBackground(painter, rect)
# self.drawBackgroundGrid(painter, rect)
if self.isLineDrawing:
if not self.begin.isNull() and not self.destination.isNull():
line = QLineF(self.begin, self.destination)
painter.setPen(Qt.red)
painter.drawLine(line)
This is the QGraphicsView and the QWidgetStuff to made the minimal example working properly:
class neGraphicView(QGraphicsView):
def __init__(self, graphicScene, parent=None):
super().__init__(parent)
# self._dragPos = QPoint(0, 0)
self.sharedGraphicScene = graphicScene
self.setScene(self.sharedGraphicScene)
self._mousePressed = False
self._isPanning = False
self.initUI()
def initUI(self):
self.setRenderHints(
QPainter.Antialiasing | QPainter.HighQualityAntialiasing |
QPainter.TextAntialiasing | QPainter.SmoothPixmapTransform)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setDragMode(QGraphicsView.RubberBandDrag)
class NodeEditorWindow(QWidget):
mainLayout: QVBoxLayout
mainGraphicsScene: neGraphicScene
view: neGraphicView
def __init__(self, parent=None):
super(NodeEditorWindow, self).__init__(parent)
self.initUI()
def initUI(self):
self.setGeometry(200, 200, 800, 600)
self.mainLayout = QVBoxLayout()
self.mainLayout.setContentsMargins(0, 0, 0, 0)
self.setLayout(self.mainLayout)
self.mainGraphicsScene = neGraphicScene()
self.view = neGraphicView(self.mainGraphicsScene, self)
self.mainLayout.addWidget(self.view)
self.setWindowTitle("Node Editor")
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyle("Fusion")
win = NodeEditorWindow()
win.show()
sys.exit(app.exec())
This is the grid function:
def drawBackgroundGrid(self, _painter, _rect):
# here we create our grid
_left = int(math.floor(_rect.left()))
_right = int(math.ceil(_rect.right()))
_top = int(math.floor(_rect.top()))
_bottom = int(math.ceil(_rect.bottom()))
first_left = _left - (_left % self.gridSize)
first_top = _top - (_top % self.gridSize)
try:
# compute all lines to be drawn
lightLines, darkLines = [], []
for x in range(first_left, _right, self.gridSize):
if x % (self.gridSize * self.gridSquares) != 0:
lightLines.append(QLineF(x, _top, x, _bottom))
else:
darkLines.append(QLineF(x, _top, x, _bottom))
for y in range(first_top, _bottom, self.gridSize):
if y % (self.gridSize * self.gridSquares) != 0:
lightLines.append(QLineF(_left, y, _right, y))
else:
darkLines.append(QLineF(_left, y, _right, y))
# draw the lines
_painter.setPen(self._penLight)
_painter.drawLines(*lightLines)
_painter.setPen(self._penDark)
_painter.drawLines(*darkLines)
except Exception as e:
# print(e)
pass

How to use rubber band to zoom in and out in pyqt5

I would like to use the RubberBand to zoom in and out relative to the size of the box I would like to zoom in the view to fit whatever is inside the block to the full view.
Can anyone help?
The zoom in and out with the mouse works as expected.
You can see in the code the rubberband works but I don't know how to make the zoom relative to the size of the box.
code:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
from math import sqrt
class Point(QGraphicsItem):
def __init__(self, x, y):
super(Point, self).__init__()
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.rectF = QRectF(0, 0, 30, 30)
self.x=x
self.y=y
self._brush = QBrush(Qt.black)
def setBrush(self, brush):
self._brush = brush
self.update()
def boundingRect(self):
return self.rectF
def paint(self, painter=None, style=None, widget=None):
painter.fillRect(self.rectF, self._brush)
def hoverMoveEvent(self, event):
point = event.pos().toPoint()
print(point)
QGraphicsItem.hoverMoveEvent(self, event)
class Viewer(QGraphicsView):
photoClicked = pyqtSignal(QPoint)
rectChanged = pyqtSignal(QRect)
def __init__(self, parent):
super(Viewer, self).__init__(parent)
self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
self.setMouseTracking(True)
self.origin = QPoint()
self.changeRubberBand = False
self._zoom = 0
self._empty = True
self._scene = QGraphicsScene(self)
self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setFrameShape(QFrame.NoFrame)
self.area = float()
self.setPoints()
def setItems(self):
self.data = {'x': [-2414943.8686, -2417160.6592, -2417160.6592, -2417856.1783, -2417054.7618, -2416009.9966, -2416012.5232, -2418160.8952, -2418160.8952, -2416012.5232, -2417094.7694, -2417094.7694], 'y': [10454269.7008,
10454147.2672, 10454147.2672, 10453285.2456, 10452556.8132, 10453240.2808, 10455255.8752, 10455183.1912, 10455183.1912, 10455255.8752, 10456212.5959, 10456212.5959]}
maxX = max(self.data['x'])
minX = min(self.data['x'])
maxY = max(self.data['y'])
minY = min(self.data['y'])
distance = sqrt((maxX-minX)**2+(maxY-minY)**2)
self.area = QRectF(minX, minY, distance, distance)
for i,x in enumerate(self.data['x']):
x = self.data['x'][i]
y = self.data['y'][i]
p = Point(x,y)
p.setPos(x,y)
self._scene.addItem(p)
self.setScene(self._scene)
def fitInView(self, scale=True):
rect = QRectF(self.area)
if not rect.isNull():
self.setSceneRect(rect)
unity = self.transform().mapRect(QRectF(0, 0, 1, 1))
self.scale(1 / unity.width(), 1 / unity.height())
viewrect = self.viewport().rect()
scenerect = self.transform().mapRect(rect)
factor = min(viewrect.width() / scenerect.width(),
viewrect.height() / scenerect.height())
self.scale(factor, factor)
self._zoom = 0
def setPoints(self):
self._zoom = 0
self.setItems()
self.setDragMode(True)
self.fitInView()
def wheelEvent(self, event):
if event.angleDelta().y() > 0:
factor = 1.25
self._zoom += 1
else:
factor = 0.8
self._zoom -= 1
if self._zoom > 0:
self.scale(factor, factor)
elif self._zoom == 0:
self.fitInView()
else:
self._zoom = 0
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.origin = event.pos()
self.rubberBand.setGeometry(QRect(self.origin, QSize()))
self.rectChanged.emit(self.rubberBand.geometry())
self.rubberBand.show()
self.changeRubberBand = True
return
#QGraphicsView.mousePressEvent(self,event)
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.ClosedHandCursor)
self.original_event = event
handmade_event = QMouseEvent(QEvent.MouseButtonPress,QPointF(event.pos()),Qt.LeftButton,event.buttons(),Qt.KeyboardModifiers())
QGraphicsView.mousePressEvent(self,handmade_event)
super(Viewer, self).mousePressEvent(event)
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.origin = event.pos()
self.rubberBand.setGeometry(QRect(self.origin, QSize()))
self.changeRubberBand = False
QGraphicsView.mouseReleaseEvent(self,event)
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.OpenHandCursor)
handmade_event = QMouseEvent(QEvent.MouseButtonRelease,QPointF(event.pos()),Qt.LeftButton,event.buttons(),Qt.KeyboardModifiers())
QGraphicsView.mouseReleaseEvent(self,handmade_event)
super(Viewer, self).mouseReleaseEvent(event)
def mouseMoveEvent(self, event):
if self.changeRubberBand:
self.rubberBand.setGeometry(QRect(self.origin, event.pos()).normalized())
self.rectChanged.emit(self.rubberBand.geometry())
QGraphicsView.mouseMoveEvent(self,event)
super(Viewer, self).mouseMoveEvent(event)
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.viewer = Viewer(self)
self.btnLoad = QToolButton(self)
self.btnLoad.setText('Load Points')
self.btnLoad.clicked.connect(self.loadPoints)
VBlayout = QVBoxLayout(self)
VBlayout.addWidget(self.viewer)
HBlayout = QHBoxLayout()
HBlayout.setAlignment(Qt.AlignLeft)
HBlayout.addWidget(self.btnLoad)
VBlayout.addLayout(HBlayout)
def loadPoints(self):
self.viewer.setPoints()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 800, 600)
window.show()
sys.exit(app.exec_())

PyQt: Is it possible to drag/drop QWidgets in a QGridLayout to rearrange them?

I am looking for a way to create a grid of graphs that can be dragged/dropped to rearrange the order. My first try was using QDockWidgets as they allow for drag/drop, however they were limited in a lot of other ways. Would it be possible to implement this function in a QGridLayout?
For now I have a QGridLayout with 3x3 matplotlib widgets.
Here is an example of the desired layout outcome.
Sample code:
import sys
from PyQt5 import QtWidgets
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
import random
from PyQt5.QtWidgets import QGridLayout, QVBoxLayout, QHBoxLayout, QScrollArea, QWidget, QDialog, QApplication, QFrame
class IndicSelectWindow(QDialog):
def __init__(self, parent=None):
super(IndicSelectWindow, self).__init__(parent=parent)
self.resize(1000, 800)
self.layout = QtWidgets.QHBoxLayout(self)
self.scrollArea = QScrollArea(self)
self.scrollArea.setWidgetResizable(True)
self.scrollAreaWidgetContents = QWidget()
self.gridLayout = QGridLayout(self.scrollAreaWidgetContents)
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
self.layout.addWidget(self.scrollArea)
for i in range(3):
for j in range(3):
self.Frame = QFrame(self)
self.Frame.setStyleSheet("background-color: white;")
self.Frame.setFrameStyle(QFrame.Panel | QFrame.Raised)
self.Frame.setLineWidth(2)
self.layout = QHBoxLayout(self.Frame)
self.figure = Figure() # a figure to plot on
self.canvas = FigureCanvas(self.figure)
self.ax = self.figure.add_subplot(111) # create an axis
data = [random.random() for i in range(10)]
self.ax.plot(data, '*-') # plot data
self.canvas.draw() # refresh canvas
self.layout.addWidget(self.canvas)
Box = QVBoxLayout()
Box.addWidget(self.Frame)
self.gridLayout.addLayout(Box, i, j)
self.gridLayout.setColumnStretch(i % 3, 1)
self.gridLayout.setRowStretch(j, 1)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = IndicSelectWindow()
w.show()
sys.exit(app.exec_())
Here is an implementation that will swap the positions of the items involved in a drag/drop. The 3 main steps are:
(1) Reimplement mousePressEvent to get the index of the LayoutItem based on mouse coordinates.
(2) Reimplement mouseMoveEvent to set up a QDrag of the FigureCanvas.
(3) Reimplement dropEvent to swap the target items in the layout.
Since the matplotlib widgets absorb mouse events you also need to reimplement eventFilter to detect them.
import sys, random
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
class IndicSelectWindow(QDialog):
def __init__(self, parent=None):
super(IndicSelectWindow, self).__init__(parent=parent)
self.resize(1000, 800)
self.target = None
self.setAcceptDrops(True)
self.layout = QHBoxLayout(self)
self.scrollArea = QScrollArea(self)
self.scrollArea.setWidgetResizable(True)
self.scrollAreaWidgetContents = QWidget()
self.gridLayout = QGridLayout(self.scrollAreaWidgetContents)
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
self.layout.addWidget(self.scrollArea)
for i in range(3):
for j in range(3):
self.Frame = QFrame(self)
self.Frame.setStyleSheet("background-color: white;")
self.Frame.setFrameStyle(QFrame.Panel | QFrame.Raised)
self.Frame.setLineWidth(2)
self.layout = QHBoxLayout(self.Frame)
self.figure = Figure() # a figure to plot on
self.canvas = FigureCanvas(self.figure)
self.ax = self.figure.add_subplot(111) # create an axis
data = [random.random() for i in range(10)]
self.ax.plot(data, '*-') # plot data
self.canvas.draw() # refresh canvas
self.canvas.installEventFilter(self)
self.layout.addWidget(self.canvas)
Box = QVBoxLayout()
Box.addWidget(self.Frame)
self.gridLayout.addLayout(Box, i, j)
self.gridLayout.setColumnStretch(i % 3, 1)
self.gridLayout.setRowStretch(j, 1)
def eventFilter(self, watched, event):
if event.type() == QEvent.MouseButtonPress:
self.mousePressEvent(event)
elif event.type() == QEvent.MouseMove:
self.mouseMoveEvent(event)
elif event.type() == QEvent.MouseButtonRelease:
self.mouseReleaseEvent(event)
return super().eventFilter(watched, event)
def get_index(self, pos):
for i in range(self.gridLayout.count()):
if self.gridLayout.itemAt(i).geometry().contains(pos) and i != self.target:
return i
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.target = self.get_index(event.windowPos().toPoint())
else:
self.target = None
def mouseMoveEvent(self, event):
if event.buttons() & Qt.LeftButton and self.target is not None:
drag = QDrag(self.gridLayout.itemAt(self.target))
pix = self.gridLayout.itemAt(self.target).itemAt(0).widget().grab()
mimedata = QMimeData()
mimedata.setImageData(pix)
drag.setMimeData(mimedata)
drag.setPixmap(pix)
drag.setHotSpot(event.pos())
drag.exec_()
def mouseReleaseEvent(self, event):
self.target = None
def dragEnterEvent(self, event):
if event.mimeData().hasImage():
event.accept()
else:
event.ignore()
def dropEvent(self, event):
if not event.source().geometry().contains(event.pos()):
source = self.get_index(event.pos())
if source is None:
return
i, j = max(self.target, source), min(self.target, source)
p1, p2 = self.gridLayout.getItemPosition(i), self.gridLayout.getItemPosition(j)
self.gridLayout.addItem(self.gridLayout.takeAt(i), *p2)
self.gridLayout.addItem(self.gridLayout.takeAt(j), *p1)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = IndicSelectWindow()
w.show()
sys.exit(app.exec_())

Fading on Tab Change in pyqt

i have a page containing two tabs.i want to add a fadeIn effect when i change the tabs.Is that possible?
import sys
from PyQt4.QtCore import QTimeLine
from PyQt4.QtGui import *
class FaderWidget(QWidget):
def __init__(self, old_widget, new_widget):
QWidget.__init__(self, new_widget)
self.old_pixmap = QPixmap(new_widget.size())
old_widget.render(self.old_pixmap)
self.pixmap_opacity = 1.0
self.timeline = QTimeLine()
self.timeline.valueChanged.connect(self.animate)
self.timeline.finished.connect(self.close)
self.timeline.setDuration(333)
self.timeline.start()
self.resize(new_widget.size())
self.show()
def paintEvent(self, event):
painter = QPainter()
painter.begin(self)
painter.setOpacity(self.pixmap_opacity)
painter.drawPixmap(0, 0, self.old_pixmap)
painter.end()
def animate(self, value):
self.pixmap_opacity = 1.0 - value
self.repaint()
class StackedWidget(QStackedWidget):
def __init__(self, parent = None):
QStackedWidget.__init__(self, parent)
def setCurrentIndex(self, index):
self.fader_widget = FaderWidget(self.currentWidget(), self.widget(index))
QStackedWidget.setCurrentIndex(self, index)
def setPage1(self):
self.setCurrentIndex(0)
def setPage2(self):
self.setCurrentIndex(1)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = QWidget()
stack = StackedWidget()
stack.addWidget(QCalendarWidget())
editor = QTextEdit()
editor.setPlainText("Hello world! "*100)
stack.addWidget(editor)
page1Button = QPushButton("Page 1")
page2Button = QPushButton("Page 2")
page1Button.clicked.connect(stack.setPage1)
page2Button.clicked.connect(stack.setPage2)
layout = QGridLayout(window)
layout.addWidget(stack, 0, 0, 1, 2)
layout.addWidget(page1Button, 1, 0)
layout.addWidget(page2Button, 1, 1)
window.show()
sys.exit(app.exec_())
this is code that shows some fade effect but i m getting nothing from it and how it works and how to implement on tabs. it will be really appreciable if someone could help me implement it on tabs as well.
thanks in advance.
With the same logic as the code you show, each widget will be placed inside a QStackedWidget, where one of them will be the widget that will be displayed and the other will be the FaderWidget.
class FaderWidget(QWidget):
def __init__(self, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
self.pixmap_opacity = None
self.timeline = QTimeLine(333, self)
self.timeline.valueChanged.connect(self.animate)
self.timeline.finished.connect(self.close)
def start(self, old_widget, new_widget):
self.pixmap_opacity = 1.0
self.old_pixmap = QPixmap(new_widget.size())
old_widget.render(self.old_pixmap)
self.timeline.start()
self.resize(new_widget.size())
self.show()
def paintEvent(self, event):
if self.pixmap_opacity:
QWidget.paintEvent(self, event)
painter = QPainter(self)
painter.setOpacity(self.pixmap_opacity)
painter.drawPixmap(0, 0, self.old_pixmap)
def animate(self, value):
self.pixmap_opacity = 1.0 - value
self.update()
class FaderTabWidget(QTabWidget):
def __init__(self, parent=None):
QTabWidget.__init__(self, parent)
self.currentChanged.connect(self.onCurrentIndex)
self.last = -1
self.current = self.currentIndex()
def onCurrentIndex(self, index):
self.last = self.current
self.current = self.currentIndex()
if self.widget(self.last):
self.widget(self.last).setCurrentIndex(1)
old_widget = self.widget(self.last).widget(0)
current_widget = self.widget(self.current).widget(0)
fade = self.widget(self.current).widget(1)
fade.start(old_widget, current_widget)
def addTab(self, widget, text):
stack = QStackedWidget(self)
stack.addWidget(widget)
fade = FaderWidget(self)
fade.timeline.finished.connect(lambda: stack.setCurrentIndex(0))
stack.addWidget(fade)
stack.setCurrentIndex(0 if self.currentIndex() == -1 else 1)
QTabWidget.addTab(self, stack, text)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = QWidget()
tabWidget = FaderTabWidget()
tabWidget.addTab(QCalendarWidget(), "Tab1")
editor = QTextEdit()
editor.setPlainText("Hello world! " * 100)
tabWidget.addTab(editor, "Tab2")
layout = QVBoxLayout(window)
layout.addWidget(tabWidget)
window.show()
sys.exit(app.exec_())

Video Speed is too first in PyQt5, Opencv

When i run this code the video speed is very fast. Maybe it is happening because of the frame rate(because different frame rate is used for HD and FHD). How can i solve the issue and fixed the frame rate.i use cap.set(15,-.8) to slow down the frame rate but it doesn't work.The video size is 1080p(full HD).
import sys
import cv2
from PyQt5.QtCore import QThread, pyqtSignal, Qt
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.QtWidgets import QApplication, QWidget, QLabel
import datetime
class App(QWidget):
def __init__(self):
super().__init__()
self.title = 'PyQt5 Video'
self.left = 100
self.top = 100
self.width = 640
self.height = 480
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.resize(1800, 1200)
# create a label
# create a label
label = QLabel(self)
label.move(280, 120)
label.resize(640, 480)
label1 = QLabel(self)
label1.move(580, 620)
self.th = Thread(self)
self.th.changePixmap.connect(label.setPixmap)
self.th.changeLabel.connect(label1.setText)
self.th.start()
def closeEvent(self, event):
self.th.stop()
QWidget.closeEvent(self, event)
class Thread(QThread):
changePixmap = pyqtSignal(QPixmap)
changeLabel = pyqtSignal(str)
def __init__(self, parent=None):
QThread.__init__(self, parent=parent)
self.isRunning = True
def run(self):
cap = cv2.VideoCapture('a.mp4')
while self.isRunning:
ret, frame = cap.read()
rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
convertToQtFormat = QImage(rgbImage.data, rgbImage.shape[1], rgbImage.shape[0], QImage.Format_RGB888)
convertToQtFormat = QPixmap.fromImage(convertToQtFormat)
p = convertToQtFormat.scaled(640, 400, Qt.KeepAspectRatio)
self.changePixmap.emit(p)
now = datetime.datetime.now()
sec = now.second
self.changeLabel.emit(str(sec))
def stop(self):
self.isRunning = False
self.quit()
self.wait()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
ex.show()
sys.exit(app.exec_())

Resources