Fix crash on closing item stats window when mutation's tab animation was in progress

This commit is contained in:
DarkPhoenix
2019-08-07 22:49:30 +03:00
parent 035c69c60a
commit fb93aa1ad5
4 changed files with 61 additions and 209 deletions

View File

@@ -9,18 +9,17 @@ from gui.utils import anim_effects
class AttributeGauge(wx.Window):
def __init__(self, parent, max_range=100, animate=True, leading_edge=True, edge_on_neutral=True, guide_lines=False, size=(-1, 30), *args,
**kargs):
super().__init__(parent, size=size, *args, **kargs)
def __init__(
self, parent, max_range=100, animate=True, leading_edge=True,
edge_on_neutral=True, guide_lines=False, size=(-1, 30), *args, **kwargs
):
super().__init__(parent, size=size, *args, **kwargs)
self._size = size
self.guide_lines = guide_lines
self._border_colour = wx.BLACK
self._bar_colour = None
self._bar_gradient = None
self.leading_edge = leading_edge
self.edge_on_neutral = edge_on_neutral
@@ -29,8 +28,6 @@ class AttributeGauge(wx.Window):
self._max_range = max_range
self._value = 0
self._fraction_digits = 0
self._timer_id = wx.NewId()
self._timer = None
@@ -44,14 +41,6 @@ class AttributeGauge(wx.Window):
self._anim_direction = 0
self.anim_effect = anim_effects.OUT_QUAD
# transition colors used based on how full (or overfilled) the gauge is.
self.transition_colors = [
(wx.Colour(191, 191, 191), wx.Colour(96, 191, 0)), # < 0-100%
(wx.Colour(191, 167, 96), wx.Colour(255, 191, 0)), # < 100-101%
(wx.Colour(255, 191, 0), wx.Colour(255, 128, 0)), # < 101-103%
(wx.Colour(255, 128, 0), wx.Colour(255, 0, 0)) # < 103-105%
]
self.goodColor = wx.Colour(96, 191, 0)
self.badColor = wx.Colour(255, 64, 0)
@@ -68,51 +57,17 @@ class AttributeGauge(wx.Window):
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_TIMER, self.OnTimer)
self.Bind(wx.EVT_ENTER_WINDOW, self.OnWindowEnter)
self.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave)
self.SetBackgroundStyle(wx.BG_STYLE_PAINT)
def OnEraseBackground(self, event):
pass
def OnWindowEnter(self, event):
self._show_remaining = True
self.Refresh()
def OnWindowLeave(self, event):
self._show_remaining = False
self.Refresh()
def GetBorderColour(self):
return self._border_colour
def SetBorderColour(self, colour):
self._border_colour = colour
def GetBarColour(self):
return self._bar_colour
def SetBarColour(self, colour):
self._bar_colour = colour
def SetFractionDigits(self, digits):
self._fraction_digits = digits
def GetBarGradient(self):
if self._bar_gradient is None:
return None
return self._bar_gradient[0]
def SetBarGradient(self, gradient=None):
if gradient is None:
self._bar_gradient = None
else:
if not isinstance(gradient, list):
self._bar_gradient = [gradient]
else:
self._bar_gradient = list(gradient)
def GetBorderPadding(self):
return self._border_padding
@@ -134,6 +89,11 @@ class AttributeGauge(wx.Window):
self._anim_value = self._percentage
self.Refresh()
def FreezeAnimation(self):
self._animate = False
if self._timer:
self._timer.Stop()
def SetRange(self, range, reinit=False, animate=True):
"""
Sets the range of the gauge. The gauge length is its
@@ -231,51 +191,50 @@ class AttributeGauge(wx.Window):
pad = 1 + self.GetBorderPadding()
rect.Deflate(pad, pad)
if True:
# if we have a bar color set, then we will use this
colour = self.goodColor if value >= 0 else self.badColor
# if we have a bar color set, then we will use this
colour = self.goodColor if value >= 0 else self.badColor
is_even = rect.width % 2 == 0
is_even = rect.width % 2 == 0
# the size of half our available drawing area (since we're only working in halves)
half = (rect.width / 2)
# the size of half our available drawing area (since we're only working in halves)
half = (rect.width / 2)
# calculate width of bar as a percentage of half the space
w = abs(half * (value / 100))
w = min(w, half) # Ensure that we don't overshoot our drawing area
w = math.ceil(w) # round up to nearest pixel, this ensures that we don't lose representation for sub pixels
# calculate width of bar as a percentage of half the space
w = abs(half * (value / 100))
w = min(w, half) # Ensure that we don't overshoot our drawing area
w = math.ceil(w) # round up to nearest pixel, this ensures that we don't lose representation for sub pixels
# print("Percentage: {}\t\t\t\t\tValue: {}\t\t\t\t\tWidth: {}\t\t\t\t\tHalf: {}\t\t\t\t\tRect Width: {}".format(
# round(self._percentage, 3), round(value,3), w, half, rect.width))
# print("Percentage: {}\t\t\t\t\tValue: {}\t\t\t\t\tWidth: {}\t\t\t\t\tHalf: {}\t\t\t\t\tRect Width: {}".format(
# round(self._percentage, 3), round(value,3), w, half, rect.width))
# set guide_lines every 10 pixels of the main gauge (not including borders)
if self.guide_lines:
for x in range(1, 20):
dc.SetBrush(wx.Brush(wx.LIGHT_GREY))
dc.SetPen(wx.Pen(wx.LIGHT_GREY))
dc.DrawRectangle(x * 10, 1, 1, rect.height)
# set guide_lines every 10 pixels of the main gauge (not including borders)
if self.guide_lines:
for x in range(1, 20):
dc.SetBrush(wx.Brush(wx.LIGHT_GREY))
dc.SetPen(wx.Pen(wx.LIGHT_GREY))
dc.DrawRectangle(x * 10, 1, 1, rect.height)
dc.SetBrush(wx.Brush(colour))
dc.SetPen(wx.Pen(colour))
dc.SetBrush(wx.Brush(colour))
dc.SetPen(wx.Pen(colour))
# If we have an even width, we can simply dedicate the middle-most pixels to both sides
# However, if there is an odd width, the middle pixel is shared between the left and right gauge
# If we have an even width, we can simply dedicate the middle-most pixels to both sides
# However, if there is an odd width, the middle pixel is shared between the left and right gauge
if value >= 0:
padding = (half if is_even else math.ceil(half - 1)) + 1
dc.DrawRectangle(padding, 1, w, rect.height)
if value >= 0:
padding = (half if is_even else math.ceil(half - 1)) + 1
dc.DrawRectangle(padding, 1, w, rect.height)
else:
padding = half - w + 1 if is_even else math.ceil(half) - (w - 1)
dc.DrawRectangle(padding, 1, w, rect.height)
if self.leading_edge and (self.edge_on_neutral or value != 0):
dc.SetPen(wx.Pen(wx.WHITE))
dc.SetBrush(wx.Brush(wx.WHITE))
if value > 0:
dc.DrawRectangle(min(padding + w, rect.width), 1, 1, rect.height)
else:
padding = half - w + 1 if is_even else math.ceil(half) - (w - 1)
dc.DrawRectangle(padding, 1, w, rect.height)
if self.leading_edge and (self.edge_on_neutral or value != 0):
dc.SetPen(wx.Pen(wx.WHITE))
dc.SetBrush(wx.Brush(wx.WHITE))
if value > 0:
dc.DrawRectangle(min(padding + w, rect.width), 1, 1, rect.height)
else:
dc.DrawRectangle(max(padding - 1, 1), 1, 1, rect.height)
dc.DrawRectangle(max(padding - 1, 1), 1, 1, rect.height)
def OnTimer(self, event):
old_value = self._old_percentage

View File

@@ -163,111 +163,3 @@ if __name__ == "__main__":
frame = TestAttributeSlider(None, wx.ID_ANY)
frame.Show()
app.MainLoop()
# class AttributeSliderDEV(wx.Panel):
# # Slider which abstracts users values from internal values (because the built in slider does not deal with floats
# # and the like), based on http://wxpython-users.wxwidgets.narkive.com/ekgBzA7u/anyone-ever-thought-of-a-floating-point-slider
#
# def __init__(self, parent, baseValue, minMod, maxMod):
# wx.Panel.__init__(self, parent)
#
# self.parent = parent
#
# self.base_value = baseValue
#
# self.UserMinValue = minMod
# self.UserMaxValue = maxMod
#
# # The internal slider basically represents the percentage towards the end of the range. It has to be normalized
# # in this way, otherwise when we start off with a base, if the range is skewed to one side, the base value won't
# # be centered. We use a range of -100,100 so that we can depend on the SliderValue to contain the percentage
# # toward one end
#
# # Additionally, since we want the slider to be accurate to 3 decimal places, we need to blow out the two ends here
# # (if we have a slider that needs to land on 66.66% towards the right, it will actually be converted to 66%. Se we need it to support 6,666)
#
# self.SliderMinValue = -100_000
# self.SliderMaxValue = 100_000
# self.SliderValue = 0
#
# self.statxt1 = wx.StaticText(self, wx.ID_ANY, 'left',
# style=wx.ST_NO_AUTORESIZE | wx.ALIGN_LEFT)
# self.statxt2 = wx.StaticText(self, wx.ID_ANY, 'middle',
# style=wx.ST_NO_AUTORESIZE | wx.ALIGN_CENTRE)
# self.statxt3 = wx.StaticText(self, wx.ID_ANY, 'right',
# style=wx.ST_NO_AUTORESIZE | wx.ALIGN_RIGHT)
#
# self.statxt1.SetLabel("{0:.3f}".format(self.UserMinValue * self.base_value))
# self.statxt1.SetToolTip("{0:+f}%".format((1-self.UserMinValue)*-100))
# self.statxt2.SetLabel("{0:.3f}".format(self.base_value))
# self.statxt3.SetLabel("{0:.3f}".format(self.UserMaxValue * self.base_value))
# self.statxt3.SetToolTip("{0:+f}%".format((1-self.UserMaxValue)*-100))
#
# self.slider = wx.Slider(
# self, wx.ID_ANY,
# self.SliderValue,
# self.SliderMinValue,
# self.SliderMaxValue,
# style=wx.SL_HORIZONTAL)
#
# self.slider.SetTickFreq((self.SliderMaxValue - self.SliderMinValue) / 15)
#
# self.slider.Bind(wx.EVT_SCROLL, self.OnScroll)
#
# b = 20
# hsizer1 = wx.BoxSizer(wx.HORIZONTAL)
# hsizer1.Add(self.statxt1, 1, wx.RIGHT, b)
# hsizer1.Add(self.statxt2, 1, wx.LEFT | wx.RIGHT, b)
# hsizer1.Add(self.statxt3, 1, wx.LEFT, b)
#
# b = 4
# vsizer1 = wx.BoxSizer(wx.VERTICAL)
# vsizer1.Add(hsizer1, 0, wx.EXPAND | wx.ALL, b)
# vsizer1.Add(self.slider, 0, wx.EXPAND | wx.LEFT | wx.TOP | wx.BOTTOM, b)
#
# self.SetSizerAndFit(vsizer1)
# self.parent.SetClientSize((500, vsizer1.GetSize()[1]))
#
# def OnScroll(self, event):
# self.CalculateUserValue()
#
# def SetValue(self, value):
# # todo: check this against values that might be 2.5x and whatnot
# mod = value / self.base_value
# slider_percentage = 0
# if mod < 1:
# modEnd = -1 * self.UserMinValue
# slider_percentage = (modEnd / mod) * 10_000
# elif mod > 1:
# modEnd = self.UserMaxValue
# slider_percentage = ((mod-1)/(modEnd-1)) * 100_000
#
# self.slider.SetValue(slider_percentage)
# self.CalculateUserValue()
#
# def CalculateUserValue(self):
# self.SliderValue = self.slider.GetValue()
#
# mod = 1
#
# # The slider value tells us when mod we're going to use, depending on its sign
# if self.SliderValue < 0:
# mod = self.UserMinValue
# elif self.SliderValue > 0:
# mod = self.UserMaxValue
#
# # Get the slider value percentage as an absolute value
# slider_mod = abs(self.SliderValue/1_000) / 100
#
# # Gets our new mod by use the slider's percentage to determine where in the spectrum it is
# new_mod = mod + ((1 - mod) - ((1 - mod) * slider_mod))
#
# # Modifies our base value, to get out modified value
# newValue = new_mod * self.base_value
#
# if mod == 1:
# self.statxt2.SetLabel("{0:.3f}".format(newValue))
# else:
# self.statxt2.SetLabel("{0:.3f} ({1:+.3f})".format(newValue, newValue - self.base_value, ))
# self.statxt2.SetToolTip("{0:+f}%".format(new_mod*100))

View File

@@ -61,8 +61,8 @@ class ItemMutatorPanel(wx.Panel):
self.SetSizer(mainSizer)
self.Layout()
def submitMutationChanges(self):
self.mutaList.submitMutationChanges()
def OnWindowClose(self):
self.mutaList.OnWindowClose()
class ItemMutatorList(wx.ScrolledWindow):
@@ -211,7 +211,8 @@ class ItemMutatorList(wx.ScrolledWindow):
slider.SetValue(value)
evt.Skip()
def submitMutationChanges(self):
def OnWindowClose(self):
# Submit mutation changes
fit = Fit.getInstance().getFit(self.carryingFitID)
if self.mod in fit.modules:
currentMutation = {}
@@ -223,6 +224,9 @@ class ItemMutatorList(wx.ScrolledWindow):
position=fit.modules.index(self.mod),
mutation=currentMutation,
oldMutation=self.initialMutations))
# Stop animations to prevent crashes when window is closed while it's in progress
for slider in self.event_mapping:
slider.slider.FreezeAnimation()
def callLater(self):
self.timer = None

View File

@@ -129,12 +129,12 @@ class ItemStatsDialog(wx.Dialog):
self.SetSize(size)
self.parentWnd.RegisterStatsWindow(self)
self.Show()
self.Bind(wx.EVT_CLOSE, self.OnClose)
self.Bind(wx.EVT_CHAR_HOOK, self.kbEvent)
self.Bind(wx.EVT_CLOSE, self.closeEvent)
self.Bind(wx.EVT_ACTIVATE, self.OnActivate)
self.Show()
def OnActivate(self, event):
self.parentWnd.SetActiveStatsWindow(self)
@@ -142,21 +142,18 @@ class ItemStatsDialog(wx.Dialog):
keycode = event.GetKeyCode()
mstate = wx.GetMouseState()
if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE:
self.closeWindow()
self.Close()
return
event.Skip()
def closeEvent(self, event):
self.container.onParentClose()
self.closeWindow()
event.Skip()
def closeWindow(self):
def OnClose(self, event):
self.container.OnWindowClose()
if self.dlgOrder == ItemStatsDialog.counter:
ItemStatsDialog.counter -= 1
self.parentWnd.UnregisterStatsWindow(self)
self.Destroy()
class ItemStatsContainer(wx.Panel):
def __init__(self, parent, stuff, item, context=None):
@@ -218,7 +215,7 @@ class ItemStatsContainer(wx.Panel):
if tab != -1:
self.nbContainer.SetSelection(tab)
def onParentClose(self):
def OnWindowClose(self):
mutaPanel = getattr(self, 'mutator', None)
if mutaPanel is not None:
mutaPanel.submitMutationChanges()
mutaPanel.OnWindowClose()