def _createModelFromData(self, headers_blocks, data_blocks): """Create the model needed for GUI calculated data table and chart (based on data 2d list created previously).""" if self._project.experimentsCount() > 1: print("Currently, only 1 measured datablock is supported. Given: ", self._project.experimentsCount()) experiment_id = self._project.experimentsIds()[ 0] # only 1st measured datablock is currently taken into account headers, data = headers_blocks[experiment_id], data_blocks[ experiment_id] row_count = len(data) column_count = len(data[0]) # set headers headerModel = QStandardItemModel(1, column_count) for column_index in range(column_count): index = headerModel.index(0, column_index) value = headers[column_index] headerModel.setData(index, value, Qt.DisplayRole) #Qt.WhatsThisRole # set model data dataModel = QStandardItemModel(row_count, column_count) for row_index in range(row_count): for column_index in range(column_count): index = dataModel.index(row_index, column_index) value = data[row_index][column_index] dataModel.setData(index, value, Qt.DisplayRole) return headerModel, dataModel
class QCampaignList(QListView): def __init__(self): super(QCampaignList, self).__init__() self.model = QStandardItemModel(self) self.setModel(self.model) self.setMinimumWidth(250) self.setMinimumHeight(350) self.campaigns = [] self.setSelectionBehavior(QAbstractItemView.SelectItems) self.setup_content() def setup_content(self): for i, campaign in enumerate(CAMPAIGNS): self.campaigns.append(campaign) item = QCampaignItem(*campaign) self.model.appendRow(item) self.setSelectedCampaign(0) self.repaint() def setSelectedCampaign(self, row): self.selectionModel().clearSelection() index = self.model.index(row, 0) if not index.isValid(): index = self.model.index(0, 0) self.selectionModel().setCurrentIndex(index, QItemSelectionModel.Select) self.repaint() def clear_layout(self): self.model.removeRows(0, self.model.rowCount())
def testLen(self): model = QStandardItemModel(2, 2) model.insertRow(0) model.insertRow(1) model.insertColumn(0) model.insertColumn(1) selection = QItemSelection(model.index(0,0), model.index(1,1)) self.assertEqual(len(selection), 1)
def testLen(self): model = QStandardItemModel(2, 2) model.insertRow(0) model.insertRow(1) model.insertColumn(0) model.insertColumn(1) selection = QItemSelection(model.index(0, 0), model.index(1, 1)) self.assertEqual(len(selection), 1)
class QCampaignList(QListView): CampaignRole = Qt.UserRole def __init__(self, campaigns: list[Campaign], show_incompatible: bool) -> None: super(QCampaignList, self).__init__() self.campaign_model = QStandardItemModel(self) self.setModel(self.campaign_model) self.setMinimumWidth(250) self.setMinimumHeight(350) self.campaigns = campaigns self.setSelectionBehavior(QAbstractItemView.SelectItems) self.setup_content(show_incompatible) @property def selected_campaign(self) -> Campaign: return self.currentIndex().data(QCampaignList.CampaignRole) def setup_content(self, show_incompatible: bool) -> None: self.selectionModel().blockSignals(True) try: self.campaign_model.clear() for campaign in self.campaigns: if show_incompatible or campaign.is_compatible: item = QCampaignItem(campaign) self.campaign_model.appendRow(item) finally: self.selectionModel().blockSignals(False) self.selectionModel().setCurrentIndex( self.campaign_model.index(0, 0, QModelIndex()), QItemSelectionModel.Select)
class TableViewAllStock(QTableView): def __init__(self, parent=None): super(TableViewAllStock, self).__init__(parent) # self.mouseDoubleClickEvent() self.doubleClicked.connect(self.slotDoubleClicked) self.creatUI() self.initData() def creatUI(self): self.model = QStandardItemModel() self.setModel(self.model) self.verticalHeader().setSectionResizeMode(QHeaderView.Fixed) self.verticalHeader().setDefaultSectionSize(8) self.verticalHeader().hide() self.setSelectionBehavior(QAbstractItemView.SelectRows) self.setEditTriggers(QTableView.NoEditTriggers) self.setShowGrid(True) arr = ["代码", "名称", "日期", "最新价", "涨跌幅", "涨跌额", "成交量(手)", "成交额", "振幅", "最高", "最低", "今开", "换手率"] self.model.setColumnCount(len(arr)) for i in range(len(arr)): self.model.setHeaderData(i, Qt.Horizontal, arr[i]) def Item(self, var): item = QStandardItem(str(var)) item.setTextAlignment(Qt.AlignVCenter|Qt.AlignHCenter); return item def initData(self): kline = Kline() #kline.setYear(2021) res = kline.getLatestKlineData() for row in res: items = [] items.append(self.Item(row['code'])) items.append(self.Item(row['SEC_NAME'])) items.append(self.Item(row['date'])) items.append(self.Item(row['close'])) items.append(self.Item(str(row['p_change'])+'%')) items.append(self.Item(row['price_change'])) items.append(self.Item(row['amount'])) items.append(self.Item(row['turnover'])) items.append(self.Item(str(row['amplitude'])+'%')) items.append(self.Item(row['high'])) items.append(self.Item(row['low'])) items.append(self.Item(row['open'])) items.append(self.Item(str(row['exchange'])+'%')) self.model.appendRow(items) # QModelIndex index def slotDoubleClicked(self, index): code = self.model.index(index.row(), 0).data(); name = self.model.index(index.row(), 1).data(); d = KlineWidget(self) d.setWindowTitle("%s(%s)"%(code, name)) d.exec()
class QPlannedFlightsView(QListView): def __init__(self, game_model: GameModel, cp: ControlPoint) -> None: super(QPlannedFlightsView, self).__init__() self.game_model = game_model self.cp = cp self.model = QStandardItemModel(self) self.setModel(self.model) self.flight_items = [] self.setIconSize(QSize(91, 24)) self.setSelectionBehavior(QAbstractItemView.SelectItems) self.set_flight_planner() def setup_content(self): self.flight_items = [] for package in self.game_model.ato_model.packages: for flight in package.flights: if flight.from_cp == self.cp: item = QFlightItem(package.package, flight) self.flight_items.append(item) self.flight_items.sort(key=self.mission_start_for_flight) for item in self.flight_items: self.model.appendRow(item) self.set_selected_flight(0) def set_selected_flight(self, row): self.selectionModel().clearSelection() index = self.model.index(row, 0) if not index.isValid(): index = self.model.index(0, 0) self.selectionModel().setCurrentIndex(index, QItemSelectionModel.Select) self.repaint() def clear_layout(self): self.model.removeRows(0, self.model.rowCount()) def set_flight_planner(self) -> None: self.clear_layout() self.setup_content() @staticmethod def mission_start_for_flight(flight_item: QFlightItem) -> timedelta: return TotEstimator(flight_item.package).mission_start_time( flight_item.flight)
def _createModelFromData(self, headers, data): """Create the model needed for GUI measured data table and chart (based on data 2d list created previously).""" row_count = len(data) column_count = len(data[0]) # set headers headerModel = QStandardItemModel(1, column_count) for column_index in range(column_count): index = headerModel.index(0, column_index) value = headers[column_index] headerModel.setData(index, value, Qt.DisplayRole) #Qt.WhatsThisRole # set model data dataModel = QStandardItemModel(row_count, column_count) for row_index in range(row_count): for column_index in range(column_count): index = dataModel.index(row_index, column_index) value = data[row_index][column_index] dataModel.setData(index, value, Qt.DisplayRole) return headerModel, dataModel
def testOperators(self): model = QStandardItemModel() for i in range(100): model.appendRow(QStandardItem("Item: %d"%i)) first = model.index(0, 0) second = model.index(10, 0) third = model.index(20, 0) fourth = model.index(30, 0) sel = QItemSelection(first, second) sel2 = QItemSelection() sel2.select(third, fourth) sel3 = sel + sel2 #check operator + self.assertEqual(len(sel3), 2) sel4 = sel sel4 += sel2 #check operator += self.assertEqual(len(sel4), 2) self.assertEqual(sel4, sel3)
def testOperators(self): model = QStandardItemModel() for i in range(100): model.appendRow(QStandardItem("Item: %d" % i)) first = model.index(0, 0) second = model.index(10, 0) third = model.index(20, 0) fourth = model.index(30, 0) sel = QItemSelection(first, second) sel2 = QItemSelection() sel2.select(third, fourth) sel3 = sel + sel2 #check operator + self.assertEqual(len(sel3), 2) sel4 = sel sel4 += sel2 #check operator += self.assertEqual(len(sel4), 2) self.assertEqual(sel4, sel3)
class QPlannedFlightsView(QListView): def __init__(self, flight_planner: FlightPlanner): super(QPlannedFlightsView, self).__init__() self.model = QStandardItemModel(self) self.setModel(self.model) self.flightitems = [] self.setIconSize(QSize(91, 24)) self.setSelectionBehavior(QAbstractItemView.SelectItems) if flight_planner: self.set_flight_planner(flight_planner) def update_content(self): for i, f in enumerate(self.flight_planner.flights): self.flightitems[i].update(f) def setup_content(self, row=0): self.flightitems = [] for i, f in enumerate(self.flight_planner.flights): item = QFlightItem(f) self.model.appendRow(item) self.flightitems.append(item) self.setSelectedFlight(row) self.repaint() def setSelectedFlight(self, row): self.selectionModel().clearSelection() index = self.model.index(row, 0) if not index.isValid(): index = self.model.index(0, 0) self.selectionModel().setCurrentIndex(index, QItemSelectionModel.Select) self.repaint() def clear_layout(self): self.model.removeRows(0, self.model.rowCount()) def set_flight_planner(self, flight_planner: FlightPlanner, row=0): self.clear_layout() self.flight_planner = flight_planner if self.flight_planner: self.setup_content(row)
def testIntDelegate(self): """PYSIDE-1250: When creating a QVariant, use int instead of long long for anything that fits into a int. Verify by checking that a spin box is created as item view editor for int.""" item = QStandardItem() item.setData(123123, Qt.EditRole) # <-- QVariant conversion here model = QStandardItemModel() model.appendRow(item) style_option = QStyleOptionViewItem() delegate = QStyledItemDelegate() editor = delegate.createEditor(None, style_option, model.index(0, 0)) self.assertEqual(type(editor), QSpinBox)
class ObjectNameListEditor(ManageItemsDialog): """A dialog to select the object name list for a relationship using Google-like search bars.""" def __init__(self, parent, index, object_class_names, object_names_lists, current_object_names): """Initializes widget. Args: parent (DataStoreForm) index (QModelIndex) object_class_names (list): string object class names object_names_lists (list): lists of string object names current_object_names (list) """ super().__init__(parent, None) self.setWindowTitle("Select objects") self._index = index self.model = QStandardItemModel(self) self.init_model(object_class_names, object_names_lists, current_object_names) self.table_view.setModel(self.model) self.resize_window_to_columns() self.table_view.verticalHeader().hide() delegate = SearchBarDelegate(self) self.table_view.setItemDelegate(delegate) self.connect_signals() def connect_signals(self): """Connect signals to slots.""" super().connect_signals() self.button_box.accepted.connect(self.accept) self.button_box.rejected.connect(self.reject) def init_model(self, object_class_names, object_names_lists, current_object_names): self.model.setHorizontalHeaderLabels(object_class_names) item_list = [] for k, object_names_list in enumerate(object_names_lists): try: obj_name = current_object_names[k] except IndexError: obj_name = None qitem = QStandardItem(obj_name) qitem.setData(object_names_list, role=Qt.UserRole) item_list.append(qitem) self.model.invisibleRootItem().appendRow(item_list) @Slot() def accept(self): self._index.model().setData( self._index, ",".join( self.model.index(0, j).data() for j in range(self.model.columnCount()))) super().accept()
class CellParametersModel(BaseModel): def __init__(self, parent=None): super().__init__(parent) self._project_dict = None self._model = QStandardItemModel() # set roles self._a_role, self._b_role, self._c_role, self._alpha_role, self._beta_role, self._gamma_role = [ Qt.UserRole + 1 + i for i in range(6) ] self._model.setItemRoleNames({ self._a_role: b'length_a', self._b_role: b'length_b', self._c_role: b'length_c', self._alpha_role: b'angle_alpha', self._beta_role: b'angle_beta', self._gamma_role: b'angle_gamma' }) self._log = logger.getLogger(self.__class__.__module__) def _setModelsFromProjectDict(self): """Create the model needed for GUI ...""" self._log.info("Starting to set Model from Project Dict") for phase_id, phase_dict in self._project_dict['phases'].items(): # block model signals self._model.blockSignals(True) # set helper data list data = [{ self._a_role: phase_dict.getItemByPath(['cell', 'length_a']).value, self._b_role: phase_dict.getItemByPath(['cell', 'length_b']).value, self._c_role: phase_dict.getItemByPath(['cell', 'length_c']).value, self._alpha_role: phase_dict.getItemByPath(['cell', 'angle_alpha']).value, self._beta_role: phase_dict.getItemByPath(['cell', 'angle_beta']).value, self._gamma_role: phase_dict.getItemByPath(['cell', 'angle_gamma']).value, }] # set model size self._model.setColumnCount(1) self._model.setRowCount(len(data)) # set model from data list created above for row_index, dict in enumerate(data): index = self._model.index(row_index, 0) for role, value in dict.items(): self._model.setData(index, value, role) # unblock signals and emit model layout changed self._model.blockSignals(False) self._model.layoutChanged.emit() self._log.info("Finished setting Model from Project Dict")
class AircraftTypeList(QListView): page_index_changed = Signal(int) def __init__(self, air_wing: AirWing) -> None: super().__init__() self.setIconSize(QSize(91, 24)) self.setMinimumWidth(300) self.item_model = QStandardItemModel(self) self.setModel(self.item_model) self.selectionModel().setCurrentIndex(self.item_model.index(0, 0), QItemSelectionModel.Select) self.selectionModel().selectionChanged.connect( self.on_selection_changed) for aircraft in air_wing.squadrons: self.add_aircraft_type(aircraft) def remove_aircraft_type(self, aircraft: AircraftType): for item in self.item_model.findItems(aircraft.name): self.item_model.removeRow(item.row()) self.page_index_changed.emit( self.selectionModel().currentIndex().row()) def add_aircraft_type(self, aircraft: AircraftType): aircraft_item = QStandardItem(aircraft.name) icon = self.icon_for(aircraft) if icon is not None: aircraft_item.setIcon(icon) aircraft_item.setEditable(False) aircraft_item.setSelectable(True) self.item_model.appendRow(aircraft_item) def on_selection_changed(self, selected: QItemSelection, _deselected: QItemSelection) -> None: indexes = selected.indexes() if len(indexes) > 1: raise RuntimeError( "Aircraft list should not allow multi-selection") if not indexes: return self.page_index_changed.emit(indexes[0].row()) @staticmethod def icon_for(aircraft: AircraftType) -> Optional[QIcon]: name = aircraft.dcs_id if name in AIRCRAFT_ICONS: return QIcon(AIRCRAFT_ICONS[name]) return None
def createModel(self, start, end): if start == end: return None model = QStandardItemModel(min(self.block_size, len(self.data) - self.curr_start), len(self.model_meta_data) - 1) step = 1 if start < end else -1 for i in range(1, self.model_col_num): model.setHeaderData(i - 1, Qt.Horizontal, self.model_meta_data[i][0]) i = 0 for row in range(start, end, step): data = self.data[row] #print("build model index {}, data: {}".format(row, data)) for col in range(0, self.model_col_num - 1): model.setData(model.index(i, col), str(data[col + 1])) i += 1 return model
def _createModelFromData(self, data): """Create the model needed for GUI fitables table (based on data dict created previously).""" model = QStandardItemModel() model.setRowCount(len(data)) model.setColumnCount(1) #model.setColumnCount(len(data[0])) # set model role names start_role = Qt.UserRole + 1 role_names = {} for key, _ in data[0].items(): column_index = list(data[0].keys()).index(key) role = start_role + column_index role_names[role] = key.encode() model.setItemRoleNames(role_names) # set model data for row_index, row_dict in enumerate(data): for key, value in row_dict.items(): column_index = list(row_dict.keys()).index(key) index = model.index(row_index, 0) role = start_role + column_index model.setData(index, value, role) return model
def dropEvent(self, event): tmp = QStandardItemModel() tmp.dropMimeData(event.mimeData(), event.dropAction(), 0, 0, QtCore.QModelIndex()) self.add_to_layout(LayerBoxWidget(tmp.index(0, 0).data()))
class QFlightWaypointList(QTableView): def __init__(self, package: Package, flight: Flight): super().__init__() self.package = package self.flight = flight self.model = QStandardItemModel(self) self.setModel(self.model) self.model.setHorizontalHeaderLabels(["Name", "Alt", "TOT/DEPART"]) header = self.horizontalHeader() header.setSectionResizeMode(0, QHeaderView.ResizeToContents) self.update_list() self.selectionModel().setCurrentIndex( self.indexAt(QPoint(1, 1)), QItemSelectionModel.Select ) def update_list(self): # We need to keep just the row and rebuild the index later because the # QModelIndex will not be valid after the model is cleared. current_index = self.currentIndex().row() self.model.clear() self.model.setHorizontalHeaderLabels(["Name", "Alt", "TOT/DEPART"]) waypoints = self.flight.flight_plan.waypoints for row, waypoint in enumerate(waypoints): self.add_waypoint_row(row, self.flight, waypoint) self.selectionModel().setCurrentIndex( self.model.index(current_index, 0), QItemSelectionModel.Select ) self.resizeColumnsToContents() total_column_width = self.verticalHeader().width() + self.lineWidth() for i in range(0, self.model.columnCount()): total_column_width += self.columnWidth(i) + self.lineWidth() self.setFixedWidth(total_column_width) def add_waypoint_row( self, row: int, flight: Flight, waypoint: FlightWaypoint ) -> None: self.model.insertRow(self.model.rowCount()) self.model.setItem(row, 0, QWaypointItem(waypoint, row)) altitude = int(waypoint.alt.feet) altitude_type = "AGL" if waypoint.alt_type == "RADIO" else "MSL" altitude_item = QStandardItem(f"{altitude} ft {altitude_type}") altitude_item.setEditable(False) self.model.setItem(row, 1, altitude_item) tot = self.tot_text(flight, waypoint) tot_item = QStandardItem(tot) tot_item.setEditable(False) self.model.setItem(row, 2, tot_item) def tot_text(self, flight: Flight, waypoint: FlightWaypoint) -> str: if waypoint.waypoint_type == FlightWaypointType.TAKEOFF: return self.takeoff_text(flight) prefix = "" time = flight.flight_plan.tot_for_waypoint(waypoint) if time is None: prefix = "Depart " time = flight.flight_plan.depart_time_for_waypoint(waypoint) if time is None: return "" time = timedelta(seconds=int(time.total_seconds())) return f"{prefix}T+{time}" @staticmethod def takeoff_text(flight: Flight) -> str: takeoff_time = flight.flight_plan.takeoff_time() # Handle custom flight plans where we can't estimate the takeoff time. if takeoff_time is None: takeoff_time = timedelta() start_time = timedelta(seconds=int(takeoff_time.total_seconds())) return f"T+{start_time}"
class MainWindowUi(QMainWindow): """sets up ui properties of MainWindowUi class""" def __init__(self) -> None: """inits MainWindow class configuring parameters of MainWindow class and inherits from QtWidget.QMainWindow loads .ui file sets up file and directory path vars, inits click events(menuebar, coboboxes, btns) and shows gui the first time Returns: None""" super(MainWindowUi, self).__init__() self.setWindowTitle("It_Hilfe") self.resize(820, 450) self.setWindowIcon(QIcon("./data/favicon2.png")) self.setMinimumSize(700, 250) self.file_path = None self.dir = None self.last_open_file_path = None self.last_open_file_dir = None self.initial_theme = None self.registered_devices = {} self.user_config_file = "./data/user_config.json" # setup stackedwidget self.stacked_widget = QStackedWidget() self.setCentralWidget(self.stacked_widget) self.setup_menubar() self.setup_p_view() self.setup_p_register() self.setup_p_create() self.setup_p_preferences() self.setup_signals() self.font = QFont() self.font.setPointSize(9) self.validate(self.set_user_preferences, file_path=self.user_config_file, schema=validate_json.ItHilfeUserPreferencesSchema, forbidden=[""]) # setup statusbar self.statusbar = self.statusBar() self.stacked_widget.setCurrentWidget(self.p_view) def setup_menubar(self) -> None: """inits menubar Returns: None""" self.menu_Bar = self.menuBar() menu_file = self.menu_Bar.addMenu("file") self.action_open = QAction("open") self.action_save = QAction("save") self.action_new = QAction("new") self.action_print = QAction("print") self.action_preferences = QAction("preferences") self.action_hide_menu_bar = QAction("hide menubar") self.action_print.setShortcut(QKeySequence("Ctrl+p")) self.action_open.setShortcut(QKeySequence("Ctrl+o")) self.action_save.setShortcut(QKeySequence("Ctrl+s")) self.action_hide_menu_bar.setShortcut(QKeySequence("Ctrl+h")) self.action_hide_menu_bar.setIcon(QIcon("./data/show_hide.ico")) self.action_print.setIcon(QIcon("./data/print2.ico")) self.action_open.setIcon(QIcon("./data/open.ico")) self.action_save.setIcon(QIcon("./data/save.ico")) self.action_new.setIcon(QIcon("./data/newfile.ico")) self.action_preferences.setIcon(QIcon("./data/preferences.ico")) menu_file.addAction(self.action_open) menu_file.addAction(self.action_save) menu_file.addAction(self.action_new) menu_file.addAction(self.action_print) menu_file.addAction(self.action_preferences) menu_edit = self.menu_Bar.addMenu("edit") self.action_register = QAction("register") self.action_register.setShortcut(QKeySequence("Ctrl+n")) self.action_register.setIcon(QIcon("./data/register.ico")) menu_edit.addAction(self.action_register) menu_view = self.menu_Bar.addMenu("view") menu_view.addAction(self.action_hide_menu_bar) def setup_p_view(self) -> None: """inits stacked widget page widget Returns: None""" self.p_view = QtWidgets.QWidget() self.stacked_widget.addWidget(self.p_view) self.model = QStandardItemModel(self.p_view) self.model.setHorizontalHeaderLabels(labels) self.filters = [] source_model = self.model for filter_num in range(7): filter = QSortFilterProxyModel() filter.setSourceModel(source_model) filter.setFilterKeyColumn(filter_num) source_model = filter self.filters.append(filter) delegate = ComboDelegate() self.table = QtWidgets.QTableView(self.p_view) self.table.setModel(self.filters[-1]) self.table.setItemDelegateForColumn(2, delegate) self.table.horizontalHeader().setStretchLastSection(True) self.table.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.header = FilterHeader(self.table) self.header.set_filter_boxes() self.header.setMaximumHeight(50) self.table.setHorizontalHeader(self.header) self.bt_burger = QPushButton(self.p_view) self.bt_burger.setIcon(QIcon("./data/menu2.svg")) self.bt_burger.setIconSize(QSize(30, 30)) self.bt_burger.setToolTip('slide out description') l_burger = QLabel("menu", self.p_view) self.bt_register_new = QPushButton(self.p_view) self.bt_register_new.setIcon(QIcon("./data/add.ico")) self.bt_register_new.setIconSize(QSize(30, 30)) self.bt_register_new.setToolTip("register new") l_register_new = QLabel("register new", self.p_view) self.bt_delete_column = QPushButton(self.p_view) self.bt_delete_column.setIcon(QIcon("./data/remove.ico")) self.bt_delete_column.setIconSize(QSize(30, 30)) self.bt_delete_column.setToolTip( "delete columns with min 1 cell selected") l_delete = QLabel("delete column", self.p_view) self.bt_hide_show_filter = QPushButton(self.p_view) self.bt_hide_show_filter.setIcon(QIcon("./data/show_hide.ico")) self.bt_hide_show_filter.setIconSize(QSize(30, 30)) self.bt_hide_show_filter.setToolTip("hide/show filter input") l_hide_show = QLabel("hide/show", self.p_view) self.left_btn_frame = QFrame(self.p_view) self.left_btn_frame.setMaximumWidth(40) self.left_btn_frame.setContentsMargins(0, 0, 0, 0) self.left_menu_frame = QFrame(self.p_view) self.left_menu_frame.setMaximumWidth(0) self.left_menu_frame.setContentsMargins(0, 0, 0, 0) p_view_layout2 = QtWidgets.QVBoxLayout(self.left_btn_frame) p_view_layout2.addWidget(self.bt_burger) p_view_layout2.addWidget(self.bt_register_new) p_view_layout2.addWidget(self.bt_delete_column) p_view_layout2.addWidget(self.bt_hide_show_filter) p_view_layout2.setAlignment(Qt.AlignTop) p_view_layout2.setContentsMargins(0, 0, 0, 0) self.p_view_layout3 = QtWidgets.QVBoxLayout(self.left_menu_frame) self.p_view_layout3.addWidget(l_burger) self.p_view_layout3.addWidget(l_register_new) self.p_view_layout3.addWidget(l_delete) self.p_view_layout3.addWidget(l_hide_show) self.p_view_layout3.setAlignment(Qt.AlignTop | Qt.AlignCenter) self.p_view_layout3.setContentsMargins(0, 0, 0, 0) self.p_view_layout3.setSpacing(25) p_view_layout = QHBoxLayout(self.p_view) p_view_layout.setContentsMargins(0, 0, 0, 0) p_view_layout.addWidget(self.left_btn_frame) p_view_layout.addWidget(self.left_menu_frame) p_view_layout.addWidget(self.table) self.p_view.setLayout(p_view_layout) self.p_view.addAction(self.action_open) self.p_view.addAction(self.action_save) self.p_view.addAction(self.action_new) self.p_view.addAction(self.action_print) self.p_view.addAction(self.action_register) self.p_view.addAction(self.action_hide_menu_bar) def setup_p_register(self) -> None: """inits stacked widget page widgets Returns: None""" self.p_register = QtWidgets.QWidget() self.stacked_widget.addWidget(self.p_register) l_user = QtWidgets.QLabel("Username", self.p_register) self.in_username = QtWidgets.QLineEdit(self.p_register) l_devicename = QtWidgets.QLabel("Devicename", self.p_register) self.in_devicename = QtWidgets.QLineEdit(self.p_register) l_devicetype = QtWidgets.QLabel("DeviceType", self.p_register) self.in_combobox_devicetype = QtWidgets.QComboBox(self.p_register) l_os = QtWidgets.QLabel("OS", self.p_register) self.in_combobox_os = QtWidgets.QComboBox(self.p_register) l_comment = QtWidgets.QLabel("Comment", self.p_register) self.in_comment = QtWidgets.QTextEdit(self.p_register) self.bt_enter_register = QPushButton("register", self.p_register) self.bt_cancel_register = QPushButton("cancel", self.p_register) p_register_layout = QtWidgets.QVBoxLayout(self.p_register) p_register_layout.addWidget(l_user) p_register_layout.addWidget(self.in_username) p_register_layout.addWidget(l_devicename) p_register_layout.addWidget(self.in_devicename) p_register_layout.addWidget(l_devicetype) p_register_layout.addWidget(self.in_combobox_devicetype) p_register_layout.addWidget(l_os) p_register_layout.addWidget(self.in_combobox_os) p_register_layout.addWidget(l_comment) p_register_layout.addWidget(self.in_comment) p_register_layout.addWidget(self.bt_enter_register) p_register_layout.addWidget(self.bt_cancel_register) def setup_p_create(self) -> None: """inits stacked widget page widget Returns: None""" self.p_create = QtWidgets.QWidget() self.stacked_widget.addWidget(self.p_create) l_new_filepath = QtWidgets.QLabel("new filepath", self.p_create) self.bt_mod_new_path = QPushButton("mod filepath", self.p_create) self.in_new_filepath = QtWidgets.QLineEdit(self.p_create) l_new_filename = QtWidgets.QLabel("new filename", self.p_create) self.in_new_filename = QtWidgets.QLineEdit(self.p_create) self.bt_create = QPushButton("create", self.p_create) self.bt_cancel_create = QPushButton("cancel", self.p_create) p_create_layout = QtWidgets.QVBoxLayout(self.p_create) p_create_layout.addWidget(l_new_filepath) p_create_layout.addWidget(self.in_new_filepath) p_create_layout.addWidget(l_new_filename) p_create_layout.addWidget(self.in_new_filename) p_create_layout.addStretch(100) p_create_layout.addWidget(self.bt_mod_new_path) p_create_layout.addWidget(self.bt_create) p_create_layout.addWidget(self.bt_cancel_create) def setup_p_preferences(self) -> None: """inits setup_p_preferences stacked widget page widget Returns: None""" self.p_preferences = QWidget() self.p_preferences.resize(500, 250) self.p_preferences.setWindowTitle("preferences") self.list_Widget = QListWidget(self.p_preferences) self.list_Widget.addItems(["appearance", "about"]) self.list_Widget.setMaximumWidth(100) self.stacked_widget_preferences = QStackedWidget(self.p_preferences) # setup appearance self.apperence_widget = QWidget() self.stacked_widget_preferences.addWidget(self.apperence_widget) self.in_combo_themes = QComboBox(self.apperence_widget) self.in_combo_themes.addItems(["dark_theme", "light_theme"]) self.in_combo_theme_initial = QComboBox(self.apperence_widget) self.in_combo_theme_initial.addItems(["dark_theme", "light_theme"]) self.text_size_slider = QSlider(QtCore.Qt.Orientation.Horizontal, self.apperence_widget) self.text_size_slider.setTickPosition(QSlider.TickPosition.TicksAbove) self.text_size_slider.setMaximum(15) self.text_size_slider.setMinimum(8) stacked_widget_preferences_layout = QGridLayout(self.apperence_widget) stacked_widget_preferences_layout.setAlignment(QtCore.Qt.AlignTop) stacked_widget_preferences_layout.addWidget(QLabel("theme"), 0, 0) stacked_widget_preferences_layout.addWidget(self.in_combo_themes, 0, 1) stacked_widget_preferences_layout.addWidget(QLabel("initial theme"), 1, 0) stacked_widget_preferences_layout.addWidget( self.in_combo_theme_initial, 1, 1) stacked_widget_preferences_layout.addWidget(QLabel("Fontsize"), 2, 0) stacked_widget_preferences_layout.addWidget(self.text_size_slider, 2, 1) self.about_widget = QWidget() self.stacked_widget_preferences.addWidget(self.about_widget) about_text_edit = QTextEdit(self.about_widget) about_text_edit.setText( "developed by Maurice Jarck\nwith kind support from Shuai Lou\n07.2020-04.2021" ) about_text_edit.setEnabled(False) stacked_widget_about_layout = QGridLayout(self.about_widget) stacked_widget_about_layout.addWidget(about_text_edit) p_apperance_layout = QHBoxLayout(self.p_preferences) p_apperance_layout.addWidget(self.list_Widget) p_apperance_layout.addWidget(self.stacked_widget_preferences) def setup_signals(self) -> None: """connects signals Returns: None""" # header for filter, editor in zip(self.filters, self.header.editors): editor.textChanged.connect(filter.setFilterRegExp) # line edit self.in_new_filename.returnPressed.connect(lambda: self.validate( self.new, line_edit_list=[self.in_new_filepath, self.in_new_filename], data=False)) # comboboxes self.in_combobox_devicetype.addItems( ["choose here"] + [x.__name__ for x in valid_devices]) self.in_combobox_devicetype.currentIndexChanged.connect( lambda: self.update_combobox( self.in_combobox_os, valid_devices[ self.in_combobox_devicetype.currentIndex() - 1].expected_OS )) self.in_combo_themes.currentIndexChanged.connect( lambda: self.change_theme(self.in_combo_themes.currentText())) self.in_combo_theme_initial.currentTextChanged.connect(lambda: setattr( self, "initial_theme", self.in_combo_theme_initial.currentText())) # btns self.bt_delete_column.clicked.connect(self.delete) # self.bt_hide_show_filter.clicked.connect(lambda: self.toggle_hide_show_ani(37, 47, "height", self.header, b"maximumHeight")) self.bt_hide_show_filter.clicked.connect(self.header.hide_show) # self.bt_hide_show_filter.clicked.connect(lambda: self.toggle_hide_show_ani(30, 44, "height", self.header, b"maximumHeight")) self.bt_register_new.clicked.connect( lambda: self.stacked_widget.setCurrentWidget(self.p_register)) self.bt_enter_register.clicked.connect(lambda: self.validate( self.register, line_edit_list=[self.in_username, self.in_devicename], combo_box_list=[self.in_combobox_devicetype, self.in_combobox_os], forbidden=list(self.registered_devices.keys()), checkfname=True)) self.bt_create.clicked.connect(lambda: self.validate( self.new, line_edit_list=[self.in_new_filepath, self.in_new_filename], data=False)) self.bt_mod_new_path.clicked.connect(lambda: self.new(True)) self.bt_burger.clicked.connect(lambda: self.toggle_hide_show_ani( 0, 66, "width", self.left_menu_frame, b"maximumWidth", )) # menu bar self.action_register.triggered.connect( lambda: self.stacked_widget.setCurrentWidget(self.p_register)) self.action_open.triggered.connect(self.get_open_file_path) self.action_save.triggered.connect(self.save) self.action_new.triggered.connect(lambda: self.new(True)) self.action_print.triggered.connect( lambda: self.validate(self.print, data=False, checkfname=True)) self.action_hide_menu_bar.triggered.connect( lambda: self.toggle_hide_show(self.menu_Bar)) self.action_preferences.triggered.connect(self.p_preferences.show) # cancel self.bt_cancel_register.clicked.connect(lambda: self.cancel([ self.in_username, self.in_devicename, self.in_combobox_os, self. in_comment ])) # list widget self.list_Widget.currentRowChanged.connect( lambda: self.stacked_widget_preferences.setCurrentIndex( self.list_Widget.currentIndex().row())) # slider self.text_size_slider.sliderMoved.connect( lambda: self.change_font_size(self.text_size_slider.value())) # self.text_size_slider.sliderMoved.connect(lambda: print(self.text_size_slider.value())) def change_theme(self, theme) -> None: """changes theme according to combobox selection Returns: None""" with open(f"./data/{theme}.css", "r") as file: stylesheed = " ".join(file.readlines()) self.setStyleSheet(stylesheed) self.p_preferences.setStyleSheet(stylesheed) if self.in_combo_themes.currentText() == "dark_theme": self.left_btn_frame.setStyleSheet( u"background: #455364; border: 0px solid;") self.p_view_layout3.setSpacing(30) else: self.left_btn_frame.setStyleSheet( u"background: #ADADAD; border: 0px solid;") self.p_view_layout3.setSpacing(25) return self.in_combo_themes.currentText() def toggle_hide_show_ani(self, collapsed_val: int, expanded_val: int, actual: str, to_animate, property: bytes): """interpolates over a defined range of vales and sets it to a given property of a given widget""" if getattr(to_animate, actual)() == expanded_val: destination = collapsed_val else: destination = expanded_val print(getattr(to_animate, actual)(), destination) self.ani = QPropertyAnimation(to_animate, property) self.ani.setDuration(300) self.ani.setStartValue(getattr(to_animate, actual)()) self.ani.setEndValue(destination) self.ani.setEasingCurve(QEasingCurve.Linear) self.ani.start() def toggle_hide_show(self, widget: QWidget) -> None: """toggles visibiliy of a given widget Arg: widget: widget which is aimed to be hidden or shown Returs: None""" if widget.isVisible(): widget.hide() else: widget.show() def reopen_last_file(self) -> None: """asks for reopening of the last opened file""" if self.last_open_file_path != "" or self.last_open_file_path is not None: reopen_dialog = QMessageBox.question( self.p_view, "reopen last file?", "Do you want to reopen the last edited file?", QMessageBox.Yes | QMessageBox.No) if reopen_dialog == QMessageBox.Yes: self.file_path = self.last_open_file_path self.load() def change_font_size(self, size: int) -> None: """changes all font sizes""" self.font.setPointSize(size) self.menu_Bar.setFont(self.font) self.header.setFont(self.font) self.table.setFont(self.font) self.p_preferences.setFont(self.font) def set_user_preferences(self) -> None: """Reads user_config file and sets its propertys""" with open(self.user_config_file, "r") as config_file: data = dict(json.load(config_file)) self.last_open_file_path = data["last_open_file_path"] self.initial_theme = data['initial_theme'] self.change_font_size(data['font_size']) self.text_size_slider.setValue(data['font_size']) self.in_combo_theme_initial.setCurrentText(self.initial_theme) self.in_combo_themes.setCurrentText(self.initial_theme) with open(f"./data/{self.initial_theme}.css") as file: style_sheed = " ".join(file.readlines()) self.setStyleSheet(style_sheed) self.p_preferences.setStyleSheet(style_sheed) self.bt_burger.setStyleSheet( "border: 0px solid; background: transparent;") self.bt_register_new.setStyleSheet( "border: 0px solid; background: transparent;") self.bt_delete_column.setStyleSheet( "border: 0px solid; background: transparent;") self.bt_hide_show_filter.setStyleSheet( "border: 0px solid; background: transparent;") self.left_menu_frame.setStyleSheet(u" border: 0px solid;") if self.initial_theme == "dark_theme": self.left_btn_frame.setStyleSheet( u"background: #455364; border: 0px solid;") else: self.left_btn_frame.setStyleSheet( u"background: #ADADAD; border: 0px solid;") def cancel(self, widgets: list) -> None: """click event for all cancel buttons shows fist page in stacked widget and clears all widgets in widgets Args: widgets: defines list containing widgets to clear, only widgets with method .clear() are possible Returns: None""" for widget in widgets: widget.clear() self.stacked_widget.setCurrentWidget(self.p_view) def update_combobox(self, box, data: list) -> None: """ clears combo box updates combobox so that old content not needed any more isnt displayed and adds 'choose here' dummy to ensure an index change will be made (updating next box depends on index change) Args: box: instance of pyqt5.QtWidgets.qComboBox data: data supposed to be inserted into combobox Returns: None""" box.clear() box.addItems(["choose here"] + data) def validate(self, command, file_path: str = None, schema=None, line_edit_list: list = None, combo_box_list: list = None, data=None, forbidden: list = None, checkfname: bool = None) -> None: """validates user input Args: command: function to be called after vailidation process if finished line_edit_list: contents pyqt5.QtWidgets.QlineEdit instances to be checked if empty or current text in forbidden or not in allowed combo_box_list: contents pyqt5.QtWidgets.qComboBox instances to be checked if nothing selected data: data to be passed into command function if needed forbidden: houses keys which are not allowed to be entered checkfname: check weather an file path exists or not Returns: None""" fails = 0 if line_edit_list is not None: for x in line_edit_list: if x.text() == "": x.setText("fill all fields") fails += 1 if forbidden is not None and x.text() in forbidden: x.setText("in forbidden!!") fails += 1 if combo_box_list is not None: for combobox in combo_box_list: if combobox.currentText() == "": self.statusbar.showMessage("all comboboxes must be filled") fails += 1 if checkfname is True and self.file_path is None: self.statusbar.showMessage( "no file path specified, visit Ctrl+o or menuebar/edit/open to fix" ) fails += 1 if file_path is not None: if forbidden is not None and file_path in forbidden: fails += 1 self.statusbar.showMessage("select a file to continue") else: try: validate_json.validate(file_path, schema) except ValidationError as e: self.msg_box = QtWidgets.QMessageBox.critical( self, "validation failed", f"Invalid Json file, problem in: {e.messages}") fails += 1 if fails == 0: if data is None: command() else: command(data) else: message = f"problem\ncommand: {command.__name__}\nfails: {fails}" print(message) return message def register(self) -> None: """registers a new device and saves Returns: None""" logic.register(devname=self.in_devicename.text(), devtype=[ device for device in valid_devices if device.__name__ == self.in_combobox_devicetype.currentText() ].pop(), username=self.in_username.text(), os=self.in_combobox_os.currentText(), comment=self.in_comment.toPlainText(), datetime=str(datetime.datetime.now()), registered_devices=self.registered_devices) new_values = [ self.in_devicename.text(), self.in_username.text(), self.in_combobox_os.currentText(), [ device.__name__ for device in valid_devices if device.__name__ == self.in_combobox_devicetype.currentText() ].pop(), self.in_comment.toPlainText(), str(datetime.datetime.now()) ] row = [QStandardItem(str(item)) for item in new_values] self.model.appendRow(row) self.stacked_widget.setCurrentWidget(self.p_view) self.in_devicename.clear() self.in_username.clear() self.in_combobox_os.clear() self.in_comment.clear() self.save() def delete(self) -> None: """deletes all rows associated with min 1 slected cell Returns: None""" rows = sorted(set(index.row() for index in self.table.selectedIndexes()), reverse=True) qb = QMessageBox() answ = qb.question(self, 'delete rows', f"Are you sure to delete {rows} rows?", qb.Yes | qb.No) if answ == qb.Yes: for row in rows: self.registered_devices.pop( str(self.model.index(row, 0).data())) self.model.removeRow(row) qb.information(self, 'notification', f"deleted {rows} row") else: qb.information(self, 'notification', "Nothing Changed") self.save() def get_open_file_path(self) -> None: """gets file-path and set it to self.file_path, extra step for json validation Returns: None""" self.file_path = \ QFileDialog.getOpenFileName(self, "open file", f"{self.last_open_file_dir or 'c://'}", "json files (*json)")[0] self.validate(command=self.load, file_path=self.file_path, schema=validate_json.ItHilfeDataSchema, forbidden=[""]) def load(self) -> None: """opens json file and loads its content into registered devices Returns: None""" self.model.clear() self.registered_devices.clear() with open(self.file_path, "r") as file: data = dict(json.load(file)) devices = data["devices"].values() self.last_open_file_dir = data["last_open_file_dir"] for value in devices: row = [] for i, item in enumerate(value): cell = QStandardItem(str(item)) row.append(cell) if i == 0 or i == 3 or i == 5: cell.setEditable(False) self.model.appendRow(row) new = [x for x in valid_devices if x.__name__ == value[3]].pop(0)(value[0], value[1], value[4], value[5]) new.OS = value[2] self.registered_devices[value[0]] = new self.model.setHorizontalHeaderLabels(labels) self.statusbar.showMessage("") # auto complete for a in range(len(self.header.editors)): completer = QCompleter([ self.model.data(self.model.index(x, a)) for x in range(self.model.rowCount()) ]) completer.setCompletionMode(QCompleter.InlineCompletion) self.header.editors[a].setCompleter(completer) def save(self) -> None: """saves content fo self.registered_devices into specified json file Returns: None""" if not self.file_path: self.statusbar.showMessage( "no file path set all changes get lost if closed") else: with open( self.file_path, 'w', ) as file: devices = { k: [ v.name, v.user, v.OS, v.__class__.__name__, v.comment, v.datetime ] for (k, v) in enumerate(self.registered_devices.values()) } last_open_file_dir = "/".join(self.file_path.split("/")[:-1]) resulting_dict = { "devices": devices, "last_open_file_dir": last_open_file_dir } json.dump(resulting_dict, file) self.statusbar.showMessage("saved file") with open(self.user_config_file, "w") as user_preferences_file: json.dump( { "last_open_file_path": self.last_open_file_path, "initial_theme": self.initial_theme, "font_size": self.text_size_slider.value() }, user_preferences_file) def new(self, stage: bool, test: bool = False) -> None: """creates new csv file to save into stage is True: set filepath stage is False: set new name, save Args: stage: determines a which stage to execute this function Returns: None""" if stage is True: if not test: self.dir = QFileDialog.getExistingDirectory( self, "select a folder", "c://") self.stacked_widget.setCurrentWidget(self.p_create) self.in_new_filepath.setText(self.dir) self.registered_devices.clear() else: self.file_path = self.dir + f"/{self.in_new_filename.text()}.json" self.save() self.stacked_widget.setCurrentWidget(self.p_view) def print(self, test: bool) -> None: """setup and preview pViewTable for paper printing Returns: None""" with open(self.file_path) as f: self.data = json.dumps(dict(json.load(f)), sort_keys=True, indent=6, separators=(".", "=")) self.document = QtWidgets.QTextEdit() self.document.setText(self.data) if not test: printer = QPrinter() previewDialog = QPrintPreviewDialog(printer, self) previewDialog.paintRequested.connect( lambda: self.document.print_(printer)) previewDialog.exec_()
class SearchBarEditor(QTableView): """A Google-like search bar, implemented as a QTableView with a _CustomLineEditDelegate in the first row. """ data_committed = Signal() def __init__(self, parent, tutor=None): """Initializes instance. Args: parent (QWidget): parent widget tutor (QWidget, NoneType): another widget used for positioning. """ super().__init__(parent) self._tutor = tutor self._base_size = QSize() self._base_offset = QPoint() self._original_text = None self._orig_pos = None self.first_index = QModelIndex() self.model = QStandardItemModel(self) self.proxy_model = QSortFilterProxyModel(self) self.proxy_model.setSourceModel(self.model) self.proxy_model.filterAcceptsRow = self._proxy_model_filter_accepts_row self.setModel(self.proxy_model) self.verticalHeader().hide() self.horizontalHeader().hide() self.setShowGrid(False) self.setMouseTracking(True) self.setTabKeyNavigation(False) delegate = _CustomLineEditDelegate(self) delegate.text_edited.connect(self._handle_delegate_text_edited) self.setItemDelegateForRow(0, delegate) def set_data(self, current, items): """Populates model. Args: current (str) items (Sequence(str)) """ item_list = [QStandardItem(current)] for item in items: qitem = QStandardItem(item) item_list.append(qitem) qitem.setFlags(~Qt.ItemIsEditable) self.model.invisibleRootItem().appendRows(item_list) self.first_index = self.proxy_model.mapFromSource( self.model.index(0, 0)) def set_base_size(self, size): self._base_size = size def set_base_offset(self, offset): self._base_offset = offset def update_geometry(self): """Updates geometry. """ self.horizontalHeader().setDefaultSectionSize(self._base_size.width()) self.verticalHeader().setDefaultSectionSize(self._base_size.height()) self._orig_pos = self.pos() + self._base_offset if self._tutor: self._orig_pos += self._tutor.mapTo(self.parent(), self._tutor.rect().topLeft()) self.refit() def refit(self): self.move(self._orig_pos) table_height = self.verticalHeader().length() size = QSize(self._base_size.width(), table_height + 2).boundedTo(self.parent().size()) self.resize(size) # Adjust position if widget is outside parent's limits bottom_right = self.mapToGlobal(self.rect().bottomRight()) parent_bottom_right = self.parent().mapToGlobal( self.parent().rect().bottomRight()) x_offset = max(0, bottom_right.x() - parent_bottom_right.x()) y_offset = max(0, bottom_right.y() - parent_bottom_right.y()) self.move(self.pos() - QPoint(x_offset, y_offset)) def data(self): return self.first_index.data(Qt.EditRole) @Slot("QString") def _handle_delegate_text_edited(self, text): """Filters model as the first row is being edited.""" self._original_text = text self.proxy_model.setFilterRegExp("^" + text) self.proxy_model.setData(self.first_index, text) self.refit() def _proxy_model_filter_accepts_row(self, source_row, source_parent): """Always accept first row. """ if source_row == 0: return True return QSortFilterProxyModel.filterAcceptsRow(self.proxy_model, source_row, source_parent) def keyPressEvent(self, event): """Sets data from current index into first index as the user navigates through the table using the up and down keys. """ super().keyPressEvent(event) event.accept( ) # Important to avoid unhandled behavior when trying to navigate outside view limits # Initialize original text. TODO: Is there a better place for this? if self._original_text is None: self.proxy_model.setData(self.first_index, event.text()) self._handle_delegate_text_edited(event.text()) # Set data from current index in model if event.key() in (Qt.Key_Up, Qt.Key_Down): current = self.currentIndex() if current.row() == 0: self.proxy_model.setData(self.first_index, self._original_text) else: self.proxy_model.setData(self.first_index, current.data()) def currentChanged(self, current, previous): super().currentChanged(current, previous) self.edit_first_index() def edit_first_index(self): """Edits first index if valid and not already being edited. """ if not self.first_index.isValid(): return if self.isPersistentEditorOpen(self.first_index): return self.edit(self.first_index) def mouseMoveEvent(self, event): """Sets the current index to the one hovered by the mouse.""" if not self.currentIndex().isValid(): return index = self.indexAt(event.pos()) if index.row() == 0: return self.setCurrentIndex(index) def mousePressEvent(self, event): """Commits data.""" index = self.indexAt(event.pos()) if index.row() == 0: return self.proxy_model.setData(self.first_index, index.data(Qt.EditRole)) self.data_committed.emit()
class MyWidget(QWidget): def __init__(self, parent=None): super(MyWidget, self).__init__(parent) self.view = View() self.grid = DateTimeGrid() self.slider = QSlider(self) self.model = QStandardItemModel(self) self.cmodel = ConstraintModel() ## proxyModel.setSourceModel( &model ); for i in range(0, 10): item = MyStandardItem("Multi Item " + str(i)) for j in range(0, 20, 3): item.appendRow([ MyStandardItem("Item " + str(j)), MyStandardItem(KDGantt.TypeTask), MyStandardItem(QDateTime.currentDateTime().addDays(j), KDGantt.StartTimeRole), MyStandardItem( QDateTime.currentDateTime().addDays(j + 1 + i / 7), KDGantt.EndTimeRole), MyStandardItem(50) ]) item.appendRow([ MyStandardItem("Event"), MyStandardItem(KDGantt.TypeEvent), MyStandardItem(QDateTime.currentDateTime(), KDGantt.StartTimeRole), MyStandardItem(QDateTime(), KDGantt.EndTimeRole), MyStandardItem("") ]) self.model.appendRow([ item, MyStandardItem(KDGantt.TypeMulti), MyStandardItem(""), MyStandardItem(""), MyStandardItem("") ]) pidx = self.model.index(0, 0) pidx = self.model.index(0, 0, pidx) self.cmodel.addConstraint( Constraint(self.model.index(0, 0, pidx), self.model.index(1, 0, pidx))) self.cmodel.addConstraint( Constraint(self.model.index(1, 0, pidx), self.model.index(0, 0, pidx))) self.cmodel.addConstraint( Constraint(self.model.index(1, 0, pidx), self.model.index(10, 0, pidx))) self.cmodel.addConstraint( Constraint(self.model.index(3, 0, pidx), self.model.index(5, 0, pidx))) self.cmodel.addConstraint( Constraint(self.model.index(7, 0, pidx), self.model.index(4, 0, pidx))) self.slider.setOrientation(Qt.Horizontal) self.slider.setRange(1, 1000) self.slider.setValue(100) l = QVBoxLayout(self) l.addWidget(self.view) l.addWidget(self.slider) self.grid.setStartDateTime(QDateTime.currentDateTime().addDays(-3)) self.grid.setDayWidth(100) self.grid.setFreeDays([Qt.Saturday, Qt.Sunday]) self.grid.setFreeDaysBrush(QBrush(Qt.red)) lv = MyListView(self) self.view.setLeftView(lv) self.view.setRowController( ListViewRowController(lv, self.view.ganttProxyModel())) self.view.setGrid(self.grid) self.view.setModel(self.model) ##view.setConstraintModel( &cmodel ); self.slider.valueChanged.connect(self.slotZoom) def slotZoom(self, z): self.grid.setDayWidth(z)
class StatusModel(BaseModel): def __init__(self, parent=None): super().__init__(parent) self._calculator = None # Create the status items chi_item = StatusItem('chiSq', title='Current \u03c7\u00b2', additionalData=1) chi_item.setReturn(True) chi_item.title = 'Previous \u03c7\u00b2' chi_item.setReturn(False) self._interestedList = StatusList([ chi_item, StatusItem('numPars', title='Fit parameters', additionalData=1), StatusItem('numData', title='Experiments', additionalData=0), StatusItem('numPhases', title='Phases', additionalData=0) ]) # minor properties self._first_role = Qt.UserRole + 1 self._statusBarModel = QStandardItemModel() self._chartDisplayModel = QStandardItemModel() # set role names self._role_names_list = ['label', 'value'] self._roles_list = [] self._roles_dict = {'status': {}, 'plot': {}} self._setRolesListAndDict() self._statusBarModel.setItemRoleNames(self._roles_dict['status']) self._chartDisplayModel.setItemRoleNames(self._roles_dict['plot']) self._log = logger.getLogger(self.__class__.__module__) def _setRolesListAndDict(self): """...""" offset = 100 for i, role_name in enumerate(self._role_names_list): display_role = self._first_role + i self._roles_dict['status'][display_role] = role_name.encode() self._roles_list.append(display_role) self._roles_dict['plot'][self._first_role + i + offset] = role_name.encode() def _setModelsFromProjectDict(self): """Create the initial data list with structure for GUI fitables table.""" _ = self._statusBarModel.removeColumns( 0, self._statusBarModel.columnCount()) _ = self._chartDisplayModel.removeColumns( 0, self._chartDisplayModel.columnCount()) column_status_bar = [] column_chart_display = [] self._updateStatusList() for interest in self._interestedList: # Add the status bar item column_status_bar.append( self._makeItem(interest, self._roles_dict['status'].items())) # Does this need to added to the plot? if interest.additionalData == 1: column_chart_display.append( self._makeItem(interest, self._roles_dict['plot'].items())) # Does this item also have previous values which need to be shown? if interest.hasPrevious: interest.setReturn(True) if interest.value is not None: column_status_bar.append( self._makeItem(interest, self._roles_dict['status'].items())) interest.setReturn(False) # Set the models self._statusBarModel.appendColumn(column_status_bar) self._chartDisplayModel.appendColumn(column_chart_display) self._statusBarModel.dataChanged.emit( self._statusBarModel.index(0, 0), self._statusBarModel.index(self._statusBarModel.rowCount() - 1, self._statusBarModel.columnCount() - 1), self._roles_list) def _updateStatusList(self): """Update the values of the Item List""" project_dict = self._project_dict # Set chi squared try: self._interestedList.setItemValue( 'chiSq', round(float(project_dict['info']['chi_squared'].value), 2)) except KeyError: self._interestedList.setItemValue('chiSq', 0) # Get number of parameters num_pars = get_num_refine_pars(project_dict.asDict()) # Set the other parameters. self._interestedList.setItemValue('numPars', num_pars) self._interestedList.setItemValue('numPhases', len(project_dict['phases'])) self._interestedList.setItemValue('numData', len(project_dict['experiments'])) def returnStatusBarModel(self): """Return the status bar model.""" return self._statusBarModel def onRefinementDone(self): """Define what to do when the refinement done is triggered""" self._setModelsFromProjectDict() self._chartDisplayModel.layoutChanged.emit() def returnChartModel(self): """Return the chart model""" return self._chartDisplayModel @staticmethod def _makeItem(this_interest, these_items): """Make an item. This can be a plot or status bar""" item = QStandardItem() for role, role_name_bytes in these_items: role_name = role_name_bytes.decode() if role_name == 'label': value = this_interest.title elif role_name == 'value': value = this_interest.value else: continue item.setData(value, role) return item
class MainWindow(QMainWindow): _tr = QCoreApplication.translate def __init__(self, parent=None): super().__init__(parent) # 调用父类构造函数,创建窗体 self.__scene = None # 创建QGraphicsScene self.__view = None # 创建图形视图组件 self.ui = Ui_MainWindow() # 创建UI对象 self.ui.setupUi(self) # 构造UI界面 self.operatorFile = OperatorFile(self) self.__translator = None title = self.tr("基于Python的图的绘制及相关概念的可视化展示") self.setWindowTitle(title) self.ui.nodeDetails.setEnabled(False) self.ui.edgeDetails.setEnabled(False) self.ui.actionSave.setEnabled(False) self.edgeModel = QStandardItemModel(5, 5, self) self.edgeSelectionModel = QItemSelectionModel(self.edgeModel) self.edgeModel.dataChanged.connect(self.do_updateEdgeWeight) self.nodeModel = QStandardItemModel(5, 4, self) self.nodeSelectionModel = QItemSelectionModel(self.nodeModel) self.nodeModel.dataChanged.connect(self.do_updateNodeWeight) self.spinWeight = WeightSpinDelegate(0, 200, 1, self) self.ui.tabWidget.setVisible(False) self.ui.tabWidget.clear() self.ui.tabWidget.setTabsClosable(True) self.ui.tabWidget.setDocumentMode(True) self.setCentralWidget(self.ui.tabWidget) self.setAutoFillBackground(True) self.__buildStatusBar() # 构造状态栏 self.__buildUndoCommand() # 初始化撤销重做系统 self.__initModeMenu() self.__lastColumnFlag = Qt.NoItemFlags self.iniGraphicsSystem() self.__ItemId = 0 # 绘图项自定义数据的key self.__ItemDesc = 1 # 绘图项自定义数据的key self.__nodeNum = 0 # 结点的序号 self.__edgeNum = 0 # 边的序号 self.__textNum = 0 self.lastColumnFlags = (Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) self.__graph = Graph() ## ==============自定义功能函数============ def nodeNum(self): return self.__nodeNum def edgeNum(self): return self.__edgeNum def scene(self): self.viewAndScene() return self.__scene def view(self): self.viewAndScene() return self.__view def graph(self): return self.__graph def __buildStatusBar(self): ##构造状态栏 self.__labViewCord = QLabel(self._tr("MainWindow", "视图坐标:")) self.__labViewCord.setMinimumWidth(150) self.ui.statusbar.addWidget(self.__labViewCord) self.__labSceneCord = QLabel(self._tr("MainWindow", "场景坐标:")) self.__labSceneCord.setMinimumWidth(150) self.ui.statusbar.addWidget(self.__labSceneCord) self.__labItemCord = QLabel(self._tr("MainWindow", "图元坐标:")) self.__labItemCord.setMinimumWidth(150) self.ui.statusbar.addWidget(self.__labItemCord) self.__labItemInfo = QLabel(self._tr("MainWindow", "图元信息: ")) self.ui.statusbar.addPermanentWidget(self.__labItemInfo) self.__labModeInfo = QLabel(self._tr("MainWindow", "有向图模式")) self.ui.statusbar.addPermanentWidget(self.__labModeInfo) def __buildUndoCommand(self): self.undoStack = QUndoStack() self.addAction(self.ui.actionUndo) self.addAction(self.ui.actionRedo) self.ui.undoView.setStack(self.undoStack) def __setItemProperties(self, item, desc): ##item是具体类型的QGraphicsItem self.__nodeNum = len(self.singleItems(BezierNode)) self.__edgeNum = len(self.singleItems(BezierEdge)) self.__textNum = len(self.singleItems(BezierText)) item.setFlag(QGraphicsItem.ItemIsFocusable) item.setFlag(QGraphicsItem.ItemIsMovable) item.setFlag(QGraphicsItem.ItemIsSelectable) item.setPos(-150 + randint(1, 200), -200 + randint(1, 200)) if type(item) is BezierNode: newNum = self.checkSort(self.__scene.uniqueIdList(BezierNode)) if newNum is not None: item.setData(self.__ItemId, newNum) item.textCp.setPlainText("V" + str(newNum)) else: item.setData(self.__ItemId, self.__nodeNum) item.textCp.setPlainText("V" + str(self.__nodeNum)) self.__nodeNum = 1 + self.__nodeNum elif type(item) is BezierEdge: newNum = self.checkSort(self.__scene.uniqueIdList(BezierEdge)) if newNum is not None: item.setData(self.__ItemId, newNum) item.textCp.setPlainText("e" + str(newNum)) else: item.setData(self.__ItemId, self.__edgeNum) item.textCp.setPlainText("e" + str(self.__edgeNum)) self.__edgeNum = 1 + self.__edgeNum elif type(item) is BezierText: newNum = self.checkSort(self.__scene.uniqueIdList(BezierText)) if newNum is not None: item.setData(self.__ItemId, newNum) else: item.setData(self.__ItemId, self.__textNum) self.__textNum = 1 + self.__textNum item.setData(self.__ItemDesc, desc) # 图件描述 self.__scene.addItem(item) self.__scene.clearSelection() item.setSelected(True) def __setBrushColor(self, item): ##设置填充颜色 color = item.brush().__color() color = QColorDialog.getColor(color, self, self._tr("MainWindow", "选择填充颜色")) if color.isValid(): item.setBrush(QBrush(color)) def __initFileMenu(self): self.ui.actionOpen.triggered.connect(self.do_open_file) self.ui.actionSave.triggered.connect(self.do_save_file) self.ui.actionQuit.triggered.connect(self.close) def __initModeMenu(self): modeMenuGroup = QActionGroup(self) modeMenuGroup.addAction(self.ui.actionDigraph_Mode) modeMenuGroup.addAction(self.ui.actionRedigraph_Mode) def __updateEdgeView(self): edges = self.singleItems(BezierEdge) if len(edges): self.ui.edgeDetails.setEnabled(True) else: return edgeColCount = 5 self.edgeModel.clear() edgeHeaderList = [ self._tr("MainWindow", 'ID'), self._tr("MainWindow", '始点'), self._tr("MainWindow", '终点'), self._tr("MainWindow", '坐标'), self._tr("MainWindow", '权重') ] self.edgeModel.setHorizontalHeaderLabels(edgeHeaderList) self.edgeSelectionModel.currentChanged.connect(self.do_curEdgeChanged) self.ui.edgeDetails.setModel(self.edgeModel) self.ui.edgeDetails.setSelectionModel(self.edgeSelectionModel) self.ui.edgeDetails.verticalHeader().setSectionResizeMode( QHeaderView.Fixed) self.ui.edgeDetails.horizontalHeader().setSectionResizeMode( QHeaderView.ResizeToContents) self.ui.edgeDetails.horizontalHeader().setSectionResizeMode( QHeaderView.ResizeToContents) self.ui.edgeDetails.setAlternatingRowColors(True) self.edgeModel.setRowCount(len(edges)) self.ui.edgeDetails.setItemDelegateForColumn(4, self.spinWeight) edges.reverse() for i in range(len(edges)): edge: BezierEdge = edges[i] sourceNode = f'V{edge.sourceNode.data(self.__ItemId)}' if edge.sourceNode else None destNode = f'V{edge.destNode.data(self.__ItemId)}' if edge.destNode else None strList = [ f"e{edge.data(self.__ItemId)}", sourceNode, destNode, f"x:{edge.pos().x()},y:{edge.pos().y()}", f"{edge.weight()}" ] for j in range(edgeColCount): item = QStandardItem(strList[j]) if j != edgeColCount - 1: item.setFlags(self.__lastColumnFlag) self.edgeModel.setItem(i, j, item) def __updateNodeView(self): nodes = self.singleItems(BezierNode) if len(nodes): self.ui.nodeDetails.setEnabled(True) else: return nodeColCount = 4 self.nodeModel.clear() nodeHeaderList = [ self._tr("MainWindow", 'ID'), self._tr("MainWindow", '边数'), self._tr("MainWindow", '坐标'), self._tr("MainWindow", '权重') ] if self.ui.actionDigraph_Mode.isChecked(): nodeHeaderList.append(self._tr("MainWindow", '出度')) nodeHeaderList.append(self._tr("MainWindow", '入度')) nodeColCount += 2 else: nodeHeaderList.append(self._tr("MainWindow", "度")) nodeColCount += 1 self.nodeModel.setHorizontalHeaderLabels(nodeHeaderList) self.nodeModel.setRowCount(len(nodes)) self.nodeSelectionModel.currentChanged.connect(self.do_curNodeChanged) self.ui.nodeDetails.setModel(self.nodeModel) self.ui.nodeDetails.setSelectionModel(self.nodeSelectionModel) self.ui.nodeDetails.verticalHeader().setSectionResizeMode( QHeaderView.Fixed) self.ui.nodeDetails.horizontalHeader().setSectionResizeMode( QHeaderView.ResizeToContents) self.ui.nodeDetails.setAlternatingRowColors(True) self.ui.nodeDetails.setItemDelegateForColumn(3, self.spinWeight) nodes.reverse() for i in range(len(nodes)): node: BezierNode = nodes[i] strList = [ f"V{node.data(self.__ItemId)}", str(len(node.bezierEdges)), f"x:{node.pos().x()},y:{node.pos().y()}", str(node.weight()) ] if self.ui.actionDigraph_Mode.isChecked(): strList.append( f'{node.degrees(self.ui.actionDigraph_Mode.isChecked())[1]}' ) strList.append( f'{node.degrees(self.ui.actionDigraph_Mode.isChecked())[0]}' ) else: strList.append( f'{node.degrees(self.ui.actionDigraph_Mode.isChecked())}') for j in range(nodeColCount): item = QStandardItem(strList[j]) if j != 3: item.setFlags(self.__lastColumnFlag) self.nodeModel.setItem(i, j, item) def iniGraphicsSystem(self, name=None): ##初始化 Graphics View系统 scene = GraphicsScene() # 创建QGraphicsScene view = GraphicsView(self, scene) # 创建图形视图组件 view.mouseMove.connect(self.do_mouseMove) # 鼠标移动 view.mouseClicked.connect(self.do_mouseClicked) # 左键按下 scene.itemMoveSignal.connect(self.do_shapeMoved) scene.itemLock.connect(self.do_nodeLock) scene.isHasItem.connect(self.do_checkIsHasItems) if name: title = name else: text = self.tr('未命名') title = f'{text}{self.ui.tabWidget.count()}' curIndex = self.ui.tabWidget.addTab(view, title) self.ui.tabWidget.setCurrentIndex(curIndex) self.ui.tabWidget.setVisible(True) ## 4个信号与槽函数的关联 # self.view.mouseDoubleClick.connect(self.do_mouseDoubleClick) # 鼠标双击 # self.view.keyPress.connect(self.do_keyPress) # 左键按下 def singleItems(self, className) -> list: self.viewAndScene() return self.__scene.singleItems(className) def connectGraph(self): self.__graph.setMode(self.ui.actionDigraph_Mode.isChecked()) items = self.__scene.uniqueItems() nodeList = [] edgeList = [] if not len(items): return for item in items: if type(item) is BezierNode: nodeList.append(item) elif type(item) is BezierEdge: edgeList.append(item) for node in nodeList: self.__graph.addVertex(node.data(self.__ItemId)) badEdgeList = [] for i in range(len(edgeList)): for edge in edgeList: edge: BezierEdge if edge.data(self.__ItemId) == i: if edge.sourceNode and edge.destNode: self.__graph.addEdge( edge.sourceNode.data(self.__ItemId), edge.destNode.data(self.__ItemId), edge.weight()) else: badEdgeList.append(edge) if len(badEdgeList) != 0: self.disconnectGraph() string = "" for x in range(len(badEdgeList)): demo = "、" if x == len(badEdgeList) - 1: demo = "" string = f'{string}e{badEdgeList[x].data(self.__ItemId)}{demo}' QMessageBox.warning( self, self._tr("MainWindow", "连接故障!"), self._tr("MainWindow", "警告,") + string + self._tr("MainWindow", "的连接不完整")) return False return True def disconnectGraph(self): self.__graph.clearAllData() def viewAndScene(self): if self.ui.tabWidget.count(): self.__view: GraphicsView = self.ui.tabWidget.currentWidget() self.__scene = self.__view.scene() def standardGraphData(self): mode = int(self.ui.actionDigraph_Mode.isChecked()) nodes = self.__scene.singleItems(BezierNode) edges = self.__scene.singleItems(BezierEdge) texts = self.__scene.singleItems(BezierText) nodeDataList = [] edgeDataList = [] textDataList = [] for node in nodes: node: BezierNode data = [ node.data(self.__ItemId), node.weight(), node.pos().x(), node.pos().y() ] nodeDataList.append(data) for edge in edges: edge: BezierEdge data = [edge.data(self.__ItemId)] if edge.sourceNode: data.append(edge.sourceNode.data(self.__ItemId)) else: data.append(-1) if edge.destNode: data.append(edge.destNode.data(self.__ItemId)) else: data.append(-1) data = data + [ edge.weight(), edge.beginCp.point().x(), edge.beginCp.point().y(), edge.edge1Cp.point().x(), edge.edge1Cp.point().y(), edge.edge2Cp.point().x(), edge.edge2Cp.point().y(), edge.endCp.point().x(), edge.endCp.point().y(), edge.scenePos().x(), edge.scenePos().y() ] edgeDataList.append(data) for text in texts: text: BezierText data = [ text.data(self.__ItemId), text.toPlainText(), text.scenePos().x(), text.scenePos().y() ] textDataList.append(data) nodeDataList.reverse() edgeDataList.reverse() textDataList.reverse() return [mode, nodeDataList, edgeDataList, textDataList] def reverseStandardData(self, excelData): graphName = excelData[0] mode = excelData[1] nodes = [] edges = [] texts = [] self.ui.actionDigraph_Mode.setChecked(bool(mode)) for nodeDetail in excelData[2]: node = BezierNode() node.textCp.setPlainText(f"V{nodeDetail[0]}") node.setData(self.__ItemId, nodeDetail[0]) nodeText = self._tr("MainWindow", "顶点") node.setData(self.__ItemDesc, nodeText) if len(nodeDetail) < 3: for i in range(2): intRandom = randint(-400, 400) nodeDetail.append(intRandom) node.setPos(QPointF(nodeDetail[2], nodeDetail[3])) node.weightCp.setPlainText(str(nodeDetail[1])) nodes.append(node) for edgeDetail in excelData[3]: edge = BezierEdge() edge.setData(self.__ItemId, edgeDetail[0]) edge.setData(self.__ItemDesc, "边") edge.textCp.setPlainText(f"e{edgeDetail[0]}") edge.weightCp.setPlainText(str(edgeDetail[3])) if len(edgeDetail) <= 4: for i in range(10): intRandom = randint(-400, 400) edgeDetail.append(intRandom) edge.setPos(QPointF(edgeDetail[12], edgeDetail[13])) if edgeDetail[1] >= 0: for node in nodes: node: BezierNode if node.data(self.__ItemId) == edgeDetail[1]: edge.setSourceNode(node) node.addBezierEdge(edge, ItemType.SourceType) line = QLineF(edge.mapFromScene(node.pos()), edge.edge1Cp.point()) length = line.length() edgeOffset = QPointF(line.dx() * 10 / length, line.dy() * 10 / length) source = edge.mapFromScene(node.pos()) + edgeOffset edge.setSpecialControlPoint(source, ItemType.SourceType) edge.beginCp.setVisible(False) else: edge.setSpecialControlPoint( QPointF(edgeDetail[4], edgeDetail[5]), ItemType.SourceType) if edgeDetail[2] >= 0: for node in nodes: node: BezierNode if node.data(self.__ItemId) == edgeDetail[2]: edge.setDestNode(node) node.addBezierEdge(edge, ItemType.DestType) line = QLineF(edge.mapFromScene(node.pos()), edge.edge2Cp.point()) length = line.length() edgeOffset = QPointF(line.dx() * 10 / length, line.dy() * 10 / length) if mode: dest = edge.mapFromScene( node.pos()) + edgeOffset * 2.3 else: dest = edge.mapFromScene(node.pos()) + edgeOffset edge.setSpecialControlPoint(dest, ItemType.DestType) edge.endCp.setVisible(False) else: edge.setSpecialControlPoint( QPointF(edgeDetail[10], edgeDetail[11]), ItemType.DestType) edge.setEdgeControlPoint(QPointF(edgeDetail[6], edgeDetail[7]), ItemType.SourceType) edge.setEdgeControlPoint(QPointF(edgeDetail[8], edgeDetail[9]), ItemType.DestType) edge.centerCp.setPoint(edge.updateCenterPos()) edges.append(edge) if len(excelData) <= 4: return [graphName, nodes + edges] for textDetail in excelData[4]: text = BezierText(str(textDetail[1])) text.setData(self.__ItemId, textDetail[0]) text.setData(self.__ItemDesc, "文本") text.setPos(textDetail[2], textDetail[3]) texts.append(text) return [graphName, nodes + edges + texts] def setTranslator(self, translator, language): self.__translator = translator if language == 'EN': self.ui.actionSetEnglish.setChecked(True) else: self.ui.actionSetChinese.setChecked(True) @classmethod def checkSort(cls, index: list): index.sort() for i in range(len(index)): if i != index[i]: return i # ==============event处理函数========================== # def closeEvent(self, event): # 退出函数 # # msgBox = QMessageBox() # msgBox.setWindowTitle('关闭') # msgBox.setText("是否保存") # msgBox.setIcon(QMessageBox.Question) # btn_Do_notSave = msgBox.addButton('不保存', QMessageBox.AcceptRole) # btn_cancel = msgBox.addButton('取消', QMessageBox.RejectRole) # btn_save = msgBox.addButton('保存', QMessageBox.AcceptRole) # msgBox.setDefaultButton(btn_save) # msgBox.exec_() # # if msgBox.clickedButton() == btn_Do_notSave: # event.accept() # elif msgBox.clickedButton() == btn_cancel: # event.ignore() # elif msgBox.clickedButton() == btn_save: # self.do_save_file() # event.accept() # def contextMenuEvent(self, event): # 右键菜单功能 # rightMouseMenu = QMenu(self) # # rightMouseMenu.addAction(self.ui.actionNew) # rightMouseMenu.addAction(self.ui.actionOpen) # # self.action = rightMouseMenu.exec_(self.mapToGlobal(event.pos())) # ==========由connectSlotsByName()自动连接的槽函数============ @Slot() # 新建画板 def on_actionNew_triggered(self): self.iniGraphicsSystem() @Slot() # 添加边 def on_actionArc_triggered(self): # 添加曲线 item = BezierEdge() item.setGraphMode(self.ui.actionDigraph_Mode.isChecked()) self.__setItemProperties(item, self._tr("MainWindow", "边")) self.do_addItem(item) self.__updateEdgeView() self.__updateNodeView() @Slot() # 添加顶点 def on_actionCircle_triggered(self): # 添加原点 self.viewAndScene() item = BezierNode() self.__setItemProperties(item, self._tr("MainWindow", "顶点")) self.do_addItem(item) self.__updateNodeView() self.__updateEdgeView() @Slot() # 添加注释 def on_actionAdd_Annotation_triggered(self): self.viewAndScene() strText, OK = QInputDialog.getText(self, self._tr("MainWindow", "输入"), self._tr("MainWindow", "请输入文字")) if not OK: return item = BezierText(strText) self.__setItemProperties(item, self._tr("MainWindow", "注释")) self.do_addItem(item) @Slot(bool) # 显示和隐藏结点权重 def on_actionShowNodesWeight_toggled(self, check: bool): nodes = self.__scene.singleItems(BezierNode) for node in nodes: node: BezierNode node.weightCp.setVisible(check) # if check: # self.ui.actionShowNodesWeight.setText("隐藏顶点权重") # else: # self.ui.actionShowNodesWeight.setText("显示顶点权重") @Slot(bool) # 显示和隐藏边权重 def on_actionShowEdgesWeight_toggled(self, check: bool): edges = self.__scene.singleItems(BezierEdge) for edge in edges: edge: BezierEdge edge.weightCp.setVisible(check) # if check: # self.ui.actionShowEdgesWeight.setText("隐藏边权重") # else: # self.ui.actionShowEdgesWeight.setText("显示边权重") @Slot(bool) # 显示和隐藏边的控制点 def on_actionHideControlPoint_toggled(self, check: bool): edges = self.__scene.singleItems(BezierEdge) for edge in edges: edge: BezierEdge for point in edge.pointList: point.setVisible(check) if edge.sourceNode: edge.beginCp.setVisible(False) if edge.destNode: edge.endCp.setVisible(False) @Slot() # 简单通路 def on_actionEasy_Pathway_triggered(self): self.viewAndScene() items = self.__scene.nodeList if len(items) == 0: QMessageBox.warning(self, self._tr("MainWindow", "警告"), self._tr("MainWindow", "对不起,你没有选择起始节点")) return elif len(items) != 2: QMessageBox.warning(self, self._tr("MainWindow", "警告"), self._tr("MainWindow", "选择的起始点数目不符合要求")) return if self.connectGraph(): PathWay = ShowDataWidget(self, items, self.__graph, name=self._tr("MainWindow", "简单通路")) PathWay.pathSignal.connect(self.do_ShowSelectPath) if PathWay.easyPath(): PathWay.updateToolWidget() PathWay.show() self.disconnectGraph() @Slot() # 简单回路 def on_actionEasy_Loop_triggered(self): self.viewAndScene() items = self.__scene.nodeList if len(items) == 0: QMessageBox.warning(self, self._tr("MainWindow", "警告"), self._tr("MainWindow", "对不起,你没有选择起点")) return if self.connectGraph(): LoopWay = ShowDataWidget(self, items, self.__graph, name=self._tr("MainWindow", "简单回路")) LoopWay.pathSignal.connect(self.do_ShowSelectPath) if LoopWay.easyLoop(): LoopWay.updateToolWidget(mode=1) LoopWay.show() self.disconnectGraph() @Slot() # 初级通路 def on_actionPrimary_Pathway_triggered(self): items = self.__scene.nodeList if len(items) == 0: QMessageBox.warning(self, self._tr("MainWindow", "警告"), self._tr("MainWindow", "对不起,你没有选择起始节点")) return elif len(items) != 2: QMessageBox.warning(self, self._tr("MainWindow", "警告"), self._tr("MainWindow", "选择的起始点数目不符合要求")) return if self.connectGraph(): PathWay = ShowDataWidget(self, items, self.__graph, name="初级通路") PathWay.pathSignal.connect(self.do_ShowSelectPath) if PathWay.primaryPath(): PathWay.updateToolWidget(path=1) PathWay.show() self.disconnectGraph() @Slot() # 初级回路 def on_actionPrimary_Loop_triggered(self): items = self.__scene.nodeList if len(items) == 0: QMessageBox.warning(self, self._tr("MainWindow", "警告"), self._tr("MainWindow", "对不起,你没有选择起点")) return if self.connectGraph(): LoopWay = ShowDataWidget(self, items, self.__graph, name=self._tr("MainWindow", "初级回路")) LoopWay.pathSignal.connect(self.do_ShowSelectPath) if LoopWay.primaryLoop(): LoopWay.updateToolWidget(mode=1, path=1) LoopWay.show() self.disconnectGraph() @Slot() # 邻接矩阵 边数 def on_action_EdgeNum_triggered(self): self.viewAndScene() items = self.__scene.singleItems(BezierNode) if len(items) == 0: QMessageBox.warning(self, self._tr("MainWindow", "警告"), self._tr("MainWindow", "图中没有结点")) return if self.connectGraph(): MatrixTable = ShowMatrixWidget(self, self.__graph, self._tr("MainWindow", "邻接矩阵"), 0) MatrixTable.show() @Slot() # 邻接矩阵 权重 def on_actionWeight_triggered(self): self.viewAndScene() items = self.__scene.singleItems(BezierNode) if len(items) == 0: QMessageBox.warning(self, self._tr("MainWindow", "警告"), self._tr("MainWindow", "图中没有结点")) return if self.connectGraph(): if not self.__graph.multipleOrSimple(): MatrixTable = ShowMatrixWidget(self, self.__graph, self._tr("MainWindow", "邻接矩阵"), 1) MatrixTable.show() else: QMessageBox.information(self, "Sorry", "这个图不是简单图") self.disconnectGraph() @Slot() # 可达矩阵 def on_actionReachable_Matrix_triggered(self): self.viewAndScene() items = self.__scene.singleItems(BezierNode) if len(items) == 0: QMessageBox.warning(self, self._tr("MainWindow", "警告"), self._tr("MainWindow", "图中没有结点")) return if self.connectGraph(): MatrixTable = ShowMatrixWidget(self, self.__graph, self._tr("MainWindow", "可达矩阵")) MatrixTable.show() @Slot() # 关联矩阵 def on_actionIncidence_Matrix_Undigraph_triggered(self): self.viewAndScene() items = self.__scene.singleItems(BezierNode) if len(items) == 0: QMessageBox.warning(self, self._tr("MainWindow", "警告"), self._tr("MainWindow", "图中没有结点")) return if self.ui.actionDigraph_Mode.isChecked(): items = self.singleItems(BezierEdge) badNodeList = [] badNodes = '' for x in range(len(items)): if items[x].sourceNode is not None and items[ x].destNode is not None: if items[x].sourceNode == items[x].destNode: badNodeList.append(items[x]) demo = "、" if x == len(items) - 1: demo = "" badNodes = f"{badNodes}V{items[x].sourceNode.data(self.__ItemId)}{demo}" if len(badNodeList): text = self._tr("MainWindow", '有向图的关联矩阵需要有向图无环,而') text1 = self._tr("MainWindow", '存在环!') QMessageBox.warning(self, self._tr("MainWindow", "致命错误"), f"{text}{badNodes}{text1}") return if self.connectGraph(): MatrixTable = ShowMatrixWidget(self, self.__graph, self._tr("MainWindow", "关联矩阵")) MatrixTable.show() @Slot() # 图的连通性 def on_actionConnectivity_triggered(self): name = '' if self.connectGraph(): num = self.__graph.connectivity() if num is False: name = self._tr("MainWindow", '此图为非连通图') elif num == 2: name = self._tr("MainWindow", "此图为单向连通图") elif num == 3: name = self._tr("MainWindow", "此图为强连通图") elif num == 1: name = self._tr("MainWindow", '此图为连通图') QMessageBox.information(self, self._tr("MainWindow", "图的连通性"), name) self.disconnectGraph() @Slot() # 完全图判定 def on_actionCompleteGraph_triggered(self): if self.connectGraph(): edge = self.__graph.completeGraph() if edge: name = self._tr("MainWindow", "此图为完全图") else: name = self._tr("MainWindow", '此图不是完全图') QMessageBox.information(self, self._tr("MainWindow", "完全图判定"), name) self.disconnectGraph() @Slot() # 简单图多重图判定 def on_actionMultipleOrSimple_triggered(self): if self.connectGraph(): edges = self.__graph.multipleOrSimple() if not edges: QMessageBox.information(self, self._tr("MainWindow", "简单图与多重图的判定"), self._tr("MainWindow", "此图为简单图")) else: parallelSides = ShowDataWidget( self, edges, self.__graph, self._tr("MainWindow", "简单图与多重图的判定")) parallelSides.multipleOrSimple() parallelSides.show() @Slot() # 最短路径 def on_actionShortestPath_triggered(self): items = self.__scene.nodeList if len(items) == 0: QMessageBox.warning(self, self._tr("MainWindow", "警告"), self._tr("MainWindow", "对不起,你没有选择结点")) return if self.connectGraph(): ShortestPath = ShowDataWidget(self, items, self.__graph, name=self._tr("MainWindow", "最短路径")) ShortestPath.pathSignal.connect(self.do_ShowSelectPath) if ShortestPath.shortestPath(): ShortestPath.updateToolWidget(mode=1, path=2) ShortestPath.show() @Slot() # 撤销 def on_actionUndo_triggered(self): # 撤销 self.undoStack.undo() self.__updateEdgeView() self.__updateNodeView() @Slot() # 重做 def on_actionRedo_triggered(self): # 重做 self.viewAndScene() self.undoStack.redo() self.__updateEdgeView() self.__updateNodeView() @Slot() # 帮助 def on_actionHelp_Document_triggered(self): open("https://github.com/BBlance/Discrete_math.graph_theory") # @Slot() # def on_actionPen_Color_triggered(self): # 画笔颜色 # iniColor = self.view.getPenColor() # color = QColorDialog.getColor(iniColor, self, "选择颜色") # if color.isValid(): # self.view.setPenColor(color) # @Slot() # def on_actionPen_Thickness_triggered(self): # 画笔粗细 # self.viewAndScene() # iniThickness = self.__view.getPenThickness() # intPenStyle = self.__view.getPenStyle() # thicknessDialog = ThicknessDialog(None, self._tr("MainWindow", "画笔粗细与样式"), iniThickness, intPenStyle) # ret = thicknessDialog.exec_() # thickness = thicknessDialog.getThickness() # penStyle = thicknessDialog.getPenStyle() # self.__view.setPenStyle(penStyle) # self.__view.setPenThickness(thickness) @Slot() def on_actionBackground_Color_triggered(self): self.viewAndScene() # iniColor = self.__view.getBackgroundColor() # color = QColorDialog.getColor(iniColor, self, "选择颜色") # if color.isValid(): # self.__view.setBackgroundBrush(color) for item in self.standardGraphData(): print(item) # @Slot(bool) # def on_actionProperty_And_History_triggered(self, checked): # self.ui.dockWidget.setVisible(checked) @Slot() # 保存文件 def on_actionSave_triggered(self): self.viewAndScene() tableName = self.ui.tabWidget.tabText(self.ui.tabWidget.currentIndex()) filename = self.operatorFile.saveGraphData(self.standardGraphData(), tableName) if filename: index = self.ui.tabWidget.currentIndex() self.ui.tabWidget.setTabText(index, filename.baseName()) @Slot() # 读取文件 def on_actionOpen_triggered(self): graph = self.operatorFile.openGraphData() if graph: graph = self.reverseStandardData(graph) self.iniGraphicsSystem(graph[0]) for item in graph[1]: self.__scene.addItem(item) self.__updateNodeView() self.__updateEdgeView() self.__scene.update() @Slot() # 另存为 def on_actionSave_As_triggered(self): filename = self.operatorFile.saveExcelAs(self.standardGraphData()) if filename: index = self.ui.tabWidget.currentIndex() self.ui.tabWidget.setTabText(index, filename.baseName()) @Slot() # 导出数据 def on_actionOutputData_triggered(self): data = self.standardGraphData() data = data[:3] dataCpoy = [data[0], [], []] for node in data[1]: node = node[:2] dataCpoy[1].append(node) for edge in data[2]: edge = edge[:4] dataCpoy[2].append(edge) if self.operatorFile.outputData(dataCpoy): title = self.tr("恭喜") strInfo = self.tr("数据导出成功") QMessageBox.information(self, title, strInfo) @Slot() # 导入数据 def on_actionImportData_triggered(self): data = self.operatorFile.inputData() if data: graph = self.reverseStandardData(data) self.iniGraphicsSystem(graph[0]) for item in graph[1]: self.__scene.addItem(item) self.__updateNodeView() self.__updateEdgeView() self.__scene.update() @Slot() def on_actionSave_Image_triggered(self): self.viewAndScene() savePath, fileType = QFileDialog.getSaveFileName( self, self._tr("MainWindow", '保存图片'), '.\\', '*bmp;;*.png') filename = os.path.basename(savePath) if filename != "": self.__view.saveImage(savePath, fileType) @Slot() def on_actionDelete_triggered(self): self.viewAndScene() self.do_deleteItem() @Slot(bool) def on_actionDigraph_Mode_toggled(self, checked: bool): self.__labModeInfo.setText(self._tr("MainWindow", "有向图模式")) self.__graph.setMode(checked) items = self.__scene.singleItems(BezierEdge) for item in items: item: BezierEdge item.setGraphMode(True) item.update() @Slot(bool) def on_actionRedigraph_Mode_toggled(self, checked: bool): self.__labModeInfo.setText(self._tr("MainWindow", "无向图模式")) self.__graph.setMode(checked) items = self.__scene.singleItems(BezierEdge) for item in items: item: BezierEdge item.setGraphMode(False) item.update() @Slot(int) def on_tabWidget_currentChanged(self, index): # ui.tabWidget当前页面变化 self.viewAndScene() if self.__view and self.__scene: self.__updateEdgeView() self.__updateNodeView() hasTabs = self.ui.tabWidget.count() > 0 # 再无页面时 self.ui.tabWidget.setVisible(hasTabs) self.ui.dockWidget.setVisible(hasTabs) self.ui.actionProperty_And_History.setChecked(hasTabs) @Slot(int) def on_tabWidget_tabCloseRequested(self, index): # 分页关闭时关闭窗体 if index < 0: return view = self.ui.tabWidget.widget(index) view.close() # self.__view = None # self.__scene = None # =============自定义槽函数=============================== def do_nodeLock(self, item): self.__updateNodeView() self.__updateEdgeView() def do_mouseMove(self, point): ##鼠标移动 ##鼠标移动事件,point是 GraphicsView的坐标,物理坐标 view = self._tr("MainWindow", '视图坐标:') scene = self._tr("MainWindow", '场景坐标:') self.__labViewCord.setText("%s%d,%d" % (view, point.x(), point.y())) pt = self.ui.tabWidget.currentWidget().mapToScene(point) # 转换到Scene坐标 self.__labSceneCord.setText("%s%.0f,%.0f" % (scene, pt.x(), pt.y())) def do_mouseClicked(self, point): ##鼠标单击 pt = self.__view.mapToScene(point) # 转换到Scene坐标 item = self.__scene.itemAt(pt, self.__view.transform()) # 获取光标下的图形项 if item is None: return pm = item.mapFromScene(pt) # 转换为绘图项的局部坐标 itemInfo = self._tr("MainWindow", "Item 坐标:") self.__labItemCord.setText("%s%.0f,%.0f" % (itemInfo, pm.x(), pm.y())) data = f"{item.data(self.__ItemDesc)}, ItemId={item.data(self.__ItemId)}" if type(item) is BezierEdge: data = f"{data},EdgeId=e{item.data(self.__ItemId)}" elif type(item) is BezierNode: data = f"{data}, NodeId=V{item.data(self.__ItemId)}" self.__labItemInfo.setText(data) def do_mouseDoubleClick(self, point): ##鼠标双击 pt = self.__view.mapToScene(point) # 转换到Scene坐标,QPointF item = self.__scene.itemAt(pt, self.__view.transform()) # 获取光标下的绘图项 if item is None: return className = str(type(item)) # 将类名称转换为字符串 if className.find("QGraphicsRectItem") >= 0: # 矩形框 self.__setBrushColor(item) elif className.find( "QGraphicsEllipseItem") >= 0: # 椭圆和圆都是 QGraphicsEllipseItem self.__setBrushColor(item) elif className.find("QGraphicsPolygonItem") >= 0: # 梯形和三角形 self.__setBrushColor(item) elif className.find("QGraphicsLineItem") >= 0: # 直线,设置线条颜色 pen = item.pen() color = item.pen().__color() color = QColorDialog.getColor(color, self, "选择线条颜色") if color.isValid(): pen.setColor(color) item.setPen(pen) elif className.find("QGraphicsTextItem") >= 0: # 文字,设置字体 font = item.font() font, OK = QFontDialog.getFont(font) if OK: item.setFont(font) def do_addItem(self, item): add = AddCommand(self, self.__scene, item) self.undoStack.push(add) def do_shapeMoved(self, item, pos): move = MoveCommand(item, pos) self.undoStack.push(move) def do_deleteItem(self): items = self.__scene.selectedItems() cnt = len(items) for i in range(cnt): item = items[i] if str(type(item)).find("BezierNode") >= 0: item: BezierNode for edge in item.bezierEdges: for node, itemType in edge.items(): if itemType == ItemType.SourceType: node.setSourceNode(None) elif itemType == ItemType.DestType: node.setDestNode(None) self.__nodeNum -= 1 elif str(type(item)).find("BezierEdge") >= 0: item: BezierEdge sourceNode: BezierNode = item.sourceNode destNode: BezierNode = item.destNode if sourceNode: sourceNodeList = sourceNode.bezierEdges for sourceEdge in sourceNodeList: for edge in sourceEdge.keys(): if item is edge: sourceNodeList.remove(sourceEdge) if destNode: destNodeList = destNode.bezierEdges for destEdge in destNodeList: for edge in destEdge.keys(): if item is edge: destNodeList.remove(destEdge) self.__edgeNum -= 1 self.__scene.removeItem(item) # 删除绘图项 def do_curEdgeChanged(self, current, previous): if current is not None: text = f"当前单元格{current.row()},{current.column()}" item = self.edgeModel.itemFromIndex(current) def do_curNodeChanged(self, current, previous): if current is not None: text = f"当前单元格{current.row()},{current.column()}" item = self.nodeModel.itemFromIndex(current) def do_updateEdgeWeight(self, topLeft, bottomRight): if topLeft.column() == 4: edges = self.__scene.singleItems(BezierEdge) for edge in edges: edge: BezierEdge if edge.textCp.toPlainText() == self.edgeModel.index( topLeft.row(), 0, QModelIndex()).data(): edge.weightCp.setPlainText(str(topLeft.data())) self.__scene.update() def do_updateNodeWeight(self, topLeft, bottomRight): if topLeft.column() == 3: nodes = self.__scene.singleItems(BezierNode) for node in nodes: node: BezierNode if node.textCp.toPlainText() == self.nodeModel.index( topLeft.row(), 0, QModelIndex()).data(): node.weightCp.setPlainText(str(topLeft.data())) self.__scene.update() def do_ShowSelectPath(self, pathList: list): self.__scene.clearSelection() items = self.__scene.uniqueItems() for item in items: if item.textCp.toPlainText() in pathList: item.setSelected(True) def do_checkIsHasItems(self, num): if num: self.ui.actionSave.setEnabled(True) else: self.ui.actionSave.setEnabled(False)
class AtomSitesModel(BaseModel): def __init__(self, parent=None): super().__init__(parent) self._project_dict = None self._model = QStandardItemModel() # set roles self._label_role = Qt.UserRole + 1 self._atom_role = Qt.UserRole + 2 self._color_role = Qt.UserRole + 3 self._x_role = Qt.UserRole + 4 self._y_role = Qt.UserRole + 5 self._z_role = Qt.UserRole + 6 self._occupancy_role = Qt.UserRole + 7 self._model.setItemRoleNames({ self._label_role: b'label', self._atom_role: b'atom', self._color_role: b'colorStr', self._x_role: b'xPos', self._y_role: b'yPos', self._z_role: b'zPos', self._occupancy_role: b'occupancy' }) self._log = logger.getLogger(self.__class__.__module__) def _setModelsFromProjectDict(self): """Create the model needed for GUI ...""" self._log.info("Starting to set Model from Project Dict") for phase_id, phase_dict in self._project_dict['phases'].items(): # block model signals self._model.blockSignals(True) # set list of atoms data = [] for atom_id, atom_dict in phase_dict['atoms'].items(): label = atom_id atom = str(atom_dict['type_symbol'].value) color = atom_dict['scat_length_neutron'].value.real x = atom_dict['fract_x'].value y = atom_dict['fract_x'].value z = atom_dict['fract_x'].value occupancy = atom_dict['occupancy'].value data.append({ self._label_role: label, self._atom_role: atom, self._color_role: color, self._x_role: x, self._y_role: y, self._z_role: z, self._occupancy_role: occupancy }) # set model size self._model.setColumnCount(1) self._model.setRowCount(len(data)) # set model from data array created above for row_index, dict_value in enumerate(data): index = self._model.index(row_index, 0) for role, value in dict_value.items(): self._model.setData(index, value, role) # unblock signals and emit model layout changed self._model.blockSignals(False) self._model.layoutChanged.emit() self._log.info("Finished setting Model from Project Dict")
class MainWindow(QMainWindow): def __init__(self, parent=None, flags=Qt.WindowFlags()): super(MainWindow, self).__init__(parent, flags) self.dayWidth = 70 self.ui = Ui_MainWindow() self.ui.setupUi(self) self.initModel() self.initActions() self.initItemDelegate() self.initGrid() leftView = self.ui.ganttView.leftView() leftView.setColumnHidden(1, True) leftView.setColumnHidden(2, True) leftView.setColumnHidden(3, True) leftView.setColumnHidden(4, True) leftView.setColumnHidden(5, True) leftView.header().setStretchLastSection(True) self.ui.ganttView.leftView().customContextMenuRequested.connect( self.showContextMenu) self.ui.ganttView.selectionModel().selectionChanged.connect( self.enableActions) self.ui.ganttView.graphicsView().clicked.connect(self.slotClicked) self.ui.ganttView.graphicsView().qrealClicked.connect( self.slotDoubleClicked) def initModel(self): self.model = QStandardItemModel(0, 6, self) self.model.setHeaderData(0, Qt.Horizontal, "Task") self.ui.ganttView.setModel(self.model) self.l = QWidget(self) self.l.setWindowTitle("Legend") self.l.show() ##self.l.setModel(self.model) self.constraintModel = ConstraintModel(self) self.ui.ganttView.setConstraintModel(self.constraintModel) def initActions(self): self.newEntryAction = QAction("New entry", self) self.newEntryAction.setShortcut(QKeySequence.New) self.newEntryAction.triggered.connect(self.addNewEntry) self.removeEntryAction = QAction("Remove entry", self) self.removeEntryAction.setShortcut(QKeySequence.Delete) self.removeEntryAction.triggered.connect(self.removeEntry) self.demoAction = QAction("Demo entry", self) self.demoAction.triggered.connect(self.addDemoEntry) self.printAction = QAction("Print Preview...", self) self.printAction.triggered.connect(self.printPreview) self.zoomInAction = QAction("Zoom In", self) self.zoomInAction.setShortcut(QKeySequence.ZoomIn) self.zoomInAction.triggered.connect(self.zoomIn) self.zoomOutAction = QAction("Zoom Out", self) self.zoomOutAction.setShortcut(QKeySequence.ZoomOut) self.zoomOutAction.triggered.connect(self.zoomOut) self.ui.ganttView.leftView().setContextMenuPolicy(Qt.CustomContextMenu) self.ui.ganttView.leftView().addAction(self.newEntryAction) self.ui.ganttView.leftView().addAction(self.removeEntryAction) menuBar = QMenuBar(self) self.setMenuBar(menuBar) entryMenu = menuBar.addMenu("Entry") entryMenu.addAction(self.newEntryAction) entryMenu.addAction(self.removeEntryAction) entryMenu.addSeparator() entryMenu.addAction(self.demoAction) entryMenu.addSeparator() entryMenu.addAction(self.printAction) zoomMenu = menuBar.addMenu("Zoom") zoomMenu.addAction(self.zoomInAction) zoomMenu.addAction(self.zoomOutAction) ##self.enableActions(QItemSelection()) def initItemDelegate(self): delegate = EntryDelegate(self.constraintModel, self) self.ui.ganttView.leftView().setItemDelegate(delegate) def initGrid(self): self.grid = DateTimeGrid() self.grid.setDayWidth(self.dayWidth) self.ui.ganttView.setGrid(self.grid) def showContextMenu(self, pos): if not self.ui.ganttView.leftView().indexAt(pos).isValid(): self.ui.ganttView.selectionModel().clearSelection() menu = QMenu(self.ui.ganttView.leftView()) menu.addAction(self.newEntryAction) menu.addAction(self.removeEntryAction) menu.exec_(self.ui.ganttView.leftView().viewport().mapToGlobal(pos)) def enableActions(self, selected): if len(selected.indexes()) == 0: self.newEntryAction.setEnabled(True) self.removeEntryAction.setEnabled(False) return selectedIndex = selected.indexes()[0] dataType = self.model.data(self.model.index(selectedIndex.row(), 1)) if dataType in [KDGantt.TypeEvent, KDGantt.TypeTask]: self.newEntryAction.setEnabled(False) self.removeEntryAction.setEnabled(True) return self.newEntryAction.setEnabled(True) self.removeEntryAction.setEnabled(True) def addNewEntry(self): dialog = EntryDialog(self.model) dialog.setWindowTitle("New Entry") if dialog.exec_() == QDialog.Rejected: dialog = None return selectedIndexes = self.ui.ganttView.selectionModel().selectedIndexes() if len(selectedIndexes) > 0: parent = selectedIndexes[0] else: parent = QModelIndex() if not self.model.insertRow(self.model.rowCount(parent), parent): return row = self.model.rowCount(parent) - 1 if row == 0 and parent.isValid(): self.model.insertColumns(self.model.columnCount(paren), 5, parent) self.model.setData(self.model.index(row, 0, parent), dialog.name()) self.model.setData(self.model.index(row, 1, parent), dialog.type()) if dialog.type() != KDGantt.TypeSummary: self.model.setData(self.model.index(row, 2, parent), dialog.startDate(), KDGantt.StartTimeRole) self.model.setData(self.model.index(row, 3, parent), dialog.endDate(), KDGantt.EndTimeRole) self.model.setData(self.model.index(row, 4, parent), dialog.completion()) self.model.setData(self.model.index(row, 5, parent), dialog.legend()) self.addConstraint(dialog.depends(), self.model.index(row, 0, parent)) self.setReadOnly(self.model.index(row, 0, parent), dialog.readOnly()) dialog = None def setReadOnly(self, index, readOnly): row = index.row() parent = index.parent() for column in range(0, 5): item = self.model.itemFromIndex( self.model.index(row, column, parent)) flags = None if readOnly: flags = item.flags() & ~Qt.ItemIsEditable else: flags = item.flags() | Qt.ItemIsEditable item.setFlags(flags) def addConstraint(self, index1, index2): if not index1.isValid() or not index2.isValid(): return c = Constraint(index1, index2) self.ui.ganttView.constraintModel().addConstraint(c) def addConstraintFromItem(self, item1, item2): self.addConstraint(self.model.indexFromItem(item1), self.model.indexFromItem(item2)) def removeEntry(self): selectedIndexes = self.ui.ganttView.selectionModel().selectedIndexes() if len(selectedIndexes) > 0: index = selectedIndexes[0] else: index = QModelIndex() if not index.isValid(): return self.model.removeRow(index.row(), index.parent()) def addDemoEntry(self): softwareRelease = MyStandardItem("Software Release") codeFreeze = MyStandardItem("Code Freeze") codeFreeze.setData(KDGantt.TextPositionRole, StyleOptionGanttItem.Right) packaging = MyStandardItem("Packaging") upload = MyStandardItem("Upload") testing = MyStandardItem("Testing") updateDocumentation = MyStandardItem("Update Documentation") now = QDateTime.currentDateTime() softwareRelease.appendRow([ codeFreeze, MyStandardItem(KDGantt.TypeEvent), MyStandardItem(now, KDGantt.StartTimeRole) ]) softwareRelease.appendRow([ packaging, MyStandardItem(KDGantt.TypeTask), MyStandardItem(now.addDays(5), KDGantt.StartTimeRole), MyStandardItem(now.addDays(10), KDGantt.EndTimeRole) ]) softwareRelease.appendRow([ upload, MyStandardItem(KDGantt.TypeTask), MyStandardItem( now.addDays(10).addSecs(2 * 60 * 60), KDGantt.StartTimeRole), MyStandardItem(now.addDays(11), KDGantt.EndTimeRole) ]) softwareRelease.appendRow([ testing, MyStandardItem(KDGantt.TypeTask), MyStandardItem(now.addSecs(3 * 60 * 60), KDGantt.StartTimeRole), MyStandardItem(now.addDays(5), KDGantt.EndTimeRole) ]) softwareRelease.appendRow([ updateDocumentation, MyStandardItem(KDGantt.TypeTask), MyStandardItem(now.addSecs(3 * 60 * 60), KDGantt.StartTimeRole), MyStandardItem(now.addDays(3), KDGantt.EndTimeRole) ]) self.model.appendRow( [softwareRelease, MyStandardItem(KDGantt.TypeSummary)]) self.addConstraintFromItem(codeFreeze, packaging) self.addConstraintFromItem(codeFreeze, testing) self.addConstraintFromItem(codeFreeze, updateDocumentation) self.addConstraintFromItem(packaging, upload) self.addConstraintFromItem(testing, packaging) self.addConstraintFromItem(updateDocumentation, packaging) def zoomIn(self): self.dayWidth += 10 if self.dayWidth > 400: self.grid.setScale(DateTimeGrid.ScaleHour) self.grid.setDayWidth(self.dayWidth) def zoomOut(self): self.dayWidth -= 10 if self.dayWidth < 10: self.dayWidth = 10 if self.dayWidth <= 400: self.grid.setScale(DateTimeGrid.ScaleDay) self.grid.setDayWidth(self.dayWidth) def printPreview(self): preview = QLabel(self, Qt.Window) preview.setAttribute(Qt.WA_DeleteOnClose) preview.setScaledContents(True) preview.setWindowTitle("Print Preview") pix = QPixmap(1000, 300) pix.fill(Qt.white) p = QPainter(pix) p.setRenderHints(QPainter.Antialiasing) self.ui.ganttView.print_(p, pix.rect()) preview.setPixmap(pix) preview.show() def slotClicked(self, index): self.statusBar().showMessage( "(%d,%d,_,%s) clicked" % (index.row(), index.column(), index.model())) def slotDoubleClicked(self, index): self.statusBar().showMessage( "(%d,%d,_,%s) qreal clicked" % (index.row(), index.column(), index.model()))
return False def setModelData(self, editor, model, index): ''' The user wanted to change the old state in the opposite. ''' model.setData(index, 1 if int(index.data()) == 0 else 0, QtCore.Qt.EditRole) if __name__ == '__main__': import sys app = QApplication(sys.argv) model = QStandardItemModel(4, 3) tableView = QTableView() tableView.setModel(model) delegate = CheckBoxDelegate(None) tableView.setItemDelegateForColumn(1, delegate) for row in range(4): for column in range(3): index = model.index(row, column, QModelIndex()) model.setData(index, 1) tableView.setWindowTitle("Check Box Delegate") tableView.show() sys.exit(app.exec_())
class AuswahlDialog(QDialog): def __init__(self, title=None, parent=None): QDialog.__init__(self, parent) self.title = title self.listView = QListView() self.font = QFont("Arial", 14) self.okButton = QPushButton("OK") self.cancelButton = QPushButton("Abbrechen") self.model = QStandardItemModel() self.listView.setModel(self.model) self._selectedIndexes = "" self._createGui() def _createGui(self): hbox = QHBoxLayout() hbox.addStretch(1) hbox.addWidget(self.okButton) hbox.addWidget(self.cancelButton) vbox = QVBoxLayout(self) vbox.addWidget(self.listView, stretch=1) # vbox.addStretch(1) vbox.addLayout(hbox) self.okButton.setDefault(True) if self.title: self.setWindowTitle(self.title) else: self.setWindowTitle("Auswahl") self.okButton.clicked.connect(self.onAccepted) self.cancelButton.clicked.connect(self.reject) def appendItemList(self, itemlist: List[str]): for i in itemlist: self.appendItem(i, None) def appendItem(self, text: str, userdata: Any = None): item = CustomItem(text) if userdata: item.userdata = userdata item.setFont(self.font) self.model.appendRow(item) if self.model.rowCount() == 1: self.listView.setCurrentIndex(self.model.index(0, 0)) def onAccepted(self): self._selectedIndexes = self.listView.selectedIndexes() self.accept() def getSelectedIndexes(self): return self._selectedIndexes def getSelection(self) -> List[Tuple]: sel = self.getSelectedIndexes() l = list() for idx in sel: item: CustomItem = self.model.item(idx.row(), idx.column()) t = (item.text(), item.userdata) l.append(t) return l
class MyWidget(QWidget): def __init__(self): super(MyWidget, self).__init__(None) self.view = View() self.grid = DateTimeGrid() self.slider = QSlider() self.model = QStandardItemModel() self.cmodel = ConstraintModel() for h in range(0, 19): topitem = MyStandardItem("Top Item " + str(h)) for i in range(0, 19): item = MyStandardItem("Multi Item " + str(i)) for j in range(0, 29, 3): item.appendRow([ MyStandardItem("Item " + str(j)), MyStandardItem(KDGantt.TypeTask), MyStandardItem(QDateTime.currentDateTime().addDays(j), KDGantt.StartTimeRole), MyStandardItem( QDateTime.currentDateTime().addDays(j + 1 + i / 7), KDGantt.EndTimeRole), MyStandardItem(50) ]) item.appendRow([ MyStandardItem("Event"), MyStandardItem(KDGantt.TypeEvent), MyStandardItem(QDateTime.currentDateTime(), KDGantt.StartTimeRole), MyStandardItem(QDateTime(), KDGantt.EndTimeRole), MyStandardItem("") ]) topitem.appendRow([ item, MyStandardItem(KDGantt.TypeMulti), MyStandardItem(""), MyStandardItem(""), MyStandardItem("") ]) self.model.appendRow([ topitem, MyStandardItem(KDGantt.TypeMulti), MyStandardItem(""), MyStandardItem(""), MyStandardItem("") ]) self.model.appendRow([MyStandardItem("No data")]) ##cmodel.addConstraint( KDGantt::Constraint( proxyModel.index( 0, 3 ), proxyModel.index( 10, 3 ) ) ); ##cmodel.addConstraint( KDGantt::Constraint( proxyModel.index( 10, 3 ), proxyModel.index( 5, 3 ) ) ); pidx = self.model.index(0, 0) pidx = self.model.index(0, 0, pidx) self.cmodel.addConstraint( Constraint(self.model.index(0, 0, pidx), self.model.index(1, 0, pidx))) self.cmodel.addConstraint( Constraint(self.model.index(1, 0, pidx), self.model.index(0, 0, pidx))) self.cmodel.addConstraint( Constraint(self.model.index(1, 0, pidx), self.model.index(10, 0, pidx))) self.cmodel.addConstraint( Constraint(self.model.index(3, 0, pidx), self.model.index(5, 0, pidx))) self.cmodel.addConstraint( Constraint(self.model.index(7, 0, pidx), self.model.index(4, 0, pidx))) self.slider.setOrientation(Qt.Horizontal) self.slider.setRange(1, 10000) self.slider.setValue(100) l = QVBoxLayout(self) l.addWidget(self.view) l.addWidget(self.slider) self.grid.setStartDateTime(QDateTime.currentDateTime().addDays(-3)) self.grid.setDayWidth(100) ##grid.setNoInformationBrush( Qt::NoBrush ); self.view.setGrid(self.grid) self.view.setModel(self.model) ##view.setConstraintModel( &cmodel ); self.slider.valueChanged.connect(self.slotZoom) pb1 = QPushButton("Print Preview...", self) pb2 = QPushButton("Print...", self) l.addWidget(pb1) l.addWidget(pb2) pb1.clicked.connect(self.slotPrintPreview) pb2.clicked.connect(self.slotPrint) gv = self.view.graphicsView() gv.setHeaderContextMenuPolicy(Qt.CustomContextMenu) gv.headerContextMenuRequested.connect(self.slotHeaderMenu) def slotZoom(self, z): self.grid.setDayWidth(z) def slotPrintPreview(self): pix = QPixmap(1000, 200) pix.fill(Qt.white) painter = QPainter(pix) view.print(painter, pix.rect()) painter.end() label = QLabel(this) label.setPixmap(pix) label.show() def slotPrint(self): printer = QPrinter(QPrinter.HighResolution) pd = QPrintDialog(printer, self) if pd.exec_() == QDialog.Accepted: r = self.view.graphicsView().mapToScene( self.view.graphicsView().viewport().rect()).boundingRect() self.view.print(printer, r.left(), r.right()) def slotHeaderMenu(self, pt): menu = QMenu() menu.addAction("This") menu.addAction("is") menu.addAction("just") menu.addAction("a") menu.addAction("test") menu.exec_(pt)
class CheckListEditor(QTableView): """A check list editor.""" def __init__(self, parent, tutor=None): """Initialize class.""" super().__init__(parent) self._tutor = tutor self._base_size = None self.model = QStandardItemModel(self) self.setModel(self.model) self.verticalHeader().hide() self.horizontalHeader().hide() self.setShowGrid(False) self.setMouseTracking(True) def keyPressEvent(self, event): """Toggles checked state if the user presses space.""" super().keyPressEvent(event) if event.key() == Qt.Key_Space: index = self.currentIndex() self.toggle_checked_state(index) def toggle_checked_state(self, index): """Toggles checked state of given index. Args: index (QModelIndex) """ item = self.model.itemFromIndex(index) if item.checkState() == Qt.Checked: item.setCheckState(Qt.Unchecked) else: item.setCheckState(Qt.Checked) def mouseMoveEvent(self, event): """Sets the current index to the one under mouse.""" index = self.indexAt(event.pos()) self.setCurrentIndex(index) def mousePressEvent(self, event): """Toggles checked state of pressed index.""" index = self.indexAt(event.pos()) self.toggle_checked_state(index) def set_data(self, items, checked_items): """Sets data and updates geometry. Args: items (Sequence(str)): All items. checked_items (Sequence(str)): Initially checked items. """ for item in items: qitem = QStandardItem(item) if item in checked_items: qitem.setCheckState(Qt.Checked) else: qitem.setCheckState(Qt.Unchecked) qitem.setFlags(~Qt.ItemIsEditable & ~Qt.ItemIsUserCheckable) qitem.setData(qApp.palette().window(), Qt.BackgroundRole) # pylint: disable=undefined-variable self.model.appendRow(qitem) self.selectionModel().select(self.model.index(0, 0), QItemSelectionModel.Select) def data(self): """Returns a comma separated list of checked items. Returns str """ data = [] for q in self.model.findItems('*', Qt.MatchWildcard): if q.checkState() == Qt.Checked: data.append(q.text()) return ",".join(data) def set_base_size(self, size): self._base_size = size def update_geometry(self): """Updates geometry. """ self.horizontalHeader().setDefaultSectionSize(self._base_size.width()) self.verticalHeader().setDefaultSectionSize(self._base_size.height()) total_height = self.verticalHeader().length() + 2 size = QSize(self._base_size.width(), total_height).boundedTo(self.parent().size()) self.resize(size) if self._tutor: self.move(self.pos() + self._tutor.mapTo(self.parent(), self._tutor.parent().pos())) # Adjust position if widget is outside parent's limits bottom_right = self.mapToGlobal(self.rect().bottomRight()) parent_bottom_right = self.parent().mapToGlobal( self.parent().rect().bottomRight()) x_offset = max(0, bottom_right.x() - parent_bottom_right.x()) y_offset = max(0, bottom_right.y() - parent_bottom_right.y()) self.move(self.pos() - QPoint(x_offset, y_offset))
class FileWriterCtrl(Ui_FilewriterCtrl, QMainWindow): def __init__(self, instrument: Instrument): super().__init__() self.instrument = instrument self.setupUi() self.known_writers = {} self.known_files = {} self.status_consumer = None self.command_producer = None def setupUi(self): super().setupUi(self) self.status_consumer = None self.status_broker_led = Led(self) self.status_topic_layout.addWidget(self.status_broker_led) self.status_broker_change_timer = QTimer() self._set_up_broker_fields( self.status_broker_led, self.status_broker_edit, self.status_broker_change_timer, self.status_broker_timer_changed, StatusConsumer, ) self.command_producer = None self.command_broker_led = Led(self) self.command_broker_layout.addWidget(self.command_broker_led) self.command_broker_change_timer = QTimer() self._set_up_broker_fields( self.command_broker_led, self.command_broker_edit, self.command_broker_change_timer, self.command_broker_timer_changed, CommandProducer, ) self.command_widget.ok_button.clicked.connect(self.send_command) self.update_status_timer = QTimer() self.update_status_timer.timeout.connect(self._check_connection_status) self.update_status_timer.start(500) self.files_list.clicked.connect(self.file_list_clicked) self.stop_file_writing_button.clicked.connect(self.stop_file_writing_clicked) self.model = QStandardItemModel(0, 2, self) self.model.setHeaderData(0, QtCore.Qt.Horizontal, "File writer") self.model.setHeaderData(1, QtCore.Qt.Horizontal, "Last seen") self.file_writers_list.setModel(self.model) self.file_writers_list.setColumnWidth(0, 320) self.file_list_model = QStandardItemModel(0, 3, self) self.file_list_model.setHeaderData(0, QtCore.Qt.Horizontal, "File name") self.file_list_model.setHeaderData(1, QtCore.Qt.Horizontal, "Last seen") self.file_list_model.setHeaderData(2, QtCore.Qt.Horizontal, "File writer") self.files_list.setModel(self.file_list_model) @staticmethod def _set_up_broker_fields( led: Led, edit: QLineEdit, timer: QTimer, timer_callback: Callable, kafka_obj_type: Type[KafkaInterface], ): led.turn_off() validator = BrokerAndTopicValidator() edit.setValidator(validator) validator.is_valid.connect(partial(validate_line_edit, edit)) edit.textChanged.connect(lambda: timer.start(1000)) timer.setSingleShot(True) timer.timeout.connect(partial(timer_callback, kafka_obj_type)) def _check_connection_status(self): if self.status_consumer is None: self.status_broker_led.turn_off() else: connection_ok = self.status_consumer.connected self.status_broker_led.set_status(connection_ok) if connection_ok: current_writers = self.status_consumer.file_writers self._update_writer_list(current_writers) self._update_files_list(self.status_consumer.files) if self.command_producer is None: self.command_broker_led.turn_off() else: self.command_broker_led.set_status(self.command_producer.connected) def status_broker_timer_changed(self, kafka_obj_type: KafkaInterface): result = BrokerAndTopicValidator.extract_addr_and_topic( self.status_broker_edit.text() ) if result is not None: if self.status_consumer is not None: self.status_consumer.close() self.status_consumer = kafka_obj_type(*result) def command_broker_timer_changed(self, kafka_obj_type: KafkaInterface): result = BrokerAndTopicValidator.extract_addr_and_topic( self.command_broker_edit.text() ) if result is not None: if self.command_producer is not None: self.command_producer.close() self.command_producer = kafka_obj_type(*result) def _update_writer_list(self, updated_list: Dict[str, Dict]): for key in updated_list: current_time, time_str = self.get_time(key, updated_list) if key not in self.known_writers: number_of_filewriter_rows = self.model.rowCount(QtCore.QModelIndex()) new_file_writer = FileWriter(key, number_of_filewriter_rows) self.known_writers[key] = new_file_writer self.model.insertRow(number_of_filewriter_rows) self.model.setData(self.model.index(number_of_filewriter_rows, 0), key) self.model.setData( self.model.index(number_of_filewriter_rows, 1), time_str ) current_file_writer = self.known_writers[key] if current_time != current_file_writer.last_time: self._set_time(self.model, current_file_writer, current_time, time_str) def _update_files_list(self, updated_list: Dict[str, Dict]): for key in updated_list: current_time, time_str = self.get_time(key, updated_list) if key not in self.known_files: number_of_file_rows = self.file_list_model.rowCount( QtCore.QModelIndex() ) new_file = File(key, row=number_of_file_rows) self.known_files[key] = new_file self.file_list_model.insertRow(number_of_file_rows) self.file_list_model.setData( self.file_list_model.index(number_of_file_rows, 0), key ) self.file_list_model.setData( self.file_list_model.index(number_of_file_rows, 1), time_str ) self.file_list_model.setData( self.file_list_model.index(number_of_file_rows, 2), new_file.writer_id, ) current_file = self.known_files[key] if current_time != current_file.last_time: self._set_time( self.file_list_model, current_file, current_time, time_str ) @staticmethod def get_time(key: str, updated_list: Dict[str, Dict]) -> Tuple[int, str]: current_time = updated_list[key]["last_seen"] time_struct = time.localtime(current_time / 1000) time_str = time.strftime("%Y-%m-%d %H:%M:%S%Z", time_struct) return current_time, time_str @staticmethod def _set_time( model: QAbstractItemModel, current_index: Union[File, FileWriter], current_time: str, time_str: str, ): model.setData(model.index(current_index.row, 1), time_str) current_index.last_time = current_time def send_command(self): if self.command_producer is not None: ( nexus_file_name, broker, start_time, stop_time, service_id, abort_on_uninitialised_stream, use_swmr, ) = self.command_widget.get_arguments() in_memory_file = io.StringIO() generate_json( data=self.instrument, file=in_memory_file, nexus_file_name=nexus_file_name, broker=broker, start_time=start_time, stop_time=stop_time, service_id=service_id, use_swmr=use_swmr, ) in_memory_file.seek(0) msg_to_send = in_memory_file.read() self.command_producer.send_command(msg_to_send) self.command_widget.ok_button.setEnabled(False) def file_list_clicked(self): if len(self.files_list.selectedIndexes()) > 0: self.stop_file_writing_button.setEnabled(True) else: self.stop_file_writing_button.setEnabled(False) def stop_file_writing_clicked(self): selected_files = self.files_list.selectedIndexes() for index in selected_files: for fileKey in self.known_files: current_file = self.known_files[fileKey] if index.row() == current_file.row: send_msg = f' "cmd": "FileWriter_stop", "job_id": "{current_file.job_id}", "service_id": "{current_file.writer_id}" ' self.command_producer.send_command( f'{{"{send_msg}"}}'.encode("utf-8") ) break def closeEvent(self, event: QCloseEvent): if self.status_consumer is not None: self.status_consumer.close() if self.command_producer is not None: self.command_producer.close()
class AtomMspsModel(BaseModel): def __init__(self, parent=None): super().__init__(parent) self._project_dict = None self._model = QStandardItemModel() # set roles self._label_role = Qt.UserRole + 1 self._type_role = Qt.UserRole + 2 self._chiiso_role = Qt.UserRole + 3 self._chi11_role = Qt.UserRole + 4 self._chi22_role = Qt.UserRole + 5 self._chi33_role = Qt.UserRole + 6 self._chi12_role = Qt.UserRole + 7 self._chi13_role = Qt.UserRole + 8 self._chi23_role = Qt.UserRole + 9 self._model.setItemRoleNames({ self._label_role: b'label', self._type_role: b'type', self._chiiso_role: b'chiiso', self._chi11_role: b'chi11', self._chi22_role: b'chi22', self._chi33_role: b'chi33', self._chi12_role: b'chi12', self._chi13_role: b'chi13', self._chi23_role: b'chi23' }) self._log = logger.getLogger(self.__class__.__module__) def _setModelsFromProjectDict(self): """Create the model needed for GUI ...""" self._log.info("Starting to set Model from Project Dict") for phase_id, phase_dict in self._project_dict['phases'].items(): # block model signals self._model.blockSignals(True) # set list of atoms data = [] for atom_id, atom_dict in phase_dict['atoms'].items(): data.append({ self._label_role: atom_id, self._type_role: atom_dict.getItemByPath(['MSP', 'type']).value, self._chiiso_role: "", self._chi11_role: atom_dict.getItemByPath(['MSP', 'chi_11']).value, self._chi22_role: atom_dict.getItemByPath(['MSP', 'chi_22']).value, self._chi33_role: atom_dict.getItemByPath(['MSP', 'chi_33']).value, self._chi12_role: atom_dict.getItemByPath(['MSP', 'chi_12']).value, self._chi13_role: atom_dict.getItemByPath(['MSP', 'chi_13']).value, self._chi23_role: atom_dict.getItemByPath(['MSP', 'chi_23']).value, }) # set model size self._model.setColumnCount(1) self._model.setRowCount(len(data)) # set model from data list created above for row_index, dict_value in enumerate(data): index = self._model.index(row_index, 0) for role, value in dict_value.items(): self._model.setData(index, value, role) # unblock signals and emit model layout changed self._model.blockSignals(False) self._model.layoutChanged.emit() self._log.info("Finished setting Model from Project Dict")