Friday, November 30, 2012

เมนูบันทึกไฟล์รูป

เราลองมาเพิ่มเมนูสำหรับการบันทึกไฟล์รูปหลังจากประมวลผลกันครับ



หลักการก็คือต้องสร้างเมนู Save as... จากนั้นก็เพิ่มโค้ดในส่วนของการบันทึกรูป โค้ดทั้งหมดก็จะเป็นตามนี้ครับ

from Tkinter import Tk,Menu,Label
from PIL import Image,ImageTk
import tkFileDialog

class MainApp:
    #constructor
    def __init__(self,mainwindow):
        #assign class variable to input parameter
        self.mainwindow = mainwindow
        #set title
        self.mainwindow.title("Menu")
        self.lbl = Label(self.mainwindow)

    def createMenu(self):
        #create menubar
        menubar = Menu(self.mainwindow)
        self.mainwindow.config(menu=menubar)
        #FILE menu
        fileMenu = Menu(menubar, tearoff=0)
        menubar.add_cascade(label="File", menu=fileMenu)
        fileMenu.add_command(label="Open...",command=self.openFile)
        fileMenu.add_command(label="Save as...",command=self.saveFile)
        fileMenu.add_separator()
        fileMenu.add_command(label="Exit", command=self.mainwindow.destroy)
        #EDIT menu
        editMenu = Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Edit", menu=editMenu)
        editMenu.add_command(label="Restore Image", command=self.imgRestore)
        #PROCESS menu
        processMenu = Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Process", menu=processMenu)
        processMenu.add_command(label="Rotate", command=self.imgRotate)

    def openFile(self):
        #file type filter
        ftypes = [('Image files', '*.jpg *.png *.gif'),
        ('All files', '*')]
        filename = tkFileDialog.askopenfilename(parent=self.mainwindow,title='Choose a file',filetypes=ftypes)
        if filename:
           try:
                #open image
                self.im = Image.open(filename)
                #keep its copy for restoring later
                self.imOrigin = self.im.copy()
                self.showImage()
           except: pass

    def saveFile(self):
        ftypes = [('JPEG', '*.jpg')]
        filename = tkFileDialog.asksaveasfilename(parent=self.mainwindow,title='Save image as',filetypes=ftypes)
        if len(filename)>0:
            try:
                #in Linux, comment this line, it is not useful
                filename = filename+".jpg"
                #save image
                self.im.save(filename,"JPEG")
            except IOError:
                tkMessageBox.showerror("Error","Cannot save image!")

    def showImage(self):
        self.imTk = ImageTk.PhotoImage(self.im)
        self.lbl.destroy()
        self.lbl = Label(self.mainwindow, image=self.imTk)
        self.lbl.pack()

    def imgRestore(self):
        self.im = self.imOrigin.copy()
        self.showImage()

    def imgRotate(self):
        self.im = self.im.rotate(45)
        self.showImage()

#---Main app starts here---
root = Tk()
app = MainApp(root)
#create menu
app.createMenu()
root.mainloop()

Thursday, November 29, 2012

เมนูเปิดไฟล์รูป

 ในบทความนี้ เราจะเพิ่มเติมเมนูสำหรับเปิดไฟล์รูป เพื่อความสะดวกในการเลือกไฟล์รูปใดๆในเครื่องคอมพิวเตอร์นั้น





ลองดูตามโค้ดด้านล่างนี้ได้ครับ
from Tkinter import Tk,Menu,Label
from PIL import Image,ImageTk
import tkFileDialog

class MainApp:
    #constructor
    def __init__(self,mainwindow):
        #assign class variable to input parameter
        self.mainwindow = mainwindow
        #set title
        self.mainwindow.title("Menu")
        self.lbl = Label(self.mainwindow)

    def createMenu(self):
        #create menubar
        menubar = Menu(self.mainwindow)
        self.mainwindow.config(menu=menubar)
        #FILE menu
        fileMenu = Menu(menubar, tearoff=0)
        menubar.add_cascade(label="File", menu=fileMenu)
        fileMenu.add_command(label="Open...",command=self.openFile)
        fileMenu.add_separator()
        fileMenu.add_command(label="Exit", command=self.mainwindow.destroy)
        #EDIT menu
        editMenu = Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Edit", menu=editMenu)
        editMenu.add_command(label="Restore Image", command=self.imgRestore)
        #PROCESS menu
        processMenu = Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Process", menu=processMenu)
        processMenu.add_command(label="Rotate", command=self.imgRotate)

    def openFile(self):
        #file type filter
        ftypes = [('Image files', '*.jpg *.png *.gif'),
        ('All files', '*')]
        filename = tkFileDialog.askopenfilename(parent=self.mainwindow,title='Choose a file',filetypes=ftypes)
        if filename:
           try:
                #open image
                self.im = Image.open(filename)
                #keep its copy for restoring later
                self.imOrigin = self.im.copy()
                self.showImage()
           except: pass

    def showImage(self):
        self.imTk = ImageTk.PhotoImage(self.im)
        self.lbl.destroy()
        self.lbl = Label(self.mainwindow, image=self.imTk)
        self.lbl.pack()

    def imgRestore(self):
        self.im = self.imOrigin.copy()
        self.showImage()

    def imgRotate(self):
        self.im = self.im.rotate(45)
        self.showImage()

#---Main app starts here---
root = Tk()
app = MainApp(root)
#create menu
app.createMenu()
root.mainloop()

กลับไปสู่ภาพต้นฉบับ

ในบทความนี้เราจะลองสร้างเมนูใหม่ คือ Edit และมีคำสั่ง Restore Image เพื่อให้เราสามารถย้อนกลับไปสู่ภาพต้นฉบับ ในกรณีที่ทำการประมวลผลไปแล้ว




หลักการก็ตรงไปตรงมาครับ สำเนารูปต้นฉบับไว้ก่อน ด้วยคำสั่ง
Image.copy()

ตัวอย่างเช่น
imOrigin = im.copy()

จากนั้นเมื่อต้องการกลับไปสู่ต้นฉบับ ก็สำเนากลับ โดยใช้คำสั่ง
im = imOrigin.copy()

ลองมาดูโค้ดเต็มๆที่เพิ่มเมนูนี้ครับ
from Tkinter import Tk,Menu,Label
from PIL import Image,ImageTk

class MainApp:
    #constructor
    def __init__(self,mainwindow):
        #assign class variable to input parameter
        self.mainwindow = mainwindow
        #set title
        self.mainwindow.title("Menu")
        self.lbl = Label(self.mainwindow)

    def createMenu(self):
        #create menubar
        menubar = Menu(self.mainwindow)
        self.mainwindow.config(menu=menubar)
        #FILE menu
        fileMenu = Menu(menubar, tearoff=0)
        menubar.add_cascade(label="File", menu=fileMenu)
        fileMenu.add_command(label="Exit", command=self.mainwindow.destroy)
        #EDIT menu
        editMenu = Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Edit", menu=editMenu)
        editMenu.add_command(label="Restore Image", command=self.imgRestore)
        #PROCESS menu
        processMenu = Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Process", menu=processMenu)
        processMenu.add_command(label="Rotate", command=self.imgRotate)

    def loadImage(self):
        self.im = Image.open("lena.jpg")
        self.imOrigin = self.im.copy()
        self.showImage()

    def showImage(self):
        self.imTk = ImageTk.PhotoImage(self.im)
        self.lbl.destroy()
        self.lbl = Label(self.mainwindow, image=self.imTk)
        self.lbl.pack()

    def imgRestore(self):
        self.im = self.imOrigin.copy()
        self.showImage()

    def imgRotate(self):
        self.im = self.im.rotate(45)
        self.showImage()

#---Main app starts here---
root = Tk()
app = MainApp(root)
#create menu
app.createMenu()
app.loadImage()
root.mainloop()

Tuesday, November 27, 2012

เมนู+รูป+การประมวลผล 2

ต่อเนื่องจากคราวที่แล้วครับ แก้ปัญหา Label เก่าซ้อนกับอันใหม่ เพื่อให้ได้ผลลัพธ์ตามนี้


วิธีการแก้ปัญหาคือ ต้องทำลาย Label อันเก่าที่ค้างอยู่บนหน้าต่างก่อนที่จะแสดง Label ใหม่ นั่นคือ
1. ให้ตัวแปร Label เป็นตัวแปรของคลาส และ
2. ใช้คำสั่ง destroy เพื่อทำลาย Label อันเก่า

โคัดทั้งหมดหลังจากการแก้ไขก็จะเป็นดังนี้ครับ
from Tkinter import Tk,Menu,Label
from PIL import Image,ImageTk

class MainApp:
    #constructor
    def __init__(self,mainwindow):
        #assign class variable to input parameter
        self.mainwindow = mainwindow
        #set title
        self.mainwindow.title("Menu")
        self.lbl = Label(self.mainwindow)

    def createMenu(self):
        #create menubar
        menubar = Menu(self.mainwindow)
        self.mainwindow.config(menu=menubar)
        #FILE menu
        fileMenu = Menu(menubar, tearoff=0)
        menubar.add_cascade(label="File", menu=fileMenu)
        fileMenu.add_command(label="Exit", command=self.mainwindow.destroy)
        #PROCESS menu
        processMenu = Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Process", menu=processMenu)
        processMenu.add_command(label="Rotate",command=self.imgRotate)

    def loadImage(self):
        self.im = Image.open("lena.jpg")
        self.showImage()

    def showImage(self):
        self.imTk = ImageTk.PhotoImage(self.im)
        self.lbl.destroy()
        self.lbl = Label(self.mainwindow, image=self.imTk)
        self.lbl.pack()

    def imgRotate(self):
        self.im = self.im.rotate(45)
        self.showImage()

#---Main app starts here---
root = Tk()
app = MainApp(root)
#create menu
app.createMenu()
app.loadImage()
root.mainloop()

โค้ดข้างต้นก็จะเป็นต้นแบบหลักๆในการประมวลผลรูปไ้ด้แล้วครับ ครั้งหน้าเราจะลองเพิ่มเมนูในการเรียกรูปต้นฉบับกลับคืนมา หลังจากประมวลผลไปแล้วครับ

เมนู+รูป+การประมวลผล

คราวนี้เราจะลองเอา เมนูในบทความที่แล้ว มาประกอบกับรูปในบทความก่อนหน้านั้นครับ


โคัดเมื่อรวมๆกันแล้วก็จะได้แบบนี้
from Tkinter import Tk,Menu,Label
from PIL import Image,ImageTk

class MainApp:
    #constructor
    def __init__(self,mainwindow):
        #assign class variable to input parameter
        self.mainwindow = mainwindow
        #set title
        self.mainwindow.title("Menu")

    def createMenu(self):
        #create menubar
        menubar = Menu(self.mainwindow)
        self.mainwindow.config(menu=menubar)
        #FILE menu
        fileMenu = Menu(menubar, tearoff=0)
        menubar.add_cascade(label="File", menu=fileMenu)
        fileMenu.add_command(label="Exit", command=self.mainwindow.destroy)

    def loadImage(self):
        self.im = Image.open("lena.jpg")
        self.showImage()

    def showImage(self):
        self.imTk = ImageTk.PhotoImage(self.im)
        lbl = Label(self.mainwindow, image=self.imTk)
        lbl.pack()

#---Main app starts here---
root = Tk()
app = MainApp(root)
#create menu
app.createMenu()
app.loadImage()
root.mainloop()

จากนั้น เราจะลองเพิ่มเมนู Process สำหรับการประมวลผลภาพ เพื่อทำการหมุนรูปใหัได้ผลคือ


ลองเพิ่มโค้ดต่อไปนี้ในส่วนของ createMenu() ครับ
#PROCESS menu
processMenu = Menu(menubar, tearoff=0)
menubar.add_cascade(label="Process", menu=processMenu)
processMenu.add_command(label="Rotate",command=self.imgRotate) 

จะเห็นว่าเราได้เพิ่มคำสั่ง Rotate ในส่วนของเมนู Process ดังนั้น เราจำเป็นต้องเขียน method ใหม่ให้สอดคล้องกับคำสั่งดังกล่าว เพื่อหมุนรูปดังนี้
    def imgRotate(self):
        self.im = self.im.rotate(45)
        self.showImage()

เมื่อแก้ไขข้างต้นแล้ว หากรันโปรแกรมแ้ล้วเลือกเมนู Process / Rotate จะได้ผลลัพธ์คือ



อ้าว ทำไมเป็นแบบนี้ล่ะ มีส่วนว่างๆข้างบนมาได้อย่างไร?

เหตุผลก็เป็นเพราะว่า ใน showImage() มีการสร้าง Label ใหม่เรื่อยๆ ส่วน Label เก่าที่ถูกสร้างไว้ก็ยังเหลืออยู่ (ยกเว้นรูป เพราะถูก garbage collector ทำลายไปแล้ว) ทำให้เกิดพื้นที่ว่างที่เป็นของ Label เก่าค้างอยู่ในหน้าต่าง

เราจะมาลองแก้ปัญหานี้ในคราวถัดไปครับ