Advertisement
gregwa

FCM 87 - Python part 56 - Cross Stitch program

Jul 11th, 2014
557
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 16.74 KB | None | 0 0
  1. #======================================================
  2. #  Cross Stitch Pattern Generator
  3. #  cspg.py
  4. #  Written by G.D. Walters
  5. #  Created for Full Circle Magazine issues 85, 86, 87 & 88
  6. #======================================================
  7. from Tkinter import *
  8. import tkFileDialog
  9. import tkCommonDialog
  10. import tkMessageBox
  11. import ttk
  12. from PIL import Image,ImageTk,ImageOps
  13. import Pmw
  14. import apsw  # Database Access
  15. import math  # Math library
  16. import sys
  17.  
  18.  
  19. class Test:
  20.     def __init__(self, master):
  21.         self.picFormats = [
  22.         ('JPEG / JFIF','*.jpg'),
  23.         ('Portable Network Graphics','*.png'),
  24.         ('CompuServer GIF','*.gif'),
  25.         ('Windows Bitmap','*.bmp'),
  26.         ('All File Types *.*','*.*'),
  27.         ]
  28.  
  29.         self.openimage = PhotoImage(file='open.gif')
  30.         self.DefaultImage =ImageTk.PhotoImage(self.Thumbnail("default.jpg",450,450))
  31.         #-------------------------------------------
  32.         #            Global Definitions
  33.         #-------------------------------------------
  34.         #               UI Required
  35.         global OriginalFilename
  36.         OriginalFilename = StringVar()
  37.         global OriginalColorCount
  38.         OriginalColorCount = StringVar()
  39.         global OriginalSize
  40.         OriginalSize = StringVar()
  41.         global OriginalImage
  42.         OriginalImage = StringVar()
  43.         global ComboStitch
  44.         ComboStitch = IntVar()
  45.         global ComboSize
  46.         ComboSize = StringVar()
  47.         global FabricWidth
  48.         FabricWidth = DoubleVar()
  49.         global FabricHeight
  50.         FabricHeight = DoubleVar()
  51.         global MaxColors
  52.         MaxColors = IntVar()
  53.         global BorderSize
  54.         BorderSize = DoubleVar()
  55.         global ProcessedColors
  56.         ProcessedColors = StringVar()
  57.         global ProcessedSize
  58.         ProcessedSize = StringVar()
  59.         global DmcColor
  60.         DmcColor = StringVar()
  61.         #-------------------------------------------
  62.         global ShowGrid
  63.         ShowGrid = True
  64.         global ProcessedImage
  65.         ProcessedImage = ""
  66.         global GridImage
  67.         GridImage = ""
  68.         global backgroundColor1
  69.         backgroundColor1 = (120,)*3
  70.         global backgroundColor2
  71.         backgroundColor2 = (0,)*3  
  72.         global ReadyToProcess
  73.         ReadyToProcess = False 
  74.         #-------------------------
  75.         self.OpenDB()
  76.         self.MakeMenu(master)
  77.         frm = self.BuildWidgets(master)
  78.         self.PlaceWidgets(frm)
  79.        
  80. #======================================================    
  81. #                BEGIN UI DEFINITION
  82. #======================================================
  83.     def MakeMenu(self,master):
  84.         menu = Menu(master)
  85.         root.config(menu=menu)
  86.         filemenu = Menu(menu, tearoff=0)
  87.         process = Menu(menu,tearoff=0)
  88.         help = Menu(menu,tearoff=0)
  89.         #-------------------------------------------
  90.         #               File Menu
  91.         #-------------------------------------------
  92.         menu.add_cascade(label="File", menu=filemenu)
  93.         menu.add_cascade(label="Process",menu=process)
  94.         menu.add_cascade(label="Help",menu=help)
  95.         filemenu.add_command(label="New")
  96.         filemenu.add_command(label="Open", command=self.GetFileName)
  97.         filemenu.add_command(label="Save", command=self.FileSave)
  98.         filemenu.add_separator()
  99.         filemenu.add_command(label="Exit", command=self.DoExit)
  100.         #-------------------------------------------
  101.         #                Process Menu
  102.         #-------------------------------------------
  103.         process.add_command(label="All",command=self.Process1)
  104.         #-------------------------------------------
  105.         #                 Help Menu
  106.         #-------------------------------------------
  107.         help.add_command(label="Help",command=self.ShowHelp)
  108.         help.add_separator()
  109.         help.add_command(label="About",command=self.ShowAbout)
  110.        
  111.     def BuildWidgets(self,master):
  112.         self.frame = Frame(master,width=1024,height=850)
  113.         # ---------------- TOP FRAME ---------------------
  114.         self.frm1 = Frame(self.frame,width=1024,height=100,bd=4,relief=GROOVE)
  115.         self.label1 = Label(self.frm1,text = "Original Filename: ")
  116.         self.entFileName = Entry(self.frm1,width=50,textvariable=OriginalFilename)
  117.         self.btnGetFN = Button(self.frm1,width=28,image=self.openimage,command=self.GetFileName)
  118.         self.label2 = Label(self.frm1,text = "Original Colors: ")
  119.         self.lblOriginalColorCount = Label(self.frm1,text="",width=10,textvariable=OriginalColorCount)
  120.         self.label3 = Label(self.frm1,text = "Original Size: ")
  121.         self.lblOriginalSize = Label(self.frm1,text="",width=10,textvariable=OriginalSize)
  122.         # ---------------Middle Frame --------------------
  123.         self.frm2 = Frame(self.frame,width=1024,height=160,bd=4,relief=GROOVE)
  124.         self.lbl4 = Label(self.frm2,text="Aida Stitch Size: ")
  125.         self.lbl5 = Label(self.frm2,text="Aida Fabric Size: ")
  126.         self.TCombobox1 = ttk.Combobox(self.frm2,textvariable=ComboStitch,width=8)
  127.         self.TCombobox1.bind('<<ComboboxSelected>>', self.StitchSizeSelect)
  128.         self.TCombobox1['values'] = (7,10,11,12,14,16,18,22)
  129.         self.TCombobox2 = ttk.Combobox(self.frm2,textvariable=ComboSize,width = 8)
  130.         self.TCombobox2.bind('<<ComboboxSelected>>',self.AidaSizeSelect)
  131.         self.TCombobox2['values'] = ("12x18","15x18","30")
  132.         self.lbl6 = Label(self.frm2,text="Max Colors: ")
  133.         self.entMaxColors = Entry(self.frm2,textvariable=MaxColors,width=3)
  134.         self.lbl7 = Label(self.frm2,text="Border Size: ")
  135.         self.entBorderSize = Entry(self.frm2,textvariable=BorderSize,width = 8)
  136.         self.frmLine = Frame(self.frm2,width=6,height=80,bd=3,relief="raised")
  137.         self.lbl8 = Label(self.frm2,text="      Processed Image Colors: ")
  138.         self.lbl9 = Label(self.frm2,text="Processed Image Stitch Count: ")
  139.         self.lblProcessedColors=Label(self.frm2,width=10,textvariable=ProcessedColors,justify=LEFT)
  140.         self.lblProcessedSize=Label(self.frm2,width=10,textvariable=ProcessedSize,justify=LEFT)
  141.         self.btnDoIt = Button(self.frm2,text="Process",width=11,command = self.Process1)
  142.         self.btnShowGrid = Button(self.frm2,text="Hide Grid",width=11,command=self.ShowHideGrid)
  143.         ComboStitch.set(14)
  144.         ComboSize.set("15x18")
  145.         FabricWidth.set(15)
  146.         FabricHeight.set(18)
  147.         MaxColors.set(50)
  148.         BorderSize.set(1.0)    
  149.         # --------------- Bottom Frame ------------------
  150.         self.frm3 = Frame(self.frame,width=1024, height = 768,bd=4,relief=GROOVE)
  151.         self.lblImageL = Label(self.frm3,image=self.DefaultImage,height=500,width=500,borderwidth=2,relief=GROOVE,textvariable=OriginalImage)
  152.         self.lblImageR = Label(self.frm3,image=self.DefaultImage,height=500,width=500,borderwidth=2,relief=GROOVE)
  153.         #---------------- Side Frame -------------------
  154.         self.frm4 = Frame(self.frame,width = 300,height=610,bd=4,relief=GROOVE)
  155.         self.lbl20 = Label(self.frm4,text="Processed Color List",width = 50)
  156.         # Create the ScrolledFrame.
  157.         self.sf = Pmw.ScrolledFrame(self.frm4,
  158.                 labelpos = 'n', label_text = 'Processed Color List',
  159.                 usehullsize = 1,
  160.                 hull_width = 300,
  161.                 hull_height = 590,
  162.         )
  163.         return self.frame
  164.        
  165.     def PlaceWidgets(self,frame):
  166.         frame.grid(column = 0, row = 0)
  167.         # ---------------- TOP FRAME ---------------------
  168.         self.frm1.grid(column=0,row=0,rowspan=2,sticky="new")
  169.         self.label1.grid(column=0,row=0,sticky='w')
  170.         self.entFileName.grid(column=1,row=0,sticky='w',columnspan = 5)
  171.         self.btnGetFN.grid(column=7,row = 0,sticky='w')
  172.         self.label2.grid(column=9,row=0,sticky='w',padx=10)
  173.         self.lblOriginalColorCount.grid(column=10,row=0,sticky='w')
  174.         self.label3.grid(column=9,row=1,sticky='w',padx=10,pady=5)
  175.         self.lblOriginalSize.grid(column=10,row=1,sticky='w')
  176.         # ---------------- MIDDLE FRAME ---------------------
  177.         self.frm2.grid(column=0,row=2,rowspan=2,sticky="new")
  178.         self.lbl4.grid(column=0,row=0,sticky="new",pady=5)
  179.         self.lbl5.grid(column=0,row=1,sticky="new")
  180.         self.TCombobox1.grid(column=1,row=0,sticky="new",pady=5)
  181.         self.TCombobox2.grid(column=1,row=1,sticky="new")
  182.         self.lbl6.grid(column=2,row = 0,sticky="new",padx=5,pady=5)
  183.         self.entMaxColors.grid(column=3,row=0,sticky="new",pady=5)
  184.         self.lbl7.grid(column=2,row=1,sticky='new',padx=5)
  185.         self.entBorderSize.grid(column=3,row=1,sticky='new')
  186.         self.frmLine.grid(column=4,row=0,rowspan=2,sticky='new',padx=15)
  187.         self.lbl8.grid(column=5,row=0,sticky='new',pady=5)
  188.         self.lbl9.grid(column=5,row=1,sticky='new',pady=5)
  189.         self.lblProcessedColors.grid(column=6,row=0,sticky='w',pady=5)
  190.         self.lblProcessedSize.grid(column=6,row=1,sticky='w',pady=5)
  191.         self.btnDoIt.grid(column=7,row=0,sticky='e',padx=5,pady = 5)
  192.         self.btnShowGrid.grid(column=7,row=1,sticky='e',padx=5,pady = 5)
  193.        
  194.         # ---------------- BOTTOM FRAME ---------------------
  195.         self.frm3.grid(column=0,row=4,sticky="nsew")
  196.         self.lblImageL.grid(column=0,row=0,sticky="w")
  197.         self.lblImageR.grid(column=1,row=0,sticky="e")
  198.         # ---------------- SIDE FRAME ---------------------
  199.         self.frm4.grid(column=2,row=0,rowspan=12,sticky="new")
  200.         #self.lbl20.grid(column=0,row=0,columnspan=2,sticky='ewn')
  201.         self.sf.grid(column=0,row=1)
  202.         self.sfFrame = self.sf.interior()
  203.         self.lblch1 = Label(self.sfFrame,text="    Original")
  204.         self.lblch2 = Label(self.sfFrame,text="    DMC")
  205.         self.lblch3 = Label(self.sfFrame,text="Name/Number")
  206.         self.lblch1.grid(column=0,row=0,sticky='w')
  207.         self.lblch2.grid(column=1,row=0,sticky='w')
  208.         self.lblch3.grid(column=2,row=0,sticky="w")
  209. #======================================================
  210. #               END UI DEFINITION
  211. #======================================================
  212.  
  213.        
  214.     def GetFileName(self):
  215.         global ReadyToProcess
  216.         #---------------------------------     
  217.         fileName = tkFileDialog.askopenfilename(parent=root,filetypes=self.picFormats ,title="Select File to open...")
  218.         print fileName
  219.         OriginalFilename.set(fileName)
  220.         OriginalColorCount.set(self.GetColorCount(fileName))
  221.         OriginalSize.set(self.GetHW(fileName))
  222.         masterimage=Image.open(fileName)
  223.         masterimage.thumbnail((500,500))
  224.         self.img = ImageTk.PhotoImage(masterimage)
  225.         self.lblImageL['image'] = self.img
  226.         ReadyToProcess = True
  227.        
  228.     def Thumbnail(self,file,hsize,wsize):
  229.         size = hsize,wsize
  230.         extpos = file.rfind(".")
  231.         outfile = file[:extpos] + ".thumbnail"
  232.         im = Image.open(file)
  233.         im.thumbnail(size)
  234.         im.save(outfile,"JPEG")
  235.         return im
  236.        
  237.     def OriginalInfo(self,file):
  238.         im = Image.open(file)
  239.         imFormat = im.format
  240.         imSize = im.size
  241.         imMode = im.mode
  242.  
  243.         self.size = imSize
  244.         self.imformat = imFormat
  245.         self.immode = imMode       
  246.    
  247.     def GetColorCount(self,file):
  248.         im = Image.open(file)
  249.         numColors = im.getcolors(1600000)
  250.         self.colors = len(numColors)   
  251.         print self.colors      
  252.         return self.colors
  253.        
  254.     def GetHW(self,file):
  255.         im = Image.open(file)
  256.         tmp = "{0}x{1}".format(im.size[0],im.size[1])
  257.         print tmp
  258.         return tmp
  259.  
  260.     def GetHW2(self,file):
  261.         im = Image.open(file)
  262.         return im.size[0],im.size[1]
  263.        
  264.     def GetColors(self,image):
  265.         numColors = image.getcolors(1600000)
  266.         colors = len(numColors)
  267.         print colors       
  268.        
  269.     def Process1(self):
  270.         #---------------------------------
  271.         # Reduce Colours
  272.         # Resize image
  273.         # Place image into right label
  274.         global ReadyToProcess
  275.         #---------------------------------     
  276.         if ReadyToProcess == False:
  277.             tkMessageBox.showinfo(title="ERROR...",message='You must load an original imaage first.')
  278.         else:
  279.             newimage = self.Pixelate(OriginalFilename.get(),5)
  280.             Reduced = self.ReduceColours(newimage)
  281.             W,H = self.GetHW2(Reduced)
  282.             siz = "{0}x{1}".format(W/5,H/5)
  283.             ProcessedSize.set(siz)
  284.             # Place image
  285.             self.im2=Image.open(Reduced)
  286.             self.im2.thumbnail((500,500))
  287.             self.img3 = ImageTk.PhotoImage(self.im2)
  288.             self.lblImageR['image'] = self.img3
  289.             self.ProcessedImage = 'im1.png'
  290.             self.MakeLines(Reduced,5)
  291.             self.MakeLines2('output.png',50)
  292.             self.im2 = Image.open('output2.png')
  293.             self.im2.thumbnail((500,500))
  294.             self.img3 = ImageTk.PhotoImage(self.im2)
  295.             self.lblImageR['image'] = self.img3
  296.             self.FillScrolledList('output.png')
  297.             self.GridImage = 'output2.png'
  298.            
  299.     def Pixelate(self,im,pixelSize):
  300.         image = Image.open(im)
  301.         self.GetColors(image)
  302.         image = image.resize((image.size[0]/pixelSize, image.size[1]/pixelSize),Image.NEAREST)
  303.         image = image.resize((image.size[0]*pixelSize, image.size[1]*pixelSize),Image.NEAREST)
  304.         self.GetColors(image)
  305.         #image.show()
  306.         image.save('newimage.png')
  307.         return 'newimage.png'
  308.        
  309.     def ReduceColours(self,ImageName):
  310.         #Reduce colors
  311.         numcolors=MaxColors.get()
  312.         print("Numcolors={0}".format(numcolors))
  313.         image = Image.open(ImageName)
  314.         output = image.convert('P', palette=Image.ADAPTIVE, colors=numcolors)
  315.         x = output.convert("RGB")
  316.         self.GetColors(x)
  317.         numcolors = x.getcolors()
  318.         ProcessedColors.set(len(numcolors))
  319.         for i in numcolors:
  320.             print i    
  321.         x.save('im1.png')
  322.         return 'im1.png'       
  323.  
  324.     def MakeLines(self,im,pixelSize):
  325.         global backgroundColor1
  326.         #---------------------------------
  327.         image = Image.open(im)
  328.         pixel = image.load()
  329.         for i in range(0,image.size[0],pixelSize):
  330.             for j in range(0,image.size[1],pixelSize):
  331.                 for r in range(pixelSize):
  332.                     pixel[i+r,j] = backgroundColor1
  333.                     pixel[i,j+r] = backgroundColor1
  334.         image.save('output.png')
  335.            
  336.     def MakeLines2(self,im,pixelSize):
  337.         global backgroundColor2
  338.         #---------------------------------
  339.         image = Image.open(im)
  340.         pixel = image.load()
  341.         for i in range(0,image.size[0],pixelSize):
  342.             for j in range(0,image.size[1],pixelSize):
  343.                 for r in range(pixelSize):
  344.                     try:
  345.                         pixel[i+r,j] = backgroundColor2
  346.                         pixel[i,j+r] = backgroundColor2
  347.                     except:
  348.                         pass
  349.         image.save('output2.png')
  350.                    
  351.     def ResizeImage(self,im):
  352.         #im.show()
  353.         tempsize = im.size
  354.         print tempsize
  355.         BS = BorderSize.get()
  356.         FW = FabricWidth.get()-BS
  357.         FH = FabricHeight.get()-BS
  358.         print("FW={0},FH={1}".format(FW,FH))
  359.         StitchSize = ComboStitch.get()
  360.         MaxStitchW = (FW*StitchSize)/tempsize[0]
  361.         MaxStitchH = (FH*StitchSize)/tempsize[1]
  362.         print("MSW={0},MSH={1}".format(MaxStitchW,MaxStitchH))
  363.         NewImageHeight = MaxStitchH * tempsize[1]
  364.         NewImageWidth = MaxStitchW * tempsize[0]
  365.         print("NIW={0},NIH={1}".format(NewImageWidth,NewImageHeight))
  366.         newImage = im.resize((int(NewImageWidth),int(NewImageHeight)),Image.NEAREST)
  367.         newImage.save('temp.jpg')                  
  368.         return newImage
  369.                        
  370.     def StitchSizeSelect(self,p):
  371.         selection = ComboStitch.get()
  372.         print selection
  373.        
  374.     def AidaSizeSelect(self,p):
  375.         selection = ComboSize.get()
  376.         print selection
  377.         if selection != "30":
  378.             pos = selection.find("x")
  379.             width = int(selection[:pos])
  380.             height=int(selection[pos+1:])
  381.             print("W={0},H={1}".format(width,height))
  382.         else:
  383.             width = 30
  384.             height = 30
  385.         FabricWidth.set(width)
  386.         FabricHeight.set(height)
  387.        
  388.     def rgb2hex(self,rgb):
  389.         return '#%02x%02x%02x' % rgb
  390.        
  391.     def FillScrolledList(self,filename):
  392.         im = Image.open(filename)
  393.         numColors = im.getcolors()
  394.         colors = len(numColors)
  395.         cntr = 1
  396.         for c in numColors:
  397.             hexcolor = self.rgb2hex(c[1])
  398.             lblColor=Label(self.sfFrame,text="                ",bg=hexcolor,relief=GROOVE)
  399.             lblColor.grid(row = cntr, column = 0, sticky = 'nsew',padx=10,pady=5)
  400.             pkID = self.GetBestDistance(c[1][0],c[1][1],c[1][2])
  401.             sql = "SELECT * FROM DMC WHERE pkID = {0}".format(pkID)
  402.             rset = cursor.execute(sql)
  403.             for r in rset:
  404.                 hexcolor2 = r[6]
  405.                 dmcnum = r[1]
  406.                 colorname = r[2]
  407.             lblColor2=Label(self.sfFrame,text="                ",bg="#" + hexcolor2,relief=GROOVE)
  408.             lblColor2.grid(row = cntr,column = 1,sticky = 'w',padx=5,pady=5)
  409.             lblColor3=Label(self.sfFrame,text = str(dmcnum) + "-" + colorname,justify=LEFT)
  410.             DmcColor.set(dmcnum)
  411.             lblColor3.grid(row = cntr, column = 2,sticky = "w",padx=1,pady=5)
  412.             cntr += 1
  413.              
  414.     def OpenDB(self):
  415.         global connection
  416.         global cursor
  417.         #---------------------------------     
  418.         connection = apsw.Connection("floss.db3")
  419.         cursor = connection.cursor()
  420.        
  421.     def GetBestDistance(self,r1,g1,b1):
  422.         # dist = math.sqrt(((r1-r2)**2) + ((g1-g2)**2) + ((b1-b2)**2))
  423.         sql = "SELECT * FROM DMC"
  424.         rset = cursor.execute(sql)
  425.         BestDist = 10000.0
  426.         for r in rset:
  427.             pkID = r[0]
  428.             r2 = r[3]
  429.             g2 = r[4]
  430.             b2 = r[5]
  431.             dist = math.sqrt(((r1-r2)**2) + ((g1-g2)**2) + ((b1-b2)**2))
  432.             if dist < BestDist:
  433.                 BestDist = dist
  434.                 BestpkID = pkID
  435.         return BestpkID
  436.        
  437.     def ShowHideGrid(self):
  438.         global ShowGrid
  439.         #---------------------------------     
  440.         if ShowGrid == False:
  441.             self.btnShowGrid['text'] = 'Hide Grid'
  442.             ShowGrid = True
  443.             self.im2=Image.open(self.GridImage)
  444.             self.im2.thumbnail((500,500))
  445.             self.img3 = ImageTk.PhotoImage(self.im2)
  446.             self.lblImageR['image'] = self.img3        
  447.         else:
  448.             self.btnShowGrid['text'] = 'Show Grid'
  449.             ShowGrid = False
  450.             self.im2=Image.open(self.ProcessedImage)
  451.             self.im2.thumbnail((500,500))
  452.             self.img3 = ImageTk.PhotoImage(self.im2)
  453.             self.lblImageR['image'] = self.img3
  454.  
  455.     def FileSave(self):
  456.         tkMessageBox.showinfo(title="File Save",message='Sorry, but the File Save function is not yet available.')
  457.        
  458.     def ShowHelp(self):
  459.         tkMessageBox.showinfo(title="Help",message='Sorry, but help is not yet available.')
  460.        
  461.     def ShowAbout(self):
  462.         tkMessageBox.showinfo(title="About",message='Sorry, but the About function is not yet available.')
  463.        
  464.     def DoExit(self):
  465.         sys.exit()
  466.        
  467. root = Tk()
  468. root.title("Cross Stitch Pattern Creator")
  469. test = Test(root)
  470. root.mainloop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement