Image Wizard: Scale and Align an Image: Difference between revisions

From Scribus Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 15: Line 15:


'''Warning: You can leave the script dialog open, and continue to scale and align more images that you select. However, due to a multi-threading bug with Python scripts in Scribus, if you attempt to launch another script while this one is running, Scribus will probably crash and you'll lose your document. So be careful, and save often! Also, when the script is running, it may interfere with other Scribus actions such as using the Properties toolbar.'''
'''Warning: You can leave the script dialog open, and continue to scale and align more images that you select. However, due to a multi-threading bug with Python scripts in Scribus, if you attempt to launch another script while this one is running, Scribus will probably crash and you'll lose your document. So be careful, and save often! Also, when the script is running, it may interfere with other Scribus actions such as using the Properties toolbar.'''
Here are example screenshots of the script in action.
In the first screenshot, an image has been loaded that is too large for its current frame.
[[Image:ImageWizard1.png]]
In the second screenshot, the "Scale to Fill Frame" button has been clicked, and the image now fits well in the image frame.
[[Image:ImageWizard2.png]]


Save this script with a filename of Image_Wizard.py, for example.
Save this script with a filename of Image_Wizard.py, for example.

Revision as of 20:36, 17 September 2007

This article is part of the Scripts series.

You may have seen these scripts that Scale an Image to Fill a Frame Proportionally and Align an Image in its Frame. The following script brings up a dialog that allows you to perform both functions, as well as two other scaling functions.

  • Scale to Fill Frame (proportional scaling with cropping)
  • Scale to Fit Frame (standard Scribus "Scale to Frame Size: Proportional" - no cropping)
  • Scale Image to: x % (proportional scaling by the given percentage)
  • Align (9 possible alignments)

Each button click performs a separate action on all the currently selected images. So if you need to scale the image and then align it, you should first click the scaling button, then select your alignment and press the Align button.

I hope you'll find this script useful. This script has been immensely helpful to me in laying out the pictures for a photo album. More functions may be forthcoming if I find some that are useful. Maybe this additional alignment and scaling functionality could be incorporated into Scribus at some point.

The script has been tested in Scribus 1.3.3.9 and 1.3.4. It requires Tkinter to be properly installed.

Warning: You can leave the script dialog open, and continue to scale and align more images that you select. However, due to a multi-threading bug with Python scripts in Scribus, if you attempt to launch another script while this one is running, Scribus will probably crash and you'll lose your document. So be careful, and save often! Also, when the script is running, it may interfere with other Scribus actions such as using the Properties toolbar.

Here are example screenshots of the script in action. In the first screenshot, an image has been loaded that is too large for its current frame. ImageWizard1.png

In the second screenshot, the "Scale to Fill Frame" button has been clicked, and the image now fits well in the image frame. ImageWizard2.png

Save this script with a filename of Image_Wizard.py, for example.

from scribus import *

try:
    # I wish PyQt installed everywhere :-/
    from Tkinter import *
    from tkFont import Font
except ImportError:
    print "This script requires Python's Tkinter properly installed."
    messageBox('Script failed',
               'This script requires Python\'s Tkinter properly installed.',
               ICON_CRITICAL)
    sys.exit(1)



class TkImageAlignmentWizard(Frame):
    """ GUI interface for aligning an image in a frame"""

    def __init__(self, master=None):
        """ Setup the dialog """
        # refernce to the localization dictionary
        self.key = 'English'
        Frame.__init__(self, master)
        self.master.resizable(0, 0)
        self.master.title('Image Wizard')
        
        #define widgets
        #define framed areas
        self.grid()
        self.align_frame = Frame(self, bd=1, relief=RIDGE)
        self.scale_frame = Frame(self, bd=1, relief=RIDGE)
        
        # alignment options
        self.alignLabel = Label(self.align_frame, text='Align Image')
        self.alignVar = StringVar()
        self.alignRadio1 = Radiobutton(self.align_frame, text='', variable=self.alignVar, value="TL")
        self.alignRadio2 = Radiobutton(self.align_frame, text='', variable=self.alignVar, value="TC")
        self.alignRadio3 = Radiobutton(self.align_frame, text='', variable=self.alignVar, value="TR")
        self.alignRadio4 = Radiobutton(self.align_frame, text='', variable=self.alignVar, value="ML")
        self.alignRadio5 = Radiobutton(self.align_frame, text='', variable=self.alignVar, value="MC")
        self.alignRadio6 = Radiobutton(self.align_frame, text='', variable=self.alignVar, value="MR")
        self.alignRadio7 = Radiobutton(self.align_frame, text='', variable=self.alignVar, value="BL")
        self.alignRadio8 = Radiobutton(self.align_frame, text='', variable=self.alignVar, value="BC")
        self.alignRadio9 = Radiobutton(self.align_frame, text='', variable=self.alignVar, value="BR")
        self.alignButton = Button(self.align_frame, text='Align', command=self.alignImage)
        
        # scaling options
        self.scaleLabel = Label(self.scale_frame, text='Scale Image:')
        self.scaleToFillButton = Button(self.scale_frame, text='Scale to Fill Frame', command=self.scaleImageToFill)
        self.scaleToFitButton = Button(self.scale_frame, text='Scale to Fit Frame', command=self.scaleImageToFit)
        self.scalePercentVar = StringVar()
        self.scalePercentLabel1 = Label(self.scale_frame, text='Scale Image to:')
        self.scalePercentLabel2 = Label(self.scale_frame, text='%')
        self.scalePercentEntry = Entry(self.scale_frame, textvariable=self.scalePercentVar, width=8)
        self.scalePercentButton = Button(self.scale_frame, text='Scale', command=self.scaleImageToPercent)
        
        # closing/running
        self.doneButton = Button(self, text="Done", command=self.quit)
        
        #status label
        self.statusVar = StringVar()
        self.statusLabel = Label(self, textvariable=self.statusVar)
        self.statusVar.set('Status:')
        
        # setup values
        self.alignRadio5.select()
        
        # make align layout
        currRow = 0
        self.alignLabel.grid(column=0, row=currRow, columnspan=3)
        currRow += 1
        self.alignRadio1.grid(column=0, row=currRow)
        self.alignRadio2.grid(column=1, row=currRow)
        self.alignRadio3.grid(column=2, row=currRow)
        currRow += 1
        self.alignRadio4.grid(column=0, row=currRow)
        self.alignRadio5.grid(column=1, row=currRow)
        self.alignRadio6.grid(column=2, row=currRow)
        currRow += 1
        self.alignRadio7.grid(column=0, row=currRow)
        self.alignRadio8.grid(column=1, row=currRow)
        self.alignRadio9.grid(column=2, row=currRow)
        currRow += 1
        self.alignButton.grid(column=0, row=currRow, columnspan=3)
        self.doneButton.grid(column=3, row=currRow, columnspan=3)
        
        #make scale layout
        currRow = 0
        self.scaleLabel.grid(column=0, row=currRow, columnspan=3)
        currRow += 1
        self.scaleToFillButton.grid(column=0, row=currRow, columnspan=3)
        currRow += 1
        self.scaleToFitButton.grid(column=0, row=currRow, columnspan=3)
        currRow += 1
        self.scalePercentLabel1.grid(column=0, row=currRow, columnspan=3, sticky=W)
        currRow += 1
        self.scalePercentEntry.grid(column=0, row=currRow)
        self.scalePercentLabel2.grid(column=1, row=currRow)
        self.scalePercentButton.grid(column=2, row=currRow)
        
        #make overall layout
        self.grid()
        self.align_frame.grid(row=0, column=0, sticky=N)
        self.scale_frame.grid(row=0, column=1, sticky=N)
        self.doneButton.grid(row=1, column=0, columnspan=2)
        #self.statusLabel.grid(row=2, column=0, columnspan=2, stick=W)


    def alignImage(self):
        if haveDoc():
            nbrSelected = selectionCount()
            objList = []
            for i in range(nbrSelected):
                objList.append(getSelectedObject(i))
            for i in range(nbrSelected):
                try:
                    obj = objList[i]
                    frameW = getProperty(obj, "width")
                    frameH = getProperty(obj, "height")
                    saveScaleX = getProperty(obj, "imageXScale")
                    saveScaleY = getProperty(obj, "imageYScale")
                    setScaleImageToFrame(True, False, obj)
                    fullScaleX = getProperty(obj, "imageXScale")
                    fullScaleY = getProperty(obj, "imageYScale")
                    setScaleImageToFrame(False, False, obj)
                    scaleImage(saveScaleX, saveScaleY, obj)
                    imageW = frameW * (saveScaleX / fullScaleX)
                    imageH = frameH * (saveScaleY / fullScaleY)
                    imageX = 0.0
                    imageY = 0.0
                    
                    if self.alignVar.get()[0] == "T":
                        imageY = 0.0
                    elif self.alignVar.get()[0] == "M":
                        imageY = (frameH - imageH) / 2.0 / saveScaleY
                    elif self.alignVar.get()[0] == "B":
                        imageY = (frameH - imageH) / saveScaleY
                    if self.alignVar.get()[1] == "L":
                        imageX = 0.0
                    elif self.alignVar.get()[1] == "C":
                        imageX = (frameW - imageW) / 2.0 / saveScaleX
                    elif self.alignVar.get()[1] == "R":
                        imageX = (frameW - imageW) / saveScaleX

                    setProperty(obj, "imageXOffset", imageX)
                    setProperty(obj, "imageYOffset", imageY)
                    docChanged(1)
                    setRedraw(True)
                except:
                    nothing = "nothing"

    def scaleImageToFill(self):
        if haveDoc():
            nbrSelected = selectionCount()
        objList = []
        for i in range(nbrSelected):
            objList.append(getSelectedObject(i))
        for i in range(nbrSelected):
            try:
                obj = objList[i]
                setScaleImageToFrame(True, False, obj)
                scaleX, scaleY = getImageScale(obj)
                setScaleImageToFrame(False, False, obj)
                if scaleX > scaleY:
                    scale = scaleX
                    scaleImage(scale, scale, obj)
                elif scaleY > scaleX:
                    scale = scaleY
                    scaleImage(scale, scale, obj)
                docChanged(1)
                setRedraw(True)
            except:
        	    nothing = "nothing"

    def scaleImageToFit(self):
        if haveDoc():
            nbrSelected = selectionCount()
        objList = []
        for i in range(nbrSelected):
            objList.append(getSelectedObject(i))
        for i in range(nbrSelected):
            try:
                obj = objList[i]
                setScaleImageToFrame(True, True, obj)
                docChanged(1)
                setRedraw(True)
            except:
        	    nothing = "nothing"
    
    def scaleImageToPercent(self):
        if haveDoc():
            nbrSelected = selectionCount()
        objList = []
        for i in range(nbrSelected):
            objList.append(getSelectedObject(i))
        for i in range(nbrSelected):
            try:
                tempScale = float(self.scalePercentVar.get())
                tempScale = tempScale / 100.0
                obj = objList[i]
                scaleImage(tempScale, tempScale, obj)
                setRedraw(True)
                docChanged(1)
            except:
                nothing = "nothing"

    def quit(self):
        self.master.destroy()

		
def main():
    """ Application/Dialog loop with Scribus sauce around """
    try:
        root = Tk()
        app = TkImageAlignmentWizard(root)
        root.mainloop()
    finally:
        if haveDoc():
            redrawAll()

if __name__ == '__main__':
    main()