问题
I have a background in C# (.NET2.0) desktop application development and for the last years C for microcontrollers. Now for GUI applications on Linux and Windows, I'm learning python through wxPython.
I'm on Linux Mint 19 Mate, Python 2.7, wxPython 3.0, wxGlade 0.8.0-1, Stani's Python Editor 0.8.4.
wxGlade created some GUI code and I coded a bit of event handling into it. My problem is that my code calls wxPanel.Refresh() on a mouse event but the panel doesn't get refreshed. I know that the drawing code works because it does when the window is buried under another one and comes back to the front. Therefore I tried to call wxPanel.Hide() and wxPanel.Show instead of refreshing and that works.
But refreshing should work on its own. I have a bad feeling of using hide-and-show for dragging stuff on the panel.
What am I missing about wxPanel.Refresh()?
I also tried wxPanel.Update(), but to no avail. There are no errors on the console at runtime. Here's the complete code. Interesting part is the commented-out line 78.
# -*- coding: UTF-8 -*-
#
# generated by wxGlade 0.8.0 on Thu Jan 9 14:00:42 2020
#
import wx
# begin wxGlade: dependencies
# end wxGlade
# begin wxGlade: extracode
# end wxGlade
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
# begin wxGlade: MyFrame.__init__
kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE
wx.Frame.__init__(self, *args, **kwds)
self.SetSize((729, 521))
# Menu Bar
self.MainMenuBar = wx.MenuBar()
wxglade_tmp_menu = wx.Menu()
wxglade_tmp_menu.Append(wx.ID_ANY, "Open", "")
wxglade_tmp_menu.Append(wx.ID_ANY, "Close", "")
wxglade_tmp_menu.AppendSeparator()
wxglade_tmp_menu.Append(wx.ID_ANY, "Exit", "")
self.MainMenuBar.Append(wxglade_tmp_menu, "File")
wxglade_tmp_menu = wx.Menu()
wxglade_tmp_menu.Append(wx.ID_ANY, "Preferences", "")
self.MainMenuBar.Append(wxglade_tmp_menu, "Edit")
wxglade_tmp_menu = wx.Menu()
wxglade_tmp_menu.Append(wx.ID_ANY, "About", "")
self.MainMenuBar.Append(wxglade_tmp_menu, "?")
self.SetMenuBar(self.MainMenuBar)
# Menu Bar end
self.DrawPanel = wx.Panel(self, wx.ID_ANY)
self.Bind(wx.EVT_LEFT_DOWN, self.DrawPanel_HandleLEFT_DOWN)
self.Bind(wx.EVT_LEFT_UP, self.DrawPanel_HandleLEFT_UP)
self.Bind(wx.EVT_MOTION, self.DrawPanel_HandleMOTION)
self.Bind(wx.EVT_PAINT, self.DrawPanel_HandlePAINT)
self.__set_properties()
self.__do_layout()
# end wxGlade
self.DrawPanel_LmbDown = False
self.coords = []
def __set_properties(self):
# begin wxGlade: MyFrame.__set_properties
self.SetTitle("frame")
# end wxGlade
def __do_layout(self):
# begin wxGlade: MyFrame.__do_layout
MainSizer = wx.BoxSizer(wx.VERTICAL)
Statusbar = wx.BoxSizer(wx.HORIZONTAL)
MainSizer.Add(self.DrawPanel, 0, wx.EXPAND, 0)
Statusbar.Add((0, 0), 0, 0, 0)
Statusbar.Add((0, 0), 0, 0, 0)
Statusbar.Add((0, 0), 0, 0, 0)
Statusbar.Add((0, 0), 0, 0, 0)
MainSizer.Add(Statusbar, 0, 0, 0)
self.SetSizer(MainSizer)
self.Layout()
# end wxGlade
def DrawPanel_HandleLEFT_DOWN(self, event):
self.DrawPanel_LmbDown = True
coord = event.GetPosition()
print("Lmb Down at ", coord[0], ";", coord[1])
if(coord not in self.coords):
self.coords.append(coord)
print(self.coords)
# self.DrawPanel.Refresh() # Todo: Find out why Refresh() doesn't work and
self.DrawPanel.Hide() # A combination of Hide() and
self.DrawPanel.Show() # Show() must do the trick.
event.Skip()
def DrawPanel_HandleLEFT_UP(self, event):
self.DrawPanel_LmbDown = False
coord = event.GetPosition()
print("Lmb Up at ", coord[0], ";", coord[1])
event.Skip()
def DrawPanel_HandleMOTION(self, event):
if(self.DrawPanel_LmbDown):
coord = event.GetPosition()
print("Moving with Lmb Down to ", coord[0], ";", coord[1])
event.Skip()
def DrawPanel_HandlePAINT(self, event):
dc = wx.PaintDC(self)
brush = wx.Brush("white")
dc.SetBackground(brush)
dc.Clear()
pen = wx.Pen("red")
dc.SetPen(pen)
for coord in self.coords:
dc.DrawLine(coord[0] - 2, coord[1] - 2, coord[0] + 2, coord[1] + 2)
dc.DrawLine(coord[0] - 2, coord[1] + 2, coord[0] + 2, coord[1] - 2)
event.Skip()
# end of class MyFrame
class MyApp(wx.App):
def OnInit(self):
self.MainFrame = MyFrame(None, wx.ID_ANY, "")
self.SetTopWindow(self.MainFrame)
self.MainFrame.Show()
return True
# end of class MyApp
if __name__ == "__main__":
app = MyApp(0)
app.MainLoop()
回答1:
It appears that you are expecting to handle the mouse and paint events for self.DrawPanel
, but you are actually binding the event handlers to the frame instead. This has consequences. For example, in your paint event handler self
is the frame, not the DrawPanel
so the dc you create will do its drawing on the frame's client area instead of the panel. The panel will likely be getting in the way of seeing what's on the frame, and blocking some of the mouse events, etc.
A better design would be to make the draw panel be a separate class, and move the related event bindings and event handlers to that class. Then the frame will just need to create an instance of that new class instead of a base wx.Panel
.
来源:https://stackoverflow.com/questions/59689325/how-to-wxpanel-refresh