Gauge displaying too thin

I am having trouble with displaying wx.Gauge. The trough is appearing very thin, no matter what size I use.

Here is a simple piece code:

import wx

class GaugeFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, 'Gauge Example', size=(350, 150))
        panel = wx.Panel(self, -1)
        self.count = 0
        self.gauge = wx.Gauge(panel, -1, 50, (10, 10), (-1, -1))
        self.gauge.SetBackgroundColour('light green')
        self.gauge.SetMinSize(wx.Size(300,100))
        self.gauge.SetSize(wx.Size(300,100))
        self.timer = wx.Timer(self)
        self.timer.Start(100)
        self.Bind(wx.EVT_TIMER, self.TimerHandler)

    def __del__(self):
        self.timer.Stop()

    def TimerHandler(self, event):
        self.count = self.count+1
        if self.count == 50:
            self.timer.Stop()
        self.gauge.SetValue(self.count)

app = wx.App()
GaugeFrame().Show()
app.MainLoop()

That produces

I have tried to run this on CentOS 7 (python 3.6, wxpython4) and Rocky 9 (python 3.9, wxpython4) with no luck. Am I missing a setting? Is it a theme issue?

Passing the required size to the Gauge constructor seems to work for me, using wxPython 4.2.3 gtk3 (phoenix) wxWidgets 3.2.7 + Python 3.12.3 + Linux Mint 22.1

import wx

class GaugeFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, 'Gauge Example', size=(350, 150))
        panel = wx.Panel(self, -1)
        self.count = 0
        self.gauge = wx.Gauge(panel, -1, 50, (10, 10), (300, 100))
        self.gauge.SetBackgroundColour('light green')
        self.timer = wx.Timer(self)
        self.timer.Start(100)
        self.Bind(wx.EVT_TIMER, self.TimerHandler)

    def __del__(self):
        self.timer.Stop()

    def TimerHandler(self, event):
        self.count = self.count+1
        if self.count == 50:
            self.timer.Stop()
        self.gauge.SetValue(self.count)

app = wx.App()
GaugeFrame().Show()
app.MainLoop()

You would think that would change it but it still creates a gauge with a slim trough

What exact version of wxPython are you using?

EDIT: the reason I ask is that I found an issue raised on the wxWidgets repository that looks like it is related:

A fix for that issue was included in wxWidgets 3.2.5 which was released on 13th May 2024. The first version of wxPython that included the fix was 4.2.2 which was released 11th November 2024.

CentOS7 - python36-wxpython4-4.0.7-6.el7.x86_64
Rocky8 - python3-wxpython4-4.0.7-13.el8.x86_64
Rocky9 - python3-wxpython4-4.2.0-3.el9.x86_64

Yep, sounds like the exact problem. Thanks for the answer.

I guess there is no workaround, just need to wait for a newer version

I would suggest using PyGauge instead of Gauge, but it has problems with older versions of wxPython when using Python >= 3.10.

The following example works on my system using wxPython 4.2.3 gtk3 (phoenix) wxWidgets 3.2.7 + Python 3.12.3 + Linux Mint 22.1

import wx
import wx.lib.agw.pygauge as PG

class GaugeFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, 'PyGauge Example', size=(350, 150))
        panel = wx.Panel(self, -1)
        self.gauge = PG.PyGauge(panel, -1, size=(300, 100))
        self.gauge.SetBackgroundColour('light green')
        self.gauge.SetBorderColor(wx.BLACK)
        self.gauge.SetRange(50)
        self.gauge.SetValue(0)
        self.gauge.Update(50, 5000)


app = wx.App()
GaugeFrame().Show()
app.MainLoop()

Yay, that worked for me as well.

I tried to integrate this into my example and I am not seeing any update when I use SetValue in the Timer, ie not using Update

I think you need to call self.gauge.Refresh() after SetValue().

import wx
import wx.lib.agw.pygauge as PG

class GaugeFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, 'Gauge Example', size=(350, 150))
        panel = wx.Panel(self, -1)
        self.count = 0
        self.gauge = PG.PyGauge(panel, -1, size=(300, 100))
        self.gauge.SetBackgroundColour('light green')
        self.gauge.SetBorderColor(wx.BLACK)
        self.gauge.SetRange(50)
        self.gauge.SetValue(0)

        self.timer = wx.Timer(self)
        self.timer.Start(100)
        self.Bind(wx.EVT_TIMER, self.TimerHandler)

    def __del__(self):
        self.timer.Stop()

    def TimerHandler(self, event):
        self.count = self.count+1
        if self.count == 50:
            self.timer.Stop()
        self.gauge.SetValue(self.count)
        self.gauge.Refresh()

app = wx.App()
GaugeFrame().Show()
app.MainLoop()

Yep, that did the trick. I was looking for that on the PyGauge, but I guess that is part of the base class. thanks a lot!!

I started with my basic example to show the problem. Well, my actual implementation is a vertical gauge and it looks like vertical mode is not implemented for PyGauge, lol

I found an old thread on Stack Overflow which refers to vertical not being supported. The OP posted a snippet of code which acts as workaround.

I tried to incorporate that snippet in a class derived from PyGauge, by overriding its OnPaint() method. It only works in solid colour mode (not gradient mode) and text values may be in the wrong position (not tested).

When the example below is run the gauge grows from the top to the bottom, so you may need to change that.

import copy
import wx
import wx.lib.agw.pygauge as PG

class CustomPyGauge(PG.PyGauge):

    def OnPaint(self, event):
        """
        Handles the ``wx.EVT_PAINT`` event for :class:`PyGauge`.

        :param `event`: a :class:`PaintEvent` event to be processed.
        """

        dc = wx.BufferedPaintDC(self)
        rect = self.GetClientRect()

        dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
        dc.Clear()
        colour = self.GetBackgroundColour()
        dc.SetBrush(wx.Brush(colour))
        dc.SetPen(wx.Pen(colour))
        dc.DrawRectangle(rect)


        if self._border_colour:
            dc.SetPen(wx.Pen(self.GetBorderColour()))
            dc.DrawRectangle(rect)
            pad = 1 + self.GetBorderPadding()
            rect.Deflate(pad,pad)


        if self.GetBarGradient():
            for i, gradient in enumerate(self._barGradientSorted):
                c1,c2 = gradient
                w = rect.width * (float(self._valueSorted[i]) / self._range)
                r = wx.Rect(rect)
                r.width = int(w)
                dc.GradientFillLinear(r, c1, c2, wx.EAST)
        else:
            for i, colour in enumerate(self._barColourSorted):
                dc.SetBrush(wx.Brush(colour))
                dc.SetPen(wx.Pen(colour))
                r = copy.copy(rect)
                if self.WindowStyle & wx.GA_VERTICAL:
                    h = rect.height * (float(self._valueSorted[i]) / self._range)
                    r.height = int(h)
                else:
                    w = rect.width * (float(self._valueSorted[i]) / self._range)
                    r.width = int(w)
                dc.DrawRectangle(r)


        if self._drawIndicatorText:
            dc.SetFont(self._drawIndicatorText_font)
            dc.SetTextForeground(self._drawIndicatorText_colour)
            drawValue = self._valueSorted[i]

            if self._drawIndicatorText_drawPercent:
                drawValue = (float(self._valueSorted[i]) * 100)  / self._range

            drawString = self._drawIndicatorText_formatString.format(
                drawValue, value=drawValue, range=self._range)
            rect = self.GetClientRect()
            (textWidth, textHeight, descent, extraLeading) = dc.GetFullTextExtent(drawString)
            textYPos = (rect.height-textHeight)//2

            if textHeight > rect.height:
                textYPos = 0-descent+extraLeading

            textXPos = (rect.width-textWidth)//2

            if textWidth>rect.width:
                textXPos = 0

            dc.DrawText(drawString, textXPos, textYPos)


class GaugeFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, 'Gauge Example', size=(150, 500))
        panel = wx.Panel(self, -1)
        self.count = 0
        self.gauge = CustomPyGauge(panel, -1, size=(100, 300), style=wx.GA_VERTICAL)
        self.gauge.SetBackgroundColour('light green')
        self.gauge.SetBorderColor(wx.BLACK)
        self.gauge.SetRange(50)
        self.gauge.SetValue(0)

        self.timer = wx.Timer(self)
        self.timer.Start(100)
        self.Bind(wx.EVT_TIMER, self.TimerHandler)

    def __del__(self):
        self.timer.Stop()

    def TimerHandler(self, event):
        self.count = self.count+1
        if self.count == 50:
            self.timer.Stop()
        self.gauge.SetValue(self.count)
        self.gauge.Refresh()

app = wx.App()
GaugeFrame().Show()
app.MainLoop()

1 Like

wow, i really appreciate the extra help. Gives me something to play with.
Thank you, RichardT.