User interface 图像查看器-标准gui控件,自底向上还是什么?
我需要制作一个基本图像查看器。 主要关注的是如何实现(在gui组件和图像处理方面)诸如:缩小、滚动和“手动工具”等功能 实现这一目标似乎有几种选择,主要是在图形界面框架上的责任程度不同于手动实现 对我来说显而易见的两个解决方案是: 1)使用某些图像处理库的功能自行调整图像可见部分的大小和裁剪。然后在某个窗口/控件上以重写的onPaint()方法绘制图像(或其部分)。需要编写滚动条更新(使用“手动工具”时)和操作(直接使用时)代码 2)包含图像的超大控件(StaticBitmap或其他任何控件)放在自动滚动的窗口中。然后需要弄清楚如何将图像坐标转换为滚动坐标 这两种方法看起来都很尴尬。有什么好办法可以干净利落地做吗?还是我觉得丑陋只是唯一的出路 我将Python与wxPython/wxWidgets和PIL一起使用,但问题在很大程度上是语言和平台独立的User interface 图像查看器-标准gui控件,自底向上还是什么?,user-interface,image,User Interface,Image,我需要制作一个基本图像查看器。 主要关注的是如何实现(在gui组件和图像处理方面)诸如:缩小、滚动和“手动工具”等功能 实现这一目标似乎有几种选择,主要是在图形界面框架上的责任程度不同于手动实现 对我来说显而易见的两个解决方案是: 1)使用某些图像处理库的功能自行调整图像可见部分的大小和裁剪。然后在某个窗口/控件上以重写的onPaint()方法绘制图像(或其部分)。需要编写滚动条更新(使用“手动工具”时)和操作(直接使用时)代码 2)包含图像的超大控件(StaticBitmap或其他任何控件)放
欢迎使用示例代码和指向(不太臃肿的内容)源代码的链接。这里有一个教程可能会有所帮助 实际上我并没有看所有的视频,所以我不能说它能很好地解决你的具体问题
还有,杰夫·阿特伍德(Jeff Atwood)的一篇博客文章,介绍了如何编写恐怖代码。它说明了什么时候应该花时间编写自己的代码,什么时候应该只使用第三方解决方案。我实际上刚刚用wxPython和PIL制作了一个简单的图像查看器。我不想,但我很难找到一个像我想要的那样简单的查看器。不管怎样,我从一个应用程序开始,并努力开发了一个应用程序,可以缩放、旋转和浏览文件夹中的所有图像。回家后,如果你愿意,我可以发布完整的代码。我是新来的,搜索了一段时间后,我仍然找不到上传文件的方法。哦,好吧,这是帖子里的代码。对于非描述性变量名和缺少注释,我们深表歉意。我想您要了解的主要功能是processPicture和showPicture 编辑:只是重申一下,我是从年的例子开始的
导入wx、操作系统、字符串、系统
从PIL导入图像
#滚轮和+/-进行放大/缩小。f切换全屏。r旋转。
#m将PIL模式从低质量(快速)更改为高质量(慢速)。
#1000x1000以下的图像自动显示为高质量图像。
#拖动时按下中键可移动图像,如箭头所示
#关键点(如果图像大于窗口)。
#鼠标左键和右键是下一个和上一个图像。
#没有加载图像的功能。当创建可执行文件时
#查看器是通过打开图像启动的。
#要从命令行运行此文件,请注释掉第55行并取消注释
#第54行,然后执行“viewer.py sampleImage”
#有几行特定于Windows。他们(可能)都有
#与路径有关,即“/”vs“\”。
类ImageFrame(wx.Frame):
定义初始化(自):
wx.Frame.\uuuu init\uuuuu(self,None,title=“viewer”)
自我中心()
self.Size=(450450)
self.imageBox=wx.Window(self)
self.vbox=wx.BoxSizer(wx.VERTICAL)
self.CreateStatusBar(5)
自我设置状态宽度([-1,70,50,50,30])
self.cursor=wx.StockCursor(wx.cursor\u箭头)
self.moveCursor=wx.StockCursor(wx.CURSOR\u大小)
self.vbox.Add(self.imageBox,比例=1,标志=wx.EXPAND)
self.SetSizer(self.vbox)
self.Show()
self.sbm=0
self.sbmList=[]
self.name=“”
self.url=“”
self.dir=''
自系数=1.0
自旋转=0
self.width=0
self.height=0
self.count=0
self.size=0
self.numopics=0
self.mc=False
self.fs=False
self.mode=0
self.SetStatusText(str(self.mode),4)
如果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_大小,lambda EVT:self.rescale(EVT,1))
self.imageBox.Bind(wx.EVT\u鼠标滚轮,self.zoom)
self.imageBox.Bind(wx.EVT\u KEY\u DOWN,self.keyEvent)
self.imageBox.Bind(wx.EVT\u MIDDLE\u UP,self.endDrag)
self.imageBox.setbackgroundcolor((0,0,0,0))
self.imageBox.Bind(wx.EVT_LEFT_DOWN,self.next)
self.imageBox.Bind(wx.EVT\u RIGHT\u DOWN,self.prev)
def nameFromUrl(self,url):
name=url.split(“\\”)
name=name[len(name)-1]
返回名称
def processPicture(自身,系数=0):
img=Image.open(self.url)
self.width=img.size[0]
自身高度=外形尺寸[1]
ogHeight=自我高度
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=因子*因子
如果系数==0:
自我系数=1
模式=0
如果(ogWidth self.imageBox.Size[0]:
比较=boxPos[0]-imgPos[0]
如果比较self.imageBox.Size[1]:
比较=boxPos[1]-imgPos[1]
如果比较self.imageBox.Size[0]:
比较=imgPos[0]+self.sbm.Size[0]-boxPos[0]-self.imageBox.Size[0]
如果比较self.imageBox.Size[1]:
比较=imgPos[1]+s
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()