def vertical_resize_table_view_to_contents(table_view: QtWidgets.QTableView): row_total_height = 0 count = table_view.verticalHeader().count() for i in range(0, count): if not table_view.verticalHeader().isSectionHidden(i): row_total_height += table_view.verticalHeader().sectionSize(i) if not table_view.horizontalScrollBar().isHidden(): row_total_height += table_view.horizontalScrollBar().height() if not table_view.horizontalHeader().isHidden(): row_total_height += table_view.horizontalHeader().height() table_view.setMinimumHeight(row_total_height)
class SideDock(QDockWidget): """ Side Dock/Panel, named "Navy Base Overview", displays all important data of the user. This is the first coded QWidget of WGViewer (even before LoginForm). """ sig_resized = pyqtSignal() sig_closed = pyqtSignal() def __init__(self, parent, realrun: bool): super(SideDock, self).__init__(parent) self.is_realrun = realrun _, self.user_screen_h = wgv_utils.get_user_resolution() self.qsettings = QSettings(wgv_data.get_qsettings_file(), QSettings.IniFormat) if self.qsettings.contains(QKEYS.EXP_AUTO) and self.qsettings.value(QKEYS.EXP_AUTO, type=bool): logger.debug("Auto expedition is on") self.is_exp_auto = True else: logger.debug("Auto expedition is off") self.is_exp_auto = False self.equipment_names = wgv_data.get_shipEquipmnt() self.ship_names = wgv_data.get_processed_userShipVo() # index 0 for daily, 1 for weekly, 2+ for tasks/events self.task_counter_desc_labels = [] self.task_counter_labels = [] self.task_counter_timers = [] self.task_counters = [] self.name_layout_widget = QWidget(self) self.name_layout = QHBoxLayout(self.name_layout_widget) self.name_label = QLabel(self.name_layout_widget) self.lvl_label = QLabel(self.name_layout_widget) self.ship_count_label = QLabel(self.name_layout_widget) self.equip_count_label = QLabel(self.name_layout_widget) self.collect_count_label = QLabel(self.name_layout_widget) self.sign_widget = QLineEdit(self) self.table_model = ResourceTableModel() self.table_view = QTableView(self) self.bath_list_view = BathListView() self.bath_list_view_widget = QWidget(self) self.bath_list_view_layout = QVBoxLayout(self.bath_list_view_widget) self.triple_list_view_widget = QWidget(self) self.triple_list_view = QHBoxLayout(self.triple_list_view_widget) self.build_list_view = BuildListView() self.dev_list_view = DevListView() self.exp_list_view = ExpListView(parent.main_tabs) self.task_list_view = TaskListView() self.task_panel_widget = QWidget(self) self.task_panel_view = QHBoxLayout(self.task_panel_widget) self.countdowns_layout_widget = QWidget(self) self.countdowns_layout = QVBoxLayout(self.countdowns_layout_widget) self.sig_resized.connect(self.update_geometry) self.sig_closed.connect(parent.on_dock_closed) self._init_ui() self.set_data() def set_data(self) -> None: d = wgv_data.get_api_initGame() self.on_received_lists(d) self.on_received_resource(d) self.on_received_name(d) self.on_received_tasks(d) def _init_ui(self) -> None: self.setFloating(False) self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.setMinimumWidth(int(0.4 * self.user_screen_h)) self.setWindowTitle("Navy Base Overview") self._init_name_info() self._init_sign_info() self._init_resource_info() self._init_bath_info() self._init_triple_list() self._init_task_panel() def _init_name_info(self) -> None: self.name_layout.setContentsMargins(0, 0, 0, 0) self.name_layout.addWidget(self.name_label) self.name_layout.addWidget(self.lvl_label) self.name_layout.addWidget(self.ship_count_label) self.name_layout.addWidget(self.equip_count_label) self.name_layout.addWidget(self.collect_count_label) def _init_sign_info(self) -> None: icon_path = get_data_path('assets/icons/sign_16.png') self.sign_widget.addAction(QIcon(icon_path), QLineEdit.LeadingPosition) def _init_resource_info(self) -> None: self.table_view.setModel(self.table_model) x = 0.03 * self.user_screen_h self.table_view.setIconSize(QSize(x, x)) self.table_view.verticalHeader().hide() self.table_view.horizontalHeader().hide() self.table_view.setShowGrid(False) self.table_view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_view.setEditTriggers(QAbstractItemView.NoEditTriggers) self.table_view.setFocusPolicy(Qt.NoFocus) self.table_view.setSelectionMode(QAbstractItemView.NoSelection) self.table_view.horizontalScrollBar().setEnabled(False) self.table_view.verticalScrollBar().setEnabled(False) def _init_bath_info(self) -> None: self.bath_list_view_layout.setContentsMargins(0, 0, 0, 0) self.bath_list_view_layout.addWidget(self.bath_list_view) def _init_triple_list(self) -> None: self.triple_list_view.setContentsMargins(0, 0, 0, 0) self.triple_list_view.addWidget(self.build_list_view) self.triple_list_view.addWidget(self.dev_list_view) self.triple_list_view.addWidget(self.exp_list_view) def _init_task_panel(self) -> None: self.task_panel_view.setContentsMargins(0, 0, 0, 0) self._init_countdowns() self.task_panel_view.addWidget(self.task_list_view) self.task_panel_view.addWidget(self.countdowns_layout_widget) def _init_countdowns(self) -> None: # TODO? design problem now the most suitable count is 4, 5 would be max # although MoeFantasy opens mostly 1 event at a time, rarely 2. self.countdowns_layout.setContentsMargins(0, 0, 0, 0) l1 = QLabel(self.countdowns_layout_widget) l1.setToolTip("Refreshing daily at 0300 UTC+8") # Intel' server also use CN time l1.setText("Next daily:") l1.adjustSize() self.task_counter_desc_labels.append(l1) l2 = QLabel(self.countdowns_layout_widget) l2.setText("Next weekly:") l2.setToolTip("Refreshing weekly at 0400 UTC+8 or New Year") l2.adjustSize() self.task_counter_desc_labels.append(l2) self.task_counter_labels.append(QLabel(self.countdowns_layout_widget)) self.task_counter_labels.append(QLabel(self.countdowns_layout_widget)) self.countdowns_layout.addWidget(l1) self.countdowns_layout.addWidget(self.task_counter_labels[0]) self.countdowns_layout.addWidget(l2) self.countdowns_layout.addWidget(self.task_counter_labels[1]) _, _, d_counter, w_counter = get_tasks_countdowns() self.task_counters.append(d_counter) self.task_counters.append(w_counter) self._init_task_counters() # ================================ # Getter / Setter # ================================ def add_task_countdown(self, text: str, _time: int, idx: int) -> None: l1 = QLabel(self.countdowns_layout_widget) l1.setText(text) l1.adjustSize() self.task_counter_desc_labels.append(l1) self.countdowns_layout.addWidget(l1) l2 = QLabel(self.countdowns_layout_widget) self.task_counter_labels.append(l2) self.task_counters.append(_time) self.countdowns_layout.addWidget(l2) self.start_new_timer(self.task_counters, self.task_counter_labels, self.task_counter_timers, idx) def get_ship_name(self, _id): return self.ship_names[str(_id)]['Name'] def get_equip_name(self, cid: int) -> str: return next((i for i in self.equipment_names if i['cid'] == cid), {'title': '?'})['title'] @staticmethod def get_ship_type(_id: int) -> str: return wgv_utils.get_build_type(_id) @staticmethod def _remove_widget(parent, widget: [QLayout, QWidget]) -> None: logger.warning("Deleting widget") parent.removeWidget(widget) widget.deleteLater() widget = None return def get_exp_list_view(self) -> ExpListView: return self.exp_list_view # ================================ # Timer Related # ================================ def count_down(self, counters: list, labels: list, timers: list, idx: int) -> None: # TODO? refactor; each list view has its own countdown method? counters[idx] -= 1 if counters[idx] > 0: pass else: if counters == self.task_counters: if idx < 2: # refreshing daily/weekly timers _, _, d, w = get_tasks_countdowns() counters[0] = d counters[1] = w else: counters[idx] = 0 timers[idx].stop() self._remove_widget(self.countdowns_layout, labels[idx]) self._remove_widget(self.countdowns_layout, self.task_counter_desc_labels[idx]) return elif counters == self.bath_list_view.get_counters(): counters[idx] = 0 timers[idx].stop() self.bath_list_view.update_item(idx, 0, "Repairing Dock Unused") self.bath_list_view.update_item(idx, 1, "--:--:--") elif counters == self.exp_list_view.get_counters(): counters[idx] = 0 timers[idx].stop() # To avoid "Idling" (uninitialized) issue labels[idx].setText(str(timedelta(seconds=counters[idx]))) if self.is_realrun and self.is_exp_auto: self.exp_list_view.auto_restart(idx) else: # otherwise will cause problem pass else: counters[idx] = 0 timers[idx].stop() labels[idx].setText(str(timedelta(seconds=counters[idx]))) def start_new_timer(self, counters: list, labels: list, timers: list, idx: int) -> None: """ Creates a QTimer() object and auto connects to 1 sec count down. Then auto start """ tr = QTimer() tr.setInterval(1000) tr.timeout.connect(lambda: self.count_down(counters, labels, timers, idx)) if counters == self.task_counters: timers.append(tr) else: timers[idx] = tr tr.start() def _init_task_counters(self) -> None: self.start_new_timer(self.task_counters, self.task_counter_labels, self.task_counter_timers, 0) self.start_new_timer(self.task_counters, self.task_counter_labels, self.task_counter_timers, 1) def _process_timer_data(self, _data, view, func, item_id, counters, labels, timers) -> None: for i, v in enumerate(_data): if v["locked"] == 0: if "endTime" in v: _left_time = _calc_left_time(v["endTime"]) counters[i] = _left_time self.start_new_timer(counters, labels, timers, i) val1 = func(v[item_id]) view.update_item(i, 0, val1) else: view.update_item(i, 0, "Unused") view.update_item(i, 1, "--:--:--") else: pass # ================================ # Signals # ================================ @pyqtSlot(dict) def on_received_resource(self, data: dict) -> None: if data is not None: def _get_item_by_id(item_id: int) -> int: return next((i for i in x if i["itemCid"] == item_id), {"num": 0})["num"] u = data["userVo"] x = data["packageVo"] self.table_model.update_fuel(u["oil"]) self.table_model.update_ammo(u["ammo"]) self.table_model.update_steel(u["steel"]) self.table_model.update_bauxite(u["aluminium"]) self.table_model.update_gold(u["gold"]) self.table_model.update_repair(_get_item_by_id(541)) self.table_model.update_build(_get_item_by_id(141)) self.table_model.update_bp_construct(_get_item_by_id(241)) self.table_model.update_bp_dev(_get_item_by_id(741)) self.table_model.update_revive(_get_item_by_id(66641)) self.table_model.update_DD(_get_item_by_id(10441)) self.table_model.update_CA(_get_item_by_id(10341)) self.table_model.update_BB(_get_item_by_id(10241)) self.table_model.update_CV(_get_item_by_id(10141)) self.table_model.update_SS(_get_item_by_id(10541)) self.table_model.write_csv() @pyqtSlot(dict) def update_lvl_label(self, x: dict) -> None: # userLevelVo if x is not None: self.lvl_label.setText("Lv. " + str(x["level"])) lvl_tooltip = str(x["exp"]) + " / " + str(x["nextLevelExpNeed"]) self.lvl_label.setToolTip(lvl_tooltip) @pyqtSlot(dict) def on_received_name(self, data: dict) -> None: if data is not None: x = data["userVo"]["detailInfo"] self.name_label.setText(x["username"]) name_tooltip = "resource soft cap = " + str(data["userVo"]["resourcesTops"][0]) self.name_label.setToolTip(name_tooltip) self.update_lvl_label(x) ship_icon = get_data_path('assets/icons/ship_16.png') ship_str = f"<html><img src='{ship_icon}'></html> " + str(x["shipNum"]) + " / " + str(x["shipNumTop"]) self.ship_count_label.setText(ship_str) equip_icon = get_data_path('assets/icons/equip_16.png') equip_str = f"<html><img src='{equip_icon}'></html> " + str(x["equipmentNum"]) + " / " + str(x["equipmentNumTop"]) self.equip_count_label.setText(equip_str) collect_icon = get_data_path('assets/icons/collect_16.png') collect_str = f"<html><img src='{collect_icon}'></html> " + str(len(data["unlockShip"])) + " / " + str(x["basicShipNum"]) self.collect_count_label.setText(collect_str) self.sign_widget.setText(data["friendVo"]["sign"]) def update_one_expedition(self, data: dict) -> None: # Input = pveExploreVo['levels'][_idx] _idx = int(data['fleetId'])-5 _exp_counters = self.exp_list_view.get_counters() _exp_counters[_idx] = _calc_left_time(data["endTime"]) self.start_new_timer(_exp_counters, self.exp_list_view.get_counter_labels(), self.exp_list_view.get_counter_timers(), _idx) n = "Fleet #" + data["fleetId"] + " " + data["exploreId"].replace("000", "-") self.exp_list_view.update_item(_idx, 0, n) def cancel_one_expedition(self, fleet_idx: int) -> None: self.exp_list_view.update_item(fleet_idx, 0, EXP_LABEL_L) self.exp_list_view.update_item(fleet_idx, 1, EXP_LABEL_R) self.exp_list_view.get_counters()[fleet_idx] = 0 self.exp_list_view.get_counter_timers()[fleet_idx].stop() def update_expeditions(self, data: dict) -> None: if data is not None: p = sorted(data["levels"], key=lambda x: int(x['fleetId']), reverse=False) for _, val in enumerate(p): self.update_one_expedition(val) @pyqtSlot(dict) def on_received_lists(self, data: dict) -> None: if data is not None: self._process_timer_data(data["repairDockVo"], self.bath_list_view, self.get_ship_name, "shipId", self.bath_list_view.get_counters(), self.bath_list_view.get_counter_labels(), self.bath_list_view.get_counter_timers()) self._process_timer_data(data["dockVo"], self.build_list_view, self.get_ship_type, "shipType", self.build_list_view.get_counters(), self.build_list_view.get_counter_labels(), self.build_list_view.get_counter_timers()) self._process_timer_data(data["equipmentDockVo"], self.dev_list_view, self.get_equip_name, "equipmentCid", self.dev_list_view.get_counters(), self.bath_list_view.get_counter_labels(), self.dev_list_view.get_counter_timers()) self.update_expeditions(data["pveExploreVo"]) @pyqtSlot(dict) def on_received_tasks(self, data: dict) -> None: if data is not None: t = data["taskVo"] for i in t: stat = str(i["condition"][0]["finishedAmount"]) + " / " + str(i["condition"][0]["totalAmount"]) desc = wgv_utils.clear_desc(i["desc"]) if '#' in desc: # TODO: (lowest priority) how to find `#s10030711#n` ? looks like not the same ID in docks desc = re.sub(r'\[[^]]*\]', i["title"], desc) else: pass if i['end_time'] != "": desc += "\nEvent End On: " + i['end_time'] lim_flag = True else: lim_flag = False prefix = TASK_TYPE[i['type']] title = f"{prefix}\t{i['title']}" self.task_list_view.add_item(title, stat, desc, lim_flag) m = data["marketingData"]["activeList"] for i in m: self.add_task_countdown(i["title"], i["left_time"], len(self.task_counters)) # ================================ # Events # ================================ def resizeEvent(self, event: QResizeEvent) -> None: # overriding resizeEvent() method self.sig_resized.emit() return super(SideDock, self).resizeEvent(event) def closeEvent(self, event: QCloseEvent) -> None: cb = QCheckBox('show on start-up') if self.qsettings.contains(QKEYS.UI_SIDEDOCK) is True: cb.setChecked(self.qsettings.value(QKEYS.UI_SIDEDOCK, type=bool) is True) else: pass box = QMessageBox(QMessageBox.Question, "INFO", "Do you want to close side dock?\n(Can re-open in View menu)", QMessageBox.Yes | QMessageBox.No, self) box.setStyleSheet(wgv_utils.get_color_scheme()) box.setDefaultButton(QMessageBox.No) box.setCheckBox(cb) if box.exec() == QMessageBox.Yes: event.accept() self.sig_closed.emit() else: event.ignore() self.qsettings.setValue(QKEYS.UI_SIDEDOCK, cb.isChecked()) def update_geometry(self) -> None: y = int(0.03 * self.user_screen_h) h = int(0.05 * self.user_screen_h) gap = int(0.01 * self.user_screen_h) self.name_layout_widget.setGeometry(0, y, self.geometry().width(), h) self.sign_widget.setGeometry(0, y + h, self.geometry().width(), y) y = int(2 * y + h + gap) h = int(0.09 * self.user_screen_h) self.table_view.setGeometry(0, y, self.geometry().width(), h) for i in range(5): self.table_view.setColumnWidth(i, self.geometry().width() / 5) for i in range(3): self.table_view.setRowHeight(i, h / 3) y = y + h + gap self.bath_list_view_widget.setGeometry(0, y, self.geometry().width(), h) y = y + h + gap self.triple_list_view_widget.setGeometry(0, y, self.geometry().width(), h) y = y + h + gap h = int(0.19 * self.user_screen_h) self.task_panel_widget.setGeometry(0, y, self.geometry().width(), h)
class QtComparePanel(QWidget): filterChanged = pyqtSignal(str) def __init__(self, parent=None): super(QtComparePanel, self).__init__(parent) self.visibility_flags = [] self.visibility_buttons = [] self.labels_projects = [] self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.setMinimumWidth(400) self.setMinimumHeight(100) self.data_table = QTableView() self.data_table.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.data_table.setSelectionMode(QAbstractItemView.MultiSelection) self.data_table.setSelectionMode(QAbstractItemView.ExtendedSelection) self.data_table.setSelectionBehavior(QAbstractItemView.SelectRows) self.model = None self.data = None self.combodelegate1 = ComboBoxItemDelegate(self.data_table) self.combodelegate2 = ComboBoxItemDelegate(self.data_table) lblFilter = QLabel("Filter: ") self.comboboxFilter = QComboBox() self.comboboxFilter.setMinimumWidth(80) self.comboboxFilter.addItem("All") self.comboboxFilter.addItem("Same") self.comboboxFilter.addItem("Born") self.comboboxFilter.addItem("Dead") self.comboboxFilter.addItem("Grow") self.comboboxFilter.addItem("Shrink") filter_layout = QHBoxLayout() filter_layout.addWidget(lblFilter) filter_layout.addWidget(self.comboboxFilter) filter_layout.addStretch() layout = QVBoxLayout() layout.addLayout(filter_layout) layout.addWidget(self.data_table) self.setLayout(layout) self.correspondences = None self.data = None self.comboboxFilter.currentTextChanged.connect(self.changeFilter) def setTable(self, project, img1idx, img2idx): self.correspondences = project.getImagePairCorrespondences( img1idx, img2idx) self.data = self.correspondences.data self.model = TableModel(self.data) self.sortfilter = QSortFilterProxyModel(self) self.sortfilter.setSourceModel(self.model) self.data_table.setModel(self.sortfilter) self.data_table.setVisible(False) self.data_table.verticalHeader().hide() self.data_table.resizeColumnsToContents() self.data_table.setVisible(True) self.data_table.setItemDelegateForColumn(5, self.combodelegate1) self.data_table.setItemDelegateForColumn(6, self.combodelegate2) self.data_table.setEditTriggers(QAbstractItemView.DoubleClicked) self.data_table.update() self.data_table.setStyleSheet( "QHeaderView::section { background-color: rgb(40,40,40) }") def updateData(self, corr): if corr is None: return self.correspondences = corr self.sortfilter.beginResetModel() self.model.beginResetModel() self.model._data = corr.data self.sortfilter.endResetModel() self.model.endResetModel() self.data_table.update() def selectRows(self, rows): self.data_table.clearSelection() indexes = [self.model.index(r, 0) for r in rows] mode = QItemSelectionModel.Select | QItemSelectionModel.Rows [ self.data_table.selectionModel().select(index, mode) for index in indexes ] if len(rows) > 0: value = self.data_table.horizontalScrollBar().value() column = self.data_table.columnAt(value) self.data_table.scrollTo(self.data_table.model().index( rows[0], column)) @pyqtSlot(QModelIndex) def getData(self, index): pass #column = index.column() #row = index.row() #self.data_table.model().index(row, column).data() @pyqtSlot(str) def changeFilter(self, txt): if self.data is None: return if txt == 'All': self.sortfilter.setFilterRegExp(QRegExp()) else: self.sortfilter.setFilterKeyColumn(5) self.sortfilter.setFilterRegExp(txt.lower()) self.sortfilter.setFilterRole(Qt.DisplayRole) self.filterChanged.emit(txt.lower())
class WidgetDemo(QWidget): def __init__(self, model, rows, columns): super(WidgetDemo, self).__init__() self.init_rows = rows self.init_columns = columns self.model = model self.initUI(model) def initUI(self, model): self.resize(800, 800) self.res_pos = [] self.focus_pos = None self.var_list = None self.signal = MySignal() #菜单栏 self.menu = QMenuBar() self.file = self.menu.addMenu('文件') self.edit = self.menu.addMenu('编辑') self.view = self.menu.addMenu('视图') self.help = self.menu.addMenu('帮助') #各菜单下的子菜单 #文件菜单下的子菜单 # self.new = self.file.addAction('新建') self.open = self.file.addAction('打开') self.save = self.file.addAction('保存') # self.save_as = self.file.addAction('另存为') #编辑菜单下的子菜单 self.cut = self.edit.addAction('剪切') self.copy = self.edit.addAction('复制') self.paste = self.edit.addAction('粘贴') self.delete = self.edit.addAction('删除') self.find = self.edit.addAction('查找') self.replace = self.edit.addAction('替换') # 快捷键 self.open.setShortcut('Ctrl+O') self.save.setShortcut('Ctrl+S') # self.new.setShortcut('Ctrl+N') self.find.setShortcut('Ctrl+F') #视图菜单下的子菜单 self.tool_view = QAction('工具栏', checkable=True) self.tool_view.setChecked(True) self.view.addAction(self.tool_view) self.statu_view = QAction('状态栏', checkable=True) self.statu_view.setChecked(True) self.view.addAction(self.statu_view) #帮助菜单下的子菜单 self.about = self.help.addAction('关于') #工具栏 self.tool_bar = QToolBar() # self.tool_bar.addAction(self.new) self.tool_bar.addAction(self.open) self.tool_bar.addAction(self.save) self.tool_bar.addAction(self.cut) self.tool_bar.addAction(self.copy) self.tool_bar.addAction(self.paste) self.tool_bar.addAction(self.find) # self.setting = QAction('变量设置') # self.setting.setEnabled(False) # self.tool_bar.addAction(self.setting) # self.tool_bar.addAction(self.replace) # #tool文本显示在下方 # self.tool_bar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) #findWidge self.find_widget = FindWidget() self.find_widget.hide() #表格 self.table_view = QTableView() self.table_view.setModel(self.model) #状态栏 self.status_bar = QStatusBar() self.status_bar.showMessage('状态栏') # 右键菜单栏 self.setContextMenuPolicy(Qt.CustomContextMenu) self.context_menu = QMenu() self.addRow_action = self.context_menu.addAction('增加一行') self.addRow_action.triggered.connect(self.addRow) self.delRow_action = self.context_menu.addAction('删除一行') self.delRow_action.triggered.connect( lambda: self.model.removeRow(self.table_view.currentIndex().row())) self.addColumn_action = self.context_menu.addAction('增加一列') self.addColumn_action.triggered.connect(self.addColumn) self.delColumn_action = self.context_menu.addAction('删除一列') self.delColumn_action.triggered.connect( lambda: self.model.removeColumn(self.table_view.currentIndex(). column())) self.customContextMenuRequested.connect(self.rightMenuShow) #创建布局 layout = QVBoxLayout() layout.addWidget(self.menu) layout.addWidget(self.tool_bar) layout.addWidget(self.find_widget) layout.addWidget(self.table_view) layout.addWidget(self.status_bar) self.setLayout(layout) #关联信号 self.open.triggered.connect(self.triggeredOpen) self.save.triggered.connect(self.triggeredSave) self.tool_view.triggered.connect(self.triggeredView) self.statu_view.triggered.connect(self.triggeredView) # self.new.triggered.connect(self.triggeredNew) self.find.triggered.connect(self.triggeredFind) self.find_widget.search.triggered.connect(self.dataLocation) self.find_widget.down_aciton.triggered.connect(self.downAcitonLocation) self.find_widget.up_aciton.triggered.connect(self.upAcitonLocation) self.find_widget.close_aciton.triggered.connect(self.triggeredHideFind) self.find_widget.repalce_button.clicked.connect(self.onClickReplace) self.find_widget.repalceAll_button.clicked.connect( self.onClickReplaceAll) # self.setting.triggered.connect(self.triggeredSetting) #美化 icon = QIcon() icon.addPixmap(QPixmap('./image/打开.png'), QIcon.Normal, QIcon.Off) self.open.setIcon(icon) icon.addPixmap(QPixmap('./image/保存.png'), QIcon.Normal, QIcon.Off) self.save.setIcon(icon) # icon.addPixmap(QPixmap('./image/新建.png'), QIcon.Normal, QIcon.Off) # self.new.setIcon(icon) icon.addPixmap(QPixmap('./image/剪切.png'), QIcon.Normal, QIcon.Off) self.cut.setIcon(icon) icon.addPixmap(QPixmap('./image/复制.png'), QIcon.Normal, QIcon.Off) self.copy.setIcon(icon) icon.addPixmap(QPixmap('./image/粘贴.png'), QIcon.Normal, QIcon.Off) self.paste.setIcon(icon) icon.addPixmap(QPixmap('./image/查找1.png'), QIcon.Normal, QIcon.Off) self.find.setIcon(icon) # icon.addPixmap(QPixmap('./image/设置.png'), QIcon.Normal, QIcon.Off) # self.setting.setIcon(icon) # icon.addPixmap(QPixmap('./image/替换.png'), QIcon.Normal, QIcon.Off) # self.replace.setIcon(icon) def showProgress(self, msg): self.status_bar.showMessage(msg) def loadData(self, model): print('load...') self.model = model self.table_view.setModel(self.model) qApp.processEvents() def triggeredOpen(self): self.status_bar.showMessage('打开文件', 5000) self.dialog = QFileDialog() self.dialog.setFileMode(QFileDialog.AnyFile) dir = r'D:/Learn-python-notes/projects/demo/Work/TCM_DSAS/data' self.dialog.setDirectory(dir) self.dialog.setFilter(QDir.Files) if self.dialog.exec_(): try: file_name = self.dialog.selectedFiles()[0] #这里线程实例化一定要实例化成员变量,否则线程容易销毁 self.thread = ReaderExcelThread(file_name) self.thread.standarModel_signal.connect(self.loadData) self.thread.progressRate_signal.connect(self.showProgress) self.thread.finished_signal.connect(self.thread.quit) self.thread.start() # self.setting.setEnabled(True) except Exception as e: print(e) pass def triggeredSave(self): self.status_bar.showMessage('保存文件', 5000) file_path, _ = QFileDialog.getSaveFileName( self, '保存文件', './data', 'ALL Files(*);;xlsx(*.xlsx);;xls(*.xls);;csv(*.csv)') if file_path == '': return # 文件中写入数据 try: self.write_thread = WriteExcelThread(file_path, self.model) self.write_thread.start_signal.connect(self.showProgress) self.write_thread.end_signal.connect(self.write_thread.quit) self.write_thread.start() self.status_bar.showMessage('保存完毕!') except Exception as e: print(e) #状态栏与工具栏的显示和隐藏 def triggeredView(self, state): sender = self.sender().text() if sender == '工具栏': if state: self.tool_bar.show() else: self.tool_bar.hide() else: if state: self.status_bar.show() else: self.status_bar.hide() # def triggeredNew(self): # print('New') # pass def triggeredFind(self): self.find_widget.show() #重载信号,实现ESC隐藏查找窗口 def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.find_widget.hide() #聚焦到某个cell def positionFocus(self, x, y): self.table_view.verticalScrollBar().setSliderPosition(x) self.table_view.horizontalScrollBar().setSliderPosition(y) self.table_view.openPersistentEditor(self.model.index(x, y)) self.table_view.setFocus() #得到所以匹配项的位置 def dataLocation(self): self.changeCellColor() text = self.find_widget.line_edit1.text() self.res_pos = [] flag = 0 rows = self.model.rowCount() columns = self.model.columnCount() try: for row in range(rows): for column in range(columns): if text == self.model.index(row, column).data(): self.res_pos.append((row, column)) item = self.model.item(row, column) item.setBackground(QColor(255, 255, 0)) item.setForeground(QColor(255, 0, 0)) #转到到第一个匹配值的位置,并处于可编辑状态 if not flag: flag = 1 self.positionFocus(row, column) self.focus_pos = 0 except Exception as e: print(e) #向下跳转 def downAcitonLocation(self): cnt = len(self.res_pos) if cnt == 0 or self.focus_pos == cnt - 1: return try: self.table_view.closePersistentEditor( self.model.index(self.res_pos[self.focus_pos][0], self.res_pos[self.focus_pos][1])) x, y = self.res_pos[self.focus_pos + 1] self.positionFocus(x, y) self.focus_pos += 1 except Exception as e: print(e) # 向上跳转 def upAcitonLocation(self): cnt = len(self.res_pos) if cnt == 0 or self.focus_pos == 0: return try: self.table_view.closePersistentEditor( self.model.index(self.res_pos[self.focus_pos][0], self.res_pos[self.focus_pos][1])) x, y = self.res_pos[self.focus_pos - 1] self.positionFocus(x, y) self.focus_pos -= 1 except Exception as e: print(e) def triggeredHideFind(self): self.changeCellColor() self.find_widget.hide() def changeCellColor(self): if self.res_pos is not None and len(self.res_pos): self.table_view.closePersistentEditor( self.model.index(self.res_pos[self.focus_pos][0], self.res_pos[self.focus_pos][1])) for item in self.res_pos: x, y = item item = self.model.item(x, y) item.setBackground(QColor(255, 255, 255)) item.setForeground(QColor(0, 0, 0)) def onClickReplace(self): cnt = len(self.res_pos) text = self.find_widget.line_edit2.text() if self.res_pos is None or cnt == 0: return try: x, y = self.res_pos[self.focus_pos] self.model.setItem(x, y, QStandardItem(text)) except Exception as e: print(e) def onClickReplaceAll(self): cnt = len(self.res_pos) if self.res_pos is None or cnt == 0: return try: text = self.find_widget.line_edit2.text() for x, y in self.res_pos: self.model.setItem(x, y, QStandardItem(text)) except Exception as e: print(e) # #设置变量 # def triggeredSetting(self): # self.getVar_thread = GetVarThread(self.model) # self.getVar_thread.send_signal.connect(self.initVarList) # self.getVar_thread.end_signal.connect(self.getVar_thread.quit) # self.getVar_thread.start() # # # # def initVarList(self,var_list): # dialog = VariableSettingWindow.VariableSettingWindowDemo(var_list) # dialog.signal.sender.connect(self.getVarList) # dialog.show() # # def getVarList(self,lst): # self.var_list = lst # print(lst) def addRow(self): # 当前行的下方添加一行 try: self.model.insertRows(self.table_view.currentIndex().row() + 1, 1) except Exception as e: print(e) def addColumn(self): self.model.insertColumns(self.table_view.currentIndex().column() + 1, 1) def rightMenuShow(self): try: #菜单显示的位置 self.context_menu.popup(QCursor.pos()) self.context_menu.show() except Exception as e: print(e)
class WidgetDemo(QWidget): def __init__(self,mode): super(WidgetDemo,self).__init__() self.mode = mode self.initUI(mode) def initUI(self,mode): self.resize(800,800) #这里初始化,便于直接输入数据 self.data = [['']*100 for i in range(15000)] self.res_pos = [] self.focus_pos = None self.var_list = None #菜单栏 self.menu = QMenuBar() self.file = self.menu.addMenu('文件') self.edit = self.menu.addMenu('编辑') self.view = self.menu.addMenu('视图') self.help = self.menu.addMenu('帮助') #各菜单下的子菜单 #文件菜单下的子菜单 self.new = self.file.addAction('新建') self.open = self.file.addAction('打开') self.save = self.file.addAction('保存') # self.save_as = self.file.addAction('另存为') #编辑菜单下的子菜单 self.cut = self.edit.addAction('剪切') self.copy = self.edit.addAction('复制') self.paste = self.edit.addAction('粘贴') self.delete = self.edit.addAction('删除') self.find = self.edit.addAction('查找') self.replace = self.edit.addAction('替换') # 快捷键 self.open.setShortcut('Ctrl+O') self.save.setShortcut('Ctrl+S') self.new.setShortcut('Ctrl+N') self.find.setShortcut('Ctrl+F') #视图菜单下的子菜单 self.tool_view = QAction('工具栏',checkable=True) self.tool_view.setChecked(True) self.view.addAction(self.tool_view) self.statu_view = QAction('状态栏',checkable=True) self.statu_view.setChecked(True) self.view.addAction(self.statu_view) #帮助菜单下的子菜单 self.about = self.help.addAction('关于') #工具栏 self.tool_bar = QToolBar() self.tool_bar.addAction(self.new) self.tool_bar.addAction(self.open) self.tool_bar.addAction(self.save) self.tool_bar.addAction(self.cut) self.tool_bar.addAction(self.copy) self.tool_bar.addAction(self.paste) self.tool_bar.addAction(self.find) self.setting = QAction('变量设置') self.setting.setEnabled(False) self.tool_bar.addAction(self.setting) # self.tool_bar.addAction(self.replace) # #tool文本显示在下方 # self.tool_bar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) #findWidge self.find_widget = FindWidget() self.find_widget.hide() #表格 self.table_view = QTableView() self.table_view.setModel(self.mode) #状态栏 self.status_bar = QStatusBar() self.status_bar.showMessage('这是一个状态栏') #创建布局 layout = QVBoxLayout() layout.addWidget(self.menu) layout.addWidget(self.tool_bar) layout.addWidget(self.find_widget) layout.addWidget(self.table_view) layout.addWidget(self.status_bar) self.setLayout(layout) #关联信号 self.open.triggered.connect(self.triggeredOpen) self.save.triggered.connect(self.triggeredSave) self.mode.itemChanged.connect(self.dealItemChanged) self.tool_view.triggered.connect(self.triggeredView) self.statu_view.triggered.connect(self.triggeredView) self.new.triggered.connect(self.triggeredNew) self.find.triggered.connect(self.triggeredFind) self.find_widget.search.triggered.connect(self.dataLocation) self.find_widget.down.triggered.connect(self.downLocation) self.find_widget.up.triggered.connect(self.upLocation) self.find_widget.close.triggered.connect(self.triggeredHideFind) self.find_widget.repalce_button.clicked.connect(self.onClickReplace) self.find_widget.repalceAll_button.clicked.connect(self.onClickReplaceAll) self.setting.triggered.connect(self.triggeredSetting) #美化 icon = QIcon() icon.addPixmap(QPixmap('../image/打开.png'), QIcon.Normal, QIcon.Off) self.open.setIcon(icon) icon.addPixmap(QPixmap('../image/保存.png'), QIcon.Normal, QIcon.Off) self.save.setIcon(icon) icon.addPixmap(QPixmap('../image/新建.png'), QIcon.Normal, QIcon.Off) self.new.setIcon(icon) icon.addPixmap(QPixmap('../image/剪切.png'), QIcon.Normal, QIcon.Off) self.cut.setIcon(icon) icon.addPixmap(QPixmap('../image/复制.png'), QIcon.Normal, QIcon.Off) self.copy.setIcon(icon) icon.addPixmap(QPixmap('../image/粘贴.png'), QIcon.Normal, QIcon.Off) self.paste.setIcon(icon) icon.addPixmap(QPixmap('../image/查找1.png'), QIcon.Normal, QIcon.Off) self.find.setIcon(icon) icon.addPixmap(QPixmap('../image/设置.png'), QIcon.Normal, QIcon.Off) self.setting.setIcon(icon) # icon.addPixmap(QPixmap('../image/替换.png'), QIcon.Normal, QIcon.Off) # self.replace.setIcon(icon) def triggeredOpen(self): self.status_bar.showMessage('打开文件',5000) self.dialog = QFileDialog() self.dialog.setFileMode(QFileDialog.AnyFile) dir = r'../data' self.dialog.setDirectory(dir) self.dialog.setFilter(QDir.Files) if self.dialog.exec_(): try: start = time.time() file_name = self.dialog.selectedFiles()[0] #这里读取数据返回列表便于表格中数据的更新 data_list = read_xlsx(file_name) self.data = data_list self.mode = QStandardItemModel() for rows in data_list: row = [QStandardItem(str(cell)) for cell in rows] self.mode.appendRow(row) self.mode.itemChanged.connect(self.dealItemChanged) self.table_view.setModel(self.mode) # self.table_view.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.table_view.resizeColumnsToContents() self.table_view.resizeRowsToContents() end = time.time() self.status_bar.showMessage('数据加载完毕,耗时{}秒'.format(end-start)) self.setting.setEnabled(True) except Exception as e: print(e) pass def triggeredSave(self): self.status_bar.showMessage('保存文件', 5000) file_path, _ = QFileDialog.getSaveFileName(self, '保存文件', '../data', 'ALL Files(*);;xlsx(*.xlsx);;xls(*.xls);;csv(*.csv)') if file_path == '': return # 文件中写入数据 try: wb = workbook.Workbook() wb.encoding = 'utf-8' wa = wb.active for item in self.data: # 过滤无效数据 try: if ''.join(item) == '': continue except: pass wa.append(item) wb.save(file_path) self.status_bar.showMessage('保存完毕!') except Exception as e: print(e) #数据变化信号处理 def dealItemChanged(self,item): try: row,column = item.row(),item.column() self.data[row][column] = item.text() except Exception as e: print(e) pass #状态栏与工具栏的显示和隐藏 def triggeredView(self,state): sender = self.sender().text() if sender == '工具栏': if state: self.tool_bar.show() else: self.tool_bar.hide() else: if state: self.status_bar.show() else: self.status_bar.hide() def triggeredNew(self): print('New') pass def triggeredFind(self): self.find_widget.show() #重载信号,实现ESC隐藏查找窗口 def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.find_widget.hide() #聚焦到某个cell def positionFocus(self,x,y): self.table_view.verticalScrollBar().setSliderPosition(x) self.table_view.horizontalScrollBar().setSliderPosition(y) self.table_view.openPersistentEditor(self.mode.index(x, y)) self.table_view.setFocus() #得到所以匹配项的位置 def dataLocation(self): text = self.find_widget.line_edit1.text() self.res_pos = [] flag = 0 for i,row in enumerate(self.data): try: if ''.join(row) == '': continue except: pass for j,cell in enumerate(row): if text == str(cell): # print(i,j) self.res_pos.append((i,j)) item = self.mode.item(i,j) item.setBackground(QColor(255, 255, 0)) item.setForeground(QColor(255, 0, 0)) #转到到第一个匹配值的位置,并处于可编辑状态 if not flag: flag = 1 self.positionFocus(i,j) self.focus_pos = 0 #向下跳转 def downLocation(self): cnt = len(self.res_pos) if cnt == 0 or self.focus_pos == cnt-1: return try: self.table_view.closePersistentEditor( self.mode.index(self.res_pos[self.focus_pos][0],self.res_pos[self.focus_pos][1])) x, y = self.res_pos[self.focus_pos + 1] self.positionFocus(x,y) self.focus_pos += 1 except Exception as e: print(e) # 向上跳转 def upLocation(self): cnt = len(self.res_pos) if cnt == 0 or self.focus_pos == 0: return try: self.table_view.closePersistentEditor( self.mode.index(self.res_pos[self.focus_pos][0], self.res_pos[self.focus_pos][1])) x, y = self.res_pos[self.focus_pos - 1] self.positionFocus(x, y) self.focus_pos -= 1 except Exception as e: print(e) def triggeredHideFind(self): if self.res_pos is not None and len(self.res_pos): self.table_view.closePersistentEditor( self.mode.index(self.res_pos[self.focus_pos][0], self.res_pos[self.focus_pos][1])) for item in self.res_pos: x,y = item item = self.mode.item(x,y) item.setBackground(QColor(255, 255, 255)) item.setForeground(QColor(0, 0, 0)) self.find_widget.hide() #不清楚如何修改cell值,替换功能暂时无法上线 def onClickReplace(self): print('-'*50) cnt = len(self.res_pos) if self.res_pos is None or cnt == 0: return try: x, y = self.res_pos[self.focus_pos] text = self.find_widget.line_edit2.text() self.data[x][y] = text print(self.data[x][y]) except Exception as e: print(e) def onClickReplaceAll(self): pass #设置变量 def triggeredSetting(self): # 设置变量窗口 var_list = self.data[0] if self.data[0][0]!='' else self.data[0][1:] self.dialog = VariableSettingWindow.VariableSettingWindowDemo(var_list) self.dialog.signal.sender.connect(self.getVarList) self.dialog.show() def getVarList(self,lst): self.var_list = lst