• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SPF-OST / pytrnsys_gui / 11592777954

29 Oct 2024 03:09PM UTC coverage: 67.508% (-0.08%) from 67.591%
11592777954

push

github

web-flow
Merge pull request #564 from SPF-OST/560-black-change-line-length-to-pep8-standard-of-79-and-check-ci-reaction

changed line length in black to 79

1054 of 1475 new or added lines in 174 files covered. (71.46%)

150 existing lines in 74 files now uncovered.

10399 of 15404 relevant lines covered (67.51%)

0.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.61
/trnsysGUI/BlockItem.py
1
# pylint: disable=invalid-name
2

3
import typing as _tp
1✔
4

5
import PyQt5.QtCore as _qtc
1✔
6
import PyQt5.QtGui as _qtg
1✔
7
from PyQt5 import QtWidgets as _qtw
1✔
8

9
import trnsysGUI.PortItemBase as _pib
1✔
10
import trnsysGUI.blockItemModel as _bim
1✔
11
import trnsysGUI.idGenerator as _id
1✔
12
import trnsysGUI.images as _img
1✔
13
import trnsysGUI.internalPiping as _ip
1✔
14
import trnsysGUI.moveCommand as _mc
1✔
15

16

17
# pylint: disable = fixme
18
# TODO : TeePiece and AirSourceHp size ratio need to be fixed, maybe just use original
19
#  svg instead of modified ones, TVentil is flipped. heatExchangers are also wrongly oriented
20
class BlockItem(
1✔
21
    _qtw.QGraphicsItem
22
):  # pylint: disable = too-many-public-methods, too-many-instance-attributes
23
    def __init__(self, trnsysType: str, editor, displayName: str) -> None:
1✔
24
        super().__init__(None)
1✔
25
        self.logger = editor.logger
1✔
26

27
        self.w = 120
1✔
28
        self.h = 120
1✔
29
        self.editor = editor
1✔
30

31
        if not displayName:
1✔
32
            raise ValueError("Display name cannot be empty.")
×
33

34
        self.displayName = displayName
1✔
35

36
        self.inputs: list[_pib.PortItemBase] = []
1✔
37
        self.outputs: list[_pib.PortItemBase] = []
1✔
38

39
        self.path: str | None = None
1✔
40

41
        # Export related:
42
        self.name = trnsysType
1✔
43
        self.trnsysId = self.editor.idGen.getTrnsysID()
1✔
44

45
        # Transform related
46
        self.flippedV = False
1✔
47
        self.flippedH = False
1✔
48
        self.rotationN = 0
1✔
49

50
        # To set flags of this item
51
        self.setFlags(self.ItemIsSelectable | self.ItemIsMovable)
1✔
52
        self.setFlag(self.ItemSendsScenePositionChanges, True)
1✔
53
        self.setCursor(_qtg.QCursor(_qtc.Qt.PointingHandCursor))
1✔
54

55
        self.label = _qtw.QGraphicsTextItem(self.displayName, self)
1✔
56
        self.label.setVisible(False)
1✔
57

58
        # Experimental, used for detecting genereated blocks attached to storage ports
59
        self.inFirstRow = False
1✔
60

61
        # Undo framework related
62
        self.oldPos = None
1✔
63

64
        self.origOutputsPos: _tp.Sequence[_tp.Sequence[int]] | None = None
1✔
65
        self.origInputsPos: _tp.Sequence[_tp.Sequence[int]] | None = None
1✔
66

67
        # TODO: The fact that we're assigning to a method is bad, but can't deal with it now
68
        self.isSelected = False  # type: ignore[method-assign,assignment]
1✔
69

70
    def _getImageAccessor(self) -> _tp.Optional[_img.ImageAccessor]:
1✔
71
        if isinstance(self, BlockItem):
×
NEW
72
            raise AssertionError(
×
73
                "`BlockItem' cannot be instantiated directly."
74
            )
75

76
        currentClassName = BlockItem.__name__
×
NEW
77
        currentMethodName = (
×
78
            f"{currentClassName}.{BlockItem._getImageAccessor.__name__}"
79
        )
80

81
        message = (
×
82
            f"{currentMethodName} has been called. However, this method should not be called directly but must be\n"
83
            f"implemented in a child class. This means that a) someone instantiated `{currentClassName}` directly\n"
84
            f"or b) a child class of it doesn't implement `{currentMethodName}`. Either way that's an\n"
85
            f"unrecoverable error and therefore the program will be terminated now. Please do get in touch with\n"
86
            f"the developers if you've encountered this error. Thanks."
87
        )
88

89
        exception = AssertionError(message)
×
90

91
        # I've seen exception messages mysteriously swallowed that's why we're logging the message here, too.
92
        self.logger.error(message, exc_info=exception, stack_info=True)
×
93

94
        raise exception
×
95

96
    @_tp.override
1✔
97
    def boundingRect(self) -> _qtc.QRectF:
1✔
98
        return _qtc.QRectF(0, 0, self.w, self.h)
1✔
99

100
    # Setter functions
101
    def setParent(self, p):
1✔
102
        self.editor = p
1✔
103

104
    def setDisplayName(self, newName: str) -> None:
1✔
105
        self.displayName = newName
1✔
106
        self.label.setPlainText(newName)
1✔
107

108
    # Interaction related
109
    def contextMenuEvent(self, event):
1✔
110
        menu = _qtw.QMenu()
×
111

NEW
112
        rotateCwAction = menu.addAction(
×
113
            _img.ROTATE_TO_RIGHT_PNG.icon(), "Rotate Block clockwise"
114
        )
UNCOV
115
        rotateCwAction.triggered.connect(self.rotateBlockCW)
×
116

NEW
117
        rotateCcwAction = menu.addAction(
×
118
            _img.ROTATE_LEFT_PNG.icon(), "Rotate Block counter-clockwise"
119
        )
UNCOV
120
        rotateCcwAction.triggered.connect(self.rotateBlockCCW)
×
121

122
        resetRotationAction = menu.addAction("Reset Rotation")
×
123
        resetRotationAction.triggered.connect(self.resetRotation)
×
124

125
        deleteBlockAction = menu.addAction("Delete this Block")
×
126
        deleteBlockAction.triggered.connect(self.deleteBlockCom)
×
127

128
        self._addChildContextMenuActions(menu)
×
129

130
        menu.exec_(event.screenPos())
×
131

132
    def _addChildContextMenuActions(self, contextMenu: _qtw.QMenu) -> None:
1✔
133
        pass
×
134

135
    def mouseDoubleClickEvent(
1✔
136
        self,
137
        event: _qtw.QGraphicsSceneMouseEvent,  # pylint: disable=unused-argument
138
    ) -> None:
UNCOV
139
        self.editor.showBlockDlg(self)
×
140

141
    def mousePressEvent(self, event):
1✔
142
        self.isSelected = True
×
143
        super().mousePressEvent(event)
×
144

145
    def mouseReleaseEvent(self, event):
1✔
146
        super().mouseReleaseEvent(event)
×
147
        newPos = self.scenePos()
×
148

149
        oldPos = self.oldPos
×
150
        self.oldPos = newPos
×
151

152
        if oldPos is None or newPos == oldPos:
×
153
            return
×
154

NEW
155
        command = _mc.MoveCommand(
×
156
            self,
157
            oldScenePos=oldPos,
158
            newScenePos=newPos,
159
            descr="Move BlockItem",
160
        )
UNCOV
161
        self.editor.parent().undoStack.push(command)
×
162

163
    # Transform related
164
    def changeSize(self):
1✔
165
        self._positionLabel()
1✔
166

167
    def _positionLabel(self):
1✔
168
        width, _ = self._getCappedWidthAndHeight()
1✔
169
        rect = self.label.boundingRect()
1✔
170
        labelWidth = rect.width()
1✔
171
        labelPosX = (width - labelWidth) / 2
1✔
172
        self.label.setPos(labelPosX, width)
1✔
173

174
    def _getCappedWidthAndHeight(self):
1✔
175
        width = self.w
1✔
176
        height = self.h
1✔
177
        height = max(height, 20)
1✔
178
        width = max(width, 40)
1✔
179
        return width, height
1✔
180

181
    def updateFlipStateH(self, state: bool) -> None:
1✔
182
        self.flippedH = bool(state)
1✔
183
        self._updateTransform()
1✔
184

185
    def updateFlipStateV(self, state: bool) -> None:
1✔
186
        self.flippedV = bool(state)
1✔
187
        self._updateTransform()
1✔
188

189
    def rotateBlockCW(self):
1✔
190
        self.rotateBlockToN(1)
×
191

192
    def rotateBlockCCW(self):
1✔
193
        self.rotateBlockToN(-1)
×
194

195
    def rotateBlockToN(self, n: int) -> None:
1✔
196
        nQuarterTurns = self.rotationN + n
1✔
197
        self._rotateBlock(nQuarterTurns)
1✔
198

199
    def resetRotation(self):
1✔
200
        self._rotateBlock(0)
×
201

202
    def _rotateBlock(self, nQuarterTurns: int) -> None:
1✔
203
        self.rotationN = nQuarterTurns
1✔
204
        self.label.setRotation(-self.rotationN * 90)
1✔
205
        self._updateTransform()
1✔
206

207
    def _updateTransform(self) -> None:
1✔
208
        scaleX = -1 if self.flippedH else 1
1✔
209
        scaleY = -1 if self.flippedV else 1
1✔
210

211
        scale = _qtg.QTransform.fromScale(scaleX, scaleY)
1✔
212

213
        translateX = self.h if self.flippedH else 0
1✔
214
        translateY = self.w if self.flippedV else 0
1✔
215
        translate = _qtg.QTransform.fromTranslate(translateX, translateY)
1✔
216

217
        angle = self.rotationN * 90
1✔
218
        rotate = _qtg.QTransform().rotate(angle)
1✔
219

220
        transform = scale * translate * rotate  # type: ignore[operator]
1✔
221

222
        self.setTransform(transform)
1✔
223

224
    def deleteBlock(self):
1✔
225
        self.editor.trnsysObj.remove(self)
×
226
        self.editor.diagramScene.removeItem(self)
×
227

228
    def deleteBlockCom(self):
1✔
229
        self.editor.diagramView.deleteBlockCom(self)
×
230

231
    def updateImage(self):
1✔
232
        if self.flippedH:
1✔
233
            self.updateFlipStateH(self.flippedH)
×
234

235
        if self.flippedV:
1✔
236
            self.updateFlipStateV(self.flippedV)
×
237

238
    # AlignMode related
239
    def itemChange(self, change, value):
1✔
240
        # Snap grid excludes alignment
241

242
        if change == self.ItemPositionChange:
1✔
243
            if self.editor.snapGrid:
1✔
244
                snapSize = self.editor.snapSize
×
245
                self.logger.debug("itemchange")
×
246
                self.logger.debug(type(value))
×
NEW
247
                value = _qtc.QPointF(
×
248
                    value.x() - value.x() % snapSize,
249
                    value.y() - value.y() % snapSize,
250
                )
UNCOV
251
                return value
×
252
            if self.editor.alignMode:
1✔
253
                if self.hasElementsInYBand():
×
254
                    return self.alignBlock(value)
×
255
            return value
1✔
256
        return super().itemChange(change, value)
1✔
257

258
    def alignBlock(self, value):
1✔
259
        for t in self.editor.trnsysObj:
×
260
            if isinstance(t, BlockItem) and t is not self:
×
261
                if self.elementInYBand(t):
×
262
                    value = _qtc.QPointF(self.pos().x(), t.pos().y())
×
263
                    self.editor.alignYLineItem.setLine(
×
264
                        self.pos().x() + self.w / 2,
265
                        t.pos().y(),
266
                        t.pos().x() + t.w / 2,
267
                        t.pos().y(),
268
                    )
269

270
                    self.editor.alignYLineItem.setVisible(True)
×
271

272
                    qtm = _qtc.QTimer(self.editor)
×
273
                    qtm.timeout.connect(self.timerfunc)
×
274
                    qtm.setSingleShot(True)
×
275
                    qtm.start(1000)
×
276

277
                    e = _qtg.QMouseEvent(
×
278
                        _qtc.QEvent.MouseButtonRelease,
279
                        self.pos(),
280
                        _qtc.Qt.NoButton,
281
                        _qtc.Qt.NoButton,
282
                        _qtc.Qt.NoModifier,
283
                    )
284
                    self.editor.diagramView.mouseReleaseEvent(e)
×
285
                    self.editor.alignMode = False
×
286

287
                if self.elementInXBand(t):
×
288
                    value = _qtc.QPointF(t.pos().x(), self.pos().y())
×
289
                    self.editor.alignXLineItem.setLine(
×
290
                        t.pos().x(),
291
                        t.pos().y() + self.w / 2,
292
                        t.pos().x(),
293
                        self.pos().y() + t.w / 2,
294
                    )
295

296
                    self.editor.alignXLineItem.setVisible(True)
×
297

298
                    qtm = _qtc.QTimer(self.editor)
×
299
                    qtm.timeout.connect(self.timerfunc2)
×
300
                    qtm.setSingleShot(True)
×
301
                    qtm.start(1000)
×
302

303
                    e = _qtg.QMouseEvent(
×
304
                        _qtc.QEvent.MouseButtonRelease,
305
                        self.pos(),
306
                        _qtc.Qt.NoButton,
307
                        _qtc.Qt.NoButton,
308
                        _qtc.Qt.NoModifier,
309
                    )
310
                    self.editor.diagramView.mouseReleaseEvent(e)
×
311
                    self.editor.alignMode = False
×
312

313
        return value
×
314

315
    def timerfunc(self):
1✔
316
        self.editor.alignYLineItem.setVisible(False)
×
317

318
    def timerfunc2(self):
1✔
319
        self.editor.alignXLineItem.setVisible(False)
×
320

321
    def hasElementsInYBand(self):
1✔
322
        for t in self.editor.trnsysObj:
×
323
            if isinstance(t, BlockItem):
×
324
                if self.elementInYBand(t):
×
325
                    return True
×
326

327
        return False
×
328

329
    def elementInYBand(self, t):
1✔
330
        eps = 50
×
NEW
331
        return (
×
332
            self.scenePos().y() - eps
333
            <= t.scenePos().y()
334
            <= self.scenePos().y() + eps
335
        )
336

337
    def elementInXBand(self, t):
1✔
338
        eps = 50
×
NEW
339
        return (
×
340
            self.scenePos().x() - eps
341
            <= t.scenePos().x()
342
            <= self.scenePos().x() + eps
343
        )
344

345
    def _encodeBaseModel(self) -> _bim.BlockItemBaseModel:
1✔
346
        position = (self.pos().x(), self.pos().y())
1✔
347

348
        blockItemModel = _bim.BlockItemBaseModel(
1✔
349
            position,
350
            self.trnsysId,
351
            self.flippedH,
352
            self.flippedV,
353
            self.rotationN,
354
        )
355

356
        return blockItemModel
1✔
357

358
    def _decodeBaseModel(
1✔
359
        self, blockItemModel: _bim.BlockItemBaseModel
360
    ) -> None:
361
        x = float(blockItemModel.blockPosition[0])
1✔
362
        y = float(blockItemModel.blockPosition[1])
1✔
363
        self.setPos(x, y)
1✔
364

365
        self.trnsysId = blockItemModel.trnsysId
1✔
366

367
        self.updateFlipStateH(blockItemModel.flippedH)
1✔
368
        self.updateFlipStateV(blockItemModel.flippedV)
1✔
369

370
        self.rotateBlockToN(blockItemModel.rotationN)
1✔
371

372
    def encode(self):
1✔
373
        portListInputs = []
1✔
374
        portListOutputs = []
1✔
375

376
        for inp in self.inputs:
1✔
377
            portListInputs.append(inp.id)
1✔
378
        for output in self.outputs:
1✔
379
            portListOutputs.append(output.id)
1✔
380

381
        blockPosition = (float(self.pos().x()), float(self.pos().y()))
1✔
382

383
        blockItemModel = _bim.BlockItemModel(
1✔
384
            self.name,
385
            self.displayName,
386
            blockPosition,
387
            self.trnsysId,
388
            portListInputs,  # pylint: disable = duplicate-code # 1
389
            portListOutputs,
390
            self.flippedH,
391
            self.flippedV,
392
            self.rotationN,
393
        )
394

395
        dictName = "Block-"
1✔
396
        return dictName, blockItemModel.to_dict()
1✔
397

398
    def decode(self, i, resBlockList):
1✔
399
        model = _bim.BlockItemModel.from_dict(i)
1✔
400

401
        self.setDisplayName(model.BlockDisplayName)
1✔
402
        self.setPos(
1✔
403
            float(model.blockPosition[0]), float(model.blockPosition[1])
404
        )
405
        self.trnsysId = model.trnsysId
1✔
406

407
        if len(self.inputs) != len(model.portsIdsIn) or len(
1✔
408
            self.outputs
409
        ) != len(model.portsIdsOut):
410
            temp = model.portsIdsIn
×
411
            model.portsIdsIn = model.portsIdsOut
×
412
            model.portsIdsOut = temp
×
413

414
        for index, inp in enumerate(self.inputs):
1✔
415
            inp.id = model.portsIdsIn[index]
1✔
416

417
        for index, out in enumerate(self.outputs):
1✔
418
            out.id = model.portsIdsOut[index]
1✔
419

420
        self.updateFlipStateH(model.flippedH)
1✔
421
        self.updateFlipStateV(model.flippedV)
1✔
422
        self.rotateBlockToN(model.rotationN)
1✔
423

424
        resBlockList.append(self)
1✔
425

426
    def exportMassFlows(self):
1✔
427
        return "", 0
1✔
428

429
    def exportDivSetting1(self):
1✔
430
        return "", 0
1✔
431

432
    def exportDivSetting2(self, nUnit):
1✔
433
        return "", nUnit
1✔
434

435
    def exportPipeAndTeeTypesForTemp(
1✔
436
        self, startingUnit: int
437
    ) -> _tp.Tuple[str, int]:
438
        return "", startingUnit
1✔
439

440
    def assignIDsToUninitializedValuesAfterJsonFormatMigration(
1✔
441
        self, generator: _id.IdGenerator
442
    ) -> None:
443
        pass
1✔
444

445
    def getInternalPiping(self) -> _ip.InternalPiping:
1✔
446
        raise NotImplementedError()
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc