slide QStackedWidget page - python

How can i slide QtWidgets.QStackedWidget pages on QPushButton.clicked as shown in below image (in right) ?
action: on left button press QStackedWidget page index will set to 0 & on right button press QStackedWidget page index will set to 1

Using this example written in C++ I translated it to PySide2 in addition to giving them certain improvements:
import random
from PySide2 import QtCore, QtGui, QtWidgets
class SlidingStackedWidget(QtWidgets.QStackedWidget):
def __init__(self, parent=None):
super(SlidingStackedWidget, self).__init__(parent)
self.m_direction = QtCore.Qt.Horizontal
self.m_speed = 500
self.m_animationtype = QtCore.QEasingCurve.OutCubic
self.m_now = 0
self.m_next = 0
self.m_wrap = False
self.m_pnow = QtCore.QPoint(0, 0)
self.m_active = False
def setDirection(self, direction):
self.m_direction = direction
def setSpeed(self, speed):
self.m_speed = speed
def setAnimation(self, animationtype):
self.m_animationtype = animationtype
def setWrap(self, wrap):
self.m_wrap = wrap
#QtCore.Slot()
def slideInPrev(self):
now = self.currentIndex()
if self.m_wrap or now > 0:
self.slideInIdx(now - 1)
#QtCore.Slot()
def slideInNext(self):
now = self.currentIndex()
if self.m_wrap or now < (self.count() - 1):
self.slideInIdx(now + 1)
def slideInIdx(self, idx):
if idx > (self.count() - 1):
idx = idx % self.count()
elif idx < 0:
idx = (idx + self.count()) % self.count()
self.slideInWgt(self.widget(idx))
def slideInWgt(self, newwidget):
if self.m_active:
return
self.m_active = True
_now = self.currentIndex()
_next = self.indexOf(newwidget)
if _now == _next:
self.m_active = False
return
offsetx, offsety = self.frameRect().width(), self.frameRect().height()
self.widget(_next).setGeometry(self.frameRect())
if not self.m_direction == QtCore.Qt.Horizontal:
if _now < _next:
offsetx, offsety = 0, -offsety
else:
offsetx = 0
else:
if _now < _next:
offsetx, offsety = -offsetx, 0
else:
offsety = 0
pnext = self.widget(_next).pos()
pnow = self.widget(_now).pos()
self.m_pnow = pnow
offset = QtCore.QPoint(offsetx, offsety)
self.widget(_next).move(pnext - offset)
self.widget(_next).show()
self.widget(_next).raise_()
anim_group = QtCore.QParallelAnimationGroup(
self, finished=self.animationDoneSlot
)
for index, start, end in zip(
(_now, _next), (pnow, pnext - offset), (pnow + offset, pnext)
):
animation = QtCore.QPropertyAnimation(
self.widget(index),
b"pos",
duration=self.m_speed,
easingCurve=self.m_animationtype,
startValue=start,
endValue=end,
)
anim_group.addAnimation(animation)
self.m_next = _next
self.m_now = _now
self.m_active = True
anim_group.start(QtCore.QAbstractAnimation.DeleteWhenStopped)
#QtCore.Slot()
def animationDoneSlot(self):
self.setCurrentIndex(self.m_next)
self.widget(self.m_now).hide()
self.widget(self.m_now).move(self.m_pnow)
self.m_active = False
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
slidingStacked = SlidingStackedWidget()
for i in range(10):
label = QtWidgets.QLabel(
"Qt is cool " + i * "!", alignment=QtCore.Qt.AlignCenter
)
color = QtGui.QColor(*random.sample(range(255), 3))
label.setStyleSheet(
"QLabel{ background-color: %s; color : white; font: 40pt}"
% (color.name(),)
)
slidingStacked.addWidget(label)
button_prev = QtWidgets.QPushButton(
"Previous", pressed=slidingStacked.slideInPrev
)
button_next = QtWidgets.QPushButton(
"Next", pressed=slidingStacked.slideInNext
)
hlay = QtWidgets.QHBoxLayout()
hlay.addWidget(button_prev)
hlay.addWidget(button_next)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QVBoxLayout(central_widget)
lay.addLayout(hlay)
lay.addWidget(slidingStacked)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
Nota: For PyQt5 only change Slot to pyqtSlot

For PyQt5 I have converted #eyllanesc's answer:
import random
from PyQt5 import QtCore, QtGui, QtWidgets
class SlidingStackedWidget(QtWidgets.QStackedWidget):
def __init__(self, parent=None):
super(SlidingStackedWidget, self).__init__(parent)
self.m_direction = QtCore.Qt.Horizontal
self.m_speed = 500
self.m_animationType = QtCore.QEasingCurve.InCurve
self.m_now = 0
self.m_next = 0
self.m_wrap = False
self.m_pnow = QtCore.QPoint(0, 0)
self.m_active = False
def setDirection(self, direction):
self.m_direction = direction
def setSpeed(self, speed):
self.m_speed = speed
def setAnimation(self, animation_type):
self.m_animationType = animation_type
def setWrap(self, wrap):
self.m_wrap = wrap
#QtCore.pyqtSlot()
def slideInPrev(self):
now = self.currentIndex()
if self.m_wrap or now > 0:
self.slideInIdx(now - 1)
#QtCore.pyqtSlot()
def slideInNext(self):
now = self.currentIndex()
if self.m_wrap or now < (self.count() - 1):
self.slideInIdx(now + 1)
def slideInIdx(self, idx):
if idx > (self.count() - 1):
idx = idx % self.count()
elif idx < 0:
idx = (idx + self.count()) % self.count()
self.slideInWgt(self.widget(idx))
def slideInWgt(self, new_widget):
if self.m_active:
return
self.m_active = True
_now = self.currentIndex()
_next = self.indexOf(new_widget)
if _now == _next:
self.m_active = False
return
offset_X, offset_Y = self.frameRect().width(), self.frameRect().height()
self.widget(_next).setGeometry(self.frameRect())
if not self.m_direction == QtCore.Qt.Horizontal:
if _now < _next:
offset_X, offset_Y = 0, -offset_Y
else:
offset_X = 0
else:
if _now < _next:
offset_X, offset_Y = -offset_X, 0
else:
offset_Y = 0
page_next = self.widget(_next).pos()
pnow = self.widget(_now).pos()
self.m_pnow = pnow
offset = QtCore.QPoint(offset_X, offset_Y)
self.widget(_next).move(page_next - offset)
self.widget(_next).show()
self.widget(_next).raise_()
anim_group = QtCore.QParallelAnimationGroup(self)
anim_group.finished.connect(self.animationDoneSlot)
for index, start, end in zip(
(_now, _next), (pnow, page_next - offset), (pnow + offset, page_next)
):
animation = QtCore.QPropertyAnimation(self.widget(index), b'pos')
animation.setEasingCurve(self.m_animationType)
animation.setDuration(self.m_speed)
animation.setStartValue(start)
animation.setEndValue(end)
anim_group.addAnimation(animation)
self.m_next = _next
self.m_now = _now
self.m_active = True
anim_group.start(QtCore.QAbstractAnimation.DeleteWhenStopped)
#QtCore.pyqtSlot()
def animationDoneSlot(self):
self.setCurrentIndex(self.m_next)
self.widget(self.m_now).hide()
self.widget(self.m_now).move(self.m_pnow)
self.m_active = False
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
slidingStacked = SlidingStackedWidget()
for i in range(10):
label = QtWidgets.QLabel(
"Qt is cool " + i * "!", alignment=QtCore.Qt.AlignCenter
)
color = QtGui.QColor(*random.sample(range(255), 3))
label.setStyleSheet(
"QLabel{ background-color: %s; color : white; font: 40pt}"
% (color.name(),)
)
slidingStacked.addWidget(label)
button_prev = QtWidgets.QPushButton(
"Previous", pressed=slidingStacked.slideInPrev
)
button_next = QtWidgets.QPushButton(
"Next", pressed=slidingStacked.slideInNext
)
hlay = QtWidgets.QHBoxLayout()
hlay.addWidget(button_prev)
hlay.addWidget(button_next)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QVBoxLayout(central_widget)
lay.addLayout(hlay)
lay.addWidget(slidingStacked)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())

Related

PyQt/PySide QGraphicsScene, Hover event on Ellipse Item not working

I'm currently learning how to use Qt's Qgraphics and i cant get the hoverEnterEvent to work on an QGraphicsEllipseItem object. I am setting setAcceptHoverEvents to True and implemented the hoverEnterEvent function, yet the function isn't getting called.
here's my code:
import sys
from PySide6.QtWidgets import (
QApplication,
QMainWindow,
QGraphicsView,
QGraphicsScene,
QGraphicsEllipseItem,
)
from PySide6.QtGui import QPainterPath, QTransform, QPen, QBrush, QColor, QPainter
from PySide6.QtCore import Qt
PORT_PEN_COLOR = "#000000"
PORT_BRUSH_COLOR = "#ebebeb"
EDGE_PEN_COLOR = "#474747"
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setGeometry(0, 0, 800, 600)
self.setCentralWidget(GraphicsView())
self.show()
class GraphicsView(QGraphicsView):
def __init__(self, parent=None):
super().__init__(parent)
self.setMouseTracking(True)
self.setScene(GraphicsScene())
self.setRenderHint(QPainter.RenderHint.Antialiasing)
class GraphicsScene(QGraphicsScene):
def __init__(self, parent=None):
super().__init__(parent)
self.setSceneRect(-10000, -10000, 20000, 20000)
self._port_pen = QPen(QColor(PORT_PEN_COLOR))
self._port_brush = QBrush(QColor(PORT_BRUSH_COLOR))
self._edge_pen = QPen(QColor(EDGE_PEN_COLOR))
self._edge_pen.setWidth(4)
def mousePressEvent(self, event):
clicked_item = self.itemAt(event.scenePos(), QTransform())
if event.buttons() == Qt.MouseButton.LeftButton:
if clicked_item is not None:
# edge item
pos = clicked_item.scenePos()
pos.setX(pos.x() + 6)
pos.setY(pos.y() + 6)
self.edge = self.addPath(QPainterPath())
self.edge.setPen(self._edge_pen)
self.start_pos = pos
self.end_pos = self.start_pos
self.update_path()
else:
x = event.scenePos().x()
y = event.scenePos().y()
# port item
start_port = Ellipse()
start_port.setPos(x - 6, y - 6)
start_port.setPen(self._port_pen)
start_port.setBrush(self._port_brush)
start_port.setZValue(10000.0)
self.addItem(start_port)
# edge item
self.edge = self.addPath(QPainterPath())
self.edge.setPen(self._edge_pen)
self.start_pos = event.scenePos()
self.end_pos = self.start_pos
self.update_path()
def mouseMoveEvent(self, event):
if event.buttons() == Qt.MouseButton.LeftButton:
print(f"moving, x : {event.scenePos().x()}, y : {event.scenePos().y()}")
self.end_pos = event.scenePos()
try:
self.update_path()
except AttributeError:
pass
def mouseReleaseEvent(self, event) -> None:
released_item = self.itemAt(event.scenePos(), QTransform())
if event.button() == Qt.MouseButton.LeftButton:
if released_item is not None and released_item.type() != 2:
self.end_pos = released_item.scenePos()
self.end_pos.setX(self.end_pos.x() + 6)
self.end_pos.setY(self.end_pos.y() + 6)
if not self.start_pos.isNull() and not self.end_pos.isNull():
path = QPainterPath()
path.moveTo(self.start_pos.x() - 1, self.start_pos.y() - 1)
path.lineTo(self.end_pos)
self.edge.setPath(path)
else:
x = event.scenePos().x() + 1
y = event.scenePos().y() + 1
end_port = QGraphicsEllipseItem(0, 0, 10, 10)
end_port.setPos(x - 6, y - 6)
end_port.setPen(self._port_pen)
end_port.setBrush(self._port_brush)
end_port.setZValue(10000.0)
self.addItem(end_port)
def update_path(self):
if not self.start_pos.isNull() and not self.end_pos.isNull():
path = QPainterPath()
path.moveTo(self.start_pos.x() - 1, self.start_pos.y() - 1)
path.lineTo(self.end_pos)
self.edge.setPath(path)
class Ellipse(QGraphicsEllipseItem):
def __init__(self):
super().__init__()
self.setRect(0, 0, 10, 10)
self.setAcceptHoverEvents(True)
def hoverEnterEvent(self, event) -> None:
print("hovered")
if __name__ == "__main__":
app = QApplication(sys.argv)
win = MainWindow()
sys.exit(app.exec())
What am i doing wrong here, how do i get the hover event to work please ?
Thank you

How to alignment QGridLayout [duplicate]

I am developing an app for memorizing text using PyQt4. I want to show all the words in bubbles so that you see how long the word is. But when I have all the bubbles in my QScrollArea, they are aligned one under the other. I would like to have them aligned side-by-side, but with word-wrap.
I got the bubbles to work using a QLabel with rounded borders. But now that I have the words in QLabel's, PyQt doesn't consider them as words - but as widgets. So PyQt puts one widget under the other. I would like the widgets to be aligned side-by-side until they reach the end of the line, and then they should wrap around to the next line - meaning the QLabel's should act like words in a text document.
Here is my code so far:
f = open(r'myFile.txt')
class Bubble(QtGui.QLabel):
def __init__(self, text):
super(Bubble, self).__init__(text)
self.word = text
self.setContentsMargins(5, 5, 5, 5)
def paintEvent(self, e):
p = QtGui.QPainter(self)
p.setRenderHint(QtGui.QPainter.Antialiasing,True)
p.drawRoundedRect(0,0,self.width()-1,self.height()-1,5,5)
super(Bubble, self).paintEvent(e)
class MainWindow(QtGui.QMainWindow):
def __init__(self, text, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.setupUi(self)
self.MainArea = QtGui.QScrollArea
self.widget = QtGui.QWidget()
vbox = QtGui.QVBoxLayout()
self.words = []
for t in re.findall(r'\b\w+\b', text):
label = Bubble(t)
label.setFont(QtGui.QFont('SblHebrew', 18))
label.setFixedWidth(label.sizeHint().width())
self.words.append(label)
vbox.addWidget(label)
self.widget.setLayout(vbox)
self.MainArea.setWidget(self.widget)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
myWindow = MainWindow(f.read(), None)
myWindow.show()
sys.exit(app.exec_())
When I run this I get:
But I would like the words (the Qlabel's containing the words) to be next to each other, not under each other, like this (photoshopped):
I've been doing a lot of research, but no answers help me align the widgets next to each other.
Here's a PyQt5 version of the Flow Layout demo script:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class FlowLayout(QtWidgets.QLayout):
def __init__(self, parent=None, margin=-1, hspacing=-1, vspacing=-1):
super(FlowLayout, self).__init__(parent)
self._hspacing = hspacing
self._vspacing = vspacing
self._items = []
self.setContentsMargins(margin, margin, margin, margin)
def __del__(self):
del self._items[:]
def addItem(self, item):
self._items.append(item)
def horizontalSpacing(self):
if self._hspacing >= 0:
return self._hspacing
else:
return self.smartSpacing(
QtWidgets.QStyle.PM_LayoutHorizontalSpacing)
def verticalSpacing(self):
if self._vspacing >= 0:
return self._vspacing
else:
return self.smartSpacing(
QtWidgets.QStyle.PM_LayoutVerticalSpacing)
def count(self):
return len(self._items)
def itemAt(self, index):
if 0 <= index < len(self._items):
return self._items[index]
def takeAt(self, index):
if 0 <= index < len(self._items):
return self._items.pop(index)
def expandingDirections(self):
return QtCore.Qt.Orientations(0)
def hasHeightForWidth(self):
return True
def heightForWidth(self, width):
return self.doLayout(QtCore.QRect(0, 0, width, 0), True)
def setGeometry(self, rect):
super(FlowLayout, self).setGeometry(rect)
self.doLayout(rect, False)
def sizeHint(self):
return self.minimumSize()
def minimumSize(self):
size = QtCore.QSize()
for item in self._items:
size = size.expandedTo(item.minimumSize())
left, top, right, bottom = self.getContentsMargins()
size += QtCore.QSize(left + right, top + bottom)
return size
def doLayout(self, rect, testonly):
left, top, right, bottom = self.getContentsMargins()
effective = rect.adjusted(+left, +top, -right, -bottom)
x = effective.x()
y = effective.y()
lineheight = 0
for item in self._items:
widget = item.widget()
hspace = self.horizontalSpacing()
if hspace == -1:
hspace = widget.style().layoutSpacing(
QtWidgets.QSizePolicy.PushButton,
QtWidgets.QSizePolicy.PushButton, QtCore.Qt.Horizontal)
vspace = self.verticalSpacing()
if vspace == -1:
vspace = widget.style().layoutSpacing(
QtWidgets.QSizePolicy.PushButton,
QtWidgets.QSizePolicy.PushButton, QtCore.Qt.Vertical)
nextX = x + item.sizeHint().width() + hspace
if nextX - hspace > effective.right() and lineheight > 0:
x = effective.x()
y = y + lineheight + vspace
nextX = x + item.sizeHint().width() + hspace
lineheight = 0
if not testonly:
item.setGeometry(
QtCore.QRect(QtCore.QPoint(x, y), item.sizeHint()))
x = nextX
lineheight = max(lineheight, item.sizeHint().height())
return y + lineheight - rect.y() + bottom
def smartSpacing(self, pm):
parent = self.parent()
if parent is None:
return -1
elif parent.isWidgetType():
return parent.style().pixelMetric(pm, None, parent)
else:
return parent.spacing()
class Bubble(QtWidgets.QLabel):
def __init__(self, text):
super(Bubble, self).__init__(text)
self.word = text
self.setContentsMargins(5, 5, 5, 5)
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
painter.drawRoundedRect(
0, 0, self.width() - 1, self.height() - 1, 5, 5)
super(Bubble, self).paintEvent(event)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, text, parent=None):
super(MainWindow, self).__init__(parent)
self.mainArea = QtWidgets.QScrollArea(self)
self.mainArea.setWidgetResizable(True)
widget = QtWidgets.QWidget(self.mainArea)
widget.setMinimumWidth(50)
layout = FlowLayout(widget)
self.words = []
for word in text.split():
label = Bubble(word)
label.setFont(QtGui.QFont('SblHebrew', 18))
label.setFixedWidth(label.sizeHint().width())
self.words.append(label)
layout.addWidget(label)
self.mainArea.setWidget(widget)
self.setCentralWidget(self.mainArea)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MainWindow('Harry Potter is a series of fantasy literature')
window.show()
sys.exit(app.exec_())
I thought it might be possible to use html in a QTextBrowser widget for this, but Qt's rich-text engine doesn't support the border-radius CSS property which would be needed for the bubble labels.
So it looks like you need a PyQt port of the Flow Layout example. This can "word-wrap" a collection of widgets inside a container, and also allows the margins and horizontal/vertical spacing to be adjusted.
Here is a demo script that implements the FlowLayout class and shows how to use it with your example:
import sys
from PyQt4 import QtCore, QtGui
class FlowLayout(QtGui.QLayout):
def __init__(self, parent=None, margin=-1, hspacing=-1, vspacing=-1):
super(FlowLayout, self).__init__(parent)
self._hspacing = hspacing
self._vspacing = vspacing
self._items = []
self.setContentsMargins(margin, margin, margin, margin)
def __del__(self):
del self._items[:]
def addItem(self, item):
self._items.append(item)
def horizontalSpacing(self):
if self._hspacing >= 0:
return self._hspacing
else:
return self.smartSpacing(
QtGui.QStyle.PM_LayoutHorizontalSpacing)
def verticalSpacing(self):
if self._vspacing >= 0:
return self._vspacing
else:
return self.smartSpacing(
QtGui.QStyle.PM_LayoutVerticalSpacing)
def count(self):
return len(self._items)
def itemAt(self, index):
if 0 <= index < len(self._items):
return self._items[index]
def takeAt(self, index):
if 0 <= index < len(self._items):
return self._items.pop(index)
def expandingDirections(self):
return QtCore.Qt.Orientations(0)
def hasHeightForWidth(self):
return True
def heightForWidth(self, width):
return self.doLayout(QtCore.QRect(0, 0, width, 0), True)
def setGeometry(self, rect):
super(FlowLayout, self).setGeometry(rect)
self.doLayout(rect, False)
def sizeHint(self):
return self.minimumSize()
def minimumSize(self):
size = QtCore.QSize()
for item in self._items:
size = size.expandedTo(item.minimumSize())
left, top, right, bottom = self.getContentsMargins()
size += QtCore.QSize(left + right, top + bottom)
return size
def doLayout(self, rect, testonly):
left, top, right, bottom = self.getContentsMargins()
effective = rect.adjusted(+left, +top, -right, -bottom)
x = effective.x()
y = effective.y()
lineheight = 0
for item in self._items:
widget = item.widget()
hspace = self.horizontalSpacing()
if hspace == -1:
hspace = widget.style().layoutSpacing(
QtGui.QSizePolicy.PushButton,
QtGui.QSizePolicy.PushButton, QtCore.Qt.Horizontal)
vspace = self.verticalSpacing()
if vspace == -1:
vspace = widget.style().layoutSpacing(
QtGui.QSizePolicy.PushButton,
QtGui.QSizePolicy.PushButton, QtCore.Qt.Vertical)
nextX = x + item.sizeHint().width() + hspace
if nextX - hspace > effective.right() and lineheight > 0:
x = effective.x()
y = y + lineheight + vspace
nextX = x + item.sizeHint().width() + hspace
lineheight = 0
if not testonly:
item.setGeometry(
QtCore.QRect(QtCore.QPoint(x, y), item.sizeHint()))
x = nextX
lineheight = max(lineheight, item.sizeHint().height())
return y + lineheight - rect.y() + bottom
def smartSpacing(self, pm):
parent = self.parent()
if parent is None:
return -1
elif parent.isWidgetType():
return parent.style().pixelMetric(pm, None, parent)
else:
return parent.spacing()
class Bubble(QtGui.QLabel):
def __init__(self, text):
super(Bubble, self).__init__(text)
self.word = text
self.setContentsMargins(5, 5, 5, 5)
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
painter.drawRoundedRect(
0, 0, self.width() - 1, self.height() - 1, 5, 5)
super(Bubble, self).paintEvent(event)
class MainWindow(QtGui.QMainWindow):
def __init__(self, text, parent=None):
super(MainWindow, self).__init__(parent)
self.mainArea = QtGui.QScrollArea(self)
self.mainArea.setWidgetResizable(True)
widget = QtGui.QWidget(self.mainArea)
widget.setMinimumWidth(50)
layout = FlowLayout(widget)
self.words = []
for word in text.split():
label = Bubble(word)
label.setFont(QtGui.QFont('SblHebrew', 18))
label.setFixedWidth(label.sizeHint().width())
self.words.append(label)
layout.addWidget(label)
self.mainArea.setWidget(widget)
self.setCentralWidget(self.mainArea)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MainWindow('Harry Potter is a series of fantasy literature')
window.show()
sys.exit(app.exec_())

how to replace original image with another image in runtime in using pyqt5?

I am trying to make application which can show weather net is down or up with graphical representation. For the task I am using pyqt5, But when I am trying to do it, it just overlap on same image again and again.
time2 = ""
class DigitalClock(QLCDNumber, QWidget):
def __init__(self, parent=None):
super(DigitalClock, self).__init__(parent)
self.setWindowTitle("Digital Clock")
self.showFullScreen()
self.mainlayout = QHBoxLayout()
timer = QTimer(self)
timer.timeout.connect(self.ui)
timer.start(1000)
self.ui()
self.show()
def ui(self):
global time2
time = QTime.currentTime()
text = time.toString('hh:mm')
if (time.second() % 2) == 0:
text = text[:2]+" "+ text[3:]
if text[3:] != time2:
if os.system("ping -c 1 192.168.63.145") == 0:
self.network = QLabel(self)
self.network.setPixmap(QPixmap("signal-bars.png"))
self.network.move(50,300)
self.network.show()
time2 = text[3:]
else:
self.network = QLabel(self)
self.network.setPixmap(QPixmap("no-internet.png"))
time2 = text[3:]
self.network.move(50,300)
self.network.show()
self.display(text)
def main():
app = QApplication(sys.argv)
clock = DigitalClock()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
QLCDNumber Class - Inherits: QFrame.
QFrame Class - Inherits: QWidget.
No need to create each time - self.network = QLabel (self).
You do not need global variables.
Try it:
import sys
import os
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
#time2 = ""
class DigitalClock(QLCDNumber): #, QWidget):
def __init__(self, parent=None):
super(DigitalClock, self).__init__(parent)
self.time2 = True # +
self.network = QLabel(self) # +
self.network.setGeometry(50, 300, 150, 150) # +
self.setWindowTitle("Digital Clock")
self.showFullScreen()
self.mainlayout = QHBoxLayout() # ?
timer = QTimer(self)
timer.timeout.connect(self.ui)
self.ui()
timer.start(1000)
def ui(self):
# global time2 # ---
time = QTime.currentTime()
text = time.toString('hh:mm')
if (time.second() % 2) == 0:
text = text[:2]+" "+ text[3:]
# if text[3:] != time2:
# if os.system("ping -c 1 192.168.63.145") == 0:
if self.time2:
# self.network = QLabel(self) # ---
self.network.setPixmap(QPixmap("im.png").scaled(150, 150, Qt.KeepAspectRatio, Qt.FastTransformation))
# time2 = text[3:]
else:
# self.network = QLabel(self)
self.network.setPixmap(QPixmap("Ok.png").scaled(150, 150, Qt.KeepAspectRatio, Qt.FastTransformation))
# time2 = text[3:]
self.time2 = not self.time2
self.display(text)
def main():
app = QApplication(sys.argv)
clock = DigitalClock()
clock.show() # +
sys.exit(app.exec_())
if __name__ == '__main__':
main()

PyQT Drawing in a Custom QGraphicsView showing on a QMainWindow

The basic idea is that I can't use a QGraphicsView class to draw while inside a QMainWindow. I can see the painEvent firing and that the information is flowing to the method that performs the drawing, but in the end nothing gets displayed. Here is the Class with the QGraphicsView:
class Display_Pixels(QGraphicsView):
def __init__(self, parent=None):
QGraphicsView.__init__(self, parent=parent)
#super().__init__()
self.initUI()
self.img = cv2.imread('roi.jpg')
def initUI(self):
self.setGeometry(100, 100, 650, 650)
#self.setWindowTitle('By Pixel')
#self.setMouseTracking(True)
#self.show()
res = 40
self.grid = np.array([ [-1] * res for n in range(res)]) # list comprehension
#print(self.grid.shape)
def paintEvent(self, e):
qp = QPainter()
qp.begin(self)
self.drawRectangles(qp)
qp.end()
def drawRectangles(self, qp, w = 16):
print("Drawing")
mode = 0
x,y = 0,0 # starting position
lr = 20
hr = 35
col = QColor(0, 0, 0)
col.setNamedColor('#d4d4d4')
qp.setPen(col)
#print(self.img.shape)
for g_row, img_row in zip(self.grid, self.img):
#print(img_row.shape)
for g_col, img_col in zip(g_row, img_row):
r, g, b = (img_col[0], img_col[1], img_col[2])
#print(r,g,b)
if g_col == 1:
if mode == 0:
r = int(math.log(r)*lr)
g = int(math.log(g)*hr)
b = int(math.log(b)*lr)
elif mode == 1:
if r+50 <= 220: r = r+50
if g+80 <= 255: g = g+80
if b+50 <= 220: b = b+50
else:
if r+70 <= 220: r = r+70
if g+140 <= 255: g = g+140
if b+70 <= 220: b = b+70
qp.setBrush(QColor(r, g, b))
qp.drawRect(x, y, w, w)
else:
qp.setBrush(QColor(r, g, b))
qp.drawRect(x, y, w, w)
#qp.setBrush(QColor(200, 0, 0))
#qp.drawRect(x, y, w, w)
x = x + w # move right
y = y + w # move down
x = 0 # rest to left edge
def mousePressEvent(self, QMouseEvent):
w = 16.0
#print("MOUSE:")
#print('(', int(QMouseEvent.x()/w), ', ', int(QMouseEvent.y()/w), ')')
#print (QMouseEvent.pos())
x = float(QMouseEvent.x())
y = float(QMouseEvent.y())
self.grid[int(y/w)][int(x/w)] = -1 * self.grid[int(y/w)][int(x/w)]
#print(img[int(y/w), int(x/w), :])
self.repaint()
#self.update()
And also the code for the main window:
class Window(QMainWindow):
def __init__(self, parent=None):
#This initializes the main window or form
super(Window,self).__init__(parent=parent)
self.setGeometry(1,31,900,900)
self.setWindowTitle("Pre-Alignment system")
def run():
app = QApplication.instance()
if app is None:
app = QApplication(sys.argv)
GUI = Window()
view = Display_Pixels(GUI)
#view = MyView(GUI)
GUI.show()
sys.exit(app.exec_())
run()
QGraphicsView inherits from QAbstractScrollArea so the QPainter must set in the viewport(), that is:
def paintEvent(self, e):
qp = QPainter()
qp.begin(self.viewport())
self.drawRectangles(qp)
qp.end()
Although I would paint it is not the best since QGraphicsView has a paint layer that uses the items. In this case it is best to implement a custom item, also I have improved your algorithm.:
import sys
import numpy as np
import cv2
from PyQt5 import QtCore, QtGui, QtWidgets
class OpenCVItem(QtWidgets.QGraphicsItem):
def __init__(self, img, parent=None):
super(OpenCVItem, self).__init__(parent)
res = 40
self.grid = -np.ones((res, res))
self._img = img
height, width, channel = self._img.shape
bytesPerLine = 3 * width
self._qimage = QtGui.QImage(self._img.data,
width, height,
bytesPerLine,
QtGui.QImage.Format_RGB888).rgbSwapped()
def boundingRect(self):
w, h, _ = self._img.shape
return QtCore.QRectF(0, 0, w, h)
def paint(self, painter, option, widget):
painter.drawImage(0, 0, self._qimage)
self.drawRectangles(painter)
def drawRectangles(self, painter):
mode = 0
lr = 20
hr = 35
painter.save()
painter.setPen(QtGui.QPen(QtGui.QColor("#d4d4d4")))
w1, h1 = self.grid.shape
fw = self.boundingRect().width()/w1
fh = self.boundingRect().height()/h1
s = QtCore.QSizeF(fw, fh)
for idx, v in np.ndenumerate(self.grid):
if v == 1:
r_ = QtCore.QRectF(fw*QtCore.QPointF(*idx), s)
r_int = r_.toRect()
(r, g, b), _ = cv2.meanStdDev(self._img[r_int.left():r_int.right(),
r_int.top():r_int.bottom()])
if mode == 0:
r = np.log(r+1)*lr
g = np.log(g+1)*hr
b = np.log(b+1)*lr
elif mode == 1:
if r+50 <= 220: r = r+50
if g+80 <= 255: g = g+80
if b+50 <= 220: b = b+50
else:
if r+70 <= 220: r = r+70
if g+140 <= 255: g = g+140
if b+70 <= 220: b = b+70
painter.setBrush(QtGui.QColor(*(int(x) for x in (r, g, b))))
painter.drawRect(r_)
painter.restore()
def mousePressEvent(self, event):
w1, h1 = self.grid.shape
fw = self.boundingRect().width()/w1
fh = self.boundingRect().height()/h1
xi = int(event.pos().x()/fw)
yi = int(event.pos().y()/fh)
self.grid[xi][yi] = -self.grid[xi][yi]
self.update()
super(OpenCVItem, self).mousePressEvent(event)
class Display_Pixels(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super(Display_Pixels, self).__init__(parent)
scene = QtWidgets.QGraphicsScene(self)
self.setScene(scene)
item = OpenCVItem(cv2.imread("roi.jpg"))
scene.addItem(item)
class Window(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(Window,self).__init__(parent=parent)
self.setGeometry(1,31,900,900)
self.setWindowTitle("Pre-Alignment system")
def run():
app = QtWidgets.QApplication.instance()
if app is None:
app = QtWidgets.QApplication(sys.argv)
GUI = Window()
view = Display_Pixels(GUI)
GUI.setCentralWidget(view)
GUI.show()
sys.exit(app.exec_())
run()

Use QItemDelegate to draw icon

I want to draw a second icon in a single column, on the right side of the standard painting event of that column. You can see below I draw a red icon on the far right side of the items in column 3. How would I go about doing this using a QItemDelegate? I would prefer the default paint even still happens in order to pain the decoration role and display role text.
import os, sys, pprint
from Qt import QtGui, QtWidgets, QtCore
class Window(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.resize(500, 400)
self.uiItems = QtWidgets.QTreeView()
self.uiItems.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.uiItems.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.uiItems.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.uiItems.setModel(QtGui.QStandardItemModel())
self.uiItems.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.uiItems)
self.setLayout(self.layout)
for i in range(3):
parent1 = QtGui.QStandardItem('Parente Item ' + str(i))
parent2 = QtGui.QStandardItem()
parent3 = QtGui.QStandardItem()
self.uiItems.model().appendRow([parent1, parent2, parent3])
for x in range(3):
col1 = QtGui.QStandardItem('Child Item' + str(x))
col2 = QtGui.QStandardItem('Item' + str(x))
col3 = QtGui.QStandardItem('Item' + str(x))
col3.setData(self.createDotPixmap(), role=QtCore.Qt.DecorationRole)
parent1.appendRow([col1,col2,col3])
self.uiItems.expandAll()
def createRectPixmap(self, col=QtGui.QColor(240,50,50)):
px = QtGui.QPixmap(12,12)
px.fill(QtCore.Qt.transparent)
pxSize = px.rect().adjusted(1,1,-1,-1)
painter = QtGui.QPainter(px)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.setBrush(col)
painter.setPen(QtGui.QPen(QtGui.QColor(150,20,20), 1.25))
painter.drawRect(pxSize)
painter.end()
return px
def createDotPixmap(self, col=QtGui.QColor(128,128,128)):
px = QtGui.QPixmap(12,12)
px.fill(QtCore.Qt.transparent)
pxSize = px.rect().adjusted(1,1,-1,-1)
painter = QtGui.QPainter(px)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.setBrush(col)
painter.setPen(QtGui.QPen(QtGui.QColor(15,15,15), 1.25))
painter.drawEllipse(pxSize)
painter.end()
return px
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
ex = Window()
ex.show()
app.exec_()
The solution is:
import os, sys, pprint
from Qt import QtGui, QtWidgets, QtCore
DecorationRole2 = QtCore.Qt.UserRole + 1000
class IconDelegate(QtWidgets.QStyledItemDelegate):
def paint(self, painter, option, index):
super(self.__class__, self).paint(painter, option, index)
value = index.data(DecorationRole2)
if value:
margin = 10
mode = QtGui.QIcon.Normal
if not (option.state & QtWidgets.QStyle.State_Enabled):
mode = QtGui.QIcon.Disabled
elif option.state & QtWidgets.QStyle.State_Selected:
mode = QtGui.QIcon.Selected
if isinstance(value, QtGui.QPixmap):
icon = QtGui.QIcon(value)
option.decorationSize = value.size() / value.devicePixelRatio()
elif isinstance(value, QtGui.QColor):
pixmap = QtGui.QPixmap(option.decorationSize)
pixmap.fill(value)
icon = QtGui.QIcon(pixmap)
elif isinstance(value, QtGui.Image):
icon = QtGui.QIcon(QtGui.QPixmap.fromImage(value))
option.decorationSize = value.size() / value.devicePixelRatio()
elif isinstance(value, QtGui.QIcon):
state = QtGui.QIcon.On if option.state & QtWidgets.QStyle.State_Open else QtGui.QIcon.Off
actualSize = option.icon.actualSize(option.decorationSize, mode, state)
option.decorationSize = QtCore.QSize(min(option.decorationSize.width(), actualSize.width()), min(option.decorationSize.height(), actualSize.height()))
r = QtCore.QRect(QtCore.QPoint(), option.decorationSize)
r.moveCenter(option.rect.center())
r.setRight(option.rect.right() - margin)
state = QtGui.QIcon.On if option.state & QtWidgets.QStyle.State_Open else QtGui.QIcon.Off
icon.paint(painter, r, QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter, mode, state)
class Window(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.resize(500, 400)
self.uiItems = QtWidgets.QTreeView()
self.uiItems.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.uiItems.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.uiItems.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.uiItems.setModel(QtGui.QStandardItemModel())
self.uiItems.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
delegate = IconDelegate(self.uiItems)
self.uiItems.setItemDelegateForColumn(2, delegate)
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.uiItems)
self.setLayout(self.layout)
for i in range(3):
parent1 = QtGui.QStandardItem('Parente Item ' + str(i))
parent2 = QtGui.QStandardItem()
parent3 = QtGui.QStandardItem()
self.uiItems.model().appendRow([parent1, parent2, parent3])
for x in range(3):
col1 = QtGui.QStandardItem('Child Item' + str(x))
col2 = QtGui.QStandardItem('Item' + str(x))
col3 = QtGui.QStandardItem('Item' + str(x))
col3.setData(self.createDotPixmap(), role=QtCore.Qt.DecorationRole)
col3.setData(self.createRectPixmap(), role=DecorationRole2)
parent1.appendRow([col1,col2,col3])
self.uiItems.expandAll()
def createRectPixmap(self, col=QtGui.QColor(240,50,50)):
px = QtGui.QPixmap(12,12)
px.fill(QtCore.Qt.transparent)
pxSize = px.rect().adjusted(1,1,-1,-1)
painter = QtGui.QPainter(px)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.setBrush(col)
painter.setPen(QtGui.QPen(QtGui.QColor(150,20,20), 1.25))
painter.drawRect(pxSize)
painter.end()
return px
def createDotPixmap(self, col=QtGui.QColor(128,128,128)):
px = QtGui.QPixmap(12,12)
px.fill(QtCore.Qt.transparent)
pxSize = px.rect().adjusted(1,1,-1,-1)
painter = QtGui.QPainter(px)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.setBrush(col)
painter.setPen(QtGui.QPen(QtGui.QColor(15,15,15), 1.25))
painter.drawEllipse(pxSize)
painter.end()
return px
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
ex = Window()
ex.show()
app.exec_()

Categories

Resources