source: orange/Orange/OrangeWidgets/OWDlgs.py @ 11314:2cd807719971

Revision 11314:2cd807719971, 13.5 KB checked in by Ales Erjavec <ales.erjavec@…>, 14 months ago (diff)

Added 'canSaveToMatplotlib' method.

Check if the QGraphicsScene contents can actually be saved by the
savaToMatplotlib method.

(references #1268)

Line 
1import os
2from OWBaseWidget import *
3import OWGUI
4
5_have_qwt = True
6try:
7    from PyQt4.Qwt5 import *
8except ImportError:
9    _have_qwt = False
10
11_have_gl = True
12try:
13    from PyQt4.QtOpenGL import QGLWidget
14except ImportError:
15    _have_gl = False
16
17from PyQt4.QtGui import QGraphicsScene, QGraphicsView
18from PyQt4.QtSvg import *
19from ColorPalette import *
20import OWQCanvasFuncts
21
22class OWChooseImageSizeDlg(OWBaseWidget):
23    settingsList = ["selectedSize", "customX", "customY", "lastSaveDirName", "penWidthFactor"]
24    def __init__(self, graph, extraButtons = [], defaultName="graph", parent=None, saveMatplotlib=None):
25        OWBaseWidget.__init__(self, parent, None, "Image settings", modal = TRUE, resizingEnabled = 0)
26
27        self.graph = graph
28        self.selectedSize = 0
29        self.customX = 400
30        self.customY = 400
31        self.saveAllSizes = 0
32        self.penWidthFactor = 1
33        self.lastSaveDirName = "./"
34        self.defaultName = defaultName
35
36        self.loadSettings()
37
38        self.setLayout(QVBoxLayout(self))
39        self.space = OWGUI.widgetBox(self)
40        self.layout().setMargin(8)
41        #self.layout().addWidget(self.space)
42
43        box = OWGUI.widgetBox(self.space, "Image Size")
44
45        global _have_qwt
46        if _have_qwt and isinstance(graph, QwtPlot):
47            size = OWGUI.radioButtonsInBox(box, self, "selectedSize", ["Current size", "400 x 400", "600 x 600", "800 x 800", "Custom:"], callback = self.updateGUI)
48            self.customXEdit = OWGUI.lineEdit(OWGUI.indentedBox(box), self, "customX", "Width: ", orientation = "horizontal", valueType = int)
49            self.customYEdit = OWGUI.lineEdit(OWGUI.indentedBox(box), self, "customY", "Height:", orientation = "horizontal", valueType = int)
50            OWGUI.comboBoxWithCaption(self.space, self, "penWidthFactor", label = 'Factor:   ', box = " Pen width multiplication factor ",  tooltip = "Set the pen width factor for all curves in the plot\n(Useful for example when the lines in the plot look to thin)\nDefault: 1", sendSelectedValue = 1, valueType = int, items = range(1,20))
51        elif isinstance(graph, QGraphicsScene) or isinstance(graph, QGraphicsView) or (_have_gl and isinstance(graph, QGLWidget)):
52            OWGUI.widgetLabel(box, "Image size will be set automatically.")
53
54        box = OWGUI.widgetBox(self.space, 1)
55        #self.printButton =          OWGUI.button(self.space, self, "Print", callback = self.printPic)
56        self.saveImageButton =      OWGUI.button(box, self, "Save Image", callback = self.saveImage)
57
58        # If None we try to determine if save can succeed automatically
59        if saveMatplotlib is None:
60            saveMatplotlib = self.canSaveToMatplotlib(graph)
61
62        if saveMatplotlib and not (_have_gl and isinstance(graph, QGLWidget)):
63            self.saveMatplotlibButton = OWGUI.button(box, self, "Save Graph as matplotlib Script", callback = self.saveToMatplotlib)
64        for (text, funct) in extraButtons:
65            butt = OWGUI.button(box, self, text, callback = funct)
66            self.connect(butt, SIGNAL("clicked()"), self.accept)        # also connect the button to accept so that we close the dialog
67        OWGUI.button(box, self, "Cancel", callback = self.reject)
68
69        self.resize(250,300)
70        self.updateGUI()
71
72    def saveImage(self, filename = None, size = None, closeDialog = 1):
73        if not filename:
74            filename = self.getFileName(self.defaultName, "Portable Network Graphics (*.PNG);;Windows Bitmap (*.BMP);;Graphics Interchange Format (*.GIF);;Scalable Vector Graphics (*.SVG)", ".png")
75            if not filename: return
76
77        (fil,ext) = os.path.splitext(filename)
78        if ext.lower() not in [".bmp", ".gif", ".png", ".svg"] :
79            ext = ".png"                                        # if no format was specified, we choose png
80        filename = fil + ext
81       
82        if _have_gl and isinstance(self.graph, QGLWidget):
83            img = self.graph.grabFrameBuffer()
84            if size != None:
85                img = img.scaled(size)
86            img.save(filename)
87            if closeDialog:
88                QDialog.accept(self)
89            return
90
91        real_graph = self.graph if isinstance(self.graph, QGraphicsView) else None
92        if real_graph:
93            self.graph = self.graph.scene()           
94
95        if isinstance(self.graph, QGraphicsScene):
96            source = self.getSceneBoundingRect().adjusted(-15, -15, 15, 15)
97            size = source.size()
98        elif isinstance(self.graph, QGraphicsView):
99            source = self.graph.sceneRect()
100            size = source.size()
101        elif not size:
102            size = self.getSize()
103
104        painter = QPainter()
105        if filename.lower().endswith(".svg"):
106            buffer = QSvgGenerator()
107            buffer.setFileName(filename)
108            buffer.setSize(QSize(int(size.width()), int(size.height())))
109        else:
110            buffer = QPixmap(int(size.width()), int(size.height()))
111        painter.begin(buffer)
112        painter.setRenderHint(QPainter.Antialiasing)
113        if not filename.lower().endswith(".svg"):
114            if isinstance(self.graph, QGraphicsScene) or isinstance(self.graph, QGraphicsView):
115                # make background same color as the widget's background
116                brush = self.graph.backgroundBrush()
117                if brush.style() == Qt.NoBrush:
118                    brush = QBrush(self.graph.palette().color(QPalette.Base))
119                painter.fillRect(buffer.rect(), brush)
120            else:
121                painter.fillRect(buffer.rect(), QBrush(Qt.white))
122
123        # qwt plot
124        global _have_qwt
125        if _have_qwt and isinstance(self.graph, QwtPlot):
126
127            if self.penWidthFactor != 1:
128                for curve in self.graph.itemList():
129                    pen = curve.pen(); pen.setWidth(self.penWidthFactor*pen.width()); curve.setPen(pen)
130
131            self.graph.print_(painter, QRect(0,0,size.width(), size.height()))
132
133            if self.penWidthFactor != 1:
134                for curve in self.graph.itemList():
135                    pen = curve.pen(); pen.setWidth(pen.width()/self.penWidthFactor); curve.setPen(pen)
136
137        # QGraphicsScene
138        elif isinstance(self.graph, QGraphicsScene) or isinstance(self.graph, QGraphicsView):
139            target = QRectF(0,0, source.width(), source.height())
140            self.graph.render(painter, target, source)
141
142        if not filename.lower().endswith(".svg"):
143            buffer.save(filename)
144
145        if closeDialog:
146            QDialog.accept(self)
147
148    def getSceneBoundingRect(self):
149        source = QRectF()
150        for item in self.graph.items():
151            if item.isVisible():
152                source = source.united(item.boundingRect().translated(item.pos()))
153        return source
154
155    def saveToMatplotlib(self):
156        filename = self.getFileName(self.defaultName, "Python Script (*.py)", ".py")
157        if filename:
158            global _have_qwt
159            if _have_qwt and isinstance(self.graph, QwtPlot):
160                self.graph.saveToMatplotlib(filename, self.getSize())
161            else:
162                rect = self.getSceneBoundingRect()
163                minx, maxx, miny, maxy = rect.x(), rect.x()+rect.width(), rect.y(), rect.y()+rect.height()
164                f = open(filename, "wt")
165                f.write("# This Python file uses the following encoding: utf-8\n")
166                f.write("from pylab import *\nfrom matplotlib.patches import Rectangle\n\n#constants\nx1 = %f; x2 = %f\ny1 = 0.0; y2 = %f\ndpi = 80\nxsize = %d\nysize = %d\nedgeOffset = 0.01\n\nfigure(facecolor = 'w', figsize = (xsize/float(dpi), ysize/float(dpi)), dpi = dpi)\na = gca()\nhold(True)\n" % (minx, maxx, maxy, maxx-minx, maxy-miny))
167               
168                if isinstance(self.graph, QGraphicsView):
169                    scene = self.graph.scene()
170                else:
171                    scene = self.graph
172               
173                sortedList = [(item.zValue(), item) for item in scene.items()]
174                sortedList.sort()   # sort items by z value
175
176                for (z, item) in sortedList:
177                    # a little compatibility for QT 3.3 (on Mac at least)
178                    if hasattr(item, "isVisible"):
179                        if not item.isVisible(): continue
180                    elif not item.visible(): continue
181                    if item.__class__ in [QGraphicsRectItem, QGraphicsLineItem]:
182                        penc, penAlpha  = self._getColorFromObject(item.pen())
183                        penWidth = item.pen().width()
184
185                        if isinstance(item, QGraphicsRectItem):
186                            x,y,w,h = item.rect().x(), maxy-item.rect().y()-item.rect().height(), item.rect().width(), item.rect().height()
187                            brushc, brushAlpha = self._getColorFromObject(item.brush())
188                            f.write("a.add_patch(Rectangle((%d, %d), %d, %d, edgecolor=%s, facecolor = %s, linewidth = %d, fill = %d))\n" % (x,y,w,h, penc, brushc, penWidth, type(brushc) == tuple))
189                        elif isinstance(item, QGraphicsLineItem):
190                            x1,y1, x2,y2 = item.line().x1(), maxy-item.line().y1(), item.line().x2(), maxy-item.line().y2()
191                            f.write("plot(%s, %s, marker = 'None', linestyle = '-', color = %s, linewidth = %d, alpha = %.3f)\n" % ([x1,x2], [y1,y2], penc, penWidth, penAlpha))
192                    elif item.__class__ in [QGraphicsTextItem, OWQCanvasFuncts.OWCanvasText]:
193                        if item.__class__  == QGraphicsTextItem:
194                            xalign, yalign = "left", "top"
195                            x, y = item.x(), item.y()
196                        else:
197                            align = item.alignment
198                            #xalign = (align & Qt.AlignLeft and "right") or (align & Qt.AlignRight and "left") or (align & Qt.AlignHCenter and "center")
199                            #yalign = (align & Qt.AlignBottom and "top") or (align & Qt.AlignTop and "bottom") or (align & Qt.AlignVCenter and "center")
200                            xalign = (align & Qt.AlignLeft and "left") or (align & Qt.AlignRight and "right") or (align & Qt.AlignHCenter and "center")
201                            yalign = (align & Qt.AlignBottom and "bottom") or (align & Qt.AlignTop and "top") or (align & Qt.AlignVCenter and "center")
202                            x, y = item.x, item.y
203                        vertAlign = (yalign and ", verticalalignment = '%s'" % yalign) or ""
204                        horAlign = (xalign and ", horizontalalignment = '%s'" % xalign) or ""
205                        color = tuple([item.defaultTextColor().red()/255., item.defaultTextColor().green()/255., item.defaultTextColor().blue()/255.])
206                        weight = item.font().bold() and "bold" or "normal"
207                        f.write("text(%f, %f, '%s'%s%s, color = %s, name = '%s', weight = '%s', alpha = %.3f)\n" % (item.x, maxy-item.y, unicode(item.toPlainText()).encode("utf-8"), vertAlign, horAlign, color, str(item.font().family()), weight, item.defaultTextColor().alpha()/float(255)))
208
209                f.write("# disable grid\ngrid(False)\n\n")
210                f.write("#hide axis\naxis('off')\naxis([x1, x2, y1, y2])\ngca().set_position([edgeOffset, edgeOffset, 1 - 2*edgeOffset, 1 - 2*edgeOffset])\n")
211                f.write("show()")
212                f.close()
213
214            try:
215                import matplotlib
216            except:
217                QMessageBox.information(self,'Matplotlib missing',"File was saved, but you will not be able to run it because you don't have matplotlib installed.\nYou can download matplotlib for free at matplotlib.sourceforge.net.", QMessageBox.Ok)
218
219        QDialog.accept(self)
220
221    def canSaveToMatplotlib(self, graph):
222        if _have_qwt and isinstance(graph, QwtPlot):
223            # TODO: check all curve items.
224            return True
225
226        elif isinstance(graph, QGraphicsScene):
227            items = graph.items()
228            supported = set([QGraphicsRectItem, QGraphicsLineItem,
229                             QGraphicsTextItem, OWQCanvasFuncts.OWCanvasText])
230            return all(type(item) in supported for item in items)
231        else:
232            return False
233
234    # ############################################################
235    # EXTRA FUNCTIONS ############################################
236    def getFileName(self, defaultName, mask, extension):
237        fileName = unicode(QFileDialog.getSaveFileName(self, "Save to..", os.path.join(self.lastSaveDirName, defaultName), mask))
238        if not fileName: return None
239        if not os.path.splitext(fileName)[1][1:]: fileName = fileName + extension
240
241        self.lastSaveDirName = os.path.split(fileName)[0] + "/"
242        self.saveSettings()
243        return fileName
244
245    def getSize(self):
246        if isinstance(self.graph, QGraphicsScene):
247            size = self.getSceneBoundingRect().size()
248        elif self.selectedSize == 0: size = self.graph.size()
249        elif self.selectedSize == 4: size = QSize(self.customX, self.customY)
250        else: size = QSize(200 + self.selectedSize*200, 200 + self.selectedSize*200)
251        return size
252
253    def updateGUI(self):
254        global _have_qwt
255        if _have_qwt and isinstance(self.graph, QwtPlot):
256            self.customXEdit.setEnabled(self.selectedSize == 4)
257            self.customYEdit.setEnabled(self.selectedSize == 4)
258
259    def _getColorFromObject(self, obj):
260        if isinstance(obj, QBrush) and obj.style() == Qt.NoBrush: return "'none'", 1
261        if isinstance(obj, QPen)   and obj.style() == Qt.NoPen: return "'none'", 1
262        col = [obj.color().red(), obj.color().green(), obj.color().blue()];
263        col = tuple([v/float(255) for v in col])
264        return col, obj.color().alpha()/float(255)
Note: See TracBrowser for help on using the repository browser.