class ReactionBox(QGraphicsItem): """Handle to the line edits on the map""" def __init__(self, parent: MapView, r_id: str, name): QGraphicsItem.__init__(self) self.map = parent self.id = r_id self.name = name self.item = QLineEdit() self.item.setMaximumWidth(80) r = self.map.appdata.project.cobra_py_model.reactions.get_by_id(r_id) text = "Id: " + r.id + "\nName: " + r.name \ + "\nEquation: " + r.build_reaction_string()\ + "\nLowerbound: " + str(r.lower_bound) \ + "\nUpper bound: " + str(r.upper_bound) \ + "\nObjective coefficient: " + str(r.objective_coefficient) self.item.setToolTip(text) self.proxy = self.map.scene.addWidget(self.item) self.proxy.show() palette = self.item.palette() palette.setColor(QPalette.Base, self.map.appdata.default_color) role = self.item.foregroundRole() palette.setColor(role, Qt.black) self.item.setPalette(palette) self.setCursor(Qt.OpenHandCursor) self.setAcceptedMouseButtons(Qt.LeftButton) self.item.textEdited.connect(self.value_changed) self.item.returnPressed.connect(self.returnPressed) self.item.setContextMenuPolicy(Qt.CustomContextMenu) self.item.customContextMenuRequested.connect(self.on_context_menu) # create context menu self.pop_menu = QMenu(parent) maximize_action = QAction('maximize flux for this reaction', parent) self.pop_menu.addAction(maximize_action) maximize_action.triggered.connect(self.emit_maximize_action) minimize_action = QAction('minimize flux for this reaction', parent) self.pop_menu.addAction(minimize_action) minimize_action.triggered.connect(self.emit_minimize_action) switch_action = QAction('switch to reaction mask', parent) self.pop_menu.addAction(switch_action) switch_action.triggered.connect(self.switch_to_reaction_mask) remove_action = QAction('remove from map', parent) self.pop_menu.addAction(remove_action) remove_action.triggered.connect(self.remove) self.pop_menu.addSeparator() def returnPressed(self): if validate_value(self.item.text()): self.map.value_changed(self.id, self.item.text()) # TODO: actually I want to repaint # self.map.update() def value_changed(self): test = self.item.text().replace(" ", "") if test == "": self.map.value_changed(self.id, test) self.set_color(self.map.appdata.default_color) elif validate_value(self.item.text()): self.map.value_changed(self.id, self.item.text()) if self.id in self.map.appdata.project.scen_values.keys(): self.set_color(self.map.appdata.scen_color) else: self.set_color(self.map.appdata.comp_color) else: self.set_color(Qt.magenta) # TODO: actually I want to repaint # self.map.update() def set_val_and_color(self, value: Tuple[float, float]): self.set_value(value) self.recolor() def set_value(self, value: Tuple[float, float]): (vl, vu) = value if isclose(vl, vu, abs_tol=self.map.appdata.abs_tol): self.item.setText(str(round(vl, self.map.appdata.rounding))) else: self.item.setText( str((round(vl, self.map.appdata.rounding), round(vu, self.map.appdata.rounding)))) self.item.setCursorPosition(0) def recolor(self): value = self.item.text() test = value.replace(" ", "") if test == "": self.set_color(self.map.appdata.default_color) elif validate_value(value): if self.id in self.map.appdata.project.scen_values.keys(): value = self.map.appdata.project.scen_values[self.id] self.set_color(self.map.appdata.scen_color) else: value = self.map.appdata.project.comp_values[self.id] (vl, vu) = value if math.isclose(vl, vu, abs_tol=self.map.appdata.abs_tol): if self.map.appdata.modes_coloring: if vl == 0: self.set_color(Qt.red) else: self.set_color(Qt.green) else: self.set_color(self.map.appdata.comp_color) else: if math.isclose(vl, 0.0, abs_tol=self.map.appdata.abs_tol): self.set_color(self.map.appdata.special_color_1) elif math.isclose(vu, 0.0, abs_tol=self.map.appdata.abs_tol): self.set_color(self.map.appdata.special_color_1) elif vl <= 0 and vu >= 0: self.set_color(self.map.appdata.special_color_1) else: self.set_color(self.map.appdata.special_color_2) else: self.set_color(Qt.magenta) def set_color(self, color: QColor): palette = self.item.palette() palette.setColor(QPalette.Base, color) role = self.item.foregroundRole() palette.setColor(role, Qt.black) self.item.setPalette(palette) def boundingRect(self): return QRectF(-15, -15, 20, 20) def paint(self, painter: QPainter, _option, _widget: QWidget): # set color depending on wether the value belongs to the scenario if self.id in self.map.appdata.project.scen_values.keys(): painter.setPen(Qt.magenta) painter.setBrush(Qt.magenta) else: painter.setPen(Qt.darkGray) painter.drawRect(-15, -15, 20, 20) painter.setPen(Qt.darkGray) painter.drawLine(-5, 0, -5, -10) painter.drawLine(0, -5, -10, -5) def mousePressEvent(self, _event: QGraphicsSceneMouseEvent): pass def mouseReleaseEvent(self, _event: QGraphicsSceneMouseEvent): pass def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent): drag = QDrag(event.widget()) mime = QMimeData() mime.setText(str(self.id)) drag.setMimeData(mime) # self.setCursor(Qt.ClosedHandCursor) drag.exec_() # self.setCursor(Qt.OpenHandCursor) def setPos(self, x, y): self.proxy.setPos(x, y) super().setPos(x, y) def on_context_menu(self, point): # show context menu self.pop_menu.exec_(self.item.mapToGlobal(point)) def remove(self): self.map.remove_box(self.id) self.map.drag = False def switch_to_reaction_mask(self): self.map.switchToReactionMask.emit(self.id) self.map.drag = False def emit_maximize_action(self): self.map.maximizeReaction.emit(self.id) self.map.drag = False def emit_minimize_action(self): self.map.minimizeReaction.emit(self.id) self.map.drag = False
class MainWindowTyping(QMainWindow): """Main Window.""" def __init__(self): """Main Window UI를 설정한다.""" super().__init__() self.setWindowTitle(f"Typing Number - {VER}") icon = QIcon() icon.addPixmap(QPixmap(r'ok_64x64.ico'), QIcon.Normal, QIcon.Off) self.setWindowIcon(icon) self.setMinimumSize(800, 100) # Typing Info self.typing = ManageTyping() # Setup StatusBar self.statusBar().showMessage("") self.statusBar().addPermanentWidget(VLine()) self.label_typing_avg = QLabel("평균타수 : 0.0타/초", self) self.statusBar().addPermanentWidget(self.label_typing_avg) self.statusBar().addPermanentWidget(VLine()) self.label_total_time = QLabel("총 연습시간", self) self.statusBar().addPermanentWidget(self.label_total_time) # Setup LineEdit self.line_for_number = QLineEdit(self) self.line_for_typing = QLineEdit(self) self.line_for_error = QLineEdit(self) self.setup_lineedit() vbox = QVBoxLayout() vbox.addWidget(self.line_for_number) vbox.addWidget(self.line_for_typing) vbox.addWidget(self.line_for_error) central_widget = QWidget(self) central_widget.setLayout(vbox) self.setCentralWidget(central_widget) self.new_game() def setup_lineedit(self): """LineEdit를 설정한다.""" mask = (("X" + " " * self.typing.n_space_between_chars) * self.typing.n_char_in_game) self.line_for_number.setInputMask(mask) self.line_for_typing.setInputMask(mask) self.line_for_error.setInputMask(mask) self.line_for_number.setReadOnly(True) self.line_for_error.setReadOnly(True) self.line_for_error.setStyleSheet( "QLineEdit { border:none; color: red; }") self.line_for_typing.textChanged.connect(self.update_typing) def display_result(self): """연습결과를 보여준다.""" typing_per_sec = self.typing.typing_per_sec_prev() typing_per_sec_avg = self.typing.typing_per_sec_avg() self.statusBar().showMessage(f'현재타수 : {typing_per_sec:.1f}타/초') self.label_typing_avg.setText(f'평균타수 : {typing_per_sec_avg:.1f}타/초') self.label_total_time.setText("총 연습시간 : " + self.typing.str_of_total_time()) def new_game(self): """연습에 필요한 문자열 만든다.""" self.line_for_typing.setText("") self.line_for_typing.setCursorPosition(0) self.typing.new_game() self.line_for_number.setText(self.typing.chars_answer) self.line_for_typing.setFocus() self.display_result() def update_typing(self): """Typing 정보를 갱신한다.""" typing = self.typing typing.update_time() is_correct, err_str = typing.check_result(self.line_for_typing.text()) if is_correct: self.new_game() self.line_for_error.setText("") self.line_for_error.setText(err_str)
class InteractionVolumeView(VolumeView): signalAxisChanged = Signal(int) def __init__(self, label=None, **kwargs): super(InteractionVolumeView, self).__init__(**kwargs) self.interactionFrame = QFrame(self) self.interactionFrame.setSizePolicy( QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)) self.interactionFrame.setFixedHeight(30) self.interactionLayout = QHBoxLayout(self.interactionFrame) self.interactionLayout.setContentsMargins(5, 5, 5, 5) self.sliceLabel = QLabel(self.interactionFrame) self.sliceLabel.setSizePolicy( QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)) self.updateSliceLabel() self.textLabel = QLineEdit(self.interactionFrame) self.textLabel.setReadOnly(True) self.textLabel.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)) self.textLabel.setAlignment(Qt.AlignCenter) stylesheet = \ "QLineEdit {\n" \ + "border: none;\n" \ + "background-color: rgba(255, 255, 255, 0);\n" \ + "}" self.textLabel.setStyleSheet(stylesheet) if label is not None: self.updateTextLabel(label) self.interactionLayout.addWidget(self.sliceLabel) self.interactionLayout.addWidget(self.textLabel) self.axisSelector = QComboBox(self.interactionFrame) self.axisSelector.setFixedWidth(40) self.axisSelector.insertItems(0, ["0", "1", "2"]) self.interactionLayout.addWidget(self.axisSelector) self.layout.addWidget(self.interactionFrame) self.signalSliceChanged.connect(self.updateSliceLabel) self.axisSelector.currentIndexChanged.connect(self.setAxisAndEmit) def setAxisAndEmit(self, axis): self.setAxis(axis) self.signalAxisChanged.emit(axis) def setAxis(self, axis): super(InteractionVolumeView, self).setAxis(axis) self.axisSelector.setCurrentIndex(axis) self.updateSliceLabel() def setSlice(self, slice_): super(InteractionVolumeView, self).setSlice(slice_) self.updateSliceLabel() def updateSliceLabel(self): try: length = str(len(str(self.numberOfSlices))) self.sliceLabel.setText( ("{:0" + length + "d}/{:0" + length + "}").format( self.currentSlice, self.numberOfSlices)) except AttributeError: pass def updateTextLabel(self, text): try: self.textLabel.setText(text) self.textLabel.setCursorPosition(0) except AttributeError: pass
class EditableLineEdit(QWidget): """ """ sig_text_changed = Signal(object, object) # old_text, new_text def __init__(self, title, text, regex=None, allow_empty=False): super(EditableLineEdit, self).__init__() self._label = QLabel(title) self._text = QLineEdit() self.button_edit = QPushButton() self.allow_empty = allow_empty self.regex = regex self.qregex = None self.button_edit.setIcon(qta.icon('fa.edit')) self._text.setText(text) layout = QVBoxLayout() layout.addWidget(self._label) layout_h = QHBoxLayout() layout_h.addWidget(self._text) layout_h.addWidget(self.button_edit) layout.addLayout(layout_h) self.setLayout(layout) self._text.setDisabled(True) self.button_edit.clicked.connect(self.edit) self.last_text = self._text.text() self.set_regex(regex) # def focusOutEvent(self, event): # """ # Qt override. # FIXME: # """ # super(EditableLineEdit, self).focusOutEvent(event) # event = QKeyEvent(QKeyEvent.KeyPress, Qt.Key_Escape) # self.keyPressEvent(event) def keyPressEvent(self, event): """ Qt override. """ super(EditableLineEdit, self).keyPressEvent(event) key = event.key() if key in [Qt.Key_Enter, Qt.Key_Return]: self.check_text() elif key in [Qt.Key_Escape]: self._text.setText(self.last_text) self.check_text(escaped=True) # --- Public API # ------------------------------------------------------------------------- def text(self): return self._text.text() def setText(self, text): self.set_text(text) def set_text(self, text): """ """ self._text.setText(text) def set_label_text(self, text): """ """ self.label.setText(text) def set_regex(self, regex): """ """ if regex: self.regex = regex self.qregex = QRegExp(regex) validator = QRegExpValidator(self.qregex) self._text.setValidator(validator) def check_text(self, escaped=False): """ """ self._text.setDisabled(True) self.button_edit.setDisabled(False) new_text = self._text.text() if not self.allow_empty and len(new_text) == 0: self.edit() if self.last_text != new_text and not escaped: self.sig_text_changed.emit(self.last_text, new_text) self.last_text = new_text def edit(self): """ """ self._text.setDisabled(False) self.button_edit.setDisabled(True) self._text.setFocus() self._text.setCursorPosition(len(self._text.text())) self.last_text = self._text.text()