Created
May 2, 2021 20:54
-
-
Save israel-dryer/5914a9fa6c20a5329b94a463d046c089 to your computer and use it in GitHub Desktop.
Meter with needle
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import tkinter as tk | |
from PIL import Image, ImageTk, ImageDraw | |
from tkinter import StringVar, IntVar | |
from tkinter import ttk | |
from tkinter.ttk import Frame | |
class NeedleMeter(Frame): | |
def __init__(self, | |
master=None, | |
amounttotal=100, | |
amountused=0, | |
metersize=200, | |
meterthickness=10, | |
**kw): | |
super().__init__(master=master, **kw) | |
self.box = ttk.Frame(self, width=metersize, height=metersize) | |
self.arcoffset = -180 | |
self.arcrange = 180 | |
self.amountusedvariable = IntVar(value=amountused) | |
self.amounttotalvariable = IntVar(value=amounttotal) | |
self.amountusedvariable.trace_add('write', self.draw_needle) | |
self.towardsmaximum = True | |
self.metersize = metersize | |
self.meterthickness = meterthickness | |
self.metercolorsuccess = '#22b24c' | |
self.metercolorwarning = '#f5e625' | |
self.metercolordanger = '#f57a00' | |
self.needlecolor = '#272525' | |
# meter image | |
self.meter = ttk.Label(self.box) | |
self.draw_base_image() | |
self.draw_needle() | |
self.meter.place(x=0, y=0) | |
self.box.pack() | |
@property | |
def amountused(self): | |
return self.amountusedvariable.get() | |
@amountused.setter | |
def amountused(self, value): | |
self.amountusedvariable.set(value) | |
@property | |
def amounttotal(self): | |
return self.amounttotalvariable.get() | |
@amounttotal.setter | |
def amounttotal(self, value): | |
self.amounttotalvariable.set(value) | |
def draw_base_image(self): | |
"""Draw the base image to be used for subsequent updates""" | |
self.base_image = Image.new('RGBA', (self.metersize * 5, self.metersize * 5)) | |
draw = ImageDraw.Draw(self.base_image) | |
# success | |
draw.arc((0, 0, self.metersize * 5 - 20, self.metersize * 5 - 20), | |
-180, -90, self.metercolorsuccess, self.meterthickness * 5) | |
# warning | |
draw.arc((0, 0, self.metersize * 5 - 20, self.metersize * 5 - 20), | |
-90, -45, self.metercolorwarning, self.meterthickness * 5) | |
# warning | |
draw.arc((0, 0, self.metersize * 5 - 20, self.metersize * 5 - 20), | |
-45, 0, self.metercolordanger, self.meterthickness * 5) | |
def draw_needle(self, *args): | |
"""Draw a meter | |
Args: | |
*args: if triggered by a trace, will be `variable`, `index`, `mode`. | |
""" | |
im = self.base_image.copy() | |
draw = ImageDraw.Draw(im) | |
scaled_xy = self.metersize * 5 | |
scaled_half = scaled_xy // 2 | |
draw.arc((40, 40, scaled_xy - 40, scaled_xy - 40), | |
self.meter_value() - 1, self.meter_value() + 1, self.needlecolor, scaled_xy) | |
needle_base_xy = (scaled_half - 20, scaled_half - 20, scaled_half + 20, scaled_half + 20) | |
draw.ellipse(needle_base_xy, fill=self.needlecolor) | |
self.meterimage = ImageTk.PhotoImage(im.resize((self.metersize, self.metersize), Image.CUBIC)) | |
self.meter.configure(image=self.meterimage) | |
def meter_value(self): | |
"""Calculate the meter value | |
Returns: | |
int: the value to be used to draw the arc length of the progress meter | |
""" | |
return int((self.amountused / self.amounttotal) * self.arcrange + self.arcoffset) | |
def step(self, delta=1): | |
"""Increase the indicator value by ``delta``. | |
The default increment is 1. The indicator will reverse direction and count down once it reaches the maximum | |
value. | |
Keyword Args: | |
delta (int): the amount to change the indicator. | |
""" | |
if self.amountused >= self.amounttotal: | |
self.towardsmaximum = True | |
self.amountused = self.amountused - delta | |
elif self.amountused <= 0: | |
self.towardsmaximum = False | |
self.amountused = self.amountused + delta | |
elif self.towardsmaximum: | |
self.amountused = self.amountused - delta | |
else: | |
self.amountused = self.amountused + delta | |
if __name__ == '__main__': | |
def test(meter): | |
meter.step(1) | |
meter.after(1, test, meter) | |
m = NeedleMeter(meterthickness=25, padding=20) | |
m.pack() | |
test(m) | |
m.mainloop() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment