Ejemplo n.º 1
0
    def __init__(self, module, parent = None):
        rt_qt.QtGui.QWidget.__init__(self)
        
        #customize for simulation type
        self.beamlineType = module.beamlineType
        self.classDictionary = module.classDictionary
        self.importer = module.fileImporter
        self.exporter = module.fileExporter

        self.defaultBeamline = ''

        #set layout
        self.ui = Ui_tree(self, module)

        #undo/redo 
        self.undoStack = rt_qt.QtGui.QUndoStack()

        #connections
        self.ui.workingBeamline.lengthChange.connect(self.callAfterWorkingBeamlineChanges)
        self.ui.workingBeamline.itemDoubleClicked.connect(self.editElement)
        self.ui.workingBeamline.itemPressed.connect(self.listClick)
        self.adv = advDialog(self)
        for button in self.ui.buttons + self.adv.buttons:
            button.clicked.connect(self.createNewElement)
            button.setToolTip(wordwrap(self.classDictionary[button.text()].elementDescription, 60))
        if len(self.ui.advancedNames) > 0:
            self.ui.advanced.clicked.connect(self.adv.show)
        self.ui.clearBeamlineButton.clicked.connect(self.newBeam)
        self.ui.saveBeamlineButton.clicked.connect(self.addBeam)
        self.ui.treeWidget.itemClicked.connect(self.treeClick)
        self.ui.treeWidget.itemDoubleClicked.connect(self.treeItemDoubleClicked)
        self.ui.treeWidget.itemEntered.connect(self.elementTreeHovered)
        self.ui.treeWidget.itemExited.connect(self.elementTreeExit)
        self.ui.graphicsView.itemDoubleClicked.connect(self.editElement)
        self.ui.graphicsView.wheelZoom.connect(self.zoomPreview)
        self.ui.graphicsView.dragDone.connect(self.drawLengthScale)
        self.ui.graphicsView.horizontalScrollBar().valueChanged.connect(self.drawLengthScale)
        self.ui.graphicsView.verticalScrollBar().valueChanged.connect(self.drawLengthScale)
        self.ui.graphicsView.itemDropped.connect(self.droppedOnGraphicsWindow)
        self.ui.contextMenuClicked.connect(self.createContextMenu)

        #### Keyboard shortcuts ####
        # Copy element in tree widget
        rt_qt.QtGui.QShortcut(rt_qt.QtGui.QKeySequence.Copy, self).activated.connect(lambda : self.copyElement(self.ui.treeWidget.currentItem()))

        # Zooming the preview window
        rt_qt.QtGui.QShortcut(rt_qt.QtGui.QKeySequence.ZoomIn, self).activated.connect(lambda : self.zoomPreview(1))
        rt_qt.QtGui.QShortcut(rt_qt.QtGui.QKeySequence.ZoomOut, self).activated.connect(lambda : self.zoomPreview(-1))
        
        #text
        self.addToBeamClickText = self.ui.translateUTF8('Add to current beam line')
        self.beamlineTreeLabel = self.ui.translateUTF8('Beamlines')
        self.dragTargetMessage = u'Drag elements here \u2192'
        self.ui.label.setText(self.dragTargetMessage)
        self.emptyWorkingBeamlineCheck()

        #user interaction state
        if parent == None:
            self.parent = self
            self.parent.lastUsedDirectory = os.path.expanduser('~')
        else:
            self.parent = parent
        self.elementDictionary = OrderedDict()
        self.workingBeamlineName = ''
        self.preListSave = []
        self.preListNameSave = self.workingBeamlineName
        self.preListLabelSave = self.ui.label.text()
        self.zoomScale = 1
        self.drawPreviewEnabled = True

        # Graphical length legend for preview
        self.lengthLegend = []
Ejemplo n.º 2
0
class RbCbt(rt_qt.QtGui.QWidget):
    category = 'beam lines'

    def __init__(self, module, parent = None):
        rt_qt.QtGui.QWidget.__init__(self)
        
        #customize for simulation type
        self.beamlineType = module.beamlineType
        self.classDictionary = module.classDictionary
        self.importer = module.fileImporter
        self.exporter = module.fileExporter

        self.defaultBeamline = ''

        #set layout
        self.ui = Ui_tree(self, module)

        #undo/redo 
        self.undoStack = rt_qt.QtGui.QUndoStack()

        #connections
        self.ui.workingBeamline.lengthChange.connect(self.callAfterWorkingBeamlineChanges)
        self.ui.workingBeamline.itemDoubleClicked.connect(self.editElement)
        self.ui.workingBeamline.itemPressed.connect(self.listClick)
        self.adv = advDialog(self)
        for button in self.ui.buttons + self.adv.buttons:
            button.clicked.connect(self.createNewElement)
            button.setToolTip(wordwrap(self.classDictionary[button.text()].elementDescription, 60))
        if len(self.ui.advancedNames) > 0:
            self.ui.advanced.clicked.connect(self.adv.show)
        self.ui.clearBeamlineButton.clicked.connect(self.newBeam)
        self.ui.saveBeamlineButton.clicked.connect(self.addBeam)
        self.ui.treeWidget.itemClicked.connect(self.treeClick)
        self.ui.treeWidget.itemDoubleClicked.connect(self.treeItemDoubleClicked)
        self.ui.treeWidget.itemEntered.connect(self.elementTreeHovered)
        self.ui.treeWidget.itemExited.connect(self.elementTreeExit)
        self.ui.graphicsView.itemDoubleClicked.connect(self.editElement)
        self.ui.graphicsView.wheelZoom.connect(self.zoomPreview)
        self.ui.graphicsView.dragDone.connect(self.drawLengthScale)
        self.ui.graphicsView.horizontalScrollBar().valueChanged.connect(self.drawLengthScale)
        self.ui.graphicsView.verticalScrollBar().valueChanged.connect(self.drawLengthScale)
        self.ui.graphicsView.itemDropped.connect(self.droppedOnGraphicsWindow)
        self.ui.contextMenuClicked.connect(self.createContextMenu)

        #### Keyboard shortcuts ####
        # Copy element in tree widget
        rt_qt.QtGui.QShortcut(rt_qt.QtGui.QKeySequence.Copy, self).activated.connect(lambda : self.copyElement(self.ui.treeWidget.currentItem()))

        # Zooming the preview window
        rt_qt.QtGui.QShortcut(rt_qt.QtGui.QKeySequence.ZoomIn, self).activated.connect(lambda : self.zoomPreview(1))
        rt_qt.QtGui.QShortcut(rt_qt.QtGui.QKeySequence.ZoomOut, self).activated.connect(lambda : self.zoomPreview(-1))
        
        #text
        self.addToBeamClickText = self.ui.translateUTF8('Add to current beam line')
        self.beamlineTreeLabel = self.ui.translateUTF8('Beamlines')
        self.dragTargetMessage = u'Drag elements here \u2192'
        self.ui.label.setText(self.dragTargetMessage)
        self.emptyWorkingBeamlineCheck()

        #user interaction state
        if parent == None:
            self.parent = self
            self.parent.lastUsedDirectory = os.path.expanduser('~')
        else:
            self.parent = parent
        self.elementDictionary = OrderedDict()
        self.workingBeamlineName = ''
        self.preListSave = []
        self.preListNameSave = self.workingBeamlineName
        self.preListLabelSave = self.ui.label.text()
        self.zoomScale = 1
        self.drawPreviewEnabled = True

        # Graphical length legend for preview
        self.lengthLegend = []

    def undo(self):
        self.undoStack.undo()

    def redo(self):
        self.undoStack.redo()

    def addToEndOfWorkingBeamLine(self, elementName, copies = None):
        if not copies:
            copies, ok = self.getNumberOfCopies(elementName)
        else:
            ok = True

        if ok:
            for i in range(copies):
                self.ui.workingBeamline.addItem(elementName)
            self.callAfterWorkingBeamlineChanges()
        
    def addReversedToEndOfWorkingBeamLine(self, elementName, copies = None):
        blr = self.elementDictionary[elementName].reverse()
        if not blr.isBeamline():
            return
        self.elementDictionary[blr.name] = blr
        self.addToEndOfWorkingBeamLine(blr.name, copies)

    def emptyWorkingBeamlineCheck(self):
        isEmpty = self.ui.workingBeamline.count() == 0
        self.ui.clearBeamlineButton.setDisabled(isEmpty)
        self.ui.saveBeamlineButton.setDisabled(isEmpty)

    def droppedOnGraphicsWindow(self):
        if self.ui.treeWidget.currentItem():
            self.addToEndOfWorkingBeamLine(self.ui.treeWidget.currentItem().text(0))

    def callAfterWorkingBeamlineChanges(self):
        self.fixWorkingBeamline()
        self.postListSave = self.workingBeamlineElementNames()
        if self.preListSave == self.postListSave:
            self.emptyWorkingBeamlineCheck()
            return
        self.postListNameSave = self.workingBeamlineName
        self.postListLabelSave = self.ui.label.text()
        undoAction = commandEditBeam(self)
        self.undoStack.push(undoAction)
        self.preListSave = self.postListSave
        self.preListNameSave = self.postListNameSave
        self.preListLabelSave = self.postListLabelSave
    
    def treeClick(self, item = None, column = None):
        # Draw element currently selected
        if item and item.text(column) == self.addToBeamClickText:
            self.addToEndOfWorkingBeamLine(item.text(0), 1)
        else:
            self.elementPreview()
        # Allow workingBeamline list and beamline preview to accept drops from treeWidget
        self.ui.workingBeamline.setDragDropMode(rt_qt.QtGui.QAbstractItemView.DropOnly)
        self.ui.graphicsView.setAcceptDrops(True)

    def treeItemDoubleClicked(self, item, column):
        if item and item.text(column) != self.addToBeamClickText:
            self.editElement(item.text(0))

    def elementTreeHovered(self, item, column):
        if item.text(column) == self.addToBeamClickText:
            self.ui.treeWidget.setCursor(rt_qt.QtGui.QCursor(rt_qt.QtCore.Qt.PointingHandCursor))
        else:
            self.elementTreeExit()

    def elementTreeExit(self):
        self.ui.treeWidget.setCursor(rt_qt.QtGui.QCursor(rt_qt.QtCore.Qt.ArrowCursor))

    def listClick(self):
        # Draw current working beamline
        self.workingBeamlinePreview()
        # Allow workingBeamline elements to be moved around without copying
        self.ui.workingBeamline.setDragDropMode(rt_qt.QtGui.QAbstractItemView.InternalMove)
        # Don't allow drags to beamline preview
        self.ui.graphicsView.setAcceptDrops(False)

    def createContextMenu(self, name, location, globalPos):
        element = self.elementDictionary.get(name)

        mouseMenu = rt_qt.QtGui.QMenu(self)

        if element:
            if location == 'picture':
                menuTitle = rt_qt.QtGui.QAction(element.toolTip(), mouseMenu)
                menuTitle.setEnabled(False)
                mouseMenu.addAction(menuTitle)
                mouseMenu.addSeparator()

            mouseMenu.addAction(self.ui.translateUTF8('Edit ...'), lambda: self.editElement(element.name))
            mouseMenu.addAction(self.ui.translateUTF8('New copy'), lambda: self.copyElement(element.name))
            mouseMenu.addAction(self.ui.translateUTF8('Delete element'), lambda: self.deleteElement(element.name))
            mouseMenu.addSeparator()

            if location == 'tree':
                mouseMenu.addAction(self.addToBeamClickText,
                        lambda: self.addToEndOfWorkingBeamLine(element.name, 1))
                mouseMenu.addAction('Add multiple copies to current beam line...',
                        lambda: self.addToEndOfWorkingBeamLine(element.name))
                if element.isBeamline():
                    mouseMenu.addAction('Add reversed to current beam line',
                            lambda: self.addReversedToEndOfWorkingBeamLine(element.name, 1))
                    mouseMenu.addAction('Add multiple reversed to current beam line..',
                            lambda: self.addReversedToEndOfWorkingBeamLine(element.name))

            if location == 'list':
                if element.isBeamline():
                    mouseMenu.addAction(self.ui.translateUTF8('Reverse'), self.convertToReversed)

                mouseMenu.addAction(self.ui.translateUTF8('Add another'), self.listCopy)
                mouseMenu.addAction(self.ui.translateUTF8('Add multiple copies ...'), self.listMultipleCopy)

                mouseMenu.addAction(self.ui.translateUTF8('Remove from beam line'), self.removeFromWorkingBeamline)

            containingBeamlines = []
            for bl in self.elementDictionary.values():
                if bl.isBeamline() and bl != element:
                    if bl.contains(element):
                        containingBeamlines.append(bl)
            if containingBeamlines:
                mouseMenu.addSeparator()
                blMenu = rt_qt.QtGui.QMenu('Contained in beamlines', self)
                for bl in containingBeamlines:
                    entry = rt_qt.QtGui.QAction(bl.name, self)
                    entry.setEnabled(False)
                    blMenu.addAction(entry)
                mouseMenu.addMenu(blMenu)

        if location == 'picture' and not self.ui.graphicsView.scene().zeroSized():
            mouseMenu.addSeparator()
            mouseMenu.addAction(self.ui.translateUTF8('Save preview image...'), \
                    self.savePreviewImage)
            mouseMenu.addAction(self.ui.translateUTF8('Reset zoom'), self.drawElement)
            mouseMenu.addAction(self.ui.translateUTF8('Turn ' + ('off' if self.drawPreviewEnabled else 'on') + \
                    ' graphical preview'), self.toggleDrawPreview)

        if mouseMenu.actions():
            mouseMenu.exec_(globalPos)

    def toggleDrawPreview(self):
        self.drawPreviewEnabled = not self.drawPreviewEnabled
        self.drawElement()
        
    def removeFromWorkingBeamline(self):
        row = self.ui.workingBeamline.currentRow()
        self.ui.workingBeamline.takeItem(row)
        self.callAfterWorkingBeamlineChanges()

    def editElement(self, name):
        try:
            selectedElement = self.elementDictionary[name]
        except KeyError: # Element name not found
            return

        if selectedElement.isBeamline():
            self.setWorkingBeamline(selectedElement)
            self.callAfterWorkingBeamlineChanges()
        else:
            dialog = genDialog(selectedElement)
            if dialog.exec_():
                data = dialog.info + dialog.more
                newElement = type(selectedElement)([datum[1].text() for datum in data])
                undoAction = commandEditElement(self, selectedElement, newElement)
                self.undoStack.push(undoAction)
                self.elementPreview()

    def copyElement(self, name):
        # name could be text string or QTreeWidgetItem
        if name is None:
            return
        try:
            name = name.text(0)
        except AttributeError:
            pass

        originalElement = self.elementDictionary[name]
        newElement = type(originalElement)([originalElement.name] + originalElement.data[:])

        copyLabel = '_Copy'
        copyLabelLocation = newElement.name.rfind(copyLabel)
        if copyLabelLocation == -1:
            newElement.name = newElement.name + copyLabel
        else:
            newElement.name = newElement.name[:copyLabelLocation] + copyLabel
        undoAction = commandLoadElements(self, [newElement])
        self.undoStack.push(undoAction)

    def getNumberOfCopies(self, elementName):
        return rt_qt.QtGui.QInputDialog.getInt(self, \
                "Add multiple copies", \
                "Number of copies of " \
                + elementName + " to add:", \
                1,1)

    def listMultipleCopy(self):
        copies, ok = self.getNumberOfCopies(self.ui.workingBeamline.currentItem().text())
        if ok:
            for i in range(copies):
                self.listCopy(False)
            self.callAfterWorkingBeamlineChanges()

    def listCopy(self, postList = True):
        item = self.ui.workingBeamline.currentItem()
        row = self.ui.workingBeamline.row(item)
        self.ui.workingBeamline.insertItem(row, item.text())
        if postList:
            self.callAfterWorkingBeamlineChanges()

    def rewriteBeamlineTree(self):
        # Update element list
        for group in self.topLevelTreeItems():
            for item in itemsInGroup(group):
                populateTreeItem(self.addToBeamClickText, item, self.elementDictionary[item.text(0)])
        self.fitColumns()

        # Update Working Beamline
        self.fixWorkingBeamline()

    def fitColumns(self):
        for i in range(self.ui.treeWidget.columnCount()):
            if i != 1: # don't fit Description Column
                self.ui.treeWidget.resizeColumnToContents(i)

    def createNewElement(self):
        typeName = self.sender().text()
        elementType = self.classDictionary[typeName]
        dialog = genDialog(elementType())
        if dialog.exec_():
            data = [datum[1].text() for datum in dialog.info + dialog.more]
            element = elementType(data)
            undoAction = commandLoadElements(self, [element])
            self.undoStack.push(undoAction)

    def uniqueName(self, name):
        # Element name cannot match a type name
        lowerTypes = [elementType.lower() for elementType in self.classDictionary]
        while name.lower() in lowerTypes:
            name = name + 'X'

        # Element name cannot match another element's name
        counter = 0
        baseName = name
        lowerNames = [elementName.lower() for elementName in self.elementDictionary]
        while name.lower() in lowerNames:
            counter += 1
            name = baseName + str(counter)

        return name

    def addBeam(self):
        beamline = self.beamlineType()
        if self.workingBeamlineName:
            beamline.name = self.workingBeamlineName

        for name in self.workingBeamlineElementNames():
            beamline.addElement(self.elementDictionary[name])              

        dialog = genDialog(beamline)
        if dialog.exec_():
            beamline.name = dialog.info[0][1].text()
            if self.workingBeamlineName:
                oldBeam = self.elementDictionary[self.workingBeamlineName]
                undoAction = commandEditElement(self, oldBeam, beamline)
            else:
                undoAction = commandLoadElements(self, [beamline])
            self.setWorkingBeamline()
            self.callAfterWorkingBeamlineChanges()
            self.undoStack.push(undoAction)

    def findElementInTreeByName(self, name):
        for group in self.topLevelTreeItems():
            for item in itemsInGroup(group):
                if item.text(0) == name:
                    return item

    def deleteElement(self, name):
        undoAction = commandDeleteElement(self, name)
        self.undoStack.push(undoAction)

    def newBeam(self):
        self.setWorkingBeamline()
        self.callAfterWorkingBeamlineChanges()

    def setWorkingBeamline(self, beamline = None):
        self.workingBeamlineName = beamline.name if (beamline and beamline.name in self.elementDictionary) else ''
        self.ui.label.setText(('Editing "' + self.workingBeamlineName + '"') if self.workingBeamlineName else self.dragTargetMessage)
        self.ui.workingBeamline.clear()
        if beamline:
            self.ui.workingBeamline.addItems([element.name for element in beamline.data])
        self.emptyWorkingBeamlineCheck()
        self.workingBeamlinePreview()

    def fixWorkingBeamline(self):
        beamline = self.elementDictionary.get(self.workingBeamlineName)

        # Beamlines are constructed only from previously existing elements.
        # Beamlines cannot contain themselves.
        items = [name for name in self.workingBeamlineElementNames() if name in self.elementDictionary \
                and not self.elementDictionary[name].contains(beamline)]
        self.ui.workingBeamline.clear()
        self.ui.workingBeamline.addItems(items)

    def elementPreview(self):
        try:
            item = self.ui.treeWidget.currentItem()
            self.drawElement(self.elementDictionary[item.text(0)])
        except (AttributeError, KeyError):
            self.ui.graphicsView.scene().clear()

    def workingBeamlinePreview(self):
        bl = self.beamlineType()
        for name in self.workingBeamlineElementNames():
            bl.addElement(self.elementDictionary[name])
        self.drawElement(bl)

    def drawElement(self, element = None):
        scene = self.ui.graphicsView.scene()
        scene.clear()

        if not self.drawPreviewEnabled:
            return

        drawMessage = rt_qt.QtGui.QProgressDialog('Drawing beam line ...', 'Cancel', 0, 5, self.parent)
        drawMessage.setMinimumDuration(500)
        drawMessage.setValue(0)
        if element:
            self.lastDrawnElement = element
        else:
            element = self.lastDrawnElement

        element.picture(scene)
        sceneRect = scene.itemsBoundingRect()
        scene.setSceneRect(sceneRect)

        drawMessage.setValue(1)

        sx = sceneRect.width()
        sy = sceneRect.height()

        if sx == 0 or sy == 0:
            drawMessage.setValue(drawMessage.maximum())
            return

        viewSize = self.ui.graphicsView.size()
        vx = viewSize.width()
        vy = viewSize.height()

        scale = min([vx/sx, vy/sy, 1.0])

        drawMessage.setValue(2)
        self.ui.graphicsView.resetTransform()
        drawMessage.setValue(3)
        self.ui.graphicsView.scale(scale, scale)
        drawMessage.setValue(4)
        self.zoomScale = scale

        self.drawLengthScale()
        drawMessage.setValue(drawMessage.maximum())

    def visibleSceneRect(self):
        viewportRect = rt_qt.QtCore.QRect(0,0,self.ui.graphicsView.viewport().width(),
                                        self.ui.graphicsView.viewport().height())
        return self.ui.graphicsView.mapToScene(viewportRect).boundingRect()

    def drawLengthScale(self):
        if self.ui.graphicsView.scene() is None or len(self.ui.graphicsView.scene().items()) == 0:
            return
        self.removeLengthScale()
        vis = self.visibleSceneRect()

        # Determine a reasonable length to display
        length = 1.0 # meter
        widthFraction = 0.25 # maximum length of legend w.r.t. preview window
        resolution = self.classDictionary.values()[0]().getResolution()

        while length*resolution < float(vis.width())*widthFraction:
            length = length*10.0

        while length*resolution > float(vis.width())*widthFraction:
            length = length/10.0

        textItem = rt_qt.QtGui.QGraphicsTextItem(displayWithUnitsNumber(length, 'm'))

        pixLength = int(length*resolution)
        if pixLength < 1:
            return

        # line showing length given by textItem
        try:
            rightEnd = rt_qt.QtCore.QPoint(vis.right()-vis.width()/10,
                                     vis.bottom()-vis.height()/10)
        except OverflowError:
            self.zoomPreview(1)
            return
        leftEnd = rightEnd - rt_qt.QtCore.QPoint(pixLength,0)
        self.lengthLegend.append(rt_qt.QtGui.QGraphicsLineItem(leftEnd.x(),
                                                         leftEnd.y(),
                                                         rightEnd.x(),
                                                         rightEnd.y()))
        endHeight = int(20.0/self.zoomScale) # constant-height despite zooming
        if endHeight <= 0:
            self.zoomPreview(-1)
            return
        # left upright bracket
        self.lengthLegend.append(rt_qt.QtGui.QGraphicsLineItem(leftEnd.x(),
                                                         leftEnd.y()+endHeight,
                                                         leftEnd.x(),
                                                         leftEnd.y()-endHeight))
        # right upright bracket
        self.lengthLegend.append(rt_qt.QtGui.QGraphicsLineItem(rightEnd.x(),
                                                         rightEnd.y()+endHeight,
                                                         rightEnd.x(),
                                                         rightEnd.y()-endHeight))
        # Scale textItem so it always appears the same size
        textItem.translate(rightEnd.x(),
                           rightEnd.y() - 
                           textItem.boundingRect().height()/(2*self.zoomScale))
        textItem.scale(1.0/self.zoomScale,
                       1.0/self.zoomScale)
        self.lengthLegend.append(textItem)

        for item in self.lengthLegend:
            self.ui.graphicsView.scene().addItem(item)

    def removeLengthScale(self):
        for item in self.lengthLegend:
            if item in self.ui.graphicsView.scene().items():
                self.ui.graphicsView.scene().removeItem(item)
        self.lengthLegend = []

    def zoomPreview(self, wheelClicks):
        scale = 1.2**wheelClicks
        self.zoomScale = self.zoomScale*scale
        self.ui.graphicsView.setTransformationAnchor(rt_qt.QtGui.QGraphicsView.AnchorUnderMouse)
        self.ui.graphicsView.scale(scale, scale)
        self.ui.graphicsView.setTransformationAnchor(rt_qt.QtGui.QGraphicsView.NoAnchor)
        self.drawLengthScale()

    def savePreviewImage(self):
        imageSuffixes = ['png', 'jpg', 'bmp', 'ppm', 'tiff', 'xbm', 'xpm']
        fileName = getSaveFileName(self, imageSuffixes)
        if not fileName:
            return
        fileExtension = os.path.splitext(fileName)[1]
        self.parent.lastUsedDirectory = os.path.dirname(fileName)

        view = self.ui.graphicsView
        questionBox = rt_qt.QtGui.QMessageBox(rt_qt.QtGui.QMessageBox.Question, 'RadTrack', 'Render entire beamline or just the viewable portion?')
        responses = [questionBox.addButton(text , rt_qt.QtGui.QMessageBox.ActionRole) for text in ['Entire Beamline', 'Viewable Portion']]
        questionBox.exec_()

        if questionBox.clickedButton() == responses[0]:
            boundingRectangle = view.scene().itemsBoundingRect()
        else:
            boundingRectangle = self.visibleSceneRect()

        # reduce image size if either dimension is larger than maxImageDimension
        maxImageDimension = 2**14 - 1
        sceneSize = max([boundingRectangle.width(),
                         boundingRectangle.height()])*self.zoomScale
        scale = min([1.0, maxImageDimension/sceneSize])
        self.zoomScale = self.zoomScale*scale
        view.setTransformationAnchor(rt_qt.QtGui.QGraphicsView.AnchorViewCenter)
        view.scale(scale, scale)
        view.setTransformationAnchor(rt_qt.QtGui.QGraphicsView.NoAnchor)

        try:
            progress = rt_qt.QtGui.QProgressDialog('Creating Image ...', 'Cancel', 0, 6, self)
            progress.setMinimumDuration(0)
            progress.setValue(0)
            image = rt_qt.QtGui.QImage((boundingRectangle.size()*self.zoomScale).toSize(),
                    rt_qt.QtGui.QImage.Format_ARGB32_Premultiplied)
            if progress.wasCanceled():
                return
            progress.setValue(1)
            progress.setLabelText('Filling Background ...')
            if fileExtension in ['png', 'tiff', 'xpm']:
                image.fill(rt_qt.QtGui.QColor('transparent'))
            else:
                image.fill(rt_qt.QtGui.QColor('white'))
            if progress.wasCanceled():
                return

            progress.setValue(2)
            progress.setLabelText('Creating Painter ...')
            painter = rt_qt.QtGui.QPainter(image)
            if progress.wasCanceled():
                return

            progress.setValue(3)
            progress.setLabelText('Rendering Image ...')
            view.scene().render(painter, rt_qt.QtCore.QRectF(), boundingRectangle)
            if progress.wasCanceled():
                return

            progress.setValue(4)
            progress.setLabelText('Saving Image ...')
            if not image.save(fileName):
                rt_qt.QtGui.QMessageBox(rt_qt.QtGui.QMessageBox.Warning,
                                 'RadTrack', "Image (" + fileName + ") was not saved.").exec_()
                progress.reset()
                return

        finally:
            progress.setLabelText('Deleting Painter ...')
            progress.setValue(5)
            try:
                del painter
            except Exception:
                pass
            progress.setValue(6)
            self.drawLengthScale()

    def convertToReversed(self):
        beamlineName = self.ui.workingBeamline.currentItem().text()
        reversedBeamline = self.elementDictionary[beamlineName].reverse()
        if not reversedBeamline.isBeamline():
            return
        self.elementDictionary[reversedBeamline.name] = reversedBeamline
        self.ui.workingBeamline.currentItem().setText(reversedBeamline.name)
        self.callAfterWorkingBeamlineChanges()

    def topLevelTreeItems(self):
        return [self.ui.treeWidget.topLevelItem(i) for i in range(self.ui.treeWidget.topLevelItemCount())]

    def workingBeamlineElementNames(self):
        return [self.ui.workingBeamline.item(i).text() for i in range(self.ui.workingBeamline.count())]

    def importFile(self, fileName):
        ignoreMissingImportFiles = False
        try:
            importedData = self.importer(fileName)
        except IOError as e:
            rt_qt.QtGui.QMessageBox(rt_qt.QtGui.QMessageBox.Warning, 'RadTrack', e.message).exec_()
            return

        if importedData:
            newElements, defaultBeamline = importedData

            if newElements:
                undoAction = commandLoadElements(self, newElements.values())
                self.undoStack.push(undoAction)

            if defaultBeamline:
                self.defaultBeamline = defaultBeamline

            # Copy files referenced by the elements into the current working directory
            for element in [e for e in newElements.values() if not e.isBeamline()]:
                for parameter in element.inputFileParameters:
                    index = element.parameterNames.index(parameter)
                    if element.data[index]:
                        path = os.path.join(os.path.dirname(fileName), element.data[index])
                        try:
                            shutil.copy2(path, self.parent.sessionDirectory)
                        except IOError:
                            if not ignoreMissingImportFiles:
                                box = rt_qt.QtGui.QMessageBox(rt_qt.QtGui.QMessageBox.Warning,
                                                        'Missing File Reference',
                                                        'The file "' + path.replace('\\', '/') + '" specified by element "' + \
                                                        element.name + '" cannot be found.\n\n' +\
                                                        'Do you wish to ignore future warnings of this type?',
                                                        rt_qt.QtGui.QMessageBox.Yes | rt_qt.QtGui.QMessageBox.No, self)
                                box.exec_()
                                if box.standardButton(box.clickedButton()) == rt_qt.QtGui.QMessageBox.Yes:
                                    ignoreMissingImportFiles = True

    def exportToFile(self, outputFileName = None):
        if not outputFileName:
            getSaveFileName(self)
                
            if not outputFileName:
                return # User cancelled

        self.exporter(outputFileName, self.elementDictionary, self.defaultBeamline)

    def closeEvent(self, event):
        # Large pictures seem to crash python on exiting RadTrack,
        # so clear the picture before exiting.
        self.ui.graphicsView.scene().clear()
        rt_qt.QtGui.QWidget.closeEvent(self, event)