gui.py 13.7 KB
Newer Older
Daniel Brown's avatar
Daniel Brown committed
1
2
3
4
5
6
7
# -*- coding: utf-8 -*-
"""
Created on Tue Jan 29 11:35:48 2013

@author: Daniel
"""

8
from pykat.components import Component, space
9
10
from pykat.detectors import Detector

Daniel Brown's avatar
Daniel Brown committed
11
12
from PyQt4 import QtGui, QtCore
from PyQt4.Qt import *
13
from PyQt4.QtGui import QCursor, QGraphicsItem
Daniel Brown's avatar
Daniel Brown committed
14
15
from pykat.gui.graphics import *
import qt_gui
16
17
import functools

Daniel Brown's avatar
Daniel Brown committed
18
class pyKatGUI(QtGui.QMainWindow, qt_gui.Ui_MainWindow):
19
    def __init__(self, kat, parent=None):
Daniel Brown's avatar
Daniel Brown committed
20
        super(pyKatGUI, self).__init__(parent)
21
        
Daniel Brown's avatar
Daniel Brown committed
22
        self.setupUi(self)
23
        self.graphicsView = pyKatGraphicsView(self.centralwidget, kat)
24
        self.graphicsView.setObjectName("graphicsView")
25
        self.graphicsView.setViewportUpdateMode(QGraphicsView.FullViewportUpdate)
26
27
28
        self.graphicsView.viewport().setMouseTracking(True)
        self.setMouseTracking(True)
        
29
        self.gridLayout.addWidget(self.graphicsView, 0, 0, 1, 1)
Daniel Brown's avatar
Daniel Brown committed
30
31
        
        # create a new scene
32
33
        if kat.scene == None: 
            kat.scene = pyKatGraphicsScene()  
34
        
35
        self.__scene = kat.scene
Daniel Brown's avatar
Daniel Brown committed
36
37
38
        
        # add scene to the graphics view
        self.graphicsView.setScene(self.__scene)
39
        self.graphicsView.setRenderHint(QtGui.QPainter.Antialiasing)
Daniel Brown's avatar
Daniel Brown committed
40
                
41
42
43
        self.actionExport_to_SVG.triggered.connect(lambda: self.exportToSVG())
        self.actionClose.triggered.connect(lambda: self.close)

44
        self._kat = kat        
Daniel Brown's avatar
Daniel Brown committed
45
        
46
47
48
    @property
    def kat(self): return self._kat
    
Daniel Brown's avatar
Daniel Brown committed
49
50
51
52
53
54
55
56
57
    def main(self):
        self.show()
        
        self.addComponentsToScene()
        
    def scene(self):
        return self.__scene

    def addComponentsToScene(self):
58
59
        for c in self.kat.getComponents():
            self.addComponentToScene(c)
Daniel Brown's avatar
Daniel Brown committed
60
                
61
62
63
64
65
66
67
68
69
70
71
72
    def addComponentToScene(self,c,x=0,y=0):
        itm = c.getQGraphicsItem()
            
        if itm != None:
            itm.setPos(x,y)
            # uncomment this line to stop background bitmap caching of
            # svg rendering. Important to make sure when rendering to 
            # svg file that it is in a vector format. Gradients however
            # don't work...
            itm.refresh()
            itm.setCacheMode(QGraphicsItem.NoCache)
            self.__scene.addItem(itm)
Daniel Brown's avatar
Daniel Brown committed
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
    
    def _onComponentRemoved(self, comp, nodes):
        """
        When a component has been removed from the kat object this function should update
        all gui objects. 
            comp - object that is removed
            nodes - nodes that this comp was attached too, as that information may no longer be accessible
        """
        itm = comp.getQGraphicsItem()
            
        if itm != None:

            itm.refresh()
            self.__scene.removeItem(itm)
            
            for n in nodes:
                for cc in self._kat.nodes.getNodeComponents(n):
                    if cc != None:
                        ccitm = cc.getQGraphicsItem()
                        if ccitm != None:
                            ccitm.refresh()
            
95
            
96
97
98
99
    def exportToSVG(self):
        self.statusbar.showMessage("Saving to 'output.svg'...")
        
        svg = QSvgGenerator()
100
101
102
103
104
105
        filename = QtGui.QFileDialog.getSaveFileNameAndFilter(self,'Save SVG File',filter=".svg")
        
        if filename == None:
            return None
        
        svg.setFileName(filename)
106
107
108
109
110
111
112
113
114
        svg.setSize(QSize(self.__scene.width(), self.__scene.height()))
        svg.setViewBox(QRect(0,0,self.__scene.width(), self.__scene.height()))
        svg.setTitle("pyKat output of example.kat")
        
        pntr = QPainter(svg)
        self.__scene.render(pntr)
        pntr.end()
        
        self.statusbar.showMessage("Complete: Saved to 'output.svg'")
115
116
117
118
    
    def addMirror(self, x,y):
        name = self.kat.getNewComponentName('m')
        n = self.kat.getNewNodeNames('n',2)
119
        m = pykat.components.mirror(name,n[0],n[1],R=0.5,T=0.5)
Daniel Brown's avatar
Daniel Brown committed
120
121
        
        self.kat.add(m)
122
        self.addComponentToScene(m,x,y)
Daniel Brown's avatar
Daniel Brown committed
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
    
    def addSpace(self, x,y):
        name = self.kat.getNewComponentName('s')
        n = self.kat.getNewNodeNames('n',2)
        s = pykat.components.space(name, n[0], n[1])

        self.kat.add(s)
        self.addComponentToScene(s,x,y) 
     
    def addLaser(self, x,y):
        name = self.kat.getNewComponentName('l')
        n = self.kat.getNewNodeNames('n',1)
        l = pykat.components.laser(name, n[0])

        self.kat.add(l)
        self.addComponentToScene(l,x,y)   
139
140
141
142
143
144
145
146
    
    def addPhotodiode(self, x, y):
        name = self.kat.getNewDetectorName('pd')
        n = self.kat.getNewNodeNames('n',1)
        l = pykat.detectors.photodiode(name, n[0], [])

        self.kat.add(l)
        self.addComponentToScene(l,x,y)   
147
148
149
150
    
    def deleteComponent(self, comp):
        comp.component.remove()
    
151
152
153
154
155
156
157
158
159
    def disconnect(self, node):
        comps = self.kat.nodes.getNodeComponents(node)
        
        spaces = [c for c in comps if isinstance(c, space)]
        
        if len(spaces) > 0:
            dis_comp = spaces[0]
        else:
            dis_comp = comps[0]
Daniel Brown's avatar
Daniel Brown committed
160
        
161
162
163
164
165
166
167
168
169
170
        new_node_name = self.kat.getNewNodeNames("n", 1)
        new_node = self.kat.nodes.createNode(new_node_name[0])
        
        self.kat.nodes.replaceNode(dis_comp, node, new_node)
        
        # refresh all the graphics that might be affected
        for c in node.components + new_node.components:
            if c != None:
                c.getQGraphicsItem().refresh()
    
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
class pyKatGraphicsScene(QGraphicsScene):
    def drawBackground(self, painter, rect):
        size = 10
        painter.setPen(QPen(QColor(200,200,255,255),0.5))
        
        start = round(rect.top(), size)
        
        if start > rect.top():
            start =- size
        
        y = start - size
        
        while y < rect.bottom():
            y += size
            painter.drawLine(rect.left(),y, rect.right(), y)
        
        start = round(rect.left(), size)
        
        if start > rect.left():
            start =- size
        
        y = start - size
        
        while y < rect.right():
            y += size
            painter.drawLine(y, rect.top(), y, rect.bottom())
                    
Daniel Brown's avatar
Daniel Brown committed
198
class pyKatGraphicsView(QGraphicsView):
199
200
201
    def __init__(self, val, kat):
        QGraphicsView.__init__(self, val)
        self._kat = kat
Daniel Brown's avatar
Daniel Brown committed
202
203
        self.__selected_item = None
        self.__prev_pt = None
204
205
206
207
        self.setMouseTracking(True)
        self.__itemHover = None
        self.__marked = None
        
Daniel Brown's avatar
Daniel Brown committed
208
209
    def contextMenuEvent(self, ev):  
        pt = self.mapToScene(ev.pos())
210
              
211
212
        gui = self.parentWidget().parent() # get the main gui window
        
Daniel Brown's avatar
Daniel Brown committed
213
214
        menu = QMenu(self)
        addmenu = menu.addMenu("Add...")
215
        
Daniel Brown's avatar
Daniel Brown committed
216
217
218
        action = addmenu.addAction("Space")
        action.triggered.connect(functools.partial(gui.addSpace, pt.x(), pt.y()))
        
219
        action = addmenu.addAction("Mirror")
Daniel Brown's avatar
Daniel Brown committed
220
221
222
223
224
        action.triggered.connect(functools.partial(gui.addMirror, pt.x(), pt.y()))
        
        action = addmenu.addAction("Laser")
        action.triggered.connect(functools.partial(gui.addLaser, pt.x(), pt.y()))
        
225
226
        action = addmenu.addAction("Photodiode")
        action.triggered.connect(functools.partial(gui.addPhotodiode, pt.x(), pt.y()))
Daniel Brown's avatar
Daniel Brown committed
227
        
228
        item = self.scene().itemAt(pt.x(),pt.y())
Daniel Brown's avatar
Daniel Brown committed
229
        
230
231
232
233
        print pt.x(),pt.y(),item
        
        if item is not None :
            if isinstance(item, ComponentQGraphicsItem):           
Daniel Brown's avatar
Daniel Brown committed
234
235
                menu.addSeparator()
                menu.addAction("Edit")
236
237
                action = menu.addAction("Delete")
                action.triggered.connect(functools.partial(gui.deleteComponent, item))
Daniel Brown's avatar
Daniel Brown committed
238
239
            if isinstance(item,NodeQGraphicItem):
                menu.addSeparator()
240
                comps = self._kat.nodes.getNodeComponents(item.node)
241
                
242
243
244
                if(comps.count(None) == 0):
                    action = menu.addAction("Disconnect")
                    action.triggered.connect(functools.partial(gui.disconnect, item.node))
Daniel Brown's avatar
Daniel Brown committed
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264

        menu.popup(ev.globalPos());        
        
    def mousePressEvent(self, ev):
        
        if ev.button()==Qt.LeftButton:
            
            pt = self.mapToScene(ev.pos())
            
            item = self.scene().itemAt(pt)
            
            if isinstance(item, ComponentQGraphicsItem):
                if item == None:
                    self.__selected_item = None
                    self.__prev_pt = None
                else:
                    item.setFocus(Qt.MouseFocusReason)
        
                    self.__selected_item = item
                    self.__prev_pt = pt
265
            elif isinstance(item, NodeQGraphicItem):
266
267
268
269
270
                
                 
                if isinstance(item.parentItem(),SpaceQGraphicsItem):        
                    self.__selected_item = item
                    self.__prev_pt = pt
271
                    
272
273
274
275
276
277
278
                elif isinstance(item.parentItem(),ComponentQGraphicsItem):
                    self.__selected_item = item.parentItem()
                    self.__prev_pt = pt
            
            if self.__selected_item is not None:
                self.setCursor(QCursor(Qt.ClosedHandCursor))
                        
Daniel Brown's avatar
Daniel Brown committed
279
    def mouseReleaseEvent(self, ev):
280
281
        # if we have dragged a node and marked another to connect it too
        if self.__selected_item is not None and isinstance(self.__selected_item, NodeQGraphicItem) and self.__marked is not None:
Daniel Brown's avatar
Daniel Brown committed
282
            
283
284
285
286
287
288
289
290
291
292
293
294
295
            # node attached to space which needs to be removed
            node_s = self.__selected_item.node
            
            # get the selected node which must be attached to a space, which is the nodes parent
            # and then get the space component from it
            qspace = self.__selected_item.parentItem()
            space = qspace.space
            
            # marked node, then get the parent object and component
            node_c = self.__marked.node
            qcomp = self.__marked.parentItem()
            
            # connect space of node dragged to the component node
Daniel Brown's avatar
Daniel Brown committed
296
297
            # the space node that has been dragged gets deleted and we
            # replace it with the components
298
            self._kat.nodes.replaceNode(space, node_s, node_c)
299
300
301
302
            
            # then refresh the graphical items
            qspace.refresh()
            qcomp.refresh()
303
304

            self.setCursor(QCursor(Qt.ArrowCursor))
Daniel Brown's avatar
Daniel Brown committed
305
                        
306
307
308
309
310
        if self.__marked is not None:
            self.__marked.marked = False
            self.__marked.refresh()
            self.__marked = None
            
Daniel Brown's avatar
Daniel Brown committed
311
        self.__selected_item = None
312
        
Daniel Brown's avatar
Daniel Brown committed
313
314
315
316
    def mouseMoveEvent(self, ev):
        if self.__selected_item != None:
            
            item = self.__selected_item
317
            #pt_ = self.__prev_pt
Daniel Brown's avatar
Daniel Brown committed
318
319
            pt = self.mapToScene(ev.pos())
            
320
321
322
323
            # smooth moving of item depending on where you click
            #item.moveBy(pt.x()-pt_.x(), pt.y()-pt_.y())
            # then snap to some integer value
            snap = 10.0
324
            
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
            # if we are moving a node, it must be attached to a space
            # component otherwise we shouldn't be here
            if isinstance(item, NodeQGraphicItem) and isinstance(item.parentItem(), SpaceQGraphicsItem):
                space = item.parentItem()
                
                item.setPos(pt.x()-space.x(),pt.y()-space.y())
                space.refresh()
                
                # now check to see if any other connectable nodes are within reach
                # and if so hightlight them
                select_size = 20
                rect = QRectF(pt.x()-select_size/2,pt.y()-select_size/2,select_size,select_size)
                itms = item.scene().items(rect)
                
                # remove the node we are dragging
                if item in itms:
                    itms.remove(item)
                
                if self.__marked is not None:
                    self.__marked.marked = False
                    self.__marked.refresh()
                    self.__marked = None
                    
                if len(itms) > 0:
                    for i in itms:
                        if isinstance(i,NodeQGraphicItem) and i != item:
                            i.marked = True
                            i.refresh()
                            self.__marked = i
                            break
            else:
                item.setPos(int(round(pt.x()/snap)*snap),int(round(pt.y()/snap)*snap))
                
358
359
            self.__prev_pt = pt
            
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
            return
        
        else:
            item = self.itemAt(ev.pos())
            
            if isinstance(item, (NodeQGraphicItem, ComponentQGraphicsItem)) or item is None:
                #if item is not None or self.__itemHover is not None:
                    
                if isinstance(item, ComponentQGraphicsItem):
                    self.__itemHover = item
                elif isinstance(item,NodeQGraphicItem) and (not item.node.isConnected()):
                    self.__itemHover = item
                else:
                    self.__itemHover = None
            
            if self.__itemHover is not None:                
                self.setCursor(QCursor(Qt.OpenHandCursor))
            else:
                self.setCursor(QCursor(Qt.ArrowCursor))
                        
380
381