我需要制作基本图像查看器。 主要关注的是如何实现(在gui组件和图像处理方面)诸如放大,滚动和“手工工具”等功能。
实现这一目标似乎有几种选择,主要是在图形界面框架上承担责任的程度,而不是手动实施。
对我来说显而易见的两个解决方案是:
1)使用某些图像处理库的功能,可以自行制作调整图像可见部分的大小和裁剪。然后在覆盖的 onPaint()方法中的某个窗口/控件上绘制图像(或其部分)。需要编写滚动条更新(使用'手工工具'时)和操作(直接使用时)代码。
2)包含图像的超大控件(StaticBitmap或其他)放在一个自动滚动的窗口中。然后需要弄清楚如何将图像坐标转换为滚动坐标。
两种方式看起来都很尴尬。任何想法如何以一种简洁的方式做到这一点?或者我觉得丑陋只是唯一的出路?
我正在使用Python与wxPython / wxWidgets和PIL,但问题在很大程度上是语言和平台无关的。
欢迎使用示例代码和指向源(不太臃肿的东西)的链接。
答案 0 :(得分:5)
这是一个可能有用的教程。 Build a wxPython Image Viewer
我实际上并没有看过所有的视频,所以我无法说出它解决你的具体问题的程度。
此外,这是Jeff Atwood关于Coding Horror的博客文章,可以申请。 Programming Is Hard, Let's Go Shopping!它说明你应该花时间编写自己的代码以及何时只使用第三方解决方案。
答案 1 :(得分:4)
我是新来的,经过一段时间的搜索,我仍然无法找到上传文件的方法。哦,这是帖子中的代码。很抱歉没有描述性的变量名称和缺少评论。我想你要看的主要功能是processPicture和showPicture。
编辑:重申一下,我是以this tutorial中的示例开始的。
import wx, os, string, sys
from PIL import Image
# Scroll wheel and +/- do zoom in/out. f toggles full screen. r rotates.
# m changes PIL mode from low quality (fast) to high quality (slow).
# Images under 1000x1000 are automatically on high quality.
# Middle button down while dragging moves image around, as do arrow
# keys (if image is bigger than window).
# Left and right mouse buttons are next and previous image.
# There is no functionality to load an image. When an executeable is made, the
# viewer is started by opening an image with it.
# To run this file from command line, comment out line 55 and uncomment
# line 54, then do "viewer.py sampleImage"
# There are several lines that are Windows specific. They (probably) all have
# to do with paths, i.e, "/" vs "\".
class ImageFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None,title = "viewer")
self.Centre()
self.Size = (450,450)
self.imageBox = wx.Window(self)
self.vbox = wx.BoxSizer(wx.VERTICAL)
self.CreateStatusBar(5)
self.SetStatusWidths([-1, 70, 50, 50, 30])
self.cursor = wx.StockCursor(wx.CURSOR_ARROW)
self.moveCursor = wx.StockCursor(wx.CURSOR_SIZING)
self.vbox.Add(self.imageBox,proportion=1,flag = wx.EXPAND)
self.SetSizer(self.vbox)
self.Show()
self.sbm = 0
self.sbmList = []
self.name = ''
self.url = ''
self.dir = ''
self.factor = 1.0
self.rotation = 0
self.width = 0
self.height = 0
self.count = 0
self.size = 0
self.numOfPics = 0
self.mc = False
self.fs = False
self.mode = 0
self.SetStatusText(str(self.mode), 4)
if len(sys.argv) == 2:
#self.url = os.getcwd() + '\\' + sys.argv[1]
self.url = sys.argv[1]
self.name = self.url.split('\\')[len(self.url.split('\\'))-1]
self.dir = self.url.replace('\\' + self.name,'')
self.loadDirectory(self.dir)
self.processPicture()
self.imageBox.Bind(wx.EVT_SIZE, lambda evt: self.rescale(evt,1))
self.imageBox.Bind(wx.EVT_MOUSEWHEEL,self.zoom)
self.imageBox.Bind(wx.EVT_KEY_DOWN, self.keyEvent)
self.imageBox.Bind(wx.EVT_MIDDLE_UP, self.endDrag)
self.imageBox.SetBackgroundColour((0,0,0,0))
self.imageBox.Bind(wx.EVT_LEFT_DOWN, self.next)
self.imageBox.Bind(wx.EVT_RIGHT_DOWN, self.prev)
def nameFromUrl(self,url):
name = url.split('\\')
name = name[len(name)-1]
return name
def processPicture(self, factor = 0):
img = Image.open(self.url)
self.width = img.size[0]
self.height = img.size[1]
ogHeight = self.height
ogWidth = self.width
xWin = self.imageBox.Size[0]
yWin = self.imageBox.Size[1]
winRatio = 1.0*xWin/yWin
imgRatio = 1.0*self.width/self.height
self.factor = factor*self.factor
if factor == 0:
self.factor = 1
mode = 0
if (ogWidth <=1000 and ogHeight <= 1000) or self.mode == 1:
mode = 1
if imgRatio >= winRatio: #match widths
self.width = self.factor*xWin
self.height = self.factor*xWin/imgRatio
img = img.resize((int(self.width),int(self.height)),mode)
else: #match heights
self.height = self.factor*yWin
self.width = self.factor*yWin*imgRatio
img = img.resize((int(self.width),int(self.height)),mode)
label = str(int(100*self.width/ogWidth))
name = self.nameFromUrl(self.url)
index = self.sbmList.index(name)
self.SetStatusText(name, 0)
self.SetStatusText(str(ogWidth) + 'x' + str(ogHeight), 1)
self.SetStatusText(label + '%', 2)
self.SetStatusText(str(index+1) + '/' + str(self.numOfPics), 3)
if self.rotation % 360 != 0:
img = img.rotate(self.rotation)
self.width = img.size[0]
self.height = img.size[1]
wximg = wx.EmptyImage(img.size[0],img.size[1])
wximg.SetData(img.convert("RGB").tostring())
wximg.SetAlphaData(img.convert("RGBA").tostring()[3::4])
self.showPicture(wximg)
def showPicture(self,img):
bmp = wx.BitmapFromImage(img)
x = (self.imageBox.Size[0] - self.width)/2.0
y = (self.imageBox.Size[1] - self.height)/2.0
tmp = wx.StaticBitmap(self.imageBox,wx.ID_ANY,bmp,(x,y))
tmp.Bind(wx.EVT_LEFT_DOWN, self.next)
tmp.Bind(wx.EVT_RIGHT_DOWN, self.prev)
tmp.Bind(wx.EVT_MOTION, self.drag)
tmp.Bind(wx.EVT_MIDDLE_UP, self.endDrag)
tmp.SetBackgroundColour((180,180,180,180))
if self.sbm:
self.sbm.Destroy()
self.sbm = tmp
self.imageBox.Refresh()
def loadDirectory(self,dir):
self.sbmList = []
for image in os.listdir(dir):
if image.lower().endswith('jpg') or image.lower().endswith('png') or image.lower().endswith('jpeg') or image.lower().endswith('gif') or image.lower().endswith('bmp'):
self.sbmList.append(image)
self.numOfPics = len(self.sbmList)
def next(self,event):
if self.name in self.sbmList:
n = self.sbmList.index(self.name)
if n == len(self.sbmList) - 1:
n = -1
self.name = self.sbmList[n + 1]
self.url = self.dir + '\\' + self.name
self.rotation = 0
self.processPicture()
def prev(self,event):
if self.name in self.sbmList:
n = self.sbmList.index(self.name)
if n == 0:
n = len(self.sbmList)
self.name = self.sbmList[n - 1]
self.url = self.dir + '\\' + self.name
self.rotation = 0
self.processPicture()
def rescale(self,event,factor):
if self.url and self.GetStatusBar(): #close is seen as a size event.
self.processPicture(factor)
def zoom(self,event):
factor = 1.25
if event.GetWheelRotation() < 0:
factor = 0.8
self.rescale(event,factor)
def keyEvent(self,event):
code = event.GetKeyCode()
if code == 43: #plus
self.rescale(event,1.25)
elif code == 45: #minus
self.rescale(event,0.8)
elif code == 82 and self.url: #r
self.rotation = self.rotation + 90
self.processPicture(1)
elif code == 70: #f
self.toggleFS()
elif (code == 314 or code == 315 or code == 316 or code == 317) and self.sbm:
#left, up, right, down
self.scroll(code)
elif code == 77: #m
if self.mode == 0:
self.mode = 1
else:
self.mode = 0
self.SetStatusText(str(self.mode), 4)
self.processPicture(1)
def scroll(self,code):
boxPos = self.imageBox.GetScreenPositionTuple()
imgPos = self.sbm.GetScreenPositionTuple()
delta = 20
if code == 314 and self.width > self.imageBox.Size[0]:
compare = boxPos[0] - imgPos[0]
if compare <= delta:
delta = max(compare,0)
self.imageBox.ScrollWindow(delta,0)
if code == 315 and self.height > self.imageBox.Size[1]:
compare = boxPos[1] - imgPos[1]
if compare <= delta:
delta = max(compare,0)
self.imageBox.ScrollWindow(0,delta)
if code == 316 and self.width > self.imageBox.Size[0]:
compare = imgPos[0] + self.sbm.Size[0] - boxPos[0] - self.imageBox.Size[0]
if compare <= delta:
delta = max(compare,0)
self.imageBox.ScrollWindow(-delta,0)
if code == 317 and self.height > self.imageBox.Size[1]:
compare = imgPos[1] + self.sbm.Size[1] - boxPos[1] - self.imageBox.Size[1]
if compare <= delta:
delta = max(compare,0)
self.imageBox.ScrollWindow(0,-delta)
def drag(self,event):
if event.MiddleIsDown():
if not self.mc:
self.SetCursor(self.moveCursor)
self.mc = True
boxPos = self.imageBox.GetScreenPositionTuple()
imgPos = self.sbm.GetScreenPositionTuple()
if self.count == 0:
self.x = event.GetX()
self.y = event.GetY()
self.count = self.count + 1
if self.count > 1:
deltaX = event.GetX() - self.x
deltaY = event.GetY() - self.y
if imgPos[0] >= boxPos[0] and deltaX > 0:
deltaX = 0
if imgPos[0] + self.width <= boxPos[0] + self.imageBox.Size[0] and deltaX < 0:
deltaX = 0
if imgPos[1] >= boxPos[1] and deltaY > 0:
deltaY = 0
if imgPos[1] + self.height <= boxPos[1] + self.imageBox.Size[1] and deltaY < 0:
deltaY = 0
self.imageBox.ScrollWindow(2*deltaX,2*deltaY)
self.count = 0
def endDrag(self,event):
self.count = 0
self.SetCursor(self.cursor)
self.mc = False
def toggleFS(self):
if self.fs:
self.ShowFullScreen(False)
self.fs = False
else:
self.ShowFullScreen(True)
self.fs = True
app = wx.App(redirect = False)
frame = ImageFrame()
app.MainLoop()
答案 2 :(得分:3)
还有Cornice一个用wxPython编写的开源图像查看器,它可以帮助你
答案 3 :(得分:1)
您可以尝试文档/视图架构。 我认为它应该在Python中可用。您可以查看this tutorial,它适用于C ++,但方法应该类似。它还显示了如何实现选择矩形。
答案 4 :(得分:0)
我实际上只是使用wxPython和PIL创建了一个简单的图像查看器。我不想,但我很难找到一个我想要的简单的观众。无论如何,我从this page开始,并自己创建一个应用程序,可以放大,旋转和浏览它从它开始的文件夹中的所有图像。当我回到家时,如果你愿意,我可以发布完整的代码。