First of all I new to python so please do not judge me about mess in code :). I trying to make circular progress bar but I run into some problems and can't find what it causing. Problem is setRange(0, 100) or setMmaximum(100) not working if I setValue(100) I getting filled all circle. And second problem is my progress bar running backwards. Please can someone explain what I doing wrong?
Here what I have so far:
main.py file
import wx
from src.arc import TestArc
class bandom(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(800, 600))
self.gauge = TestArc(self)
self.gauge.SetFocus()
#self.gauge.setMinimun(0)
#self.gauge.setMaximun(100)
self.gauge.setRange(0, 100)
self.gauge.setValue(10)
# timer for testing progressbar
# self.timer = wx.Timer(self)
# self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
# self.timer.Start(100)
# self.val = 0
# def OnTimer(self, evt):
# self.val += 1
# self.gauge.setValue(self.val)
#
# if self.val >= 100:
# self.val = 0
# #print(self.val)
class MyApp(wx.App):
def OnInit(self):
frame = bandom(None, -1, 'Window title here')
frame.Show(True)
self.SetTopWindow(frame)
return True
def main():
app = MyApp(0)
app.MainLoop()
if __name__ == '__main__':
main()
and arc.py file
class TestArc(wx.Panel):
def __init__(self, *args, **kwargs):
super(TestArc, self).__init__(*args, **kwargs)
self.lineWidth = 0
self.min = 0
self.max = 100
self._value = 0
self.setText = '---'
# self.font = someFont()
self.position = wx.Rect() # self.position.Set(x, y, width, height)
self.startPoint = math.radians(0)
self.endPoint = math.radians(0)
self.Bind(wx.EVT_PAINT, self.OnPaint)
def setRange(self, min, max):
self.min = min
self.max = max
if self.max < self.min:
self.max, self.min = self.min, self.max
if self._value < self.min:
self._value = self.min
elif self._value > self.max:
self._value = self.max
self.Refresh()
def setMinimun(self, min):
self.setRange(min, self.max)
def setMaximun(self, max):
self.setRange(self.min, max)
def setValue(self, val):
if self._value != val:
if val < self.min:
self._value = self.min
elif val > self.max:
self._value = self.max
else:
self._value = val
self.Refresh()
self.Refresh()
def setLineWidth(self, lineWidth):
self.lineWidth = lineWidth
def setPosition(self, x, y, width, height):
self.position = wx.Rect(x, y, width, height)
def OnPaint(self, event=None):
dc = wx.PaintDC(self)
gc = self.MakeGC(dc)
self.Draw(gc)
def MakeGC(self, dc):
try:
if False:
gcr = wx.GraphicsRenderer.GetCairoRenderer
gc = gcr() and gcr().CreateContext(dc)
if gc is None:
wx.MessageBox("Unable to create Cairo Context.", "Oops")
gc = wx.GraphicsContext.Create(dc)
else:
gc = wx.GraphicsContext.Create(dc)
except NotImplementedError:
dc.DrawText("This build of wxPython does not support the wx.GraphicsContext "
"family of classes.",
25, 25)
return None
return gc
def Draw(self, gc):
#middle progressbar line
radStart = math.radians(90)
radEnd = math.radians(0)
path = gc.CreatePath()
path.AddArc(80, 80, 50, radStart, radEnd, True)
pen = wx.Pen('#000000', 4)
pen.SetCap(wx.CAP_BUTT)
gc.SetPen(pen)
gc.SetBrush(wx.Brush('#000000', wx.TRANSPARENT))
gc.DrawPath(path)
#progress bar
start = math.radians(90)
#r = math.radians(270)
arcStep = -270 / (self.max - self.min) * self._value
end = math.radians(arcStep)
path = gc.CreatePath()
path.AddArc(80, 80, 50, start, end)
pen = wx.Pen('#CC7F32', 15)
pen.SetCap(wx.CAP_BUTT)
gc.SetPen(pen)
gc.SetBrush(wx.Brush('#000000', wx.TRANSPARENT))
gc.DrawPath(path)
It's a bit gruesome because you have offset everything by 90º, so you have to account for that.
Here goes nothing:
import wx
import math
class TestArc(wx.Panel):
def __init__(self, *args, **kwargs):
super(TestArc, self).__init__(*args, **kwargs)
self.lineWidth = 0
self.min = -90
self.max = 360
self._value = 0
self.setText = '---'
# self.font = someFont()
self.position = wx.Rect() # self.position.Set(x, y, width, height)
self.startPoint = math.radians(0)
self.endPoint = math.radians(0)
self.Bind(wx.EVT_PAINT, self.OnPaint)
def setRange(self, min, max):
self.min = min
self.max = max
if self._value < self.min:
self._value = self.min
elif self._value > self.max:
self._value = self.max
self.Refresh()
def setMinimun(self, min):
self.setRange(min, self.max)
def setMaximun(self, max):
self.setRange(self.min, max)
def setValue(self, val):
if self._value != val:
if val < self.min:
self._value = self.min
elif val > self.max:
self._value = self.max
else:
self._value = val
self.Refresh()
def setLineWidth(self, lineWidth):
self.lineWidth = lineWidth
def setPosition(self, x, y, width, height):
self.position = wx.Rect(x, y, width, height)
def OnPaint(self, event=None):
dc = wx.PaintDC(self)
gc = self.MakeGC(dc)
self.Draw(gc)
def MakeGC(self, dc):
try:
if False:
gcr = wx.GraphicsRenderer.GetCairoRenderer
gc = gcr() and gcr().CreateContext(dc)
if gc is None:
wx.MessageBox("Unable to create Cairo Context.", "Oops")
gc = wx.GraphicsContext.Create(dc)
else:
gc = wx.GraphicsContext.Create(dc)
except NotImplementedError:
dc.DrawText("This build of wxPython does not support the wx.GraphicsContext "
"family of classes.",
25, 25)
return None
return gc
def Draw(self, gc):
#middle progressbar line
radStart = math.radians(90)
radEnd = math.radians(0)
path = gc.CreatePath()
path.AddArc(80, 80, 50, radStart, radEnd, True)
pen = wx.Pen('#000000', 4)
pen.SetCap(wx.CAP_BUTT)
gc.SetPen(pen)
gc.SetBrush(wx.Brush('#000000', wx.TRANSPARENT))
gc.DrawPath(path)
#progress bar
start = math.radians(90)
#r = math.radians(270)
arcStep = 270 / (self.max - self.min) * self._value
end = math.radians(arcStep)
path = gc.CreatePath()
path.AddArc(80, 80, 50, start, end)
pen = wx.Pen('#CC7F32', 15)
pen.SetCap(wx.CAP_BUTT)
gc.SetPen(pen)
gc.SetBrush(wx.Brush('#000000', wx.TRANSPARENT))
gc.DrawPath(path)
class bandom(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(800, 600))
self.gauge = TestArc(self)
self.gauge.SetFocus()
self.gauge.setMinimun(90)
self.gauge.setMaximun(360)
self.gauge.setValue(90)
# timer for testing progressbar
self.val = 90
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
self.timer.Start(100)
def OnTimer(self, evt):
self.val += 2.7
self.gauge.setValue(self.val)
if self.val >= 360:
self.val = 90
if __name__ == '__main__':
app = wx.App()
frame = bandom(None, -1, 'Window title here')
frame.Show(True)
app.MainLoop()
I shoved it all into one file for my convenience.
Edit:
The reason for changing the maximum value to 360 and calculating the Step in the Arc with 270 is that you have chosen to use a 3/4 circle for your gauge and you are calculating the amount to increment the gauge using radians. You could have used degrees or radians but either way it's going to be calculated between 0 and 270 for degrees or 0 and 3π/2 for radians. Originally you said that you required a scale between 0 and 100, the easiest way to arrange that is to increment/decrement the gauge counter by 2.7 rather than 1 i.e. 270 points to travel / 100 (your required scale). It would be easier to start the gauge at point 0 rather than 90 but I assume that you are doing this either as an exercise or for aesthetic reasons.
Your last comment regarding needing 240 increments, conflicts with the original question and other comments but can be achieved by incrementing the gauge counter by 1.125 (270/240) rather than either 1 or 2.7
Edit 2:
Running this code on my machine I get no "flicker". It could be down to the speed of your machine and may improve if you change the value that you pass to the wx.Timer
, currently 100 milliseconds or 10 times a second. Remember that this code in essence pretends to show the progress of a task by using the timer, in reality you would be passing values into the gauge not based on a timer but on the progress of a actual process.