Added GUI

This commit is contained in:
apenzko 2021-10-17 12:49:49 +02:00
parent ae9774cf0d
commit 74df5cb3f0
23 changed files with 3174 additions and 2 deletions

0
uiwidget/__init__.py Normal file
View file

View file

@ -0,0 +1,89 @@
import numpy as np
import pyqtgraph as pg
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtGui import QColor
lst_aus = ['AU1: Inner Brow Raiser', 'AU2: Outer Brow Raiser', 'AU4: Brow Lowerer', 'AU5: Upper Lid Raiser',
'AU6: Cheek Raiser', 'AU9: Nose Wrinkler', 'AU12: Lip Corner Puller', 'AU15: Lip Corner Depressor',
'AU17: Chin Raiser', 'AU20: Lip Stretcher', 'AU25: Lips Part', 'AU26: Jaw Drop']
class WidgetFacialExpression(QtWidgets.QWidget):
def __init__(self, parent=None):
super(WidgetFacialExpression, self).__init__(parent)
self.faceLayout = QtWidgets.QHBoxLayout()
self.setLayout(self.faceLayout)
self.numberIDs = None
self.valueLabels = dict()
self.imgPlots = dict()
@QtCore.pyqtSlot(list, int)
def setInit(self, colors, numberIDs):
self.numberIDs = numberIDs
for id_no in range(numberIDs):
idLayout = QtWidgets.QHBoxLayout()
labelNameLayout = QtWidgets.QVBoxLayout()
labelValueLayout = QtWidgets.QVBoxLayout()
imageLayout = QtWidgets.QVBoxLayout()
imageWidget = pg.PlotWidget(background=QColor(53, 53, 53))
imageWidget.invertY()
imageWidget.hideAxis('bottom'), imageWidget.hideAxis('left')
imageWidget.setMaximumHeight(150), imageWidget.setMaximumWidth(150)
imageWidget.setAspectLocked(True)
self.imgPlots[id_no] = imageWidget
color = tuple([int(a * 255) for a in colors[id_no]])
labelID = QtWidgets.QLabel('ID%i' % id_no)
labelID.setStyleSheet('font: bold 12px; color: black; background-color: rgb(%i,%i,%i)' % color)
#labelID.setFixedWidth(60)
labelNameLayout.addWidget(labelID)
labelID = QtWidgets.QLabel(' ')
#labelID.setStyleSheet('background-color: rgb(%i,%i,%i)' % color)
labelValueLayout.addWidget(labelID)
lst = []
for au in lst_aus:
nLabel = QtWidgets.QLabel(au)
labelNameLayout.addWidget(nLabel)
vLabel = QtWidgets.QLabel(' ')
labelValueLayout.addWidget(vLabel)
lst.append(vLabel)
self.valueLabels[id_no] = lst
idLayout.addWidget(imageWidget)
idLayout.addLayout(labelNameLayout)
idLayout.addLayout(labelValueLayout)
self.faceLayout.addLayout(idLayout)
@QtCore.pyqtSlot(dict, int)
def updateImages(self, imgs, id_no):
if imgs[id_no] is not None:
img = np.moveaxis(imgs[id_no], 0, 1)
img = pg.ImageItem(img)
self.imgPlots[id_no].addItem(img)
@QtCore.pyqtSlot(dict)
def updateFrame(self, aus):
if self.numberIDs is None:
return
for id_no in range(self.numberIDs):
if len(aus[id_no]) > 0:
for i, label in enumerate(self.valueLabels[id_no]):
if not np.any(np.isnan(np.array(aus[id_no].flatten()[0], dtype=np.float64))):
label.setText('%.2f' % aus[id_no].flatten()[0][i])

147
uiwidget/widgetgaze.py Normal file
View file

@ -0,0 +1,147 @@
import numpy as np
import pyqtgraph
import matplotlib
matplotlib.use("Qt5Agg")
import matplotlib.animation as animation
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from PyQt5 import QtWidgets, QtGui
from PyQt5 import QtCore
import pyqtgraph as pg
import pyqtgraph.exporters
from utils.colors import random_colors
from utils.util import sperical2equirec
class WidgetGaze(QtWidgets.QWidget):
def __init__(self, parent=None):
super(WidgetGaze, self).__init__(parent)
layout = QtWidgets.QGridLayout()
# Setup gaze graph
self.gazeGraph = pg.PlotWidget()
self.gazeGraph.setBackground('w')
self.gazeGraph.setYRange(-1.25, 1.25, padding=0)
self.gazeGraph.setXRange(-1.25, 1.25, padding=0)
self.gazeGraph.hideAxis('left')
self.gazeGraph.hideAxis('bottom')
self.gazeGraph.setAspectLocked()
self.gazeGraph.getPlotItem().setTitle(title='Top-down View of Gaze')
self.gazeGraphPlots = []
self.measures = QtWidgets.QWidget()
self.measuresLayout = QtWidgets.QHBoxLayout()
# self.gazeMap = QtWidgets.QWidget()
# self.heatmapSlider = HeatmapSlider()
# self.heatmapSlider.signalSetThreshold.connect(self.setThreshold)
# self.heatmapSlider.signalSaveImage.connect(self.gazeMap.saveImage)
# row, column, row span, column span
layout.addWidget(self.measures, 0, 1, 2, 1)
layout.addWidget(self.gazeGraph, 0, 0, 2, 1)
layout.setColumnStretch(0, 1)
layout.setColumnStretch(1, 1)
self.setLayout(layout)
# layout.addWidget(self.gazeMap, 0, 0, 3, 1)
# layout.addWidget(self.heatmapSlider, 3, 0, 1, 1)
self.gazeLabels = []
self.colors = None
@QtCore.pyqtSlot(dict, list, int)
def setInit(self, measures, colors, numberIDs):
"""Initialize measure widget with labels for all IDs"""
self.colors = colors # Necessary for ID updates
idLayout = QtWidgets.QVBoxLayout()
labelID = QtWidgets.QLabel(' ')
labelID.setFixedWidth(60)
labelID.setFixedHeight(20)
labelA = QtWidgets.QLabel('LookSomeone: ')
labelNoLook = QtWidgets.QLabel('TotalNoLook: ')
labelG = QtWidgets.QLabel('TotalWatched: ')
labelRatio = QtWidgets.QLabel('RatioWatcherLookSOne: ')
label = QtWidgets.QLabel('Tracked: ')
# labelVel = QtWidgets.QLabel('totNoLook: ')
idLayout.addWidget(labelID)
idLayout.addWidget(labelA)
idLayout.addWidget(labelNoLook)
idLayout.addWidget(labelG)
idLayout.addWidget(labelRatio)
idLayout.addWidget(label)
# idLayout.addWidget(labelVel)
self.measuresLayout.insertLayout(-1, idLayout)
for id_no in range(numberIDs):
idLayout = QtWidgets.QVBoxLayout()
color = tuple([int(a * 255) for a in colors[id_no]])
labelID = QtWidgets.QLabel('ID%i' % id_no)
labelID.setStyleSheet('font: bold 12px; color: black; background-color: rgb(%i,%i,%i)' % color)
labelID.setFixedWidth(60)
labelID.setFixedHeight(20)
# Look Someone
labelA = QtWidgets.QLabel('{:.2%}'.format(measures[id_no][1] / measures[id_no][2]))
labelNoLook = QtWidgets.QLabel('{:.2%}'.format((measures[id_no][2] - measures[id_no][1]) / measures[id_no][2]))
# Total Watched
labelG = QtWidgets.QLabel('{:.2%}'.format(measures[id_no][0] / measures[id_no][2]))
# ratio totWatcher / lookSomeone
labelRatio = QtWidgets.QLabel('{:.2}'.format(measures[id_no][0] / measures[id_no][1]))
label = QtWidgets.QLabel('%i frames' % measures[id_no][2])
# labelVel = QtWidgets.QLabel('%.2f' % np.random.uniform(0, 1))
idLayout.addWidget(labelID)
idLayout.addWidget(labelA)
idLayout.addWidget(labelNoLook)
idLayout.addWidget(labelG)
idLayout.addWidget(labelRatio)
idLayout.addWidget(label)
# idLayout.addWidget(labelVel)
# self.gazeLabels.append(labelVel)
self.measuresLayout.insertLayout(-1, idLayout)
self.measures.setLayout(self.measuresLayout)
@QtCore.pyqtSlot(list, int)
def initGazeGraph(self, colors, numberIDs):
""" initialize gaze graph """
# Big circle
x1, y1 = self.get_circle(radius=1)
self.gazeGraph.addItem(self.gazeGraph.plot(x1, y1, pen=pg.mkPen(0.5)))
# Camera
x2, y2 = self.get_circle(radius=0.02)
self.gazeGraph.addItem(self.gazeGraph.plot(x2, y2, pen=pg.mkPen(color=(0, 0, 0), width=3)))
for id_no in range(numberIDs):
color = tuple([int(a * 255) for a in colors[id_no]])
plt = self.gazeGraph.plot(x=[], y=[], pen=pg.mkPen(color=color, width=2))
self.gazeGraphPlots.append(plt)
@QtCore.pyqtSlot(dict, int)
def updateGazeGraph(self, data, numberIDs):
""" frame updates for gaze graph """
for id_no in range(numberIDs):
if data and id_no in data:
self.gazeGraphPlots[id_no].setData(data[id_no][0], data[id_no][1])
def get_circle(self, radius):
""" helper function returns circle to x, y coordinates"""
theta = np.linspace(0, 2 * np.pi, 100)
x = radius * np.cos(theta)
y = radius * np.sin(theta)
return np.array(x), np.array(y)
@QtCore.pyqtSlot(list)
def onSelectedID(self, lst):
"""Change color to None of gaze graph plot if ID should not be visible"""
for i, button in enumerate(lst):
if not button.isChecked():
self.gazeGraphPlots[i].setPen(None)
else:
color = tuple([int(a * 255) for a in self.colors[i]])
pen = pg.mkPen(color=color)
self.gazeGraphPlots[i].setPen(pen)

81
uiwidget/widgetobject.py Normal file
View file

@ -0,0 +1,81 @@
import numpy as np
import pyqtgraph
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QTextEdit
import pyqtgraph as pg
class WidgetObject(QtWidgets.QWidget):
def __init__(self, parent=None):
super(WidgetObject, self).__init__(parent)
self.frame = 0
self.tagFields = QtWidgets.QWidget()
self.tagLayout = QtWidgets.QVBoxLayout()
# Setup Graph Plot Widget
self.tagGraph = pg.PlotWidget()
self.tagGraph.setBackground('w')
self.tagGraph.setYRange(0, 400, padding=0)
self.tagGraph.getPlotItem().getAxis('bottom').setTickSpacing(minor=50, major=100)
self.tagGraph.getPlotItem().setTitle(title='Movement of Object Tags')
self.tagPlots = dict()
self.tagTextFields = dict()
self.plotText = dict()
layout = QtWidgets.QGridLayout()
layout.addWidget(self.tagGraph, 0, 0)
layout.addWidget(self.tagFields, 0, 1)
self.setLayout(layout)
@QtCore.pyqtSlot(list, tuple, dict, list)
def setInit(self, tags, frameSize, tracked, colors):
for i, tag in enumerate(tags):
label = QtWidgets.QLabel('Object tag #%i:' % tag)
label.setStyleSheet('color: black; background-color: rgb(%i,%i,%i)' % colors[i])
label.setFixedHeight(20)
field = QtWidgets.QTextEdit()
field.setFixedHeight(20)
field.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
field.textChanged.connect(self.tagTextChanged)
self.tagTextFields[tag] = field
trackedLabel = QtWidgets.QLabel('Tracked: {:.0%}'.format(tracked[tag]))
oneTagLayout = QtWidgets.QHBoxLayout()
oneTagLayout.addWidget(label)
oneTagLayout.addWidget(field)
oneTagLayout.addWidget(trackedLabel)
self.tagLayout.insertLayout(-1, oneTagLayout)
x = list(range(-200, 0)) # 200 time points
y = [0 for _ in range(200)] # 200 data points
dataLine = self.tagGraph.plot(x, y, pen=pg.mkPen(color=colors[i]))
self.tagPlots[tag] = dataLine
text = pg.TextItem(text='', color=colors[i])
text.setAnchor((1, i + 1))
self.plotText[tag] = text
self.tagGraph.addItem(text)
self.tagFields.setLayout(self.tagLayout)
@QtCore.pyqtSlot(dict)
def updateTagGraph(self, tagData):
for tag, values in tagData.items():
self.tagPlots[tag].setData(x=values[1], y=values[0]) # Update the data.
if tagData:
self.tagGraph.setXRange(np.min(values[1]), np.max(values[1]))
def tagTextChanged(self):
for tag, field in self.tagTextFields.items():
self.plotText[tag].setText(field.toPlainText())
x, y = self.tagPlots[tag].getData()
if len(x) > 0 and len(y) > 0:
#print(tag, x[-1], y[-1])
self.plotText[tag].setPos(x[-1], y[-1])

309
uiwidget/widgetplayer.py Normal file
View file

@ -0,0 +1,309 @@
from PyQt5 import QtCore, QtGui, QtWidgets, QtMultimedia, QtMultimediaWidgets, Qt
import os
import numpy as np
class WidgetPlayer(QtWidgets.QWidget):
updateFrame = QtCore.pyqtSignal(int)
# sendFileName = QtCore.pyqtSignal(str)
sendState = QtCore.pyqtSignal(QtMultimedia.QMediaPlayer.State)
frameAvailable = QtCore.pyqtSignal(QtGui.QImage)
labels = list()
colors = list()
pose_data = list()
gaze_data = list()
tag_data = dict()
tags = list()
tag_colors = dict()
def __init__(self, parent=None):
super(WidgetPlayer, self).__init__(parent)
self.root = QtCore.QFileInfo(__file__).absolutePath()
# mediaplayer for decoding the video
self.mediaPlayer = QtMultimedia.QMediaPlayer(self, QtMultimedia.QMediaPlayer.VideoSurface)
#self.mediaPlayer.setMuted(True)
# top = graphicsscene, middle = graphiscview, bottom = graphicsvideoitem, lowest = graphisctextitems, ...
self._scene = QtWidgets.QGraphicsScene(self)
self._scene.setBackgroundBrush(QtGui.QBrush(QtGui.QColor('black')))
self._gv = QtWidgets.QGraphicsView(self._scene)
self._videoitem = QtMultimediaWidgets.QGraphicsVideoItem()
self._videoitem.setPos(0, 0)
self._videoitem.setZValue(-1000)
self._scene.addItem(self._videoitem)
if os.name != 'nt':
# grab frames to forward them to facial emotion tab
probe = QtMultimedia.QVideoProbe(self)
probe.videoFrameProbed.connect(self.on_videoFrameProbed)
probe.setSource(self.mediaPlayer)
# disable scrollbars
self._gv.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self._gv.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
# just a holder for the graphics view to expand to maximum to use full size
self.lay = QtWidgets.QVBoxLayout(self)
self.lay.setContentsMargins(0, 0, 0, 0)
self.lay.addWidget(self._gv)
self.errorLabel = QtWidgets.QLabel()
self.errorLabel.setSizePolicy(QtWidgets.QSizePolicy.Preferred,
QtWidgets.QSizePolicy.Maximum)
self.mediaPlayer.setVideoOutput(self._videoitem)
self.mediaPlayer.stateChanged.connect(self.on_stateChanged)
self.mediaPlayer.positionChanged.connect(self.mediaChangedPosition)
# self.mediaPlayer.durationChanged.connect(self.durationChanged)
self.mediaPlayer.error.connect(self.handleError)
self.movieDir = ''
self.duration = 0
def setInit(self, video, fps, originalVideoResolution, number_ids, colors, tags, tag_colors):
self.fps = fps
self.originalVideoResolution = originalVideoResolution
f = os.path.abspath(video)
self.mediaPlayer.setMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(f)))
self.mediaPlayer.setNotifyInterval(1000 // self.fps)
# init pose data
for i in range(number_ids):
self.pose_data.append(self._scene.addPath(QtGui.QPainterPath()))
# init gaze data
for i in range(number_ids):
self.gaze_data.append(self._scene.addPath(QtGui.QPainterPath()))
# init label data
for i in range(number_ids):
self.labels.append(self._scene.addPath(QtGui.QPainterPath()))
# init tag data
if tags:
for i, tag in enumerate(tags):
self.tag_data[tag] = self._scene.addPath(QtGui.QPainterPath())
self.tag_colors[tag] = tag_colors[i]
self.number_ids = number_ids
self.colors = colors
self.tags = tags
def play(self):
if self.mediaPlayer.state() == QtMultimedia.QMediaPlayer.PlayingState:
self.mediaPlayer.pause()
else:
self.mediaPlayer.play()
self.sendState.emit(self.mediaPlayer.state())
def pause(self):
if self.mediaPlayer.state() == QtMultimedia.QMediaPlayer.PlayingState:
self.mediaPlayer.pause()
self.sendState.emit(self.mediaPlayer.state())
def setFrame(self, frame):
# RESPECT FPS! position is time in millisconds
position = int(frame * 1000 / self.fps)
# print("Received", position)
self.mediaPlayer.setPosition(position)
def stop(self):
self.mediaPlayer.stop()
@QtCore.pyqtSlot(QtMultimedia.QMediaPlayer.State)
def on_stateChanged(self, state):
self.focus_on_video()
def mediaChangedPosition(self, position):
frame = int((position / 1000.0) * self.fps)
# print("Video Running %i" % frame)
self.updateFrame.emit(frame)
self._gv.fitInView(self._videoitem, QtCore.Qt.KeepAspectRatio)
def handleError(self):
# self.playButton.setEnabled(False)
print("Error: " + self.mediaPlayer.errorString())
def createButtons(self):
iconSize = QtCore.QSize(28, 28)
openButton = QtWidgets.QToolButton()
openButton.setStyleSheet('border: none;')
openButton.setIcon(QtGui.QIcon(self.root + '/icons/open.png'))
openButton.setIconSize(iconSize)
openButton.setToolTip("Open File")
# openButton.clicked.connect(self.open)
self.playButton = QtWidgets.QToolButton()
self.playButton.setStyleSheet('border: none;')
self.playButton.setIcon(QtGui.QIcon(self.root + '/icons/play.png'))
self.playButton.setIconSize(iconSize)
self.playButton.setToolTip("Play movie")
self.playButton.clicked.connect(self.play)
self.playButton.setEnabled(False)
self.stopButton = QtWidgets.QToolButton()
self.stopButton.setStyleSheet('border: none;')
self.stopButton.setIcon(QtGui.QIcon(self.root + '/icons/stop.png'))
self.stopButton.setIconSize(iconSize)
self.stopButton.setToolTip("Stop movie")
self.stopButton.clicked.connect(self.stop)
self.stopButton.setEnabled(False)
@QtCore.pyqtSlot(QtMultimedia.QVideoFrame)
def on_videoFrameProbed(self, frame):
cloneFrame = QtMultimedia.QVideoFrame(frame)
cloneFrame.map(QtMultimedia.QAbstractVideoBuffer.ReadOnly)
image = QtGui.QImage(cloneFrame.bits(), cloneFrame.width(), cloneFrame.height(), cloneFrame.bytesPerLine(),
QtMultimedia.QVideoFrame.imageFormatFromPixelFormat(cloneFrame.pixelFormat()))
self.frameAvailable.emit(image)
cloneFrame.unmap()
def focus_on_video(self):
native_video_resolution = self.mediaPlayer.metaData("Resolution")
# we also update the sceneview to zoom to the video
if native_video_resolution is not None:
self._videoitem.setSize(QtCore.QSizeF(native_video_resolution.width(), native_video_resolution.height()))
self._gv.fitInView(self._videoitem, QtCore.Qt.KeepAspectRatio)
# set scale of video to bigger size
if self.originalVideoResolution is not None:
width_ratio = self.originalVideoResolution[0] / native_video_resolution.width()
self._videoitem.setScale(width_ratio)
@QtCore.pyqtSlot()
def clear_tags(self):
self.focus_on_video()
# clear all tags
for tag in self.tags:
self._scene.removeItem(self.tag_data[tag])
self.tag_data[tag] = self._scene.addPath(QtGui.QPainterPath())
@QtCore.pyqtSlot(int, list, list)
def draw_tags(self, tag, lstX, lstY):
# this is removing the old tag data
self._scene.removeItem(self.tag_data[tag])
path = QtGui.QPainterPath()
path.setFillRule(Qt.Qt.WindingFill)
# set starting points
for (x, y) in zip(lstX, lstY):
path.addRect(x-50, y-50, 100, 100)
# by adding it gets converted into an QGraphicsPathItem
# save it for later removal
self.tag_data[tag] = self._scene.addPath(path)
# set colors
color = self.tag_colors[tag]
pen = QtGui.QPen(QtGui.QColor(color[0], color[1], color[2], 255), 2, QtCore.Qt.SolidLine)
self.tag_data[tag].setPen(pen)
# fill ellipses - alpha value is set to 50%
# self.tag_data[tag].setBrush(QtGui.QColor(color[0], color[1], color[2], int(0.5 * 255)))
@QtCore.pyqtSlot()
def clear_labels(self):
self.focus_on_video()
# clear all labels
for id_no in range(self.number_ids):
self._scene.removeItem(self.labels[id_no])
self.labels[id_no] = self._scene.addPath(QtGui.QPainterPath())
@QtCore.pyqtSlot(int, int, int)
def draw_labels(self, id_no, x, y):
# this is removing the old pose data
self._scene.removeItem(self.labels[id_no])
path = QtGui.QPainterPath()
# then draw text
font = QtGui.QFont("Arial", 70)
font.setStyleStrategy(QtGui.QFont.ForceOutline)
# sadly there is no easy way to claculate the width of the text so minus 100 is fine, but not ideal
# also moving the text up by 500, so that is does not cover the face
path.addText(x - 100, y - 300, font, "ID " + str(id_no))
# by adding it gets converted into an QGraphicsPathItem
# save it for later removal
self.labels[id_no] = self._scene.addPath(path)
# set colors
color = tuple([int(a * 255) for a in self.colors[id_no]])
# alpha value is set to 70%
pen = QtGui.QPen(QtGui.QColor(color[0], color[1], color[2], int(0.9 * 255)), 10, QtCore.Qt.SolidLine)
self.labels[id_no].setPen(pen)
@QtCore.pyqtSlot()
def clear_pose(self):
# empty pose data
for id_no in range(self.number_ids):
self._scene.removeItem(self.pose_data[id_no])
self.pose_data[id_no] = self._scene.addPath(QtGui.QPainterPath())
@QtCore.pyqtSlot(int, list, list)
def draw_pose(self, id_no, lstX, lstY):
# this is removing the old pose data
self._scene.removeItem(self.pose_data[id_no])
if len(lstX) > 0 and len(lstY) > 0:
path = QtGui.QPainterPath()
# set starting points
path.moveTo(lstX[0], lstY[0])
# then draw remaing lines
for (x, y) in zip(lstX[1:], lstY[1:]):
path.lineTo(x, y)
# by adding it gets converted into an QGraphicsPathItem
# save it for later removal
self.pose_data[id_no] = self._scene.addPath(path)
# set colors
color = tuple([int(a * 255) for a in self.colors[id_no]])
# alpha value is set to 70%
pen = QtGui.QPen(QtGui.QColor(color[0], color[1], color[2], int(0.7 * 255)), 10, QtCore.Qt.SolidLine)
self.pose_data[id_no].setPen(pen)
else:
self.pose_data[id_no] = self._scene.addPath(QtGui.QPainterPath())
@QtCore.pyqtSlot()
def clear_gaze(self):
# empty pose data
for id_no in range(self.number_ids):
self._scene.removeItem(self.gaze_data[id_no])
self.gaze_data[id_no] = self._scene.addPath(QtGui.QPainterPath())
@QtCore.pyqtSlot(int, list, list)
def draw_gaze(self, id_no, lstX, lstY):
# this is removing the old pose data
self._scene.removeItem(self.gaze_data[id_no])
path = QtGui.QPainterPath()
path.setFillRule(Qt.Qt.WindingFill)
# set starting points
for (x, y) in zip(lstX, lstY):
path.addEllipse(x, y, 100, 100)
# by adding it gets converted into an QGraphicsPathItem
# save it for later removal
self.gaze_data[id_no] = self._scene.addPath(path)
# set colors
color = tuple([int(a * 255) for a in self.colors[id_no]])
# alpha value is set to 50%
pen = QtGui.QPen(QtGui.QColor(color[0], color[1], color[2], int(0.5 * 255)), 1, QtCore.Qt.SolidLine)
self.gaze_data[id_no].setPen(pen)
# fill ellipses
self.gaze_data[id_no].setBrush(QtGui.QColor(color[0], color[1], color[2], int(0.5 * 255)))
@QtCore.pyqtSlot(list)
def onSelectedID(self, lst):
self.clear_labels()
self.clear_gaze()
self.clear_pose()

147
uiwidget/widgetpose.py Normal file
View file

@ -0,0 +1,147 @@
import numpy as np
import pyqtgraph as pg
from PyQt5 import QtWidgets, QtGui, QtCore
class WidgetPose(QtWidgets.QWidget):
video_label_signal = QtCore.pyqtSignal(list)
def __init__(self, parent=None):
super(WidgetPose, self).__init__(parent)
layout = QtWidgets.QGridLayout()
self.poseGraph = QtWidgets.QWidget()
self.measures = QtWidgets.QWidget()
self.measuresLayout = QtWidgets.QHBoxLayout()
# Setup Movement Graph Plot Widget
self.movementGraph = pg.PlotWidget()
self.movementGraph.setBackground('w')
self.movementGraph.setYRange(0, 400, padding=0)
self.movementGraph.getPlotItem().getAxis('bottom').setTickSpacing(minor=50, major=100)
self.movementGraph.getPlotItem().setTitle(title='Body Movement over Time')
self.movementPlots = []
layout.addWidget(self.movementGraph, 1, 0, 1, 1)
layout.addWidget(self.measures, 1, 1, 1, 1)
layout.setColumnStretch(0, 1)
layout.setColumnStretch(1, 1)
self.setLayout(layout)
self.colors = None
self.labels = dict()
@QtCore.pyqtSlot(list)
def onSelectedID(self, lst):
"""Change color of movement graph plot if ID should not be visible"""
for i, button in enumerate(lst):
if not button.isChecked():
self.movementPlots[i].setPen(None)
elif self.colors is not None:
color = tuple([int(a * 255) for a in self.colors[i]])
pen = pg.mkPen(color=color)
self.movementPlots[i].setPen(pen)
@QtCore.pyqtSlot(dict, list, int)
def updateMovementGraph(self, data, colors, numberIDs):
"""Plot ID specific movement data from processing class
data[id: (movements, frames)]
"""
# handle NaN https://github.com/pyqtgraph/pyqtgraph/issues/1057
# downgrade to 5.13 fixes the issue
for id_no in range(numberIDs):
if data.get(id_no):
if not np.all(np.isnan(data.get(id_no)[0])):
self.movementPlots[id_no].setData(data.get(id_no)[1], data.get(id_no)[0]) # Update the data.
self.movementGraph.setXRange(np.min(data.get(id_no)[1]), np.max(data.get(id_no)[1]))
@QtCore.pyqtSlot(dict, int)
def updateHandVelocity(self, data, numberIDs):
"""Update Velocity Label
data[id: velocity for frame]"""
for id_no in range(numberIDs):
if data.get(id_no) is not None:
self.labels['Velocity'][id_no].setText('%.2f' % data[id_no])
else:
self.labels['Velocity'][id_no].setText(' ')
@QtCore.pyqtSlot(list, int)
def initMovementGraph(self, colors, numberIDs):
"""Initialize plot lines with 0
colors: plot color for each ID
"""
for i in range(numberIDs):
x = list(range(-200, 0)) # 100 time points
y = [0 for _ in range(200)] # 100 data points
color = tuple([int(a * 255) for a in colors[i]])
pen = pg.mkPen(color=color)
dataLine = self.movementGraph.plot(x, y, pen=pen)
self.movementPlots.append(dataLine)
@QtCore.pyqtSlot(dict, dict, list, int)
def setInit(self, mostActivity, hand, colors, numberIDs):
self.colors = colors
idLayout = QtWidgets.QVBoxLayout()
labelID = QtWidgets.QLabel(' ')
labelID.setFixedWidth(60)
labelID.setFixedHeight(20)
label = QtWidgets.QLabel('Most body activity in frame: ')
labelA = QtWidgets.QLabel('Hands above table (relative): ')
labelG = QtWidgets.QLabel('Gestures (relative): ')
labelVel = QtWidgets.QLabel('Hand velocity: ')
idLayout.addWidget(labelID)
idLayout.addWidget(label)
idLayout.addWidget(labelA)
idLayout.addWidget(labelG)
idLayout.addWidget(labelVel)
self.measuresLayout.insertLayout(-1, idLayout)
activityLabel = []
aboveLabel = []
gestureLabel = []
velLabel = []
for id_no in range(numberIDs):
idLayout = QtWidgets.QVBoxLayout()
[total, tracked, high_vel] = hand.get(id_no)
color = tuple([int(a * 255) for a in colors[id_no]])
labelID = QtWidgets.QLabel('ID%i' % id_no)
labelID.setStyleSheet('font: bold 12px; color: black; background-color: rgb(%i,%i,%i)' % color)
labelID.setFixedWidth(60)
labelID.setFixedHeight(20)
label = QtWidgets.QLabel('%i' % mostActivity.get(id_no))
activityLabel.append(label)
labelG = QtWidgets.QLabel('%.2f' % (high_vel / total))
gestureLabel.append(labelG)
labelA = QtWidgets.QLabel('%.2f' % (tracked / total))
aboveLabel.append(labelA)
labelVel = QtWidgets.QLabel(' ')
velLabel.append(labelVel)
idLayout.addWidget(labelID)
idLayout.addWidget(label)
idLayout.addWidget(labelA)
idLayout.addWidget(labelG)
idLayout.addWidget(labelVel)
self.measuresLayout.insertLayout(-1, idLayout)
# Velocity will be updated each frame, rest is updated in _updateLabels
self.labels['Velocity'] = velLabel
self.labels['Above'] = aboveLabel
self.labels['Gesture'] = gestureLabel
self.labels['Activity'] = activityLabel
self.measures.setLayout(self.measuresLayout)
@QtCore.pyqtSlot(dict, dict, int)
def updateLables(self, mostActivity, hand, numberIDs):
""" Update above hands, gestures and most activity labels when segment was changed"""
for id_no in range(numberIDs):
[total, tracked, high_vel] = hand[id_no]
self.labels['Activity'][id_no].setText('%i' % mostActivity[id_no])
self.labels['Above'][id_no].setText('%.2f' % (tracked / total))
self.labels['Gesture'][id_no].setText('%.2f' % (high_vel / total))

157
uiwidget/widgetspeaking.py Normal file
View file

@ -0,0 +1,157 @@
import numpy as np
import pyqtgraph as pg
from PyQt5 import QtWidgets, QtCore
from utils.util import get_circle
class WidgetSpeaking(QtWidgets.QWidget):
def __init__(self, parent=None):
super(WidgetSpeaking, self).__init__(parent)
layout = QtWidgets.QGridLayout()
self.measures = QtWidgets.QWidget()
self.measuresLayout = QtWidgets.QHBoxLayout()
# Setup Speaking Graph Plot Widget
self.speakingGraph = pg.PlotWidget()
self.speakingGraph.setBackground('w')
self.speakingGraph.hideAxis('left')
self.speakingGraph.hideAxis('bottom')
self.speakingGraph.setAspectLocked()
self.speakingGraph.setYRange(-2.25, 2.25, padding=0)
self.speakingGraph.setXRange(-2.25, 2.25, padding=0)
self.speakingGraph.getPlotItem().setTitle(title='Speaking Distribution')
self.speakingGraphPlots = []
layout.addWidget(self.speakingGraph, 1, 0, 1, 1)
layout.addWidget(self.measures, 1, 1, 1, 2)
layout.setColumnStretch(0, 1)
layout.setColumnStretch(1, 2)
self.setLayout(layout)
self.colors = None
self.labels = dict()
self.speakBlob = None
self.positions = dict()
self.idBlobs = dict()
@QtCore.pyqtSlot(dict, int)
def updateLables(self, speak, numberIDs):
""" Update labels when segment was changed"""
for id_no in range(numberIDs):
self.labels['RST'][id_no].setText('%.2f' % speak[id_no][1])
# self.labels['Turns'][id_no].setText('%i' % speak[id_no][2])
# self.labels['TurnLength'][id_no].setText('%.2f sec' % speak[id_no][3])
# self.labels['TurnPerMin'][id_no].setText('%.2f /min' % speak[id_no][4])
@QtCore.pyqtSlot(dict, list, int)
def setInit(self, speak, colors, numberIDs):
self.colors = colors
idLayout = QtWidgets.QVBoxLayout()
labelID = QtWidgets.QLabel(' ')
labelID.setFixedWidth(60)
labelID.setFixedHeight(20)
labelRST = QtWidgets.QLabel('Relative speaking time:')
labelTurn = QtWidgets.QLabel('Number of speaking turns:')
labelTurnLen = QtWidgets.QLabel('Average length of turn:')
labelTurnMin = QtWidgets.QLabel('Average number of turns:')
idLayout.addWidget(labelID)
idLayout.addWidget(labelRST)
idLayout.addWidget(labelTurn)
idLayout.addWidget(labelTurnLen)
idLayout.addWidget(labelTurnMin)
self.measuresLayout.insertLayout(-1, idLayout)
rstLabels = []
turnLabels = []
turnLenLabels = []
turnMinLabels = []
for id_no in range(numberIDs):
idLayout = QtWidgets.QVBoxLayout()
color = tuple([int(a * 255) for a in colors[id_no]])
labelID = QtWidgets.QLabel('ID%i' % id_no)
labelID.setFixedWidth(60)
labelID.setFixedHeight(20)
labelID.setStyleSheet('font: bold 12px; color: black; background-color: rgb(%i,%i,%i)' % color)
labelRST = QtWidgets.QLabel('%.2f' % speak[id_no][1])
rstLabels.append(labelRST)
labelTurn = QtWidgets.QLabel('%i' % speak[id_no][2])
turnLabels.append(labelTurn)
labelTurnLen = QtWidgets.QLabel('%.2f sec' % speak[id_no][3])
turnLenLabels.append(labelTurnLen)
labelTurnMin = QtWidgets.QLabel('%.2f /min' % speak[id_no][4])
turnMinLabels.append(labelTurnMin)
idLayout.addWidget(labelID)
idLayout.addWidget(labelRST)
idLayout.addWidget(labelTurn)
idLayout.addWidget(labelTurnLen)
idLayout.addWidget(labelTurnMin)
self.measuresLayout.insertLayout(-1, idLayout)
self.labels['RST'] = rstLabels
self.labels['Turns'] = turnLabels
self.labels['TurnLength'] = turnLenLabels
self.labels['TurnPerMin'] = turnMinLabels
self.measures.setLayout(self.measuresLayout)
@QtCore.pyqtSlot(list, int)
def initSpeakingGraph(self, colors, numberIDs):
""" initialize speaking graph """
self.speakBlob = pg.ScatterPlotItem([0], [0], size=0.3, pxMode=False,
pen=pg.mkPen(color=0.5, width=2), brush=pg.mkBrush(255, 255, 255))
self.speakBlob.setZValue(100)
self.speakingGraph.addItem(self.speakBlob)
pos = 360 // numberIDs
for id_no in range(numberIDs):
color = tuple([int(a * 255) for a in colors[id_no]])
idPos = pos * id_no
x = 2 * np.cos(np.deg2rad(idPos))
y = 2 * np.sin(np.deg2rad(idPos))
scatterPlot = pg.ScatterPlotItem([x], [y], size=0.2, pxMode=False,
pen=pg.mkPen(color=color, width=2), brush=pg.mkBrush(*color))
self.speakingGraph.addItem(scatterPlot)
self.positions[id_no] = [x, y]
self.idBlobs[id_no] = scatterPlot
plt = self.speakingGraph.plot(x=[], y=[], pen=pg.mkPen(color=color, width=1))
self.speakingGraphPlots.append(plt)
@QtCore.pyqtSlot(dict, int, int)
def updateSpeakingGraph(self, rst, active_speaker, numberIDs):
""" frame updates for gaze graph """
blobPos = np.array([0, 0])
for id_no in range(numberIDs):
diff = rst[id_no] - (1.0 / numberIDs)
if diff > 0:
blobPos = blobPos + 2 * diff * np.array(self.positions[id_no])
self.speakBlob.setData([blobPos[0]], [blobPos[1]])
for id_no in range(numberIDs):
color = tuple([int(a * 255) for a in self.colors[id_no]])
pen = pg.mkPen(color=color, width=(rst[id_no]*4))
x = [self.positions[id_no][0], blobPos[0]]
y = [self.positions[id_no][1], blobPos[1]]
self.speakingGraphPlots[id_no].setData(x, y)
self.speakingGraphPlots[id_no].setPen(pen)
if id_no == active_speaker:
self.idBlobs[id_no].setData([self.positions[id_no][0]], [self.positions[id_no][1]],
pen=pg.mkPen(color=(0, 0, 0), width=2))
else:
self.idBlobs[id_no].setData([self.positions[id_no][0]], [self.positions[id_no][1]],
pen=pg.mkPen(color=color, width=2))

15
uiwidget/widgetstatic.py Normal file
View file

@ -0,0 +1,15 @@
import numpy as np
import pyqtgraph
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QLabel
class WidgetStatic(QtWidgets.QWidget):
labels = list()
def __init__(self, parent=None):
super(WidgetStatic, self).__init__(parent)
self.frame = 0
layout = QtWidgets.QGridLayout()

451
uiwidget/widgettimeline.py Normal file
View file

@ -0,0 +1,451 @@
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5 import QtGui
from PyQt5.QtCore import QSize, QFileInfo, pyqtSlot
from PyQt5.QtGui import QIcon, QKeySequence
from PyQt5.QtMultimedia import QMediaPlayer
import sys
__all__ = ['QRangeSlider']
def scale(val, src, dst):
"""
Scale the given value from the scale of src to the scale of dst.
"""
return int(((val - src[0]) / float(src[1] - src[0])) * (dst[1] - dst[0]) + dst[0])
class WidgetTimeLine(QtWidgets.QWidget):
signalSetPosition = QtCore.pyqtSignal(int)
signalPlay = QtCore.pyqtSignal()
signalStop = QtCore.pyqtSignal()
signalSelectID = QtCore.pyqtSignal(list)
def __init__(self, parent=None):
super(WidgetTimeLine, self).__init__(parent)
self.root = QFileInfo(__file__).absolutePath()
self.setFixedHeight(110)
self.frame = 0
layout = QtWidgets.QGridLayout()
iconSize = QSize(28, 28)
self.playButton = QtWidgets.QToolButton()
self.playButton.setStyleSheet('border: none;')
self.playButton.setIcon(QIcon(self.root + '/../icons/play.png'))
self.playButton.setIconSize(iconSize)
self.playButton.setToolTip("Play movie")
self.playButton.clicked.connect(self.play)
# See https://stackoverflow.com/questions/50880660/how-to-change-space-bar-behaviour-in-pyqt5-python3
# self.shortcut_play = QtWidgets.QShortcut(QKeySequence(Qt.Key_Space), self)
# self.shortcut_play.activated.connect(self.play)
self.labelFrame = QtWidgets.QLabel('Frame\n')
self.labelFrame.setAlignment(QtCore.Qt.AlignRight)
self.labelFrame.setFixedWidth(40)
self.sl = QtWidgets.QSlider(QtCore.Qt.Horizontal)
# Directly connect: Slider value changed to player set position
self.sl.valueChanged.connect(self.signalSetPosition.emit)
self.rangeslider = QMultiRangeSlider()
self.rangeslider.setFixedHeight(30)
self.rangeslider.setMin(0)
self.rangeslider.setMax(200)
self.rangeslider.setRanges([(5, 25), (30, 50), (70, 90)])
# self.rangeslider.setBackgroundStyle(
# 'background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #222, stop:1 #333);')
# szlf.rangeslider.setHandleStyle(
# 'background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #289, stop:1 #289);')
layout.addWidget(self.rangeslider, 1, 1, 1, 6)
layout.addWidget(self.labelFrame, 1, 0, 2, 1)
layout.addWidget(self.sl, 2, 1, 1, 6)
layout.addWidget(self.playButton, 2, 0, 1, 1)
self.setLayout(layout)
self.number_ids = None
self.fps = None
def setInit(self, frames, number_ids, fps):
self.fps = fps
self.frame = 0
self.sl.setMinimum(0)
# Use position = (frame * 1000) / FPS for all slider and player values!
self.sl.setMaximum(int((frames * 1000) / self.fps))
self.sl.setValue(0)
self.sl.setTickPosition(QtWidgets.QSlider.TicksBelow)
self.sl.setTickInterval(5)
self.labelFrame.setText('Frame\n %i' % self.frame)
self.playButton.setEnabled(True)
self.number_ids = number_ids
self.rangeslider.setMin(0)
self.rangeslider.setMax(frames)
self.rangeslider.setRanges([(0, frames)])
@pyqtSlot(QMediaPlayer.State)
def mediaStateChanged(self, state):
if state == QMediaPlayer.PlayingState:
print('MediaState changed...')
self.playButton.setIcon(QIcon(self.root + '/../icons/pause.png'))
self.playButton.setToolTip('Pause movie')
else:
self.playButton.setIcon(QIcon(self.root + '/../icons/play.png'))
self.playButton.setToolTip('Play movie')
def play(self):
self.signalPlay.emit()
def stop(self):
self.signalStop.emit()
def updateSlider(self, position):
# Directly connect Player position changed to slider set value
frame = int((position / 1000.0) * self.fps)
self.frame = frame
# Disable the events to prevent updating triggering a setPosition event (can cause stuttering).
self.sl.blockSignals(True)
self.sl.setValue(position)
self.sl.blockSignals(False)
self.labelFrame.setText('Frame\n %i' % frame)
class QMultiRangeSlider(QtWidgets.QWidget):
minValueChanged = QtCore.pyqtSignal(int)
maxValueChanged = QtCore.pyqtSignal(int)
segmentsChanged = QtCore.pyqtSignal(list, int)
segments = []
last_update_segment = 0
def __init__(self, parent=None):
"""Create a new QRangeSlider instance.
:param parent: QWidget parent
:return: New QRangeSlider instance.
"""
super(QMultiRangeSlider, self).__init__(parent)
self.setMinimumSize(1, 30)
self.padding = 5
self.hande_width = 4
self.selected_segment = None
self.setMin(0)
self.setMax(99)
self.setRanges([(20, 30), [40, 90]])
def setMin(self, value):
"""sets minimum value"""
assert type(value) is int
setattr(self, '__min', value)
self.minValueChanged.emit(value)
self.segments = []
def setMax(self, value):
"""sets maximum value"""
assert type(value) is int
setattr(self, '__max', value)
self.maxValueChanged.emit(value)
self.segments = []
def initSegment(self):
self.segments = []
if (self.min() != None & self.max() != None):
self.setRanges([(self.min(), self.max())])
def keyPressEvent(self, event):
"""overrides key press event to move range left and right"""
key = event.key()
if key == QtCore.Qt.Key_Left:
s = self.start() - 1
e = self.end() - 1
elif key == QtCore.Qt.Key_Right:
s = self.start() + 1
e = self.end() + 1
else:
event.ignore()
return
event.accept()
if s >= self.parent().min() and e <= self.max():
self.setRange(s, e)
def min(self):
""":return: minimum value"""
return getattr(self, '__min', None)
def max(self):
""":return: maximum value"""
return getattr(self, '__max', None)
def _setStart(self, value):
"""stores the start value only"""
setattr(self, '__start', value)
self.startValueChanged.emit(value)
# self.segmentChanged.emit(value, self.end())
def setStart(self, values):
"""sets the range slider start value"""
# assert type(value) is int
for i, s in enumerate(self.segments):
s.setStart(values[i])
def setEnd(self, values):
"""set the range slider end value"""
# assert type(value) is int
for i, s in enumerate(self.segments):
s.setEnd(values[i])
def getRanges(self):
""":return: the start and end values as a tuple"""
ret = []
for i, s in enumerate(self.segments):
ret.append(s.getRange())
return ret
def setRanges(self, values):
"""set the start and end values"""
while len(self.segments) < len(values):
self.segments.append(QRangeSliderSegment(self))
while len(self.segments) > len(values):
self.segments.remove(-1)
for i, (s, e) in enumerate(values):
self.segments[i].setStart(s)
self.segments[i].setEnd(e)
self._trigger_refresh(0)
def mouseDoubleClickEvent(self, e):
self.selected_segment = None
d_width = self.width() - (self.padding * 2)
step_size = d_width / (self.max() - self.min())
pos = (e.x() - (self.padding)) / step_size
# removing existing segment
for (i, s) in enumerate(self.segments):
if (s.start() < pos) and (pos < s.end()):
# always keep one segment
if len(self.segments) > 1:
self.segments.remove(s)
# since one segement exists all the time the first can not be -1
if i == 0:
self._trigger_refresh(0)
else:
self._trigger_refresh(i - 1)
return
# if new segment in front
if (pos < self.segments[0].end()):
start = 0
end = self.segments[0].start()
diff = end - start
start += .25 * diff
end -= .25 * diff
start = int(start)
end = int(end)
seg = QRangeSliderSegment(self)
seg.setStart(start)
seg.setEnd(end)
self.segments.insert(0, seg)
self._trigger_refresh(0)
return
# if new segment in back
if (pos > self.segments[-1].end()):
start = self.segments[-1].end()
end = self.max()
diff = end - start
start += .25 * diff
end -= .25 * diff
start = int(start)
end = int(end)
seg = QRangeSliderSegment(self)
seg.setStart(start)
seg.setEnd(end)
self.segments.append(seg)
self._trigger_refresh(len(self.segments) - 1)
return
# if new segment in between
for i in range(len(self.segments) - 1):
if (self.segments[i].end() < pos) and (pos < self.segments[i + 1].start()):
start = self.segments[i].end()
end = self.segments[i + 1].start()
diff = end - start
start += .25 * diff
end -= .25 * diff
start = int(start)
end = int(end)
seg = QRangeSliderSegment(self)
seg.setStart(start)
seg.setEnd(end)
self.segments.insert(i + 1, seg)
self._trigger_refresh(i + 1)
return
def mousePressEvent(self, e):
# d_height = painter.device().height() - (self.padding * 2)
d_width = self.width() - (self.padding * 2)
step_size = d_width / (self.max() - self.min())
pos = (e.x() - (self.padding)) / step_size
distance = sys.maxsize
for i, s in enumerate(self.segments):
if (abs(s.start() - pos) < distance):
distance = abs(s.start() - pos)
if (distance * step_size < 10):
self.selected_segment = {"Seg": s, "Type": "start", "Index": i}
if (abs(s.end() - pos) < distance):
distance = abs(s.end() - pos)
if (distance * step_size < 10):
self.selected_segment = {"Seg": s, "Type": "end", "Index": i}
self._trigger_refresh(self.last_update_segment)
def mouseMoveEvent(self, e):
d_width = self.width() - (self.padding * 2)
step_size = d_width / (self.max() - self.min())
pos = int(round((e.x() - (self.padding)) / step_size))
if self.selected_segment is None:
return
s = self.selected_segment["Seg"]
if (self.selected_segment["Type"] == "start"):
if ((self.selected_segment["Index"] == 0) & (0 <= pos)):
s.setStart(pos)
elif (self.segments[self.selected_segment["Index"] - 1].end() < pos - 5) & (s.end() > pos + 5):
s.setStart(pos)
elif (self.selected_segment["Type"] == "end"):
if (self.selected_segment["Index"] == len(self.segments) - 1) & (pos <= self.max()):
s.setEnd(pos)
elif self.selected_segment["Index"] + 1 < len(self.segments) and (self.segments[self.selected_segment["Index"] + 1].start() > pos + 5) and (s.start() < pos - 5):
s.setEnd(pos)
self._trigger_refresh(self.selected_segment["Index"])
def mouseReleaseEvent(self, e):
self.selected_segment = None
def _trigger_refresh(self, last_update_segment):
self.last_update_segment = last_update_segment
self.update()
self.segmentsChanged.emit(self.getRanges(), last_update_segment)
def paintEvent(self, e):
painter = QtGui.QPainter(self)
brush = QtGui.QBrush()
brush.setColor(QtGui.QColor(53, 53, 53))
brush.setStyle(QtCore.Qt.SolidPattern)
rect = QtCore.QRect(0, 0, painter.device().width(), painter.device().height())
painter.fillRect(rect, brush)
# Define our canvas.
d_height = painter.device().height() - (self.padding * 2)
d_width = painter.device().width() - (self.padding * 2)
step_size = d_width / (self.max() - self.min())
for s in self.segments:
brush.setColor(QtGui.QColor('white'))
rect = QtCore.QRect(
self.padding + s.start() * step_size,
self.padding * 2,
(s.end() - s.start()) * step_size,
d_height - 2 * self.padding
)
painter.fillRect(rect, brush)
brush.setColor(QtGui.QColor('red'))
rect = QtCore.QRect(
self.padding + s.end() * step_size - (self.hande_width / 2),
self.padding,
(self.hande_width / 2),
d_height
)
painter.fillRect(rect, brush)
brush.setColor(QtGui.QColor('green'))
rect = QtCore.QRect(
self.padding + s.start() * step_size - (self.hande_width / 2),
self.padding,
(self.hande_width / 2),
d_height
)
painter.fillRect(rect, brush)
class QRangeSliderSegment(QtWidgets.QWidget):
# signals
# startValueChanged = QtCore.pyqtSignal(int)
# endValueChanged = QtCore.pyqtSignal(int)
# segmentChanged = QtCore.pyqtSignal(int, int)
def __init__(self, parent=None):
"""Create a new QRangeSlider instance.
:param parent: QWidget parent
:return: New QRangeSlider instance.
"""
super(QRangeSliderSegment, self).__init__(parent)
def start(self):
""":return: range slider start value"""
return getattr(self, '__start', None)
def end(self):
""":return: range slider end value"""
return getattr(self, '__end', None)
def getRange(self):
""":return: the start and end values as a tuple"""
return (self.start(), self.end())
def setRange(self, start, end):
"""set the start and end values"""
self.setStart(start)
self.setEnd(end)
def _setEnd(self, value):
"""stores the end value only"""
setattr(self, '__end', value)
# self.endValueChanged.emit(value)
# self.segmentChanged.emit(self.start(), value)
def setEnd(self, value):
"""set the range slider end value"""
assert type(value) is int
self._setEnd(value)
def _setStart(self, value):
"""stores the start value only"""
setattr(self, '__start', value)
# self.startValueChanged.emit(value)
# self.segmentChanged.emit(value, self.end())
def setStart(self, value):
"""sets the range slider start value"""
assert type(value) is int
self._setStart(value)

View file

@ -0,0 +1,63 @@
from PyQt5 import QtWidgets, QtCore, QtGui
class WidgetVideoSettings(QtWidgets.QWidget):
signalVisualize = QtCore.pyqtSignal(dict)
signalSelectID = QtCore.pyqtSignal(list)
def __init__(self, parent=None):
super(WidgetVideoSettings, self).__init__(parent)
self.setFixedWidth(200)
layout = QtWidgets.QGridLayout()
iconSize = QtCore.QSize(28, 28)
self.checkBoxes = QtWidgets.QWidget()
self.checkBoxesLayout = QtWidgets.QVBoxLayout()
label = QtWidgets.QLabel('Visualize')
label.setFixedWidth(100)
self.checkBoxes.setLayout(self.checkBoxesLayout)
self.checkBoxesList = dict()
for data in ['Pose', 'Gaze', 'Label', 'Tags']:
box = QtWidgets.QCheckBox(data)
box.setChecked(True)
box.stateChanged.connect(lambda: self.boxstate(box))
self.checkBoxesLayout.addWidget(box)
self.checkBoxesList[data] = box
self.idButtons = QtWidgets.QWidget()
self.buttonsLayout = QtWidgets.QVBoxLayout()
labelID = QtWidgets.QLabel('Showing')
labelID.setFixedWidth(100)
self.idButtons.setLayout(self.buttonsLayout)
self.buttonsList = []
layout.addWidget(label, 0, 0)
layout.addWidget(labelID, 0, 1)
layout.addWidget(self.checkBoxes, 1, 0)
layout.addWidget(self.idButtons, 1, 1)
self.setLayout(layout)
@QtCore.pyqtSlot(list, int)
def setInit(self, colors, numberIDs):
# Emit initial state
self.signalVisualize.emit(self.checkBoxesList)
for id_no in range(numberIDs):
button = QtWidgets.QCheckBox("ID%i" % id_no)
button.setChecked(True)
button.stateChanged.connect(lambda: self.btnstate(button))
self.buttonsLayout.addWidget(button)
self.buttonsList.append(button)
def btnstate(self, b):
self.signalSelectID.emit(self.buttonsList)
def boxstate(self, b):
self.signalVisualize.emit(self.checkBoxesList)