프로젝트

일반

사용자정보

통계
| 개정판:

hytos / DTI_PID / DTI_PID / SymbolTreeWidget.py @ d8b09f9f

이력 | 보기 | 이력해설 | 다운로드 (22.5 KB)

1
# coding: utf-8
2
""" This is Symbol Tree Widget module """
3

    
4
try:
5
    from PyQt5.QtCore import *
6
    from PyQt5.QtGui import *
7
    from PyQt5.QtWidgets import *
8
except ImportError:
9
    try:
10
        from PyQt4.QtCore import *
11
        from PyQt4.QtGui import *
12
    except ImportError:
13
        raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.")
14
from AppDocData import *
15
import os
16
import sys
17
import SymbolBase
18
import symbol
19
import SymbolEditorDialog
20
import QSymbolDisplayDialog
21

    
22

    
23
class SearchProxyModel(QSortFilterProxyModel):
24
    def setFilterRegExp(self, pattern):
25
        if isinstance(pattern, str):
26
            pattern = QRegExp( pattern, Qt.CaseInsensitive, QRegExp.FixedString)
27
        super(SearchProxyModel, self).setFilterRegExp(pattern)
28

    
29
    def _accept_index(self, idx):
30
        if idx.isValid():
31
            text = idx.data(Qt.DisplayRole)
32
            if hasattr(self, 'text') and 'HIDDEN' in self.text.upper() and '(HIDDEN)' in text.upper():
33
                return True
34
            elif '(HIDDEN)' in text.upper():
35
                return False
36
                
37
            if self.filterRegExp().indexIn(text) >= 0:
38
                return True
39
            for row in range(idx.model().rowCount(idx)):
40
                if self._accept_index(idx.model().index(row, 0, idx)):
41
                    return True
42
        return False
43

    
44
    def filterAcceptsRow(self, source_row, source_parent):
45
        idx = self.sourceModel().index(source_row, 0, source_parent)
46
        return self._accept_index(idx)
47

    
48

    
49
class QSymbolTreeWidget(QTreeView):
50
    # Add signal
51
    singleClicked = pyqtSignal(SymbolBase.SymbolBase)
52
    symbols_loaded = pyqtSignal([SymbolBase.SymbolBase])
53
    TREE_DATA_ROLE = Qt.UserRole
54

    
55
    def __init__(self):
56
        QTreeView.__init__(self)
57
        self.setIconSize(QSize(32, 32))
58

    
59
        self.setDragEnabled(True) # enable drag
60
        self.initSymbolTreeView()
61
        self.isDoubleClicked = False
62
        self.doubleClicked.connect(self.itemDoubleClickEvent)
63
        self.setContextMenuPolicy(Qt.CustomContextMenu)
64
        self.customContextMenuRequested.connect(self.openContextMenu)
65

    
66
    @property
67
    def symbols(self):
68
        """return symbols"""
69
        res = []
70

    
71
        try:
72
            tree_item_stack = [self.model().sourceModel().invisibleRootItem()]
73
            while tree_item_stack:
74
                tree_item = tree_item_stack.pop(0)
75
                if tree_item and tree_item.rowCount():
76
                    for idx in range(tree_item.rowCount()):
77
                        child = tree_item.child(idx)
78
                        tree_item_stack.append(child)
79
                elif tree_item and tree_item.data(Qt.UserRole):
80
                    if type(tree_item.data(Qt.UserRole)) is SymbolBase.SymbolBase and tree_item.data(Qt.UserRole).iType != -1:
81
                        res.append(tree_item.data(Qt.UserRole))
82
        except Exception as ex:
83
            from App import App
84

    
85
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
86
                                                           sys.exc_info()[-1].tb_lineno)
87
            App.mainWnd().addMessage.emit(MessageType.Error, message)
88

    
89
        return res
90

    
91
    def openContextMenu(self, position):
92
        """show context menu"""
93
        item_position = self.mapTo(self, position)
94
        index = self.indexAt(item_position)
95
        item = self.model().sourceModel().itemFromIndex(self.model().mapToSource(index))
96
        data = item.data(self.TREE_DATA_ROLE)
97

    
98
        advanced = False
99
        app_doc_data = AppDocData.instance()
100
        configs = app_doc_data.getAppConfigs('app', 'mode')
101
        if configs and 1 == len(configs) and 'advanced' == configs[0].value:
102
            advanced = True
103

    
104
        indexes = self.selectedIndexes()
105
        if data is not None and type(data) is symbol.SymbolBase:
106
            sym = self.getSymbolByItemName(item, 0)
107
            text = item.text()
108
            if len(indexes) > 0:
109
                level = 0
110
                index = indexes[0]
111
                while index.parent().isValid():
112
                    index = index.parent()
113
                    level += 1
114
            if sym is not None:
115
                menu = QMenu()
116
                if advanced:
117
                    editSymbolAction = QAction(self.tr("Edit Symbol"))
118
                    editSymbolAction.triggered.connect(lambda: self.editSymbolActionClickEvent(item, 0))
119
                    menu.addAction(editSymbolAction)
120
                    editDisplaySymbolAction = QAction(self.tr("Edit Symbol for Display"))
121
                    editDisplaySymbolAction.triggered.connect(lambda: self.editDisplaySymbolActionClickEvent(item, 0))
122
                    menu.addAction(editDisplaySymbolAction)
123
                displaySymbolAction = QAction(self.tr("Display Symbol"))
124
                displaySymbolAction.triggered.connect(lambda: self.displaySymbolActionClickEvent(item, 0))
125
                menu.addAction(displaySymbolAction)
126
                if advanced:
127
                    deleteSymbolAction = QAction(self.tr("Delete Symbol"))
128
                    deleteSymbolAction.triggered.connect(lambda: self.deleteSymbolActionClickEvent(sym.getType(), text))
129
                    menu.addAction(deleteSymbolAction)
130
                menu.exec_(self.viewport().mapToGlobal(position))
131
        else:
132
            if advanced:
133
                menu = QMenu()
134
                editAttrAction = QAction(self.tr("Edit Attribute"))
135
                editAttrAction.triggered.connect(lambda: self.onEditAttrClicked(item, 0))
136
                menu.addAction(editAttrAction)
137

    
138
                configs = app_doc_data.getConfigs('Project', 'Operation Code')
139
                code = configs[0].value if 1 == len(configs) else ''
140
                if code == 'nK6uurpuiw==': # Samsung
141
                    spaceAttrAction = QAction(self.tr("================"))
142
                    menu.addAction(spaceAttrAction)
143
                    deleteAttrAction = QAction(self.tr("Delete Unused Symbols"))
144
                    deleteAttrAction.triggered.connect(lambda: self.onDeleteSymbolBatchByCategory(item))
145
                    menu.addAction(deleteAttrAction)
146

    
147
                resetDefaultScaleAction = QAction(self.tr("Reset All Default Symbol Scale"))
148
                resetDefaultScaleAction.triggered.connect(self.resetDefaultScale)
149
                menu.addAction(resetDefaultScaleAction)
150
                
151
                menu.exec_(self.viewport().mapToGlobal(position))
152

    
153
    def resetDefaultScale(self):
154
        text, result = QInputDialog.getText(self, self.tr('Reset all default symbol scale'), self.tr('To proceed, enter "Confirm" in the input box below.'))
155
                
156
        if result and text == 'Confirm':
157
            app_doc_data = AppDocData.instance()
158

    
159
            for sym in self.symbols:
160
                sym.setScale()
161
                app_doc_data.updateSymbol(sym)
162

    
163
            self.initSymbolTreeView()
164

    
165
            QMessageBox.information(self, self.tr('Information'), self.tr('Succeeded'))
166

    
167
    def editSymbolActionClickEvent(self, item, columNo):
168
        self.showSymbolEditorDialog(item, columNo)
169

    
170
    def editDisplaySymbolActionClickEvent(self, item, columNo):
171
        self.showSymbolEditorDialog(item, columNo, True)
172

    
173
    def onDeleteSymbolBatchByCategory(self, item):
174
        """Delete unused symbol by category"""
175
        msg = QMessageBox()
176
        msg.setIcon(QMessageBox.Critical)
177
        msg.setText(self.tr('Are you sure you want to delete symbols in this category?\nData can not be restored!'))
178
        msg.setWindowTitle(self.tr('Delete symbols'))
179
        msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
180
        result = msg.exec_()
181
        self.handleDeleteSymbolBatchAction(result, item)
182

    
183
    def handleDeleteSymbolBatchAction(self, result, item):
184
        if result == QMessageBox.Ok:
185
            text, result = QInputDialog.getText(self, self.tr('Delete symbols'), self.tr('To proceed, enter "Confirm" in the input box below.'))
186
                
187
            if result and text == 'Confirm':
188
                app_doc_data = AppDocData.instance()
189
                project = app_doc_data.getCurrentProject()
190

    
191
                data = item.data(self.TREE_DATA_ROLE)
192
                itemType = data[2]
193

    
194
                unusedSymbols = app_doc_data.getSymbolNameListByUnused()
195
                unusedUIDs = [symbol[0] for symbol in unusedSymbols]
196
                categorySymbols = app_doc_data.getSymbolListByType('type', itemType)
197
                itemNames = [symbol.sName for symbol in categorySymbols if symbol.uid in unusedUIDs]
198

    
199
                for itemName in itemNames:
200
                    res, _ = app_doc_data.deleteSymbol(itemName)
201
                    if res:
202
                        imagePath = os.path.join(project.getImageFilePath(), itemType,
203
                                                itemName + '.png')  # itemName DOESN'T includes ".png"
204
                        imageDisplayPath = os.path.join(project.getImageFilePath(), itemType,
205
                                                itemName + '_display' + '.png')  # itemName DOESN'T includes ".png"
206
                        if os.path.exists(imagePath):
207
                            os.remove(imagePath)
208
                        if os.path.exists(imageDisplayPath):
209
                            os.remove(imageDisplayPath)
210

    
211
                        svgPath = os.path.join(project.getSvgFilePath(), itemType, itemName + '.svg')
212
                        if os.path.exists(svgPath):
213
                            os.remove(svgPath)
214
                else:
215
                    pass
216
                
217
                self.initSymbolTreeView()
218

    
219

    
220
    def onEditAttrClicked(self, item, columnNo):
221
        """pop up attribute editor dialog"""
222
        from SymbolAttrEditorDialog import QSymbolAttrEditorDialog
223

    
224
        try:
225
            data = item.data(self.TREE_DATA_ROLE)
226

    
227
            dlg = QSymbolAttrEditorDialog(self, data)
228
            dlg.show()
229
            dlg.exec_()
230
        except Exception as ex:
231
            from App import App
232

    
233
            message = f"error occurred({repr(ex)}) in {sys.exc_info()[-1].tb_frame.f_code.co_filename}:" \
234
                      f"{sys.exc_info()[-1].tb_lineno}"
235
            App.mainWnd().addMessage.emit(MessageType.Error, message)
236

    
237
    def displaySymbolActionClickEvent(self, item, columnNo):
238
        # project = AppDocData.instance().getCurrentProject()
239
        # image = QImage(os.path.join(project.getImageFilePath(), itemType, itemName, "PNG")) #itemName includes ".png"
240
        try:
241
            sym = self.getSymbolByItemName(item, columnNo)
242
            if sym is not None:
243
                # origin symbol image
244
                path = sym.getPath()
245
                image = QImage(path, "PNG")
246
                # symbol image for display
247
                path = os.path.splitext(path)
248
                path = path[0] + '_display' + path[1]
249
                image2 = QImage(path, "PNG") if os.path.exists(path) else None
250
                dialog = QSymbolDisplayDialog.QSymbolDisplayDialog(image, image2)
251
                dialog.showDialog()
252
            else:
253
                QMessageBox.about(self, self.tr('Error'), self.tr('Error occurs during loading symbol data.'))
254
        except Exception as ex:
255
            from App import App
256
            from AppDocData import MessageType
257

    
258
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
259
                                                           sys.exc_info()[-1].tb_lineno)
260
            App.mainWnd().addMessage.emit(MessageType.Error, message)
261

    
262
    def deleteSymbolActionClickEvent(self, itemType, itemName):
263
        msg = QMessageBox()
264
        msg.setIcon(QMessageBox.Critical)
265
        msg.setText(self.tr('Are you sure you want to delete selected symbol?\nData can not be restored!'))
266
        msg.setWindowTitle(self.tr('Delete symbol'))
267
        msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
268
        result = msg.exec_()
269
        self.handleDeleteSymbolAction(result, itemType, itemName)
270

    
271
    '''
272
        @history    2018.05.03  Jeongwoo    Modify file path with ".png" and ".svg"
273
                                            Use project object when making svgPath
274
    '''
275
    def handleDeleteSymbolAction(self, result, itemType, itemName):
276
        if result == QMessageBox.Ok:
277
            res, _ = AppDocData.instance().deleteSymbol(itemName)
278
            if res:
279
                project = AppDocData.instance().getCurrentProject()
280
                imagePath = os.path.join(project.getImageFilePath(), itemType,
281
                                        itemName + '.png')  # itemName DOESN'T includes ".png"
282
                imageDisplayPath = os.path.join(project.getImageFilePath(), itemType,
283
                                        itemName + '_display' + '.png')  # itemName DOESN'T includes ".png"
284
                if os.path.exists(imagePath):
285
                    os.remove(imagePath)
286
                if os.path.exists(imageDisplayPath):
287
                    os.remove(imageDisplayPath)
288

    
289
                svgPath = os.path.join(project.getSvgFilePath(), itemType, itemName + '.svg')
290
                if os.path.exists(svgPath):
291
                    os.remove(svgPath)
292

    
293
            self.initSymbolTreeView()
294
        else:
295
            pass
296

    
297
    def initSymbolTreeView(self):
298
        """initialize symbol tree view"""
299
        project = AppDocData.instance().getCurrentProject()
300
        if project is not None:
301
            if self.model():
302
                self.model().clear()
303
            projectPath = project.getPath().replace("\\", "/")
304
            self.load_symbol_info()
305
            self.expandAll()
306

    
307
        self.selectionModel().selectionChanged.connect(self.on_selection_changed)
308

    
309
    def load_symbol_info(self):
310
        """load symbol information and display it on tree view"""
311
        try:
312
            app_doc_data = AppDocData.instance()
313

    
314
            model = QStandardItemModel()
315
            symbolTypeList = app_doc_data.getSymbolTypeList()
316
            for row, symbolType in enumerate(symbolTypeList):
317
                item = QStandardItem(symbolType[2])
318
                item.setData(symbolType, self.TREE_DATA_ROLE)
319
                item.setEditable(False)
320
                item.setSelectable(False)
321

    
322
                symbolList = app_doc_data.getSymbolListByType('UID', symbolType[0])
323
                for symbol in symbolList:
324
                    child = QStandardItem(symbol.getName())
325
                    child.setData(symbol, self.TREE_DATA_ROLE)
326
                    child.setEditable(False)
327

    
328
                    _, svg = app_doc_data.read_symbol_shape(symbol.sName)
329
                    if svg:
330
                        symbol.pixmap = QPixmap()
331
                        symbol.pixmap.loadFromData(svg if isinstance(svg, bytes) else svg.encode())
332
                        icon = QIcon(symbol.pixmap)
333
                        child.setIcon(icon)
334
                        child.svgFilePath = None  # save svg file path
335
                    else:
336
                        svgPath = symbol.getSvgFileFullPath()
337
                        symbol.pixmap = QPixmap(svgPath)
338
                        icon = QIcon(symbol.pixmap)
339
                        child.setIcon(icon)
340
                        child.svgFilePath = svgPath  # save svg file path
341

    
342
                    item.appendRow(child)
343

    
344
                item.sortChildren(0, Qt.AscendingOrder)
345
                model.appendRow(item)
346

    
347
            proxy_model = SearchProxyModel()
348
            proxy_model.setSourceModel(model)
349
            proxy_model.setDynamicSortFilter(True)
350
            proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive)
351
            self.setModel(proxy_model)
352

    
353
            """
354
            symbolTypeList = app_doc_data.getSymbolTypeList()
355
            for symbolType in symbolTypeList:
356
                if not symbolType[1]: continue  # skip if category is empty
357
                parent = QTreeWidgetItem(self, [symbolType[2]])
358
                parent.setData(0, self.TREE_DATA_ROLE, symbolType)
359
                symbolList = app_doc_data.getSymbolListByType('UID', symbolType[0])
360
                for symbol in symbolList:
361
                    symbolItem = QTreeWidgetItem(parent, [symbol.getName()])
362
                    symbolItem.setData(0, self.TREE_DATA_ROLE, symbol)
363

364
                    _, svg = app_doc_data.read_symbol_shape(symbol.sName)
365
                    if svg:
366
                        symbol.pixmap = QPixmap()
367
                        symbol.pixmap.loadFromData(svg if isinstance(svg, bytes) else svg.encode())
368
                        icon = QIcon(symbol.pixmap)
369
                        symbolItem.setIcon(0, icon)
370
                        symbolItem.svgFilePath = None   # save svg file path
371
                    else:
372
                        svgPath = symbol.getSvgFileFullPath()
373
                        symbol.pixmap = QPixmap(svgPath)
374
                        icon = QIcon(symbol.pixmap)
375
                        symbolItem.setIcon(0, icon)
376
                        symbolItem.svgFilePath = svgPath  # save svg file path
377

378
                parent.sortChildren(0, Qt.AscendingOrder)
379
            """
380
        except Exception as ex:
381
            from App import App
382

    
383
            message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
384
                                                           sys.exc_info()[-1].tb_lineno)
385
            App.mainWnd().addMessage.emit(MessageType.Error, message)
386

    
387
    def showSymbolEditorDialog(self, item, columnNo, display=False):
388
        """pop up symbol editor dialog"""
389

    
390
        try:
391
            sym = self.getSymbolByItemName(item, columnNo)
392
            if sym and display:
393
                # for symbol image for display
394
                path = os.path.splitext(sym.getPath())
395
                path = path[0] + '_display' + path[1]
396
                if os.path.exists(path):
397
                    image = QImage(path, "PNG")
398
                else:
399
                    path = sym.getPath()
400
                    image = QImage(path, "PNG")
401
                symbolEditorDialog = SymbolEditorDialog.QSymbolEditorDialog(self, image,
402
                                                                            AppDocData.instance().getCurrentProject(),
403
                                                                            sym, True)
404
                (isAccepted, isImmediateInsert, offsetX, offsetY, newSym) = symbolEditorDialog.showDialog()
405
                if isAccepted:
406
                    self.initSymbolTreeView()
407
            elif sym:
408
                # for symbol data and detection image
409
                path = sym.getPath()
410
                image = QImage(path, "PNG")
411
                symbolEditorDialog = SymbolEditorDialog.QSymbolEditorDialog(self, image,
412
                                                                            AppDocData.instance().getCurrentProject(),
413
                                                                            sym, False)
414
                (isAccepted, isImmediateInsert, offsetX, offsetY, newSym) = symbolEditorDialog.showDialog()
415
                if isAccepted:
416
                    self.initSymbolTreeView()
417
            else:
418
                QMessageBox.about(self, self.tr('Error'), self.tr('Error occurs during loading symbol data.'))
419
        except Exception as ex:
420
            from App import App
421
            from AppDocData import MessageType
422

    
423
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
424
                                                           sys.exc_info()[-1].tb_lineno)
425
            App.mainWnd().addMessage.emit(MessageType.Error, message)
426

    
427
    def select_symbol(self, symbol):
428
        ''' no more used '''
429
        """select a tree with given symbol"""
430

    
431
        founds = self.findItems(symbol.name, Qt.MatchExactly | Qt.MatchRecursive, 0)
432
        if founds:
433
            self.setCurrentItem(founds[0])
434

    
435
    def itemDoubleClickEvent(self, index):
436
        """pop up symbol editor"""
437
        item = self.model().sourceModel().itemFromIndex(self.model().mapToSource(index))
438
        if item:
439
            data = item.data(Qt.UserRole)
440
            if data and type(data) is SymbolBase.SymbolBase:
441
                sym = self.getSymbolByItemName(item, 0)
442
                itemName = item.text()
443
                if sym is not None:
444
                    self.showSymbolEditorDialog(item, 0)
445

    
446
    def getSymbolByItemName(self, item, columnNo):
447
        """Get Symbol data by symbol name"""
448
        name = item.text()
449
        sym = AppDocData.instance().getSymbolByQuery("name", name)
450

    
451
        return sym
452

    
453
    def on_selection_changed(self, new_selection, old_selection):
454
        """show symbol's property when selection changed"""
455

    
456
        if new_selection.indexes():
457
            proxy_model = self.model()
458
            item = proxy_model.sourceModel().itemFromIndex(proxy_model.mapToSource(new_selection.indexes()[0]))
459
            if item is not None:
460
                data = item.data(self.TREE_DATA_ROLE)
461
                if data is not None and type(data) is symbol.SymbolBase:
462
                    self.singleClicked.emit(data)
463

    
464
    def onCurrentItemChanged(self, current, previous):
465
        """show symbol's property when selection changed"""
466
        item = self.currentItem()
467
        if item is not None:
468
            data = item.data(0, self.TREE_DATA_ROLE)
469
            if data is not None and type(data) is symbol.SymbolBase:
470
                self.singleClicked.emit(data)
471

    
472
    def startDrag(self, dropAction):
473
        """start drag"""
474
        try:
475
            index = self.currentIndex()
476
            proxy_model = self.model()
477
            items = [proxy_model.sourceModel().itemFromIndex(proxy_model.mapToSource(index))]
478
            if items and hasattr(items[0], 'svgFilePath') and os.path.exists(items[0].svgFilePath):
479
                symData = items[0].data(self.TREE_DATA_ROLE)
480

    
481
                mime = QMimeData()
482
                mime.setText(symData.getName())
483
                mime.tag = symData
484

    
485
                drag = QDrag(self)
486
                drag.setMimeData(mime)
487
                originalPoint = symData.getOriginalPoint() if not symData.getConvertingPoint() else symData.getConvertingPoint()
488
                drag.setHotSpot(QPoint(int(float(originalPoint.split(",")[0])), int(float(originalPoint.split(",")[1]))))
489
                pix = QPixmap(symData.pixmap.size())
490
                pix.fill(Qt.transparent)
491
                painter = QPainter(pix)
492
                painter.setOpacity(0.3)
493
                painter.drawPixmap(QPoint(), symData.pixmap)
494
                painter.end()
495
                drag.setPixmap(pix)
496
                drag.exec(Qt.CopyAction)
497
        except Exception as ex:
498
            from App import App
499

    
500
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
501
                                                           sys.exc_info()[-1].tb_lineno)
502
            App.mainWnd().addMessage.emit(MessageType.Error, message)
클립보드 이미지 추가 (최대 크기: 500 MB)