gui.py 11.8 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
9
10
from pykat.components import Component
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
19
20
class pyKatGUI(QtGui.QMainWindow, qt_gui.Ui_MainWindow):
    def __init__(self, kat,parent=None):
        super(pyKatGUI, self).__init__(parent)
21
        
Daniel Brown's avatar
Daniel Brown committed
22
        self.setupUi(self)
23
24
        self.graphicsView = pyKatGraphicsView(self.centralwidget)
        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
49
50
51
52
53
54
55
        
    def main(self):
        self.show()
        
        self.addComponentsToScene()
        
    def scene(self):
        return self.__scene

    def addComponentsToScene(self):
        
56
57
        for c in self.kat.getComponents():
            self.addComponentToScene(c)
Daniel Brown's avatar
Daniel Brown committed
58
                
59
60
61
62
63
64
65
66
67
68
69
70
71
    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)
            
72
73
74
75
    def exportToSVG(self):
        self.statusbar.showMessage("Saving to 'output.svg'...")
        
        svg = QSvgGenerator()
76
77
78
79
80
81
        filename = QtGui.QFileDialog.getSaveFileNameAndFilter(self,'Save SVG File',filter=".svg")
        
        if filename == None:
            return None
        
        svg.setFileName(filename)
82
83
84
85
86
87
88
89
90
        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'")
91
92
93
94
    
    def addMirror(self, x,y):
        name = self.kat.getNewComponentName('m')
        n = self.kat.getNewNodeNames('n',2)
Daniel Brown's avatar
Daniel Brown committed
95
96
97
        m = pykat.components.mirror(name,n[0],n[1])
        
        self.kat.add(m)
98
        self.addComponentToScene(m,x,y)
Daniel Brown's avatar
Daniel Brown committed
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
    
    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)   
115
116
117
118
119
120
121
122
    
    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)   
Daniel Brown's avatar
Daniel Brown committed
123
        
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
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
151
152
153
154
155
156
class pyKatGraphicsView(QGraphicsView):
    def __init__(self,val):
        QGraphicsView.__init__(self,val)
        
        self.__selected_item = None
        self.__prev_pt = None
157
158
159
160
        self.setMouseTracking(True)
        self.__itemHover = None
        self.__marked = None
        
Daniel Brown's avatar
Daniel Brown committed
161
162
    def contextMenuEvent(self, ev):  
        pt = self.mapToScene(ev.pos())
163
              
164
165
        gui = self.parentWidget().parent() # get the main gui window
        
Daniel Brown's avatar
Daniel Brown committed
166
167
        menu = QMenu(self)
        addmenu = menu.addMenu("Add...")
168
        
Daniel Brown's avatar
Daniel Brown committed
169
170
171
        action = addmenu.addAction("Space")
        action.triggered.connect(functools.partial(gui.addSpace, pt.x(), pt.y()))
        
172
        action = addmenu.addAction("Mirror")
Daniel Brown's avatar
Daniel Brown committed
173
174
175
176
177
        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()))
        
178
179
        action = addmenu.addAction("Photodiode")
        action.triggered.connect(functools.partial(gui.addPhotodiode, pt.x(), pt.y()))
Daniel Brown's avatar
Daniel Brown committed
180
        
181
        item = self.scene().itemAt(pt.x(),pt.y())
Daniel Brown's avatar
Daniel Brown committed
182
        
183
184
185
186
        print pt.x(),pt.y(),item
        
        if item is not None :
            if isinstance(item, ComponentQGraphicsItem):           
Daniel Brown's avatar
Daniel Brown committed
187
188
189
190
191
192
                menu.addSeparator()
                menu.addAction("Edit")
                menu.addAction("Delete")
            if isinstance(item,NodeQGraphicItem):
                menu.addSeparator()
                menu.addAction("Disconnect")
193
                
Daniel Brown's avatar
Daniel Brown committed
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213

        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
214
            elif isinstance(item, NodeQGraphicItem):
215
216
217
218
219
                
                 
                if isinstance(item.parentItem(),SpaceQGraphicsItem):        
                    self.__selected_item = item
                    self.__prev_pt = pt
220
                    
221
222
223
224
225
226
227
                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
228
    def mouseReleaseEvent(self, ev):
229
230
        # 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
231
            
232
233
234
235
236
237
238
239
240
241
242
243
244
            # 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
245
246
247
248
            # the space node that has been dragged gets deleted and we
            # replace it with the components
            space.changeNode(node_s, node_c)
            node_s.remove() # now remove from node network completly
249
250
251
252
            
            # then refresh the graphical items
            qspace.refresh()
            qcomp.refresh()
253
254

            self.setCursor(QCursor(Qt.ArrowCursor))
Daniel Brown's avatar
Daniel Brown committed
255
                        
256
257
258
259
260
        if self.__marked is not None:
            self.__marked.marked = False
            self.__marked.refresh()
            self.__marked = None
            
Daniel Brown's avatar
Daniel Brown committed
261
        self.__selected_item = None
262
        
Daniel Brown's avatar
Daniel Brown committed
263
264
265
266
    def mouseMoveEvent(self, ev):
        if self.__selected_item != None:
            
            item = self.__selected_item
267
            #pt_ = self.__prev_pt
Daniel Brown's avatar
Daniel Brown committed
268
269
            pt = self.mapToScene(ev.pos())
            
270
271
272
273
            # 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
274
            
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
            # 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))
                
308
309
            self.__prev_pt = pt
            
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
            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))
                        
330
331