我使用wxPython创建了一个简单的图像查看器,但是当使用箭头键更改图像时,我遇到了闪烁的问题。我如何解决这个问题?下面是我到目前为止写的代码:
import wx
import os
import glob
class MyFrame(wx.Frame):
def __init__(self, parent):
super().__init__(parent, title="Image Viewer", size=(800, 600))
self.panel = wx.Panel(self)
self.panel.Bind(wx.EVT_PAINT, self.on_paint)
self.images = []
self.image_index = -1
self.image_path = None
self.Bind(wx.EVT_KEY_DOWN, self.on_key_down)
self.Bind(wx.EVT_SIZE, self.on_size)
self.SetFocus()
def on_key_down(self, event):
keycode = event.GetKeyCode()
if keycode == wx.WXK_LEFT:
if self.image_index > 0:
self.image_index -= 1
self.image_path = self.images[self.image_index]
self.display_image(self.image_path)
elif keycode == wx.WXK_RIGHT:
if self.image_index < len(self.images) - 1:
self.image_index += 1
self.image_path = self.images[self.image_index]
self.display_image(self.image_path)
event.Skip()
def on_size(self, event):
if self.image_path:
self.display_image(self.image_path)
event.Skip()
def display_image(self, path):
self.image_path = path
self.panel.Refresh()
self.panel.Update()
def on_paint(self, event):
if self.image_path:
image = wx.Image(self.image_path, wx.BITMAP_TYPE_ANY)
# Resize the image to fit the window size
window_width, window_height = self.panel.GetClientSize()
image_width, image_height = image.GetSize()
scale = min(window_width / image_width, window_height / image_height)
new_width = int(image_width * scale)
new_height = int(image_height * scale)
image = image.Scale(new_width, new_height, wx.IMAGE_QUALITY_HIGH)
bitmap = wx.Bitmap(image)
dc = wx.BufferedPaintDC(self.panel)
dc.SetBackground(wx.Brush(self.panel.GetBackgroundColour()))
dc.DrawBitmap(bitmap, (window_width - new_width) // 2, (window_height - new_height) // 2, True)
def load_images(self, filename):
supported_extensions = ('*.jpg', '*.jpeg', '*.png', '*.bmp', '*.gif')
self.images = []
for ext in supported_extensions:
self.images.extend(sorted(glob.glob(os.path.join(os.path.dirname(filename), ext), recursive=True)))
if filename in self.images:
self.image_index = self.images.index(filename)
else:
raise FileNotFoundError(f"Specified image not found: {filename}")
def select_image(self):
wildcard = "Image files (*.jpg;*.jpeg;*.png;*.bmp;*.gif)|*.jpg;*.jpeg;*.png;*.bmp;*.gif"
dialog = wx.FileDialog(self, "Open image", wildcard=wildcard, style=wx.FD_OPEN)
if dialog.ShowModal() == wx.ID_OK:
self.image_path = dialog.GetPath()
self.load_images(self.image_path)
self.display_image(self.image_path)
dialog.Destroy()
if __name__ == '__main__':
app = wx.App()
frame = MyFrame(None)
frame.Show()
frame.select_image()
app.MainLoop()
到目前为止,我们已经尝试了几种方法来消除在使用简单的图像查看器中的箭头键更改图像时的闪烁:
- 在显示新图像后在面板上调用Refresh()和Update()方法。2 . 利用wx。BufferedPaintDC而不是wx。在on_paint()方法中的PaintDC,通过双重缓冲来减少闪烁。
- 设置wx的背景色。BufferedPaintDC来匹配面板的背景色。
- 尝试使用wx。BufferedDC_None,但这会导致错误,因为wx没有一个名为BufferedDC_None的属性。
我会稍微修改一下:
import wx
import os
import glob
class MyFrame(wx.Frame):
def __init__(self, parent):
super().__init__(parent, title="Image Viewer", size=(800, 600))
self.panel = wx.Panel(self)
self.panel.Bind(wx.EVT_PAINT, self.on_paint)
self.panel.Bind(wx.EVT_ERASE_BACKGROUND, self.on_erase)
self.images = []
self.image_index = -1
self.image = None
self.Bind(wx.EVT_KEY_DOWN, self.on_key_down)
self.Bind(wx.EVT_SIZE, self.on_size)
self.SetFocus()
def on_key_down(self, event):
keycode = event.GetKeyCode()
if keycode == wx.WXK_LEFT:
if self.image_index > 0:
self.image_index -= 1
image_path = self.images[self.image_index]
self.display_image(image_path)
elif keycode == wx.WXK_RIGHT:
if self.image_index < len(self.images) - 1:
self.image_index += 1
image_path = self.images[self.image_index]
self.display_image(image_path)
event.Skip()
def on_size(self, event):
self.panel.Refresh()
event.Skip()
def display_image(self, path):
self.image = wx.Image(path, wx.BITMAP_TYPE_ANY)
self.panel.Refresh()
def on_paint(self, event):
if not self.image:
return # No image
# Resize the image to fit the window size
window_width, window_height = self.panel.GetClientSize()
image_width, image_height = self.image.GetSize()
scale = min(window_width / image_width, window_height / image_height)
new_width = int(image_width * scale)
new_height = int(image_height * scale)
image = self.image.Scale(new_width, new_height, wx.IMAGE_QUALITY_HIGH)
bitmap = wx.Bitmap(image)
dc = wx.ClientDC(self.panel)
x0 = (window_width - new_width) // 2
y0 = (window_height - new_height) // 2
x1 = x0 + new_width
y1 = y0 + new_height
dc.SetPen(wx.Pen(self.panel.GetBackgroundColour()))
dc.SetBrush(wx.Brush(self.panel.GetBackgroundColour()))
if x0 > 0:
dc.DrawRectangle(0, 0, x0, window_height)
if x1 < window_width:
dc.DrawRectangle(x1, 0, window_width-x1, window_height)
if y0 > 0:
dc.DrawRectangle(x0, 0, new_width, y0)
if y1 < window_height:
dc.DrawRectangle(x0, y1, new_width, window_height-y1)
dc.DrawBitmap(bitmap, x0, y0, True)
def on_erase(self, event):
pass
def load_images(self, filename):
supported_extensions = ('*.jpg', '*.jpeg', '*.png', '*.bmp', '*.gif')
self.images = []
for ext in supported_extensions:
self.images.extend(sorted(glob.glob(os.path.join(os.path.dirname(filename), ext), recursive=True)))
if filename in self.images:
self.image_index = self.images.index(filename)
else:
raise FileNotFoundError(f"Specified image not found: {filename}")
def select_image(self):
wildcard = "Image files (*.jpg;*.jpeg;*.png;*.bmp;*.gif)|*.jpg;*.jpeg;*.png;*.bmp;*.gif"
dialog = wx.FileDialog(self, "Open image", wildcard=wildcard, style=wx.FD_OPEN)
if dialog.ShowModal() == wx.ID_OK:
image_path = dialog.GetPath()
self.load_images(image_path)
self.display_image(image_path)
dialog.Destroy()
if __name__ == '__main__':
app = wx.App()
frame = MyFrame(None)
frame.Show()
frame.select_image()
app.MainLoop()
主要变化:
- 我阻止重新绘制窗口背景(见空OnErase处理程序) OnSize处理程序只调用Refresh
- 我只在display_image函数中加载一次图像 我使用dc擦除窗口的背景。DrawRectangle
- 我只在没有被图片覆盖的地方绘制窗口的背景