프로젝트

일반

사용자정보

개정판 b929957d

IDb929957d4af74250c9e374a830f008eeac1e4e6d
상위 65f1308c
하위 0174d0f9, 4841dcf8

백흠경이(가) 6년 이상 전에 추가함

fixed issue #628:
- 방향키로 이동 시 심볼의 정보를 속성 창에 표기

차이점 보기:

DTI_PID/DTI_PID/DTI_PID.pyproj
68 68
      <SubType>Code</SubType>
69 69
    </Compile>
70 70
    <Compile Include="QConnectAttrDialog.py" />
71
    <Compile Include="DirTreeWidget.py" />
71
    <Compile Include="SymbolTreeWidget.py" />
72 72
    <Compile Include="QEquipmentDataListDialog.py" />
73 73
    <Compile Include="QInstrumentDataListDialog.py" />
74 74
    <Compile Include="QLineDataListDialog.py" />
DTI_PID/DTI_PID/DirTreeWidget.py
1
try:
2
    from PyQt5.QtCore import *
3
    from PyQt5.QtGui import *
4
    from PyQt5.QtWidgets import *
5
except ImportError:
6
    try:
7
        from PyQt4.QtCore import *
8
        from PyQt4.QtGui import *
9
    except ImportError:
10
        raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.")
11
from AppDocData import AppDocData
12
import os
13
import sys
14
import SymbolBase
15
import symbol
16
import QSymbolEditorDialog
17
import QSymbolDisplayDialog
18

  
19
class QDirTreeWidget(QTreeWidget):
20
    #Add signal
21
    singleClicked = pyqtSignal(SymbolBase.SymbolBase)
22
    TREE_DATA_ROLE = Qt.ToolTipRole
23

  
24
    def __init__(self):
25
        QTreeWidget.__init__(self)
26
        self.setDragEnabled(True)   # enable drag
27
        self.initDirTreeWidget()
28
        self.isDoubleClicked = False
29
        self.itemDoubleClicked.connect(self.itemDoubleClickEvent)
30
        self.itemClicked.connect(self.itemClickEvent)
31
        self.setContextMenuPolicy(Qt.CustomContextMenu)
32
        self.customContextMenuRequested.connect(self.openContextMenu)
33
            
34
    '''
35
        @brief      Show Context Menu
36
        @author     Jeongwoo
37
        @date       18.04.??
38
        @history    18.04.23    Jeongwoo    Symbol object Null Check when show context menu
39
    '''
40
    def openContextMenu(self, position):
41
        indexes = self.selectedIndexes()
42
        itemPosition = self.mapTo(self, position)
43
        item = self.itemAt(itemPosition)
44
        sym = self.getSymbolByItemName(item, 0)
45
        text = item.text(0)
46
        if len(indexes) > 0:
47
            level = 0
48
            index = indexes[0]
49
            while index.parent().isValid():
50
                index = index.parent()
51
                level += 1
52
        if sym is not None:
53
            menu = QMenu()
54
            editSymbolAction = QAction(self.tr("Edit Symbol"))
55
            editSymbolAction.triggered.connect(lambda: self.editSymbolActionClickEvent(item, 0))
56
            menu.addAction(editSymbolAction)
57
            displaySymbolAction = QAction(self.tr("Display Symbol"))
58
            displaySymbolAction.triggered.connect(lambda: self.displaySymbolActionClickEvent(sym.getType(), text))
59
            menu.addAction(displaySymbolAction)
60
            deleteSymbolAction = QAction(self.tr("Delete Symbol"))
61
            deleteSymbolAction.triggered.connect(lambda: self.deleteSymbolActionClickEvent(sym.getType(), text))
62
            menu.addAction(deleteSymbolAction)
63
            menu.exec_(self.viewport().mapToGlobal(position))
64

  
65
    def editSymbolActionClickEvent(self, item, columNo):
66
        self.showSymbolEditorDialog(item, columNo)
67

  
68
    def displaySymbolActionClickEvent(self, itemType, itemName):
69
        project = AppDocData.instance().getCurrentProject()
70
        image = QImage(os.path.join(project.getImageFilePath(), itemType, itemName, "PNG")) #itemName includes ".png"
71
        dialog = QSymbolDisplayDialog.QSymbolDisplayDialog(image)
72
        dialog.showDialog()
73

  
74
    def deleteSymbolActionClickEvent(self, itemType, itemName):
75
        msg = QMessageBox()
76
        msg.setIcon(QMessageBox.Critical)
77
        msg.setText("선택한 심볼을 삭제하시겠습니까?\n삭제된 심볼은 복구할 수 없습니다.")
78
        msg.setWindowTitle("심볼 삭제")
79
        msg.setStandardButtons(QMessageBox.Ok|QMessageBox.Cancel)
80
        #msg.buttonClicked.connect(lambda: self.handleDeleteSymbolAction(itemName))
81
        result = msg.exec_()
82
        self.handleDeleteSymbolAction(result, itemType, itemName)
83

  
84
    '''
85
        @history    2018.05.03  Jeongwoo    Modify file path with ".png" and ".svg"
86
                                            Use project object when making svgPath
87
    '''
88
    def handleDeleteSymbolAction(self, result, itemType, itemName):
89
        if result == QMessageBox.Ok:
90
            project = AppDocData.instance().getCurrentProject()
91
            imagePath = os.path.join(project.getImageFilePath(), itemType, itemName  + '.png') # itemName DOESN'T includes ".png"
92
            if os.path.exists(imagePath):
93
                os.remove(imagePath)
94

  
95
            svgPath = os.path.join(project.getSvgFilePath(), itemType, itemName + '.svg')
96
            if os.path.exists(svgPath):
97
                os.remove(svgPath)
98

  
99
            AppDocData.instance().deleteSymbol(itemName)
100
            self.initDirTreeWidget()
101
        else:
102
            pass
103

  
104
    '''
105
        @history    2018.05.02  Jeongwoo    Change return value of QSymbolEditorDialog (Single variable → Tuple)
106
    '''
107
    def initDirTreeWidget(self):
108
        project = AppDocData.instance().getCurrentProject()
109
        if project is not None:
110
            self.clear()
111
            projectPath = project.getPath().replace("\\", "/")
112
            self.makeChildDir()
113
            self.loadSymbolInfo()
114
            self.expandAll()
115

  
116
    '''
117
        @brief      Load Symbol Info and add TreeItem with DB
118
        @author     Jeongwoo
119
        @date       18.04.20
120
        @history    Jeongwoo 2018.05.03 Get Svg File Path by SymbolBase.getSvgFileFullPath()
121
                    humkyung 2018.07.30 sort child items
122
    '''
123
    def loadSymbolInfo(self):
124
        symbolTypeList = AppDocData.instance().getSymbolTypeList()
125
        for symbolType in symbolTypeList:
126
            parent = QTreeWidgetItem(self, [symbolType])
127
            symbolList = AppDocData.instance().getSymbolListByQuery('type', symbolType)
128
            for symbol in symbolList:
129
                symbolItem = QTreeWidgetItem(parent, [symbol.getName()])
130
                symbolItem.setData(0, self.TREE_DATA_ROLE, symbol) ## ADD DATA
131
                svgPath = symbol.getSvgFileFullPath()
132
                icon = QIcon(svgPath)
133
                symbolItem.setIcon(0, icon)
134
                symbolItem.svgFilePath = svgPath # save svg file path
135

  
136
            parent.sortChildren(0, Qt.AscendingOrder)
137

  
138
    '''
139
        @brief      Make Directory
140
        @author     Jeongwoo
141
        @date       18.04.??
142
        @history    18.04.12    Jeongwoo    Add output, temp Directory
143
    '''
144
    def makeChildDir(self):
145
        project = AppDocData.instance().getCurrentProject()
146
        dbDir = project.getDbFilePath()
147
        if not os.path.exists(dbDir):
148
            os.makedirs(dbDir)
149
        imgDir = project.getImageFilePath()
150
        if not os.path.exists(imgDir):
151
            os.makedirs(imgDir)
152
        svgDir = project.getSvgFilePath()
153
        if not os.path.exists(svgDir):
154
            os.makedirs(svgDir)
155
        outputDir = project.getOutputPath()
156
        if not os.path.exists(outputDir):
157
            os.makedirs(outputDir)
158
        tempDir = project.getTempPath()
159
        if not os.path.exists(tempDir):
160
            os.makedirs(tempDir)
161

  
162
    def showSymbolEditorDialog(self, item, columnNo):
163
        try:
164
            sym = self.getSymbolByItemName(item, columnNo)
165
            if sym is not None:
166
                path = sym.getPath()
167
                image = QImage(path, "PNG")
168
                symbolEditorDialog = QSymbolEditorDialog.QSymbolEditorDialog(self, image, AppDocData.instance().getCurrentProject(), sym)
169
                (isAccepted, isImmediateInsert, offsetX, offsetY, newSym) = symbolEditorDialog.showDialog()
170
                self.initDirTreeWidget()
171
            else:
172
                QMessageBox.about(self, "알림", "심볼 데이터를 불러오는 중 에러가 발생했습니다.")
173
        except Exception as ex:
174
            print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
175

  
176

  
177
    def itemDoubleClickEvent(self, item, columnNo):
178
        self.isDoubleClicked = True
179
        sym = self.getSymbolByItemName(item, columnNo)
180
        itemName = item.text(columnNo)
181
        if sym is not None:
182
            self.showSymbolEditorDialog(item, columnNo)
183
        self.isDoubleClicked = False
184

  
185
    '''
186
        @history    18.04.24    Jeongwoo    Add case of symbol is None type
187
                                            Make Dummy Symbol (symId = -1, name = None, type = None)
188
    '''
189
    def itemClickEvent(self, item, columnNo):
190
        if self.isDoubleClicked == False:
191
            sym = self.getSymbolByItemName(item, columnNo)
192
            if sym is not None:
193
                self.singleClicked.emit(sym)
194
            else:
195
                self.singleClicked.emit(SymbolBase.SymbolBase(-1, None, None)) # Empty SymbolBase
196

  
197
    '''
198
        @brief      Get Symbol data by symbol name
199
        @author     Jeongwoo
200
        @date       18.04.20
201
    '''
202
    def getSymbolByItemName(self, item, columnNo):
203
        tmpItem = item
204
        itemName = item.text(columnNo)
205

  
206
        name = itemName
207
        sym = AppDocData.instance().getSymbolByQuery("name", name)
208
        return sym
209

  
210
    '''
211
        @brief  start drag
212
        @author humkyung
213
        @date   2018.04.17
214
        @history    18.04.20    Jeongwoo    Change Path in QPixmap
215
                    18.06.21    Jeongwoo    Casting string to float and int
216
    '''
217
    def startDrag(self, dropAction):
218
        try:
219
            items = self.selectedItems()
220
            if items:
221
                symData = items[0].data(0, self.TREE_DATA_ROLE)
222
                pixmap = QPixmap(items[0].svgFilePath)
223

  
224
                mime = QMimeData()
225
                mime.setText(symData.getName())
226

  
227
                drag = QDrag(self)
228
                drag.setMimeData(mime) 
229
                originalPoint = symData.getOriginalPoint()
230
                drag.setHotSpot(QPoint(int(float(originalPoint.split(",")[0])), int(float(originalPoint.split(",")[1]))))
231
                drag.setPixmap(pixmap)        
232
                drag.exec(Qt.CopyAction)
233
        except Exception as ex:
234
            print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
DTI_PID/DTI_PID/MainWindow.py
37 37
from QEngineeringSizeTextItem import QEngineeringSizeTextItem
38 38
from QEngineeringUnknownItem import QEngineeringUnknownItem
39 39
from AppDocData import AppDocData
40
import DirTreeWidget, PropertyTableWidget
40
import SymbolTreeWidget, PropertyTableWidget
41 41
import QSymbolEditorDialog
42 42
import ResultTreeWidget
43 43
import ResultPropertyTableWidget
......
81 81
        self.verticalLayout.addWidget(self.graphicsView)
82 82

  
83 83
        # Add Custom TreeWidget
84
        self.dirTreeWidget = DirTreeWidget.QDirTreeWidget()
84
        self.dirTreeWidget = SymbolTreeWidget.QSymbolTreeWidget()
85 85
        self.dirTreeWidget.header().hide()
86 86
        self.symbolTabVerticalLayout.addWidget(self.dirTreeWidget)
87 87

  
DTI_PID/DTI_PID/READ ME.txt
93 93
10) ConfigurationDialog.py
94 94
- ���α׷� ������ �ʿ��� �������� �����ϱ� ���� ���̾�α�
95 95

  
96
11) DirTreeWidget.py
96
11) SymbolTreeWidget.py
97 97
- MainWindow ���� ��ܿ� ��ġ�� TreeWidget
98 98
- ���� ������� ��ϵ� �ɺ� �̹��� ����Ʈ�� Tree �������� ǥ��
99 99
- �ɺ��� ������� ���콺 ���� Ŭ���� ��� [����, ũ�Ժ���, ����] �� ������ Context Menu ǥ��
......
109 109
- Ȯ�� ��ư Ŭ�� �� ����� �ؽ�Ʈ�� ����, ȸ�� ������ ������ TextInfo ��ȯ
110 110

  
111 111
13) QPropertyTableWidget.py
112
- DirTreeWidget ���� ���õ� �ɺ��� ������ ���
112
- SymbolTreeWidget ���� ���õ� �ɺ��� ������ ���
113 113
- TableWidget���� ���� �� Database�� ��� �ݿ��ϴ� ��ȹ�� �־�����, ��� �����Ƿ� ���� ���� ������
114 114

  
115 115
14) QRecognitionDialog.py
......
129 129
- ����� ������ ���� �� �ش� ��ġ�� QtImageViewer �� �̵� �� �� �εǸ�, ���̶����� ȿ�� ǥ��
130 130

  
131 131
17) QSymbolDisplayDialog.py
132
- DirTreeWidget ���� �ɺ� ������� ���콺 ��Ŭ���� �� �޴� ���� �� ��µǴ� ���̾�α�
132
- SymbolTreeWidget ���� �ɺ� ������� ���콺 ��Ŭ���� �� �޴� ���� �� ��µǴ� ���̾�α�
133 133
- �ܼ��� �ɺ� �̹����� ���
134 134

  
135 135
18) QSymbolEditorDialog.py
DTI_PID/DTI_PID/SymbolTreeWidget.py
1
try:
2
    from PyQt5.QtCore import *
3
    from PyQt5.QtGui import *
4
    from PyQt5.QtWidgets import *
5
except ImportError:
6
    try:
7
        from PyQt4.QtCore import *
8
        from PyQt4.QtGui import *
9
    except ImportError:
10
        raise ImportError("ImageViewerQt: Requires PyQt5 or PyQt4.")
11
from AppDocData import AppDocData
12
import os
13
import sys
14
import SymbolBase
15
import symbol
16
import QSymbolEditorDialog
17
import QSymbolDisplayDialog
18

  
19
class QSymbolTreeWidget(QTreeWidget):
20
    #Add signal
21
    singleClicked = pyqtSignal(SymbolBase.SymbolBase)
22
    TREE_DATA_ROLE = Qt.UserRole
23

  
24
    def __init__(self):
25
        QTreeWidget.__init__(self)
26
        self.setDragEnabled(True)   # enable drag
27
        self.initDirTreeWidget()
28
        self.isDoubleClicked = False
29
        self.itemDoubleClicked.connect(self.itemDoubleClickEvent)
30
        self.setContextMenuPolicy(Qt.CustomContextMenu)
31
        self.customContextMenuRequested.connect(self.openContextMenu)
32
        self.currentItemChanged.connect(self.onCurrentItemChanged)
33
            
34
    '''
35
        @brief      Show Context Menu
36
        @author     Jeongwoo
37
        @date       18.04.??
38
        @history    18.04.23    Jeongwoo    Symbol object Null Check when show context menu
39
    '''
40
    def openContextMenu(self, position):
41
        indexes = self.selectedIndexes()
42
        itemPosition = self.mapTo(self, position)
43
        item = self.itemAt(itemPosition)
44
        sym = self.getSymbolByItemName(item, 0)
45
        text = item.text(0)
46
        if len(indexes) > 0:
47
            level = 0
48
            index = indexes[0]
49
            while index.parent().isValid():
50
                index = index.parent()
51
                level += 1
52
        if sym is not None:
53
            menu = QMenu()
54
            editSymbolAction = QAction(self.tr("Edit Symbol"))
55
            editSymbolAction.triggered.connect(lambda: self.editSymbolActionClickEvent(item, 0))
56
            menu.addAction(editSymbolAction)
57
            displaySymbolAction = QAction(self.tr("Display Symbol"))
58
            displaySymbolAction.triggered.connect(lambda: self.displaySymbolActionClickEvent(sym.getType(), text))
59
            menu.addAction(displaySymbolAction)
60
            deleteSymbolAction = QAction(self.tr("Delete Symbol"))
61
            deleteSymbolAction.triggered.connect(lambda: self.deleteSymbolActionClickEvent(sym.getType(), text))
62
            menu.addAction(deleteSymbolAction)
63
            menu.exec_(self.viewport().mapToGlobal(position))
64

  
65
    def editSymbolActionClickEvent(self, item, columNo):
66
        self.showSymbolEditorDialog(item, columNo)
67

  
68
    def displaySymbolActionClickEvent(self, itemType, itemName):
69
        project = AppDocData.instance().getCurrentProject()
70
        image = QImage(os.path.join(project.getImageFilePath(), itemType, itemName, "PNG")) #itemName includes ".png"
71
        dialog = QSymbolDisplayDialog.QSymbolDisplayDialog(image)
72
        dialog.showDialog()
73

  
74
    def deleteSymbolActionClickEvent(self, itemType, itemName):
75
        msg = QMessageBox()
76
        msg.setIcon(QMessageBox.Critical)
77
        msg.setText("선택한 심볼을 삭제하시겠습니까?\n삭제된 심볼은 복구할 수 없습니다.")
78
        msg.setWindowTitle("심볼 삭제")
79
        msg.setStandardButtons(QMessageBox.Ok|QMessageBox.Cancel)
80
        result = msg.exec_()
81
        self.handleDeleteSymbolAction(result, itemType, itemName)
82

  
83
    '''
84
        @history    2018.05.03  Jeongwoo    Modify file path with ".png" and ".svg"
85
                                            Use project object when making svgPath
86
    '''
87
    def handleDeleteSymbolAction(self, result, itemType, itemName):
88
        if result == QMessageBox.Ok:
89
            project = AppDocData.instance().getCurrentProject()
90
            imagePath = os.path.join(project.getImageFilePath(), itemType, itemName  + '.png') # itemName DOESN'T includes ".png"
91
            if os.path.exists(imagePath):
92
                os.remove(imagePath)
93

  
94
            svgPath = os.path.join(project.getSvgFilePath(), itemType, itemName + '.svg')
95
            if os.path.exists(svgPath):
96
                os.remove(svgPath)
97

  
98
            AppDocData.instance().deleteSymbol(itemName)
99
            self.initDirTreeWidget()
100
        else:
101
            pass
102

  
103
    '''
104
        @history    2018.05.02  Jeongwoo    Change return value of QSymbolEditorDialog (Single variable → Tuple)
105
    '''
106
    def initDirTreeWidget(self):
107
        project = AppDocData.instance().getCurrentProject()
108
        if project is not None:
109
            self.clear()
110
            projectPath = project.getPath().replace("\\", "/")
111
            self.makeChildDir()
112
            self.loadSymbolInfo()
113
            self.expandAll()
114

  
115
    '''
116
        @brief      Load Symbol Info and add TreeItem with DB
117
        @author     Jeongwoo
118
        @date       18.04.20
119
        @history    Jeongwoo 2018.05.03 Get Svg File Path by SymbolBase.getSvgFileFullPath()
120
                    humkyung 2018.07.30 sort child items
121
    '''
122
    def loadSymbolInfo(self):
123
        symbolTypeList = AppDocData.instance().getSymbolTypeList()
124
        for symbolType in symbolTypeList:
125
            parent = QTreeWidgetItem(self, [symbolType])
126
            symbolList = AppDocData.instance().getSymbolListByQuery('type', symbolType)
127
            for symbol in symbolList:
128
                symbolItem = QTreeWidgetItem(parent, [symbol.getName()])
129
                symbolItem.setData(0, self.TREE_DATA_ROLE, symbol) ## ADD DATA
130
                svgPath = symbol.getSvgFileFullPath()
131
                icon = QIcon(svgPath)
132
                symbolItem.setIcon(0, icon)
133
                symbolItem.svgFilePath = svgPath # save svg file path
134

  
135
            parent.sortChildren(0, Qt.AscendingOrder)
136

  
137
    '''
138
        @brief      Make Directory
139
        @author     Jeongwoo
140
        @date       18.04.??
141
        @history    18.04.12    Jeongwoo    Add output, temp Directory
142
    '''
143
    def makeChildDir(self):
144
        project = AppDocData.instance().getCurrentProject()
145
        dbDir = project.getDbFilePath()
146
        if not os.path.exists(dbDir):
147
            os.makedirs(dbDir)
148
        imgDir = project.getImageFilePath()
149
        if not os.path.exists(imgDir):
150
            os.makedirs(imgDir)
151
        svgDir = project.getSvgFilePath()
152
        if not os.path.exists(svgDir):
153
            os.makedirs(svgDir)
154
        outputDir = project.getOutputPath()
155
        if not os.path.exists(outputDir):
156
            os.makedirs(outputDir)
157
        tempDir = project.getTempPath()
158
        if not os.path.exists(tempDir):
159
            os.makedirs(tempDir)
160

  
161
    def showSymbolEditorDialog(self, item, columnNo):
162
        try:
163
            sym = self.getSymbolByItemName(item, columnNo)
164
            if sym is not None:
165
                path = sym.getPath()
166
                image = QImage(path, "PNG")
167
                symbolEditorDialog = QSymbolEditorDialog.QSymbolEditorDialog(self, image, AppDocData.instance().getCurrentProject(), sym)
168
                (isAccepted, isImmediateInsert, offsetX, offsetY, newSym) = symbolEditorDialog.showDialog()
169
                self.initDirTreeWidget()
170
            else:
171
                QMessageBox.about(self, "알림", "심볼 데이터를 불러오는 중 에러가 발생했습니다.")
172
        except Exception as ex:
173
            print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
174

  
175

  
176
    def itemDoubleClickEvent(self, item, columnNo):
177
        self.isDoubleClicked = True
178
        sym = self.getSymbolByItemName(item, columnNo)
179
        itemName = item.text(columnNo)
180
        if sym is not None:
181
            self.showSymbolEditorDialog(item, columnNo)
182
        self.isDoubleClicked = False
183

  
184
    '''
185
        @brief      Get Symbol data by symbol name
186
        @author     Jeongwoo
187
        @date       18.04.20
188
    '''
189
    def getSymbolByItemName(self, item, columnNo):
190
        itemName = item.text(columnNo)
191

  
192
        name = itemName
193
        sym = AppDocData.instance().getSymbolByQuery("name", name)
194
        return sym
195

  
196
    '''
197
        @breif      show symbol's property when selection changed
198
        @author     humkyung
199
        @date       2018.07.30
200
    '''
201
    def onCurrentItemChanged(self, current, previous):
202
        item = self.currentItem()
203
        if item is not None:
204
            data = item.data(0, self.TREE_DATA_ROLE)
205
            if data is not None:
206
                self.singleClicked.emit(data)
207

  
208
    '''
209
        @brief  start drag
210
        @author humkyung
211
        @date   2018.04.17
212
        @history    18.04.20    Jeongwoo    Change Path in QPixmap
213
                    18.06.21    Jeongwoo    Casting string to float and int
214
    '''
215
    def startDrag(self, dropAction):
216
        try:
217
            items = self.selectedItems()
218
            if items:
219
                symData = items[0].data(0, self.TREE_DATA_ROLE)
220
                pixmap = QPixmap(items[0].svgFilePath)
221

  
222
                mime = QMimeData()
223
                mime.setText(symData.getName())
224

  
225
                drag = QDrag(self)
226
                drag.setMimeData(mime) 
227
                originalPoint = symData.getOriginalPoint()
228
                drag.setHotSpot(QPoint(int(float(originalPoint.split(",")[0])), int(float(originalPoint.split(",")[1]))))
229
                drag.setPixmap(pixmap)        
230
                drag.exec(Qt.CopyAction)
231
        except Exception as ex:
232
            print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))

내보내기 Unified diff

클립보드 이미지 추가 (최대 크기: 500 MB)