diff options
Diffstat (limited to 'gr-qtgui/python/qtgui/dialgauge.py')
-rw-r--r-- | gr-qtgui/python/qtgui/dialgauge.py | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/gr-qtgui/python/qtgui/dialgauge.py b/gr-qtgui/python/qtgui/dialgauge.py new file mode 100644 index 0000000000..af96f2e096 --- /dev/null +++ b/gr-qtgui/python/qtgui/dialgauge.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2020 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# + +import sys +from PyQt5.QtWidgets import QFrame, QHBoxLayout, QVBoxLayout, QLabel +from PyQt5.QtGui import QPainter, QColor, QPen, QFont, QFontMetricsF +from PyQt5 import QtCore +from PyQt5.QtCore import Qt as Qtc + +from gnuradio import gr +import pmt + +class LabeledDialGauge(QFrame): + # Positions: 1 = above, 2=below, 3=left, 4=right + def __init__(self, lbl='', barColor='blue', backgroundColor='white', fontColor='black', + minValue=0, maxValue=100, maxSize=80, position=1, + isFloat=False, showValue=False, fixedOrMin=True, parent=None): + QFrame.__init__(self, parent) + self.numberControl = DialGauge(barColor, backgroundColor, fontColor, minValue, + maxValue, maxSize, isFloat, showValue, fixedOrMin, parent) + + if position < 3: + layout = QVBoxLayout() + else: + layout = QHBoxLayout() + + self.lbl = lbl + self.showvalue = showValue + self.isFloat = isFloat + + self.lblcontrol = QLabel(lbl, self) + self.lblcontrol.setAlignment(Qtc.AlignCenter) + + # For whatever reason, the progressbar doesn't show the number in the bar if it's + # vertical, only if it's horizontal + if len: + self.lblcontrol.setText(lbl) + + if fontColor != 'default': + self.lblcontrol.setStyleSheet("QLabel { color : " + fontColor + "; }") + + # add top or left + if len: + if position == 1 or position == 3: + layout.addWidget(self.lblcontrol) + else: + self.hasLabel = False + + layout.addWidget(self.numberControl) + + # Add bottom or right + if len: + if position == 2 or position == 4: + layout.addWidget(self.lblcontrol) + + layout.setAlignment(Qtc.AlignCenter | Qtc.AlignVCenter) + self.setLayout(layout) + + self.show() + + def setValue(self, new_value): + self.numberControl.setValue(new_value) + +class DialGauge(QFrame): + def __init__(self, barColor='blue', backgroundColor='white', fontColor='black', + minValue=0, maxValue=100, maxSize=80, + isFloat=False, showValue=False, fixedOrMin=True, parent=None): + QFrame.__init__(self, parent) + + self.maxSize = maxSize + super().setMinimumSize(maxSize, maxSize) + if fixedOrMin: + super().setMaximumSize(maxSize, maxSize) + + self.backgroundColor = backgroundColor + self.barColor = barColor + self.fontColor = fontColor + self.isFloat = isFloat + self.showValue = showValue + + self.value = minValue + + self.minValue = minValue + self.maxValue = maxValue + + self.textfont = QFont(self.font()) + self.textfont.setPixelSize(16) + self.metrics = QFontMetricsF(self.textfont) + + self.startAngle = 0.0 + self.endAngle = 360.0 + self.degScaler = 16.0 # The span angle must be specified in 1/16 of a degree units + self.penWidth = max(int(0.1 * maxSize), 6) + self.halfPenWidth = int(self.penWidth / 2) + + def getValue(self): + if self.isFloat: + return float(self.value) + else: + return int(self.value) + + def setValue(self, new_value): + if new_value > self.maxValue: + new_value = self.maxValue + elif new_value < self.minValue: + new_value = self.minValue + + self.value = float(new_value) + + super().update() + + def paintEvent(self, event): + super().paintEvent(event) + + size = self.size() + + percentRange = float(self.value - self.minValue) / float(self.maxValue - self.minValue) + endAngle = self.startAngle + round(percentRange * float(self.endAngle - self.startAngle), 0) + + # Now convert angles to 1/16 scale + startAngle = int(round(self.startAngle * self.degScaler, 0)) + endAngle = int(round(endAngle * self.degScaler, 0)) + + rect = QtCore.QRect(self.halfPenWidth, self.halfPenWidth, size.width()-self.penWidth, + size.height()-self.penWidth) + + # Set up the painting canvass + painter = QPainter() + painter.begin(self) + painter.setRenderHint(QPainter.Antialiasing) + + if self.showValue: + painter.setFont(self.textfont) + painter.setPen(QPen(QColor(self.fontColor))) + + if self.isFloat: + printText = "%.2f" % self.value + else: + printText = str(int(self.value)) + + painter.drawText(size.width()/2-self.metrics.width(printText)/2, size.height()/2, + printText) + + painter.save() + painter.translate(self.width(), 0) + painter.rotate(90.0) + + # First draw complete circle + painter.setPen(QPen(QColor(self.backgroundColor), self.penWidth)) + painter.drawArc(rect, startAngle, self.endAngle*self.degScaler) + # First draw complete circle + painter.setPen(QPen(QColor(self.barColor), self.penWidth)) + painter.drawArc(rect, startAngle, -endAngle) + painter.setPen(QPen(QColor('darkgray'), 2)) + painter.drawEllipse(1, 1, rect.width()+self.penWidth-2, rect.width()+self.penWidth-2) + painter.drawEllipse(1+self.penWidth, 1+self.penWidth, rect.width()-self.penWidth-2, + rect.width()-self.penWidth-2) + painter.restore() + + painter.end() + +class GrDialGauge(gr.sync_block, LabeledDialGauge): + """ + This block creates a dial-style gauge. The value can be set + either with a variable or an input message. + """ + def __init__(self, lbl='', barColor='blue', backgroundColor='white', fontColor='black', + minValue=0, maxValue=100, maxSize=80, + position=1, isFloat=False, showValue=False, fixedOrMin=True, parent=None): + gr.sync_block.__init__(self, name="DialGauge", in_sig=None, out_sig=None) + LabeledDialGauge.__init__(self, lbl, barColor, backgroundColor, fontColor, minValue, + maxValue, maxSize, position, isFloat, showValue, fixedOrMin, + parent) + self.lbl = lbl + + if minValue > maxValue: + gr.log.error("Min value is greater than max value.") + sys.exit(1) + + self.message_port_register_in(pmt.intern("value")) + self.set_msg_handler(pmt.intern("value"), self.msgHandler) + + + def msgHandler(self, msg): + try: + new_val = pmt.to_python(pmt.cdr(msg)) + + if type(new_val) == float or type(new_val) == int: + super().setValue(new_val) + else: + gr.log.error("Value received was not an int or a float. " + "Received %s" % str(type(new_val))) + + except Exception as e: + gr.log.error("Error with message conversion: %s" % str(e)) + + + def setValue(self, new_value): + super().setValue(new_value) + |