Example #1
0
	def __init__(self, topic='/HUD', length=300.0, end_angle=300.0, min=0.0, max=100.0, main_points=11,
			warning=[], danger=[], multiplier='', units='', description=''):
		super(GaugeSimple, self).__init__()

		self.setFixedSize(300, 300)
		self.setWindowTitle('A Magnificent Gauge')
		self.setAutoFillBackground(True)
		self.redraw_interval = 40
		self.update_plot_timer = QtCore.QTimer()
		self.update_plot_timer.timeout.connect(self.update_plot)
		self._start_time = rospy.get_time()
		self._rosdata = ROSData(topic, self._start_time)

		self.min = min
		self.curr_value = min
		self.value_uncap = 0
		self.max = max
		self.length = length
		self.main_points = main_points
		self.start_angle = (end_angle + length) % 360
		self.end_angle = end_angle % 360
		self.is_circle = self.start_angle == self.end_angle

		self.gauge_ticks = []
		self.bounding_rect = QtCore.QRectF(25.0, 25.0, 250.0, 250.0)
		self.center = QtCore.QPointF(150.0, 150.0)
		self.warning = warning #zones
		self.danger = danger #zones
		self.center_radius = 5.0
		self.margin = 12
		self.units = units
		self.multiplier = multiplier
		self.description = description

		self.update_plot_timer.start(self.redraw_interval)

		#Various ui colors
		self.marker_tick_color = QtGui.QPen(QtGui.QColor('#FF9900'), 1.8)
		self.ui_color = QtGui.QPen(QtCore.Qt.green, 2.5)
		self.ui_color_tick = QtGui.QPen(QtCore.Qt.green, 1.5)
		self.gauge_color = QtGui.QPen(QtCore.Qt.lightGray, 2)
		self.warning_color = QtGui.QPen(QtCore.Qt.yellow, 2)
		self.warning_bg = QtGui.QRadialGradient(self.center, self.width()/3)
		self.warning_bg.setColorAt(0.0, QtCore.Qt.yellow)
		self.warning_bg.setColorAt(1.0, QtCore.Qt.black)
		self.danger_color = QtGui.QPen(QtCore.Qt.red, 2)
		self.danger_bg = QtGui.QRadialGradient(self.center, self.width()/3)
		self.danger_bg.setColorAt(0.0, QtCore.Qt.red)
		self.danger_bg.setColorAt(1.0, QtCore.Qt.black)

		self.current_bg = QtCore.Qt.black

		self.create_gauge()
Example #2
0
    def add_topic(self, topic_name):
        if topic_name in self._rosdata:
            qWarning('PlotWidget.add_topic(): topic already subscribed: %s' % topic_name)
            return

        self._rosdata[topic_name] = ROSData(topic_name, self._start_time)
        if self._rosdata[topic_name].error is not None:
            qWarning(str(self._rosdata[topic_name].error))
            del self._rosdata[topic_name]
        else:
            data_x, data_y = self._rosdata[topic_name].next()
            self.data_plot.add_curve(topic_name, topic_name, data_x, data_y)

            self._subscribed_topics_changed()
Example #3
0
    def add_topic(self, topic_name):
        topics_changed = False
        for topic_name in get_plot_fields(self._node, topic_name)[0]:
            if topic_name in self._rosdata:
                qWarning(
                    'PlotWidget.add_topic(): topic already subscribed: %s' %
                    topic_name)
                continue
            self._rosdata[topic_name] = ROSData(self._node, topic_name,
                                                self._start_time)
            if self._rosdata[topic_name].error is not None:
                qWarning(str(self._rosdata[topic_name].error))
                del self._rosdata[topic_name]
            else:
                data_x, data_y = self._rosdata[topic_name].next()
                self.data_plot.add_curve(topic_name, topic_name, data_x,
                                         data_y)
                topics_changed = True

        if topics_changed:
            self._subscribed_topics_changed()
Example #4
0
class GaugeSimple(QtGui.QWidget):
	''' Gauge pointer movement:
	minimum->maximum values: clockwise rotation
	maximum value > minimum-value
	'''
	marker_set = QtCore.pyqtSignal(list)

	def __init__(self, topic='/HUD', length=300.0, end_angle=300.0, min=0.0, max=100.0, main_points=11,
			warning=[], danger=[], multiplier='', units='', description=''):
		super(GaugeSimple, self).__init__()

		self.setFixedSize(300, 300)
		self.setWindowTitle('A Magnificent Gauge')
		self.setAutoFillBackground(True)
		self.redraw_interval = 40
		self.update_plot_timer = QtCore.QTimer()
		self.update_plot_timer.timeout.connect(self.update_plot)
		self._start_time = rospy.get_time()
		self._rosdata = ROSData(topic, self._start_time)

		self.min = min
		self.curr_value = min
		self.value_uncap = 0
		self.max = max
		self.length = length
		self.main_points = main_points
		self.start_angle = (end_angle + length) % 360
		self.end_angle = end_angle % 360
		self.is_circle = self.start_angle == self.end_angle

		self.gauge_ticks = []
		self.bounding_rect = QtCore.QRectF(25.0, 25.0, 250.0, 250.0)
		self.center = QtCore.QPointF(150.0, 150.0)
		self.warning = warning #zones
		self.danger = danger #zones
		self.center_radius = 5.0
		self.margin = 12
		self.units = units
		self.multiplier = multiplier
		self.description = description

		self.update_plot_timer.start(self.redraw_interval)

		#Various ui colors
		self.marker_tick_color = QtGui.QPen(QtGui.QColor('#FF9900'), 1.8)
		self.ui_color = QtGui.QPen(QtCore.Qt.green, 2.5)
		self.ui_color_tick = QtGui.QPen(QtCore.Qt.green, 1.5)
		self.gauge_color = QtGui.QPen(QtCore.Qt.lightGray, 2)
		self.warning_color = QtGui.QPen(QtCore.Qt.yellow, 2)
		self.warning_bg = QtGui.QRadialGradient(self.center, self.width()/3)
		self.warning_bg.setColorAt(0.0, QtCore.Qt.yellow)
		self.warning_bg.setColorAt(1.0, QtCore.Qt.black)
		self.danger_color = QtGui.QPen(QtCore.Qt.red, 2)
		self.danger_bg = QtGui.QRadialGradient(self.center, self.width()/3)
		self.danger_bg.setColorAt(0.0, QtCore.Qt.red)
		self.danger_bg.setColorAt(1.0, QtCore.Qt.black)

		self.current_bg = QtCore.Qt.black

		self.create_gauge()

	def detect_safe_zones(self):
		r = [(self.min, self.max)]
		unsafe = sorted(self.warning+self.danger, key=lambda i:i[0])

		for i in unsafe:
			temp = []
			for y in r:
				if i[0] > y[1] or i[1] < y[0]:
					temp.append(y)
				elif i[0]==y[0] and i[1]==y[1]:
					continue
				elif i[0]>y[0] and i[1]<y[1]:
					temp.append((y[0], i[0]))
					temp.append((i[1], y[1]))
				elif i[0]>y[0] and i[1]==y[1]:
					temp.append((i[0], i[1]))
				elif i[0]==y[0] and i[1]<y[1]:
					temp.append((i[1], y[1]))
			r = temp
		return r

	def create_gauge(self):
		def text_width(text):
			font = QtGui.QFont()
			metrics = QtGui.QFontMetrics(font)
			return metrics.width(text)

		#Main points
		divisor = self.main_points
		if self.start_angle != self.end_angle:
			divisor -= 1

		angle_step = self.length/divisor
		value_step = abs(self.max-self.min)/divisor

		#Gauge main line(the circular path)
		#Safe zones
		zones = map(self.val2deg_tuple, self.detect_safe_zones())
		self.gauge_safe = []
		for zone in zones:
			path = QtGui.QPainterPath()
			path.arcMoveTo(self.bounding_rect, self.start_angle-zone[0])
			path.arcTo(self.bounding_rect, self.start_angle-zone[0], -(zone[1]-zone[0]))
			self.gauge_safe.append(path)
		#Warning zones
		warning_zones =  map(self.val2deg_tuple, self.warning)
		self.gauge_warning = []
		for w in warning_zones:
			path = QtGui.QPainterPath()
			path.arcMoveTo(self.bounding_rect, self.start_angle-w[0])
			path.arcTo(self.bounding_rect, self.start_angle-w[0], -(w[1]-w[0]))
			self.gauge_warning.append(path)
		#Danger zones
		danger_zones =  map(self.val2deg_tuple, self.danger)
		self.gauge_danger = []
		for d in danger_zones:
			path = QtGui.QPainterPath()
			path.arcMoveTo(self.bounding_rect, self.start_angle-d[0])
			path.arcTo(self.bounding_rect, self.start_angle-d[0], -(d[1]-d[0]))
			self.gauge_danger.append(path)

		#Initial gauge position
		self.set_gauge(self.curr_value)

		for i in xrange(self.main_points):
			#Find the point on the curve
			angle = self.start_angle -i*angle_step
			value = self.min + i*value_step
			p = QtGui.QPainterPath()
			p.arcMoveTo(self.bounding_rect, angle)
			x, y = p.currentPosition().x(), p.currentPosition().y()
			x_new = x*0.9 + self.center.x()*0.1
			y_new = y*0.9 + self.center.y()*0.1

			x_text = x*0.8 + self.center.x()*0.2 - (text_width(str(round(value, 1)))-10)/2
			y_text = y*0.8 + self.center.y()*0.2 + 4

			tick_path = QtGui.QPainterPath()
			tick_path.moveTo(x_new, y_new)
			tick_path.lineTo(x, y)


			#Store the tick_length for the marker area
			self.gauge_ticks.append([QtCore.QPointF(x_text, y_text), value, tick_path])

			#Store the tick_length for the marker area
		self.tick_length = math.sqrt((x-x_new)**2+(y-y_new)**2)

	def val2deg(self, value):
		return self.length*((value-self.min)/abs(self.max-self.min))

	def val2deg_tuple(self, t):
		return map(self.val2deg, t)

	def deg2val(self, degrees):
		#Convert the given degress relative to the start_angle to the respective value
		return abs(self.max-self.min)*(degrees/self.length)+self.min

	def mouseReleaseEvent(self, e):
		self.mouseMoveEvent(e)

	def mouseMoveEvent(self, e):
		#marker_line and marker_value dont exist before the first call of this function
		click_pos = e.posF()

		x_coeff = (click_pos.x() - self.center.x())**2
		y_coeff = (click_pos.y() - self.center.y())**2
		dist = math.sqrt(x_coeff + y_coeff)

		w = self.bounding_rect.width()/2

		if w - self.tick_length <= dist <= w:
			#Find the angle between the start angle and the click point
			angle = self.angle_from_zero(self.center, click_pos, self.start_angle)
			#Return if the user clicked outside of the allowed range
			if self.deg2val(angle) > self.max or self.deg2val(angle) < self.min:
				return

			self.set_marker(self.deg2val(angle))

	def angle_from_zero(self, p1, p2, offset):
		angle = math.degrees(math.atan2(p1.y()-p2.y(), p2.x()-p1.x()))

		if angle < 0:
			angle += 360
		angle = offset - angle
		if angle < 0:
			angle += 360

		return angle

	def set_marker(self, value):
		#New values: marker_point
		#Emit the new value
		self.marker_value = max(min(value, self.max), self.min)
		self.marker_set.emit([self.description, self.marker_value])
		#Round it for presentation purposes
		self.marker_value = round(self.marker_value, 2)

		#Create the marker line
		p = QtGui.QPainterPath()
		p.arcMoveTo(self.bounding_rect, self.start_angle - self.val2deg(value))
		self.marker_point = p.currentPosition()
		self.draw_marker(y=3)

	def compute_marker_rotation(self):
		#Marker_point is already set and ready for use
		return self.angle_from_zero(self.center, self.marker_point, 90)

	def draw_marker(self, x=0, y=0, size=10):
		poly = QtGui.QPolygonF()
		poly.append(QtCore.QPointF(x-size, y))
		poly.append(QtCore.QPointF(x+size, y))
		poly.append(QtCore.QPointF(x+size, y-size))
		poly.append(QtCore.QPointF(x, y))
		poly.append(QtCore.QPointF(x-size, y-size))
		poly.append(QtCore.QPointF(x-size, y))

		self.marker_line = QtGui.QPainterPath()
		self.marker_line.addPolygon(poly)

		self.update()

	def update_plot(self):
		dump, value = self._rosdata.next()
		self.value_uncap = round(value.pop(),1)
		#print value
		if len(value)>0:
			self.set_gauge(self.value_uncap)
		self.update_plot_timer.start(self.redraw_interval)

	def set_gauge(self, value):
		#Clamp between [min, max]
		self.curr_value = round(max(min(value, self.max), self.min),1)

		p = QtGui.QPainterPath()
		p.arcMoveTo(self.bounding_rect, self.start_angle-self.val2deg(self.curr_value))
		x, y = p.currentPosition().x(), p.currentPosition().y()

		self.gauge_line = QtGui.QPainterPath()
		self.gauge_line.moveTo(self.center)
		self.gauge_line.lineTo(x, y)
		self.update()

	def increment_gauge(self, step):
		#Clamp between (min, max)
		self.curr_value = max(min(self.curr_value + step, self.max), self.min)

		p = QtGui.QPainterPath()
		p.arcMoveTo(self.bounding_rect, self.start_angle-self.val2deg(self.curr_value))
		x, y = p.currentPosition().x(), p.currentPosition().y()

		self.gauge_line = QtGui.QPainterPath()
		self.gauge_line.moveTo(self.center)
		self.gauge_line.lineTo(x, y)
		self.update()

	def set_bg_color(self):
		#Determine the zone that the gauge arrow is inside
		#Is it in a warning zone?
		for w in self.warning:
			if w[0] <= self.curr_value <= w[1]:
				self.current_bg = self.warning_bg
				return
		#Or a danger zone?
		for d in self.danger:
			if d[0] <= self.curr_value <= d[1]:
				self.current_bg = self.danger_bg
				return
		#Don't be scared, you're safe!
		self.current_bg = QtCore.Qt.black

	def paintEvent(self, event):
		def center_text(text):
			rect = painter.boundingRect(self.bounding_rect, QtCore.Qt.AlignHCenter, text)
			return rect.width()/2

		painter = QtGui.QPainter(self)
		painter.setRenderHint(QtGui.QPainter.Antialiasing)

		#Draw the background
		self.set_bg_color()
		painter.fillRect(event.rect(), self.current_bg)

		painter.setBrush(QtCore.Qt.transparent)
		painter.setPen(self.gauge_color)
		painter.drawPath(self.gauge_line)

		#Draw the safe zones
		painter.setPen(self.ui_color)
		for s in self.gauge_safe:
			painter.drawPath(s)
		#Draw the warning zones
		painter.setPen(self.warning_color)
		for w in self.gauge_warning:
			painter.drawPath(w)
		#Draw the danger zones
		painter.setPen(self.danger_color)
		for d in self.gauge_danger:
			painter.drawPath(d)

		#Draw the marker line
		if getattr(self, 'marker_line', None):
			painter.setPen(self.marker_tick_color)
			painter.translate(self.marker_point)
			painter.rotate(self.compute_marker_rotation())
			painter.drawPath(self.marker_line)
			painter.resetTransform()

		#Draw the center circle
		painter.setPen(self.ui_color)
		painter.drawEllipse(self.center.x()-self.center_radius/2, self.center.y()-self.center_radius/2, self.center_radius, self.center_radius)

		#Draw the paths
		painter.setPen(self.ui_color_tick)
		for i, path in enumerate(self.gauge_ticks):
			if not (self.is_circle and i == (len(self.gauge_ticks))):
				painter.drawText(path[0], str(int(path[1])))
				painter.drawPath(path[2])

		#Draw the text labels
		painter.drawText(QtCore.QPointF(self.center.x()-center_text(str(self.value_uncap)), self.center.y()-40), str(self.value_uncap))
		painter.drawText(QtCore.QPointF(self.center.x()-center_text(self.multiplier), self.center.y()+20+self.margin), self.multiplier)
		painter.drawText(QtCore.QPointF(self.center.x()-center_text(self.units), self.center.y()+20+self.margin*2), self.units)
		painter.drawText(QtCore.QPointF(self.center.x()-center_text(self.description), self.center.y()+20+self.margin*3), self.description)

		painter.setPen(self.marker_tick_color)
		if getattr(self, 'marker_value', None):
			painter.drawText(QtCore.QPointF(self.center.x()-center_text(str(self.marker_value)), self.center.y()-20), str(self.marker_value))

		QtGui.QWidget.paintEvent(self, event)
Example #5
0
    def __init__(self,
                 topic='/HUD',
                 length=300.0,
                 end_angle=300.0,
                 min=0.0,
                 max=100.0,
                 main_points=11,
                 warning=[],
                 danger=[],
                 multiplier='',
                 units='',
                 description=''):
        super(GaugeSimple, self).__init__()

        self.setFixedSize(300, 300)
        self.setWindowTitle('A Magnificent Gauge')
        self.setAutoFillBackground(True)
        self.redraw_interval = 40
        self.update_plot_timer = QtCore.QTimer()
        self.update_plot_timer.timeout.connect(self.update_plot)
        self._start_time = rospy.get_time()
        self._rosdata = ROSData(topic, self._start_time)

        self.min = min
        self.curr_value = min
        self.value_uncap = 0
        self.max = max
        self.length = length
        self.main_points = main_points
        self.start_angle = (end_angle + length) % 360
        self.end_angle = end_angle % 360
        self.is_circle = self.start_angle == self.end_angle

        self.gauge_ticks = []
        self.bounding_rect = QtCore.QRectF(25.0, 25.0, 250.0, 250.0)
        self.center = QtCore.QPointF(150.0, 150.0)
        self.warning = warning  #zones
        self.danger = danger  #zones
        self.center_radius = 5.0
        self.margin = 12
        self.units = units
        self.multiplier = multiplier
        self.description = description

        self.update_plot_timer.start(self.redraw_interval)

        #Various ui colors
        self.marker_tick_color = QtGui.QPen(QtGui.QColor('#FF9900'), 1.8)
        self.ui_color = QtGui.QPen(QtCore.Qt.green, 2.5)
        self.ui_color_tick = QtGui.QPen(QtCore.Qt.green, 1.5)
        self.gauge_color = QtGui.QPen(QtCore.Qt.lightGray, 2)
        self.warning_color = QtGui.QPen(QtCore.Qt.yellow, 2)
        self.warning_bg = QtGui.QRadialGradient(self.center, self.width() / 3)
        self.warning_bg.setColorAt(0.0, QtCore.Qt.yellow)
        self.warning_bg.setColorAt(1.0, QtCore.Qt.black)
        self.danger_color = QtGui.QPen(QtCore.Qt.red, 2)
        self.danger_bg = QtGui.QRadialGradient(self.center, self.width() / 3)
        self.danger_bg.setColorAt(0.0, QtCore.Qt.red)
        self.danger_bg.setColorAt(1.0, QtCore.Qt.black)

        self.current_bg = QtCore.Qt.black

        self.create_gauge()
Example #6
0
class GaugeSimple(QtGui.QWidget):
    ''' Gauge pointer movement:
	minimum->maximum values: clockwise rotation
	maximum value > minimum-value
	'''
    marker_set = QtCore.pyqtSignal(list)

    def __init__(self,
                 topic='/HUD',
                 length=300.0,
                 end_angle=300.0,
                 min=0.0,
                 max=100.0,
                 main_points=11,
                 warning=[],
                 danger=[],
                 multiplier='',
                 units='',
                 description=''):
        super(GaugeSimple, self).__init__()

        self.setFixedSize(300, 300)
        self.setWindowTitle('A Magnificent Gauge')
        self.setAutoFillBackground(True)
        self.redraw_interval = 40
        self.update_plot_timer = QtCore.QTimer()
        self.update_plot_timer.timeout.connect(self.update_plot)
        self._start_time = rospy.get_time()
        self._rosdata = ROSData(topic, self._start_time)

        self.min = min
        self.curr_value = min
        self.value_uncap = 0
        self.max = max
        self.length = length
        self.main_points = main_points
        self.start_angle = (end_angle + length) % 360
        self.end_angle = end_angle % 360
        self.is_circle = self.start_angle == self.end_angle

        self.gauge_ticks = []
        self.bounding_rect = QtCore.QRectF(25.0, 25.0, 250.0, 250.0)
        self.center = QtCore.QPointF(150.0, 150.0)
        self.warning = warning  #zones
        self.danger = danger  #zones
        self.center_radius = 5.0
        self.margin = 12
        self.units = units
        self.multiplier = multiplier
        self.description = description

        self.update_plot_timer.start(self.redraw_interval)

        #Various ui colors
        self.marker_tick_color = QtGui.QPen(QtGui.QColor('#FF9900'), 1.8)
        self.ui_color = QtGui.QPen(QtCore.Qt.green, 2.5)
        self.ui_color_tick = QtGui.QPen(QtCore.Qt.green, 1.5)
        self.gauge_color = QtGui.QPen(QtCore.Qt.lightGray, 2)
        self.warning_color = QtGui.QPen(QtCore.Qt.yellow, 2)
        self.warning_bg = QtGui.QRadialGradient(self.center, self.width() / 3)
        self.warning_bg.setColorAt(0.0, QtCore.Qt.yellow)
        self.warning_bg.setColorAt(1.0, QtCore.Qt.black)
        self.danger_color = QtGui.QPen(QtCore.Qt.red, 2)
        self.danger_bg = QtGui.QRadialGradient(self.center, self.width() / 3)
        self.danger_bg.setColorAt(0.0, QtCore.Qt.red)
        self.danger_bg.setColorAt(1.0, QtCore.Qt.black)

        self.current_bg = QtCore.Qt.black

        self.create_gauge()

    def detect_safe_zones(self):
        r = [(self.min, self.max)]
        unsafe = sorted(self.warning + self.danger, key=lambda i: i[0])

        for i in unsafe:
            temp = []
            for y in r:
                if i[0] > y[1] or i[1] < y[0]:
                    temp.append(y)
                elif i[0] == y[0] and i[1] == y[1]:
                    continue
                elif i[0] > y[0] and i[1] < y[1]:
                    temp.append((y[0], i[0]))
                    temp.append((i[1], y[1]))
                elif i[0] > y[0] and i[1] == y[1]:
                    temp.append((i[0], i[1]))
                elif i[0] == y[0] and i[1] < y[1]:
                    temp.append((i[1], y[1]))
            r = temp
        return r

    def create_gauge(self):
        def text_width(text):
            font = QtGui.QFont()
            metrics = QtGui.QFontMetrics(font)
            return metrics.width(text)

        #Main points
        divisor = self.main_points
        if self.start_angle != self.end_angle:
            divisor -= 1

        angle_step = self.length / divisor
        value_step = abs(self.max - self.min) / divisor

        #Gauge main line(the circular path)
        #Safe zones
        zones = map(self.val2deg_tuple, self.detect_safe_zones())
        self.gauge_safe = []
        for zone in zones:
            path = QtGui.QPainterPath()
            path.arcMoveTo(self.bounding_rect, self.start_angle - zone[0])
            path.arcTo(self.bounding_rect, self.start_angle - zone[0],
                       -(zone[1] - zone[0]))
            self.gauge_safe.append(path)
        #Warning zones
        warning_zones = map(self.val2deg_tuple, self.warning)
        self.gauge_warning = []
        for w in warning_zones:
            path = QtGui.QPainterPath()
            path.arcMoveTo(self.bounding_rect, self.start_angle - w[0])
            path.arcTo(self.bounding_rect, self.start_angle - w[0],
                       -(w[1] - w[0]))
            self.gauge_warning.append(path)
        #Danger zones
        danger_zones = map(self.val2deg_tuple, self.danger)
        self.gauge_danger = []
        for d in danger_zones:
            path = QtGui.QPainterPath()
            path.arcMoveTo(self.bounding_rect, self.start_angle - d[0])
            path.arcTo(self.bounding_rect, self.start_angle - d[0],
                       -(d[1] - d[0]))
            self.gauge_danger.append(path)

        #Initial gauge position
        self.set_gauge(self.curr_value)

        for i in xrange(self.main_points):
            #Find the point on the curve
            angle = self.start_angle - i * angle_step
            value = self.min + i * value_step
            p = QtGui.QPainterPath()
            p.arcMoveTo(self.bounding_rect, angle)
            x, y = p.currentPosition().x(), p.currentPosition().y()
            x_new = x * 0.9 + self.center.x() * 0.1
            y_new = y * 0.9 + self.center.y() * 0.1

            x_text = x * 0.8 + self.center.x() * 0.2 - (
                text_width(str(round(value, 1))) - 10) / 2
            y_text = y * 0.8 + self.center.y() * 0.2 + 4

            tick_path = QtGui.QPainterPath()
            tick_path.moveTo(x_new, y_new)
            tick_path.lineTo(x, y)

            #Store the tick_length for the marker area
            self.gauge_ticks.append(
                [QtCore.QPointF(x_text, y_text), value, tick_path])

            #Store the tick_length for the marker area
        self.tick_length = math.sqrt((x - x_new)**2 + (y - y_new)**2)

    def val2deg(self, value):
        return self.length * ((value - self.min) / abs(self.max - self.min))

    def val2deg_tuple(self, t):
        return map(self.val2deg, t)

    def deg2val(self, degrees):
        #Convert the given degress relative to the start_angle to the respective value
        return abs(self.max - self.min) * (degrees / self.length) + self.min

    def mouseReleaseEvent(self, e):
        self.mouseMoveEvent(e)

    def mouseMoveEvent(self, e):
        #marker_line and marker_value dont exist before the first call of this function
        click_pos = e.posF()

        x_coeff = (click_pos.x() - self.center.x())**2
        y_coeff = (click_pos.y() - self.center.y())**2
        dist = math.sqrt(x_coeff + y_coeff)

        w = self.bounding_rect.width() / 2

        if w - self.tick_length <= dist <= w:
            #Find the angle between the start angle and the click point
            angle = self.angle_from_zero(self.center, click_pos,
                                         self.start_angle)
            #Return if the user clicked outside of the allowed range
            if self.deg2val(angle) > self.max or self.deg2val(
                    angle) < self.min:
                return

            self.set_marker(self.deg2val(angle))

    def angle_from_zero(self, p1, p2, offset):
        angle = math.degrees(math.atan2(p1.y() - p2.y(), p2.x() - p1.x()))

        if angle < 0:
            angle += 360
        angle = offset - angle
        if angle < 0:
            angle += 360

        return angle

    def set_marker(self, value):
        #New values: marker_point
        #Emit the new value
        self.marker_value = max(min(value, self.max), self.min)
        self.marker_set.emit([self.description, self.marker_value])
        #Round it for presentation purposes
        self.marker_value = round(self.marker_value, 2)

        #Create the marker line
        p = QtGui.QPainterPath()
        p.arcMoveTo(self.bounding_rect, self.start_angle - self.val2deg(value))
        self.marker_point = p.currentPosition()
        self.draw_marker(y=3)

    def compute_marker_rotation(self):
        #Marker_point is already set and ready for use
        return self.angle_from_zero(self.center, self.marker_point, 90)

    def draw_marker(self, x=0, y=0, size=10):
        poly = QtGui.QPolygonF()
        poly.append(QtCore.QPointF(x - size, y))
        poly.append(QtCore.QPointF(x + size, y))
        poly.append(QtCore.QPointF(x + size, y - size))
        poly.append(QtCore.QPointF(x, y))
        poly.append(QtCore.QPointF(x - size, y - size))
        poly.append(QtCore.QPointF(x - size, y))

        self.marker_line = QtGui.QPainterPath()
        self.marker_line.addPolygon(poly)

        self.update()

    def update_plot(self):
        dump, value = self._rosdata.next()
        self.value_uncap = round(value.pop(), 1)
        #print value
        if len(value) > 0:
            self.set_gauge(self.value_uncap)
        self.update_plot_timer.start(self.redraw_interval)

    def set_gauge(self, value):
        #Clamp between [min, max]
        self.curr_value = round(max(min(value, self.max), self.min), 1)

        p = QtGui.QPainterPath()
        p.arcMoveTo(self.bounding_rect,
                    self.start_angle - self.val2deg(self.curr_value))
        x, y = p.currentPosition().x(), p.currentPosition().y()

        self.gauge_line = QtGui.QPainterPath()
        self.gauge_line.moveTo(self.center)
        self.gauge_line.lineTo(x, y)
        self.update()

    def increment_gauge(self, step):
        #Clamp between (min, max)
        self.curr_value = max(min(self.curr_value + step, self.max), self.min)

        p = QtGui.QPainterPath()
        p.arcMoveTo(self.bounding_rect,
                    self.start_angle - self.val2deg(self.curr_value))
        x, y = p.currentPosition().x(), p.currentPosition().y()

        self.gauge_line = QtGui.QPainterPath()
        self.gauge_line.moveTo(self.center)
        self.gauge_line.lineTo(x, y)
        self.update()

    def set_bg_color(self):
        #Determine the zone that the gauge arrow is inside
        #Is it in a warning zone?
        for w in self.warning:
            if w[0] <= self.curr_value <= w[1]:
                self.current_bg = self.warning_bg
                return
        #Or a danger zone?
        for d in self.danger:
            if d[0] <= self.curr_value <= d[1]:
                self.current_bg = self.danger_bg
                return
        #Don't be scared, you're safe!
        self.current_bg = QtCore.Qt.black

    def paintEvent(self, event):
        def center_text(text):
            rect = painter.boundingRect(self.bounding_rect,
                                        QtCore.Qt.AlignHCenter, text)
            return rect.width() / 2

        painter = QtGui.QPainter(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)

        #Draw the background
        self.set_bg_color()
        painter.fillRect(event.rect(), self.current_bg)

        painter.setBrush(QtCore.Qt.transparent)
        painter.setPen(self.gauge_color)
        painter.drawPath(self.gauge_line)

        #Draw the safe zones
        painter.setPen(self.ui_color)
        for s in self.gauge_safe:
            painter.drawPath(s)
        #Draw the warning zones
        painter.setPen(self.warning_color)
        for w in self.gauge_warning:
            painter.drawPath(w)
        #Draw the danger zones
        painter.setPen(self.danger_color)
        for d in self.gauge_danger:
            painter.drawPath(d)

        #Draw the marker line
        if getattr(self, 'marker_line', None):
            painter.setPen(self.marker_tick_color)
            painter.translate(self.marker_point)
            painter.rotate(self.compute_marker_rotation())
            painter.drawPath(self.marker_line)
            painter.resetTransform()

        #Draw the center circle
        painter.setPen(self.ui_color)
        painter.drawEllipse(self.center.x() - self.center_radius / 2,
                            self.center.y() - self.center_radius / 2,
                            self.center_radius, self.center_radius)

        #Draw the paths
        painter.setPen(self.ui_color_tick)
        for i, path in enumerate(self.gauge_ticks):
            if not (self.is_circle and i == (len(self.gauge_ticks))):
                painter.drawText(path[0], str(int(path[1])))
                painter.drawPath(path[2])

        #Draw the text labels
        painter.drawText(
            QtCore.QPointF(
                self.center.x() - center_text(str(self.value_uncap)),
                self.center.y() - 40), str(self.value_uncap))
        painter.drawText(
            QtCore.QPointF(self.center.x() - center_text(self.multiplier),
                           self.center.y() + 20 + self.margin),
            self.multiplier)
        painter.drawText(
            QtCore.QPointF(self.center.x() - center_text(self.units),
                           self.center.y() + 20 + self.margin * 2), self.units)
        painter.drawText(
            QtCore.QPointF(self.center.x() - center_text(self.description),
                           self.center.y() + 20 + self.margin * 3),
            self.description)

        painter.setPen(self.marker_tick_color)
        if getattr(self, 'marker_value', None):
            painter.drawText(
                QtCore.QPointF(
                    self.center.x() - center_text(str(self.marker_value)),
                    self.center.y() - 20), str(self.marker_value))

        QtGui.QWidget.paintEvent(self, event)