#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright 2011-2013, Marten de Vries
#
# This file is part of OpenTeacher.
#
# OpenTeacher is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# OpenTeacher is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with OpenTeacher. If not, see <http://www.gnu.org/licenses/>.
import datetime
import weakref
def installQtClasses():
global Graph, ProgressViewer
class Graph(QtWidgets.QFrame):
def __init__(self, test, *args, **kwargs):
"""Raises KeyError if 'test' doesn't contain time info."""
super().__init__(*args, **kwargs)
self._test = test
self.setSizePolicy(
QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.MinimumExpanding
)
self.setFrameStyle(QtWidgets.QFrame.StyledPanel)
self.setFrameShadow(QtWidgets.QFrame.Sunken)
self.start = self._test["results"][0]["active"]["start"]
self.end = self._test["results"][-1]["active"]["end"]
self._totalSeconds = (self.end - self.start).total_seconds()
@property
def _amountOfUniqueItems(self):
ids = set()
for result in self._test["results"]:
ids.add(result["itemId"])
return len(ids)
def event(self, event, *args, **kwargs):
if event.type() == QtCore.QEvent.ToolTip:
second = event.x() / self._secondsPerPixel
moment = self.start + datetime.timedelta(seconds=second)
for pause in self._test["pauses"]:
if pause["start"] < moment and pause["end"] > moment:
text = _("Pause")
break
try:
text
except NameError:
for result in self._test["results"]:
if result["active"]["start"] < moment and result["active"]["end"] > moment:
text = _("Thinking")
try:
text
except NameError:
text = _("Answering")
QtWidgets.QToolTip.showText(
event.globalPos(),
text,
)
return True
return super().event(event, *args, **kwargs)
def _paintItem(self, p, item):
x = (item["start"] - self.start).total_seconds() * self._secondsPerPixel
width = (item["end"] - item["start"]).total_seconds() * self._secondsPerPixel
p.drawRect(x, 0, width, self._h)
def paintEvent(self, event, *args, **kwargs):
p = QtGui.QPainter()
p.begin(self)
p.setPen(QtCore.Qt.NoPen)
w = self.width()
self._h = self.height()
try:
#float, because one pixel might be more than one second.
#When int was used, secondsPerPixel could become 0 showing
#nothing in that case.
self._secondsPerPixel = float(w) / self._totalSeconds
except ZeroDivisionError:
self._secondsPerPixel = 0
colors = {}
baseColor = self.palette().highlight().color()
steps = 0
colorDifference = (255 - baseColor.lightness()) / (self._amountOfUniqueItems +1)#+1 so it doesn't become 0
for result in self._test["results"]:
try:
p.setBrush(QtGui.QBrush(colors[result["itemId"]]))
except KeyError:
color = QtGui.QColor(baseColor)
hsl = list(color.getHsl())
hsl[2] = hsl[2] + colorDifference * steps
color.setHsl(*hsl)
steps += 1
colors[result["itemId"]] = QtGui.QBrush(color)
p.setBrush(colors[result["itemId"]])
if "active" in result:
self._paintItem(p, result["active"])
p.setBrush(self.palette().dark())
for pause in self._test.get("pauses", []):
self._paintItem(p, pause)
p.setBrush(QtGui.QBrush())
p.end()
super().paintEvent(event, *args, **kwargs)
def sizeHint(self):
return QtCore.QSize(200, 30)
class ProgressViewer(QtWidgets.QWidget):
def __init__(self, test, *args, **kwargs):
"""Raises KeyError if 'test' doesn't contain time info."""
super().__init__(*args, **kwargs)
self.graph = Graph(test)
format = "%X"
firstTime = QtWidgets.QLabel(self.graph.start.strftime(format))
lastTime = QtWidgets.QLabel(self.graph.end.strftime(format))
horLayout = QtWidgets.QHBoxLayout()
horLayout.addWidget(firstTime)
horLayout.addStretch()
horLayout.addWidget(lastTime)
mainLayout = QtWidgets.QVBoxLayout()
mainLayout.addLayout(horLayout)
mainLayout.addWidget(self.graph)
self.setLayout(mainLayout)
class ProgressViewerModule:
def __init__(self, moduleManager, *args, **kwargs):
super().__init__(*args, **kwargs)
self._mm = moduleManager
self.type = "progressViewer"
self.requires = (
self._mm.mods(type="ui"),
)
self.filesWithTranslations = ("progressViewer.py",)
def createProgressViewer(self, *args, **kwargs):
pv = ProgressViewer(*args, **kwargs)
self._progressViewers.add(weakref.ref(pv))
return pv
def enable(self):
global QtCore, QtGui, QtWidgets
try:
from PyQt5 import QtCore, QtGui, QtWidgets
except ImportError:
return
installQtClasses()
self._modules = set(self._mm.mods(type="modules")).pop()
self._progressViewers = set()
try:
translator = self._modules.default("active", type="translator")
except IndexError:
pass
else:
translator.languageChanged.handle(self._retranslate)
self._retranslate()
self.active = True
def _retranslate(self):
#Translations
global _
global ngettext
try:
translator = self._modules.default("active", type="translator")
except IndexError:
_, ngettext = str, lambda a, b, n: a if n == 1 else b
else:
_, ngettext = translator.gettextFunctions(
self._mm.resourcePath("translations")
)
def disable(self):
self.active = False
del self._modules
del self._progressViewers
def init(moduleManager):
return ProgressViewerModule(moduleManager)