From 4ca3f10bc947d34347c76ce40de683072fca28a0 Mon Sep 17 00:00:00 2001 From: DarkPhoenix Date: Tue, 18 Jun 2019 16:12:27 +0300 Subject: [PATCH] Move more stuff away from the frame --- gui/graphFrame/events.py | 24 ------ gui/graphFrame/frame.py | 154 +++++++-------------------------------- gui/graphFrame/lists.py | 131 +++++++++++++++++++++++++-------- gui/graphFrame/panel.py | 51 +++++++------ 4 files changed, 158 insertions(+), 202 deletions(-) delete mode 100644 gui/graphFrame/events.py diff --git a/gui/graphFrame/events.py b/gui/graphFrame/events.py deleted file mode 100644 index 2adab83b1..000000000 --- a/gui/graphFrame/events.py +++ /dev/null @@ -1,24 +0,0 @@ -# ============================================================================= -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -# ============================================================================= - - -import wx.lib.newevent - - -RefreshGraph, REFRESH_GRAPH = wx.lib.newevent.NewEvent() diff --git a/gui/graphFrame/frame.py b/gui/graphFrame/frame.py index 5040d17d4..da8853b83 100644 --- a/gui/graphFrame/frame.py +++ b/gui/graphFrame/frame.py @@ -30,8 +30,6 @@ import gui.globalEvents as GE import gui.mainFrame from gui.bitmap_loader import BitmapLoader from gui.builtinGraphs.base import Graph -from service.fit import Fit -from .events import REFRESH_GRAPH from .panel import GraphControlPanel @@ -70,6 +68,11 @@ class GraphFrame(wx.Frame): pyfalog.warning('Matplotlib is not enabled. Skipping initialization.') return + wx.Frame.__init__(self, parent, title='pyfa: Graph Generator', style=style, size=(520, 390)) + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + self.SetIcon(wx.Icon(BitmapLoader.getBitmap('graphs_small', 'gui'))) + # Remove matplotlib font cache, see #234 try: cache_dir = mpl._get_cachedir() @@ -79,19 +82,14 @@ class GraphFrame(wx.Frame): if os.access(cache_dir, os.W_OK | os.X_OK) and os.path.isfile(cache_file): os.remove(cache_file) - wx.Frame.__init__(self, parent, title='pyfa: Graph Generator', style=style, size=(520, 390)) - - i = wx.Icon(BitmapLoader.getBitmap('graphs_small', 'gui')) - self.SetIcon(i) - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - mainSizer = wx.BoxSizer(wx.VERTICAL) - # Graph selector + # Layout - graph selector self.graphSelection = wx.Choice(self, wx.ID_ANY, style=0) + self.graphSelection.Bind(wx.EVT_CHOICE, self.OnGraphSwitched) mainSizer.Add(self.graphSelection, 0, wx.EXPAND) - # Plot area + # Layout - plot area self.figure = Figure(figsize=(5, 3), tight_layout={'pad': 1.08}) rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() clr = [c / 255. for c in rgbtuple] @@ -105,45 +103,29 @@ class GraphFrame(wx.Frame): mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND) - # Graph control panel + # Layout - graph control panel self.ctrlPanel = GraphControlPanel(self, self) mainSizer.Add(self.ctrlPanel, 0, wx.EXPAND | wx.ALL, 0) + # Setup - graph selector for view in Graph.views: - view = view() - self.graphSelection.Append(view.name, view) - + self.graphSelection.Append(view.name, view()) self.graphSelection.SetSelection(0) - self.updateGraphWidgets() - self.sl1 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) - mainSizer.Add(self.sl1, 0, wx.EXPAND) + self.ctrlPanel.updateControlsForView(self.getView()) - - self.ctrlPanel.fitList.fitList.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick) - self.ctrlPanel.fitList.fitList.Bind(wx.EVT_CONTEXT_MENU, self.OnContextMenu) - self.Bind(REFRESH_GRAPH, self.OnRefreshGraph) - self.ctrlPanel.showY0Cb.Bind(wx.EVT_CHECKBOX, self.OnNonDestructiveControlsUpdate) - self.mainFrame.Bind(GE.FIT_CHANGED, self.OnFitChanged) - self.mainFrame.Bind(GE.FIT_REMOVED, self.OnFitRemoved) + # Event bindings self.Bind(wx.EVT_CLOSE, self.closeEvent) self.Bind(wx.EVT_CHAR_HOOK, self.kbEvent) - self.Bind(wx.EVT_CHOICE, self.graphChanged) + self.mainFrame.Bind(GE.FIT_CHANGED, self.OnFitChanged) from gui.builtinStatsViews.resistancesViewFull import EFFECTIVE_HP_TOGGLED # Grr crclar gons self.mainFrame.Bind(EFFECTIVE_HP_TOGGLED, self.OnEhpToggled) - self.contextMenu = wx.Menu() - removeItem = wx.MenuItem(self.contextMenu, 1, 'Remove Fit') - self.contextMenu.Append(removeItem) - self.contextMenu.Bind(wx.EVT_MENU, self.ContextMenuHandler, removeItem) - self.SetSizer(mainSizer) + + self.draw() self.Fit() self.SetMinSize(self.GetSize()) - def handleDrag(self, type, fitID): - if type == 'fit': - self.AppendFitToList(fitID) - def closeEvent(self, event): self.closeWindow() event.Skip() @@ -154,22 +136,8 @@ class GraphFrame(wx.Frame): if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE: self.closeWindow() return - elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL: - self.ctrlPanel.fitList.fitList.selectAll() - elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE: - self.removeFits(self.getSelectedFits()) event.Skip() - def OnContextMenu(self, event): - if self.getSelectedFits(): - self.PopupMenu(self.contextMenu) - - def ContextMenuHandler(self, event): - selectedMenuItem = event.GetId() - if selectedMenuItem == 1: # Copy was chosen - fits = self.getSelectedFits() - self.removeFits(fits) - def OnEhpToggled(self, event): event.Skip() view = self.getView() @@ -179,65 +147,38 @@ class GraphFrame(wx.Frame): def OnFitChanged(self, event): event.Skip() - view = self.getView() - view.clearCache(key=event.fitID) + self.getView().clearCache(key=event.fitID) self.draw() - def OnFitRemoved(self, event): - event.Skip() - fit = next((f for f in self.ctrlPanel.fits if f.ID == event.fitID), None) - if fit is not None: - self.removeFits([fit]) - - def graphChanged(self, event): - self.ctrlPanel.selectedY = None - self.updateGraphWidgets() + def OnGraphSwitched(self, event): + self.clearCache() + self.ctrlPanel.updateControlsForView(self.getView()) + self.draw() event.Skip() def closeWindow(self): from gui.builtinStatsViews.resistancesViewFull import EFFECTIVE_HP_TOGGLED # Grr gons - self.ctrlPanel.fitList.fitList.Unbind(wx.EVT_LEFT_DCLICK, handler=self.OnLeftDClick) self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.OnFitChanged) - self.mainFrame.Unbind(GE.FIT_REMOVED, handler=self.OnFitRemoved) self.mainFrame.Unbind(EFFECTIVE_HP_TOGGLED, handler=self.OnEhpToggled) + self.ctrlPanel.unbindExternalEvents() self.Destroy() def getView(self): return self.graphSelection.GetClientData(self.graphSelection.GetSelection()) - def getValues(self): - values = {} - for fieldHandle, field in self.ctrlPanel.fields.items(): - values[fieldHandle] = field.GetValue() + def clearCache(self, key=None): + self.getView().clearCache(key=key) - return values - - def OnNonDestructiveControlsUpdate(self, event): - event.Skip() - self.draw() - - def OnRefreshGraph(self, event): - self.draw() - - def updateGraphWidgets(self): - view = self.getView() - self.ctrlPanel.updateControlsForView(view) - self.draw() - - - def draw(self, event=None): + def draw(self): global mpl_version - if event is not None: - event.Skip() - # todo: FIX THIS, see #1430. draw() is not being unbound properly when the window closes, this is an easy fix, # but not a proper solution if not self: pyfalog.warning('GraphFrame handled event, however GraphFrame no longer exists. Ignoring event') return - values = self.getValues() + values = self.ctrlPanel.getValues() view = self.getView() self.subplot.clear() self.subplot.grid(True) @@ -255,7 +196,7 @@ class GraphFrame(wx.Frame): self.subplot.set(xlabel=view.xDef.axisLabel, ylabel=view.yDefs[chosenY].axisLabel) - for fit in self.ctrlPanel.fits: + for fit in self.ctrlPanel.fitList.fits: try: xs, ys = view.getPlotPoints(fit, extraInputs, xRange, 100, chosenY) @@ -318,44 +259,3 @@ class GraphFrame(wx.Frame): self.canvas.draw() self.Refresh() - - def AppendFitToList(self, fitID): - sFit = Fit.getInstance() - fit = sFit.getFit(fitID) - if fit not in self.ctrlPanel.fits: - self.ctrlPanel.fits.append(fit) - - self.ctrlPanel.fitList.fitList.update(self.ctrlPanel.fits) - self.draw() - - def OnLeftDClick(self, event): - row, _ = self.ctrlPanel.fitList.fitList.HitTest(event.Position) - if row != -1: - try: - fit = self.ctrlPanel.fits[row] - except IndexError: - pass - else: - self.removeFits([fit]) - - def removeFits(self, fits): - toRemove = [f for f in fits if f in self.ctrlPanel.fits] - if not toRemove: - return - for fit in toRemove: - self.ctrlPanel.fits.remove(fit) - self.ctrlPanel.fitList.fitList.update(self.ctrlPanel.fits) - view = self.getView() - for fit in fits: - view.clearCache(key=fit.ID) - self.draw() - - def getSelectedFits(self): - fits = [] - for row in self.ctrlPanel.fitList.fitList.getSelectedRows(): - try: - fit = self.ctrlPanel.fits[row] - except IndexError: - continue - fits.append(fit) - return fits diff --git a/gui/graphFrame/lists.py b/gui/graphFrame/lists.py index 4a89650ec..4956f23ab 100644 --- a/gui/graphFrame/lists.py +++ b/gui/graphFrame/lists.py @@ -22,47 +22,118 @@ import wx import gui.display +import gui.globalEvents as GE +from service.fit import Fit -class FitList(wx.Panel): +class FitList(gui.display.Display): - def __init__(self, parent): - wx.Panel.__init__(self, parent) - self.mainSizer = wx.BoxSizer(wx.VERTICAL) - self.SetSizer(self.mainSizer) + DEFAULT_COLS = ( + 'Base Icon', + 'Base Name') + + def __init__(self, graphFrame, parent): + super().__init__(parent) + self.graphFrame = graphFrame + self.fits = [] - self.fitList = FitDisplay(self) - self.mainSizer.Add(self.fitList, 1, wx.EXPAND) fitToolTip = wx.ToolTip('Drag a fit into this list to graph it') - self.fitList.SetToolTip(fitToolTip) + self.SetToolTip(fitToolTip) + + fit = Fit.getInstance().getFit(self.graphFrame.mainFrame.getActiveFit()) + if fit is not None: + self.fits.append(fit) + self.update(self.fits) + + self.contextMenu = wx.Menu() + removeItem = wx.MenuItem(self.contextMenu, 1, 'Remove Fit') + self.contextMenu.Append(removeItem) + self.contextMenu.Bind(wx.EVT_MENU, self.ContextMenuHandler, removeItem) + + self.graphFrame.mainFrame.Bind(GE.FIT_REMOVED, self.OnFitRemoved) + self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick) + self.Bind(wx.EVT_CHAR_HOOK, self.kbEvent) + self.Bind(wx.EVT_CONTEXT_MENU, self.OnContextMenu) -class FitDisplay(gui.display.Display): + def kbEvent(self, event): + keycode = event.GetKeyCode() + mstate = wx.GetMouseState() + if keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL: + self.selectAll() + elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE: + self.removeFits(self.getSelectedFits()) + event.Skip() - DEFAULT_COLS = ['Base Icon', - 'Base Name'] + def OnLeftDClick(self, event): + row, _ = self.HitTest(event.Position) + if row != -1: + try: + fit = self.fits[row] + except IndexError: + pass + else: + self.removeFits([fit]) - def __init__(self, parent): - gui.display.Display.__init__(self, parent) + def OnContextMenu(self, event): + if self.getSelectedFits(): + self.PopupMenu(self.contextMenu) + + def ContextMenuHandler(self, event): + selectedMenuItem = event.GetId() + if selectedMenuItem == 1: + fits = self.getSelectedFits() + self.removeFits(fits) + + def OnFitRemoved(self, event): + event.Skip() + fit = next((f for f in self.fits if f.ID == event.fitID), None) + if fit is not None: + self.removeFits([fit]) + + def getSelectedFits(self): + fits = [] + for row in self.getSelectedRows(): + try: + fit = self.fits[row] + except IndexError: + continue + fits.append(fit) + return fits + + def removeFits(self, fits): + toRemove = [f for f in fits if f in self.fits] + if not toRemove: + return + for fit in toRemove: + self.fits.remove(fit) + self.update(self.fits) + for fit in fits: + self.graphFrame.clearCache(key=fit.ID) + self.graphFrame.draw() + + def unbindExternalEvents(self): + self.graphFrame.mainFrame.Unbind(GE.FIT_REMOVED, handler=self.OnFitRemoved) + + def handleDrag(self, type, fitID): + if type == 'fit': + sFit = Fit.getInstance() + fit = sFit.getFit(fitID) + if fit not in self.fits: + self.fits.append(fit) + self.update(self.fits) + self.graphFrame.draw() -class TargetList(wx.Panel): +class TargetList(gui.display.Display): - def __init__(self, parent): - wx.Panel.__init__(self, parent) - self.mainSizer = wx.BoxSizer(wx.VERTICAL) - self.SetSizer(self.mainSizer) + DEFAULT_COLS = ( + 'Base Icon', + 'Base Name') - self.targetList = TargetDisplay(self) - self.mainSizer.Add(self.targetList, 1, wx.EXPAND) + def __init__(self, graphFrame, parent): + super().__init__(parent) + self.graphFrame = graphFrame + self.targetFits = [] fitToolTip = wx.ToolTip('Drag a fit into this list to graph it') - self.targetList.SetToolTip(fitToolTip) - - -class TargetDisplay(gui.display.Display): - - DEFAULT_COLS = ['Base Icon', - 'Base Name'] - - def __init__(self, parent): - gui.display.Display.__init__(self, parent) + self.SetToolTip(fitToolTip) diff --git a/gui/graphFrame/panel.py b/gui/graphFrame/panel.py index a37a380b7..3e9c46c2b 100644 --- a/gui/graphFrame/panel.py +++ b/gui/graphFrame/panel.py @@ -23,7 +23,6 @@ import wx from gui.bitmap_loader import BitmapLoader from service.fit import Fit -from .events import RefreshGraph from .lists import FitList, TargetList @@ -36,8 +35,6 @@ class GraphControlPanel(wx.Panel): self.fields = {} self.selectedY = None self.selectedYRbMap = {} - self.drawTimer = wx.Timer(self) - self.Bind(wx.EVT_TIMER, self.redrawRequest, self.drawTimer) mainSizer = wx.BoxSizer(wx.VERTICAL) @@ -45,6 +42,7 @@ class GraphControlPanel(wx.Panel): viewOptSizer = wx.BoxSizer(wx.VERTICAL) self.showY0Cb = wx.CheckBox(self, wx.ID_ANY, 'Always show Y = 0', wx.DefaultPosition, wx.DefaultSize, 0) self.showY0Cb.SetValue(True) + self.showY0Cb.Bind(wx.EVT_CHECKBOX, self.OnShowY0Change) viewOptSizer.Add(self.showY0Cb, 0, wx.LEFT | wx.TOP | wx.RIGHT | wx.EXPAND, 5) self.graphSubselSizer = wx.BoxSizer(wx.VERTICAL) viewOptSizer.Add(self.graphSubselSizer, 0, wx.ALL | wx.EXPAND, 5) @@ -55,27 +53,33 @@ class GraphControlPanel(wx.Panel): mainSizer.Add(paramSizer, 0, wx.EXPAND | wx.ALL, 0) srcTgtSizer = wx.BoxSizer(wx.HORIZONTAL) - fit = Fit.getInstance().getFit(self.graphFrame.mainFrame.getActiveFit()) - self.fits = [fit] if fit is not None else [] - self.fitList = FitList(self) + self.fitList = FitList(graphFrame, self) self.fitList.SetMinSize((270, -1)) - self.fitList.fitList.update(self.fits) srcTgtSizer.Add(self.fitList, 1, wx.EXPAND) self.targets = [] - self.targetList = TargetList(self) + self.targetList = TargetList(graphFrame, self) self.targetList.SetMinSize((270, -1)) - self.targetList.targetList.update(self.targets) + self.targetList.update(self.targets) srcTgtSizer.Add(self.targetList, 1, wx.EXPAND) mainSizer.Add(srcTgtSizer, 1, wx.EXPAND | wx.ALL, 0) self.SetSizer(mainSizer) + self.drawTimer = wx.Timer(self) + self.Bind(wx.EVT_TIMER, self.OnDrawTimer, self.drawTimer) + + def getValues(self): + values = {} + for fieldHandle, field in self.fields.items(): + values[fieldHandle] = field.GetValue() + return values + @property def showY0(self): return self.showY0Cb.GetValue() def updateControlsForView(self, view): - view.clearCache() + self.selectedY = None self.graphSubselSizer.Clear() self.inputsSizer.Clear() for child in self.Children: @@ -103,7 +107,7 @@ class GraphControlPanel(wx.Panel): for fieldHandle, fieldDef in (('x', view.xDef), *view.extraInputs.items()): textBox = wx.TextCtrl(self, wx.ID_ANY, style=0) self.fields[fieldHandle] = textBox - textBox.Bind(wx.EVT_TEXT, self.onFieldChanged) + textBox.Bind(wx.EVT_TEXT, self.OnFieldChanged) self.inputsSizer.Add(textBox, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 3) if fieldDef.inputDefault is not None: inputDefault = fieldDef.inputDefault @@ -127,22 +131,27 @@ class GraphControlPanel(wx.Panel): self.inputsSizer.Add(imgLabelSizer, 0, wx.ALIGN_CENTER_VERTICAL) self.Layout() + def OnShowY0Change(self, event): + event.Skip() + self.graphFrame.draw() + def OnYTypeUpdate(self, event): event.Skip() obj = event.GetEventObject() formatName = obj.GetLabel() self.selectedY = self.selectedYRbMap[formatName] - self.redrawRequest() + self.graphFrame.draw() - def redrawRequest(self, event=None): - self.drawTimer.Stop() - wx.PostEvent(self.graphFrame, RefreshGraph()) - - def delayedDraw(self, event=None): + def OnFieldChanged(self, event): + event.Skip() self.drawTimer.Stop() self.drawTimer.Start(Fit.getInstance().serviceFittingOptions['marketSearchDelay'], True) - def onFieldChanged(self, event): - view = self.graphFrame.getView() - view.clearCache() - self.delayedDraw() + def OnDrawTimer(self, event): + event.Skip() + self.graphFrame.clearCache() + self.graphFrame.draw() + + def unbindExternalEvents(self): + self.fitList.unbindExternalEvents() +