This is the updated Speedometer that was first posted here:
Since then it has been updated to provide a gauge that is reversible, orientable and invertible
Based on the original work of Kevin Schlosser
Copyright Kevin Schlosser April 2019
GNU General Public License v3.0
Amendments by RolfofSaxony 2023
Enabling the volume knob to become an all-round circular gauge, which can have multiple uses
e.g. as a speedometer, a meter for voltage, amperes, revolutions per minute, psi, a thermometer etc
It has become reversible, orientable and invertible in Version 2.0
In addition to knob.py there are a variety of example programs showing not only its proposed uses but also providing examples of how to use the majority of its functions.
Also provided is knobdemo_styles.py, which provides a demonstration of the various styles available, in addition to the visual adjustments that can be made using the functions.
Varying colours and thumb size etc., I leave you to your own devices.
It should be noted that knob.py, has its own built in demonstration, if knob.py is run directly.
The souce code:
Knob_2.1.zip (42.7 KB)
Given that I haven’t had to handle trigonometry since I last had to conjugate Latin and Greek verbs, over 40 years, there’s room in this code for oversights and errors.
“Romani ite domum” versus “Romanes eunt domus”
As usual, submit bugs, comments and insults on a postcard please
Examples of gauges/meters:
knobdemo_styles.py
Examples of selectable style combinations:
Changelog:
Amended Version 2.1
Fixed bug in mouse drag.
Attempting to prevent unnecessary events once the meter's minimum value was hit, I failed to process the fact that it had hit the minimum - Thanks go to RichardT for pointing it out.
Addition of variable DisableMinMaxMouseDrag - True or False - Default False
If set to True, disables the ability, when dragging, to instantly flip from Min to Max or Max to Min
The maximum valid mouse drag, by default, is set to 50% of the difference in degrees between start and end degrees.
This can be adjusted by setting the control's variable 'mouse_max_move' to a value of your choice e.g.
self.ctrl.mouse_max_move = 50
Any attempt to drag by more than that, is cancelled.
Obviously, this also means that you can disable mouse dragging altogether, simply by setting mouse_max_move to zero.
Amended Version 2.0
Addition of variable UseHotSpots, which notes the dial thumb position and the Odometer position
If the mouse is hovered over those positions whilst UseHotSpots is True, a ToolTip
is displayed of the current value of the control for the dial thumb and the elapsed running time for the Odometer.
The Odometer may also have Text set in addition, see below
Addition of variable OdometerToolTip
If given a string value this will be display when hovering over the Odometer, if SetHotSpot is active,
in addition to the running time.
Addition of function SetHotSpots()
SetHotSpots toggles UseHotSpots between True and False
additionally it turns off standard ToolTip if it is Set
Addition of function GetHotSpots()
returns current value of UseHotSpots
Addition of function SetDefaultTickColour(colour)
overrides the default tick colour, the colour used beyond the current value (normally Dark Grey)
or the foreground colour of the parent panel.
Addition of function GetDefaultTickColour(colour)
returns the current default tick colour
Addition of function SetTickRangePercentage(True/False) - Default True
By default the tick range values given are converted to percentages to colour the gauge
If this is set to False the values are used as discrete values
This reinstates the original method as written by Kevin Schlosser
Addition of variable Caption.
If given a string value, this text is shown centred, at the foot of the meter.
If positioned at the foot, there is only room for a single line
The Caption gets it's colour from the DefaultTickColour.
Addition CaptionPos = int Default 0
The Caption by default is positioned centrally at the bottom of the gauge
If you set this variable the caption will be positioned:
1 - Top Left
2 - Top Right
3 - Bottom Left
4 - Bottom Right
In these positions there is more room for text, which may include newlines.
Addition of coping with numeric keypad input of digits, as well as ordinary digits, to jump by a percentage
0 = min value, 1 = 10% ...... 9 = 90%
Addition of SetStartEndDegrees(start=n, end=n) - Permitting you to orient the gauge and decide how much of the circle to use
Defaults 135.0 (7:30) and 405.0 (4:30) respectively
Positions are specified in degrees with 0 degree angle corresponding to the positive horizontal axis (3 o’clock) direction
following the convention in drawing wx.DC arcs.
i.e. to set the minimum position at 9 o'clock the start angle would be 180.0
to set the maximum position at 3 o'clock the end angle would be 360.0
Each hourly position is +30°, so 7:30 would be 4.5 * 30 i.e. 7:30 minus 3:00 = 4.5 hours * 30 = 135.0
with an end position of 4:30 = (1.5 + 12) * 30 = 405.0
Both the Start and the End positions must both be Positive or both be Negative and the Range cannot exceed 360°
Negative values are permitted but remember they are inverse:
so -135° is 10:30 and -405° is 1:30
Reversed Start and End positions are allowed, to make the gauge appear to run from positive to negative
e.g. start 405.0 end 135.0 or -405.0 to -135.0
i.e.
Start End Min position Max Position Where is Midway
135 405 7:30 4:30 Top
405 135 4:30 7:30 Top
-135 -405 10:30 1:30 Bottom
-405 -135 1:30 10:30 Bottom
Addition GetStartEndDegrees() - returns a tuple
Addition SetAlwaysTickColours(True or False)
Normally the ticks are only coloured based on the current value.
If this is set to True, the ticks permanently have the colours assigned by the TickColourRanges and
the current position is denoted by the current value tick, being the default tick colour.
Addition ShowScale True or False
ShowScale now makes a best effort to show text scale values on the gauge, without overcrowding the image
By default the values are displayed on the exterior of the gauge
Addition InsideScale True or False
If set in combination with either ShowScale or ShowMinMax, the scale is displayed on the interior
of the gauge.
Given the restricted space available, this can be slightly hit and miss, best results are achieved
by adjusting the tick frequency.
Addition GaugeImage = a wx.Image
This, much like GaugeText displays the input centrally in the gauge.
The image should be a wx.Image and is expected to be transparent.
The image is resized, based on the control's size, although it makes sense to ensure
that the image is as small as is appropriate, beforehand, just for efficiency.
If you are displaying text too, you may wish to display that further up the gauge,
include 1 or 2 linefeeds in the text, to separate it from the image.
Addition GaugeImagePos = int Default 0
The image by default is positioned centrally
If you set this variable the image will be positioned:
1 - Top Left
2 - Top Right
3 - Bottom Left
4 - Bottom Right
Addition of EVT_SCROLL_CHANGED, activated if the value changes via SetValue()
This caters for events to be monitored if you feed value changes into the gauge, rather than treating it
purely as an input control.
Events available for Binding are:
wx.EVT_SCROLL_TOP, the gauge has hit maximum value;
wx.EVT_SCROLL_BOTTOM, the gauge has hit minimum value;
wx.EVT_SCROLL_LINEUP, gauge moved up one increment:;
wx.EVT_SCROLL_LINEDOWN', gauge moved down one increment
wx.EVT_SCROLL_PAGEUP, the user hit page up;
wx.EVT_SCROLL_PAGEDOWN, the user hit page down;
wx.EVT_SCROLL_THUMBRELEASE, the mouse has been released;
wx.EVT_SCROLL_CHANGED, the gauge value has changed;
and the catch all, wx.EVT_SCROLL, an event occurred.
Addition of variable OdometerBackgroundColour - Default None
Bug fixes:
The knobStyle values have been corrected.
Default: KNOB_GLOW | KNOB_DEPRESSION | KNOB_HANDLE_GLOW | KNOB_TICKS | KNOB_SHADOW
Originally the knobStyle values always seemed to produce True, so everything was always turn On, as the values
assigned were incorrect.
The knobStyle is one of the initial parameters but can be overridden with SetKnobStyle(...)
e.g. self.ctrl.SetKnobStyle(knob.KNOB_TICKS | knob.KNOB_SHADOW | knob.KNOB_DEPRESSION)
valid style values are:
KNOB_GLOW = 1 # Adds a neon glow around the gauge
KNOB_DEPRESSION = 2 # Adds the central depression
KNOB_HANDLE_GLOW = 4 # Adds a neon glow to the thumb
KNOB_TICKS = 8 # Adds ticks to the gauge
KNOB_SHADOW = 16 # Adds a shadow to the gauge
KNOB_RIM = 32 # Adds a coloured rim to indicate the position of the gauge
KNOB_RIM is a new style, as an alternative to KNOB_GLOW, (although they can be used together)
Whereas KNOB_GLOW lights up the gauge rim with the relevant diffused colour range, KNOB_RIM lights up
the gauge rim with the relevant solid colour range, only up to the current value.
In essence it acts as a separate value indicator.
Changes to the methods used to calculate the tick positions
calculating tick positions for integer values was fine but trying to set the increments for fine values,
such as a meter measuring from -1.0 to 1.0 with an increment of 0.01 and a tick frequency of 0.02,
for example, runs into floating point Modulo issues ( a horror story in its own right)
Either the tickfrequency could be refused or there would be missing ticks or too many ticks
I've attempted to resolve those by importing Decimal and using round() and some other tweaks, including
defining the required precision.
The increment determines the precision that will be used e.g. 0.01 would set precision to 2, 0.005 to 3.
Hopefully, I haven't broken anything. :)
SetTickColours now handles the various ways of defining a colour e.g.
(77, 77, 255)
wx.Colour('#7777ff')
'#7777ff'
wx.BLUE
The Bug was in setting the neon_colour for the ticks and body of the meter which expects a tuple.
Changes:
Change to existing function GetAverageSpeed()
This now returns a tuple of Average value and the elapsed time period in seconds
Change PageUp, PageDown to simple + or - 10%
Event reporting can no longer issue double events e.g. SCROLL_PAGEUP and SCROLL_CHANGED
Now the specific event is reported or SCROLL_CHANGED not both.
Change TickColourRanges to cope with values < 1
The tickcolourranges didn't handle ranges which strayed negative, they calculated a percentage of the maximum value.
They now handle a range which goes from negative to positive e.g.
SetTickColourRanges([10, 50, 75]) for a minimum value of -10.0 and max of 20.0,
would set the values at -7.0, 5.0 and 12.5 as the range is actually 30 ( -10 -> 20 )
It should also handle purely negative ranges
GaugeText replaces SpeedoText in a variable name change - sorry about that, just more appropriate
GaugeText has also become more vertically centrally located.
If you wish the text to display further up the gauge include 1 or 2 linefeeds in the text.
This is especially true if including a centrally located image with GaugeImage()
Amended Version 1.1
Bug fix for incorrect Unbind of the odometer update timer
Addition of a pointer spine
Addition of variable ShowMinMax values
Addition of variable OdometerColour
Amended version 1.0
Display optional Volume/Speed value as text
To allow for very large values when using RPM for example, if the initial value is set as an integer
only an integer is displayed, allowing for much larger numbers to be catered to.
Enable Right Click to jump to a position
Calculate tick colour as a percentage of the maximum value rather than fixed value
Optional pointer with shadow for Speedometer feel
Optional Speedo text, expected to be something like Mph, Kph, Rpm ft/s etc
Optional Odometer - defaults calculation to distance covered per hour
Optional odometer period unit - "H" per Hour, "M" per Minute, "S" per Second - Default "H"
Optional Odometer update period - expects a millisecond value like wxTimer
a timer that updates the odometer irrespective of the value being changed
based on the current Speed/Velocity
A value > than 0 turns the odometer on, <= 0 turns it off
Plus minor adjustments
Variables added:
ShowToolTip = True Shows a tooltip of the Volume/Speed value
ShowValue = True Shows the Volume/Speed value in the centre of the widget
ShowPointer = True Shows a pointer indicating the Volume/Speed
PointerColour = None Sets the Colour of the pointer, allows for transparency
The default is to use the current tick colour.
SpeedoText = '' Sets a short text to be displayed indicating the measurement
e.g. Mph, Kph, Rpm
ShowOdometer = False True/False
OdometerUpdate = 0 Value in milliseconds - Sets automatic odometer update
without this the odometer is only updated on an event
Note:
Showing the odometer with an auto update is expensive and the more
frequent the update, the more expensive
***************************************
OdometerPeriod = "H" "H", "M" or "S"
If you change this after setting it initially, the odometer readings
will be nonsense, you will have a mixture of unit readings
Additional functions:
GetOdometerUpdate() return Odometer update period
SetOdometerUpdate(value) Set odometer update period in milliseconds
Sets or cancels the odometer depending on positive or negative value
GetAverageSpeed() Returns the average speed depending on the odometer period unit
from program start to now (running time in secs, as of version 2.0)
GetOdometerValue() Returns current odometer value
GetOdometerHistory() Returns the history of speed changes as a list