2009-03-02 5 views
3

Je dois faire un visionneuse d'image de base. La principale préoccupation est de savoir comment implémenter (en termes de composants graphiques et de manipulation d'image) des fonctionnalités telles que: zoom avant, scroll et 'hand tool'.Visionneuse d'image - commandes standard de l'interface graphique, ascendante ou quoi?

Il semble y avoir plusieurs options pour atteindre cet objectif, différant principalement par le degré de responsabilité sur le cadre de l'interface graphique par opposition à la mise en œuvre manuelle des choses.

Deux solutions qui sont évidentes pour moi sont:

1) Redimensionnement et recadrage la partie visible de l'image est conçue sur son propre en utilisant la fonctionnalité d'une certaine bibliothèque de manipulation d'images. L'image (ou sa partie) est ensuite dessinée sur une fenêtre/contrôle dans une méthode onPaint() substituée. La mise à jour des barres de défilement (lorsque l'outil 'main est utilisé') et l'opération (lorsqu'elle est utilisée directement) doivent être écrites.

2) Un contrôle surdimensionné (StaticBitmap ou autre) contenant l'image est placé dans une fenêtre avec un défilement automatique. Ensuite, il faut comprendre comment convertir les coordonnées de l'image en coordonnées de défilement.

Les deux manières semblent mal à l'aise. Des idées comment le faire d'une manière ordonnée? Ou est ce que je ressens comme étant la seule voie laide? J'utilise Python avec wxPython/wxWidgets et PIL, mais la question est dans une large mesure indépendante de la langue et de la plate-forme.

Un exemple de code et des liens vers des sources (de quelque chose qui n'est pas trop gonflé) sont les bienvenus.

Répondre

5

Voici un tutoriel qui pourrait vous aider. Je n'ai pas réellement regardé toutes les vidéos, donc je ne peux pas parler à quel point il répond à vos problèmes spécifiques.

En outre, voici un article de Jeff Atwood sur Coding Horror qui pourrait s'appliquer. Programming Is Hard, Let's Go Shopping! Il s'agit de savoir quand vous devriez prendre le temps d'écrire votre propre code et quand utiliser simplement une solution tierce.

0

En fait, je viens de faire une simple visionneuse d'image avec wxPython et PIL. Je ne voulais pas, mais j'avais du mal à trouver un spectateur aussi simple que je le voulais. Quoi qu'il en soit, j'ai commencé à partir de this page et j'ai travaillé moi-même à une application qui permet de zoomer, faire pivoter et parcourir toutes les images dans le dossier à partir duquel il a été démarré. Quand je rentre à la maison, je peux poster le code complet si vous le souhaitez.

+0

Ce serait génial si vous l'avez fait. – Maleev

4

Je suis nouveau ici, et après avoir cherché pendant un moment, je n'arrive toujours pas à trouver un moyen de télécharger des fichiers. Eh bien, voici le code dans un post. Désolé pour les noms de variables non descriptives et l'absence de commentaires. Je suppose que les fonctions principales que vous voudrez regarder sont processPicture et showPicture. Editer: et juste pour réitérer, j'ai commencé ceci avec l'exemple dans 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() 
+0

Merci. Avez-vous envie de recevoir des commentaires ou des suggestions lorsque je l'ai étudié? – Maleev

+0

Bien sûr. Je serais heureux d'apprendre s'il y avait une meilleure façon de faire certaines de ces choses. Une partie qui semble horriblement inefficace est la façon dont l'image est complètement rechargée chaque fois qu'elle est agrandie. Je n'ai pas eu de chance de trouver une meilleure solution. – kanem

+0

Nice! J'ai appris un peu de votre code. Merci pour le partage! –

3

Et il y a Cornice une visionneuse d'image open source écrit en wxPython qui pourrait vous aider

1

Vous pouvez l'architecture document/Affichage. Je pense qu'il devrait être disponible en Python. Vous pouvez jeter un oeil à this tutorial, c'est pour C++ mais l'approche devrait être similaire. Il montre également comment implémenter le rectangle de sélection.

+0

A également trouvé un bon aperçu de Document/Vue à http://docs.wxwidgets.org/stable/wx_docviewoverview.html – Maleev

+0

Reconsidéré: malgré votre réponse est précieuse, ce n'est pas ce qui a été demandé, probablement parce que ma question elle-même n'est pas claire. Je vais l'éditer maintenant. – Maleev

Questions connexes