def __init__(self, arg=None): super(Table, self).__init__(arg) # 设置窗体的标题和初始大小 self.setWindowTitle("QTableView 表格视图控件的例子") self.resize(600, 300) # 准备数据模型,设置数据层次结构为6行5列 self.model = QStandardItemModel(6, 5) # 设置数据栏名称 self.model.setHorizontalHeaderLabels(['标题1', '标题2', '标题3', '标题4', '标题5']) for row in range(6): for column in range(5): item = QStandardItem("行 %s, 列 %s" % (row, column )) self.model.setItem(row, column, item) # 实例化表格视图,设置模型为自定义的模型 self.tableView = QTableView() self.tableView.setModel(self.model) ############ 下面代码让表格 100% 的填满窗口 self.tableView.horizontalHeader().setStretchLastSection(True) self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) # 局部布局 vboxLayout = QVBoxLayout() vboxLayout.addWidget(self.tableView) # 全局布局 wl = QVBoxLayout(self) wl.addLayout(vboxLayout)
def __init__(self): super(Exp, self).__init__() self.setupUi(self) self.__last_pos = self.pos() self.__trigger_menu = TriggerMenu(self, EssStandardItem()) self.__cur_parent = EssStandardItem() self.setWindowFlag(Qt.FramelessWindowHint) self.__ess_file_model = QStandardItemModel(1, len(ITEM_PROPERTIES)) self.__ess_file_model.setHorizontalHeaderLabels(ITEM_PROPERTIES) update_item_data( ItemSettingContext("$/", self.__ess_file_model.setItem)) self.fileTreeView.setModel(self.__ess_file_model) self.fileTreeView.header().resizeSection(0, 300) self.fileTreeView.header().resizeSection(1, 120) self.resize_t = ResizeType.EITHER self.bt_close.clicked.connect(self.close) self.bt_copy.clicked.connect(self.on_copy_button_clicked) self.bt_copy.mouseDoubleClickEvent = self.on_copy_button_double_clicked self.bt_maximize.clicked.connect(self.on_bt_maximize_clicked) self.bt_minimize.clicked.connect(self.showMinimized) self.fileTreeView.doubleClicked.connect(self.on_item_double_clicked) self.fileTreeView.clicked.connect(self.on_item_clicked) self.fileTreeView.edit = self.edit self.fileTreeView.customContextMenuRequested.connect( self.on_item_triggered) self.lb_cpath.setText("$/")
def setup_key_list(self, key_list): self.keyListView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.list_model = QStandardItemModel(self.keyListView) for key in key_list: item = self.make_item(key) self.list_model.appendRow(item) self.keyListView.setModel(self.list_model) self.keyListView.setContextMenuPolicy(Qt.CustomContextMenu) self.keyListView.customContextMenuRequested[QPoint].connect( self.context_menu)
def dropMimeData(self, data, action, row, column, parent): # If any albums were dropped into another album, # add all the songs in the dropped album # but not the album item itself if parent.isValid(): dummy_model = QStandardItemModel() dummy_model.dropMimeData(data, action, 0, 0, QModelIndex()) indexes = [] for r in range(dummy_model.rowCount()): for c in range(dummy_model.columnCount()): index = dummy_model.index(r, c) # QStandardItemModel doesn't recognize our items as our custom classes # so we have to treat them as QStandardItems item = dummy_model.item(r, c) if item.data( CustomDataRole.ITEMTYPE) == TreeWidgetType.ALBUM: indexes += [ child.index() for child in AlbumTreeWidgetItem. getChildrenFromStandardItem(item) ] elif item.data( CustomDataRole.ITEMTYPE) == TreeWidgetType.SONG: indexes.append(index) data = dummy_model.mimeData(indexes) ret = super().dropMimeData(data, action, row, column, parent) return ret
def __init__(self, parent, rows, selected=None, disabled=None): super().__init__(parent) self.setWindowTitle("Select XDF Stream") self.model = QStandardItemModel() self.model.setHorizontalHeaderLabels( ["ID", "Name", "Type", "Channels", "Format", "Sampling Rate"]) for index, stream in enumerate(rows): items = [] for item in stream: tmp = QStandardItem() tmp.setData(item, Qt.DisplayRole) items.append(tmp) for item in items: item.setEditable(False) if disabled is not None and index in disabled: item.setFlags(Qt.NoItemFlags) self.model.appendRow(items) self.view = QTableView() self.view.setModel(self.model) self.view.verticalHeader().setVisible(False) self.view.horizontalHeader().setStretchLastSection(True) self.view.setShowGrid(False) self.view.setSelectionMode(QAbstractItemView.SingleSelection) self.view.setSelectionBehavior(QAbstractItemView.SelectRows) if selected is not None: self.view.selectRow(selected) self.view.setSortingEnabled(True) self.view.sortByColumn(0, Qt.AscendingOrder) vbox = QVBoxLayout(self) vbox.addWidget(self.view) hbox = QHBoxLayout() self._effective_srate = QCheckBox("Use effective sampling rate") self._effective_srate.setChecked(True) hbox.addWidget(self._effective_srate) self._prefix_markers = QCheckBox("Prefix markers with stream ID") self._prefix_markers.setChecked(False) if not disabled: self._prefix_markers.setEnabled(False) hbox.addWidget(self._prefix_markers) vbox.addLayout(hbox) self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) vbox.addWidget(self.buttonbox) self.buttonbox.accepted.connect(self.accept) self.buttonbox.rejected.connect(self.reject) self.resize(775, 650) self.view.setColumnWidth(0, 90) self.view.setColumnWidth(1, 200) self.view.setColumnWidth(2, 140)
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 _create_model(parent, serialized_bookmarks): result = QStandardItemModel(0, 1, parent) last_folder_item = None for entry in serialized_bookmarks: if len(entry) == 1: last_folder_item = _create_folder_item(entry[0]) result.appendRow(last_folder_item) else: url = QUrl.fromUserInput(entry[0]) title = entry[1] icon = QIcon(entry[2]) if len(entry) > 2 and entry[2] else None last_folder_item.appendRow(_create_item(url, title, icon)) return result
def createMailModel(parent): model = QStandardItemModel(0, 3, parent) model.setHeaderData(0, Qt.Horizontal, "Subject") model.setHeaderData(1, Qt.Horizontal, "Sender") model.setHeaderData(2, Qt.Horizontal, "Date") addMail(model, "Happy New Year!", "Grace K. <*****@*****.**>", QDateTime(QDate(2006, 12, 31), QTime(17, 3))) addMail(model, "Radically new concept", "Grace K. <*****@*****.**>", QDateTime(QDate(2006, 12, 22), QTime(9, 44))) addMail(model, "Accounts", "*****@*****.**", QDateTime(QDate(2006, 12, 31), QTime(12, 50))) addMail(model, "Expenses", "Joe Bloggs <*****@*****.**>", QDateTime(QDate(2006, 12, 25), QTime(11, 39))) addMail(model, "Re: Expenses", "Andy <*****@*****.**>", QDateTime(QDate(2007, 1, 2), QTime(16, 5))) addMail(model, "Re: Accounts", "Joe Bloggs <*****@*****.**>", QDateTime(QDate(2007, 1, 3), QTime(14, 18))) addMail(model, "Re: Accounts", "Andy <*****@*****.**>", QDateTime(QDate(2007, 1, 3), QTime(14, 26))) addMail(model, "Sports", "Linda Smith <*****@*****.**>", QDateTime(QDate(2007, 1, 5), QTime(11, 33))) addMail(model, "AW: Sports", "Rolf Newschweinstein <*****@*****.**>", QDateTime(QDate(2007, 1, 5), QTime(12, 0))) addMail(model, "RE: Sports", "Petra Schmidt <*****@*****.**>", QDateTime(QDate(2007, 1, 5), QTime(12, 1))) return model
class ChannelPropertiesDialog(QDialog): def __init__(self, parent, info, title="Channel Properties"): super().__init__(parent) self.setWindowTitle(title) self.model = QStandardItemModel(info["nchan"], 4) self.model.setHorizontalHeaderLabels(["#", "Label", "Type", "Bad"]) for index, ch in enumerate(info["chs"]): item = QStandardItem() item.setData(index, Qt.DisplayRole) item.setFlags(item.flags() & ~Qt.ItemIsEditable) self.model.setItem(index, 0, item) self.model.setItem(index, 1, QStandardItem(ch["ch_name"])) kind = channel_type(info, index).upper() self.model.setItem(index, 2, QStandardItem(str(kind))) bad = QStandardItem() bad.setData(ch["ch_name"] in info["bads"], Qt.UserRole) bad.setCheckable(True) bad.setEditable(False) checked = ch["ch_name"] in info["bads"] bad.setCheckState(Qt.Checked if checked else Qt.Unchecked) self.model.setItem(index, 3, bad) self.model.itemChanged.connect(bad_changed) self.proxymodel = MySortFilterProxyModel() self.proxymodel.setDynamicSortFilter(False) self.proxymodel.setSourceModel(self.model) self.view = QTableView() self.view.setModel(self.proxymodel) self.view.setItemDelegateForColumn(2, ComboBoxDelegate(self.view)) self.view.setEditTriggers(QAbstractItemView.AllEditTriggers) self.view.verticalHeader().setVisible(False) self.view.horizontalHeader().setStretchLastSection(True) self.view.setShowGrid(False) self.view.setSelectionMode(QAbstractItemView.NoSelection) self.view.setSortingEnabled(True) self.view.sortByColumn(0, Qt.AscendingOrder) vbox = QVBoxLayout(self) vbox.addWidget(self.view) self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) vbox.addWidget(self.buttonbox) self.buttonbox.accepted.connect(self.accept) self.buttonbox.rejected.connect(self.reject) self.resize(475, 650) self.view.setColumnWidth(0, 70) self.view.setColumnWidth(1, 155) self.view.setColumnWidth(2, 90)
def _set_rows( model: QtGui.QStandardItemModel, columns: Sequence[Column], tasks: Iterable[Task]) -> None: root_item = model.invisibleRootItem() for task in tasks: row = _build_row(task, columns) root_item.appendRow(row)
def create_combo_box_items(self): model = QStandardItemModel() self.ui.combo_classes.setModel(model) self.ui.combo_functions.setModel(model) for k, v in self.data.items(): cls = QStandardItem(k) model.appendRow(cls) for value in v: func = QStandardItem(value) cls.appendRow(func) def update_combo_box(index): ind = model.index(index, 0, self.ui.combo_classes.rootModelIndex()) self.ui.combo_functions.setRootModelIndex(ind) self.ui.combo_functions.show() self.ui.combo_functions.setCurrentIndex(0) self.ui.config_label.setText('') self.ui.combo_classes.currentIndexChanged.connect(update_combo_box)
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) font = QFont('monospace', 8) font.setStyleHint(QFont.Monospace) self.setFont(font) self.setShowGrid(False) self.horizontalHeader().hide() self.verticalHeader().setSectionResizeMode( QtWidgets.QHeaderView.Fixed) self.verticalHeader().setHighlightSections(False) self.horizontalHeader().setHighlightSections(False) self.verticalHeader().setSectionsClickable(False) # Don't let the user edit the table cells. self.setEditTriggers(self.NoEditTriggers) self.setSelectionBehavior(QAbstractItemView.SelectItems) self.setSelectionMode(QAbstractItemView.ContiguousSelection) self.setModel(QStandardItemModel(1, 33)) # This will store the raw data that is displayed in the hex view. self.hex_data = None # Determine how wide ASCII columns should be. self.ascii_width = self.fontMetrics().horizontalAdvance('m') # HACK: Get how much space a hex item needs by asking temporarily creating one, and then asking Qt, # because self.fontMetrics().width('mm') isn't enough, apparently, unlike above. self.model().setItem(0, 0, QStandardItem('mm')) self.resizeColumnToContents(0) self.hex_width = self.visualRect(self.model().createIndex( 0, 0)).width() # Default to 16 hex columns, with 16 ASCII columns, and one separator column, for a total of 33. self._set_bytes_per_row(16) # HACK: Get how much space is needed for 16 bytes per row by # getting the left and right bound of the left-most and right-most items, respectively. start = self.visualRect(self.model().createIndex(0, 0)).left() end = self.visualRect(self.model().createIndex(0, 32)).right() self.full_width = end - start # Record the default background color for items, since apparently that's platform dependent. # Note: Normally we can only get the default background color if there's actually an item there, # but we made one earlier to determine the value for self.hex_width, so we don't need to do it again. self.default_background_color = self.model().item(0, 0).background() self.model().setRowCount(0) self.selectionModel().selectionChanged.connect( self._selection_changed)
def get_item_by_index(index: EssModelIndex, model: QStandardItemModel) -> EssStandardItem: ancestor_indexes = [index] while index.parent() != EssModelIndex(): ancestor_indexes.append(index.parent()) index = index.parent() ancestor_indexes.reverse() item = model.invisibleRootItem() for ancestor in ancestor_indexes: item = item.child(ancestor.row(), ancestor.column()) return item
def createProgramsList(self): self.programsListModel = QStandardItemModel(0, 1, self) self.programsList = QListView() self.programsList.setModel(self.programsListModel) self.programsListPage = QWidget() self.programsListLayout = QVBoxLayout() self.programsListButtons = QHBoxLayout() self.programsListButtonNew = QPushButton("New") self.programsListButtonDelete = QPushButton("Delete") self.programsListButtonEdit = QPushButton("Edit") self.programsListButtonSet = QPushButton("Set") self.programsListButtons.addWidget(self.programsListButtonNew) self.programsListButtons.addWidget(self.programsListButtonSet) self.programsListButtons.addWidget(self.programsListButtonEdit) self.programsListButtons.addWidget(self.programsListButtonDelete) self.programsListLayout.addLayout(self.programsListButtons) self.programsListLayout.addWidget(self.programsList) self.programsListPage.setLayout(self.programsListLayout) self.programsListButtonNew.clicked.connect(self.newProgram) self.programsListButtonEdit.clicked.connect(self.editProgram) self.programsListButtonDelete.clicked.connect(self.deleteProgram) self.programsListButtonSet.clicked.connect(self.setProgram)
def setup_ui(self): super(AtlasUI, self).setup_ui() image_clear_btn = QPushButton(text="清空") image_clear_btn.clicked.connect(self.on_image_clear_btn_clicked) image_listview = QListView() image_listview.setAcceptDrops(True) image_list_model = QStandardItemModel(image_listview) image_list_model.itemChanged.connect(self.on_image_changed) image_listview.setModel(image_list_model) self.main_layout.addWidget(image_clear_btn, 0, 0, 1, 1, QtCore.Qt.AlignLeft) self.main_layout.addWidget(image_listview, 1, 0, 1, 1) self.image_clear_btn = image_clear_btn self.image_listview = image_listview self.image_list_model = image_list_model self.refresh_images()
def __init__(self, p): super().__init__() # enforce monospaced font: f = QFont("Monospace") f.setPointSize(8) self.setFont(f) self.setEditTriggers(QTreeView.NoEditTriggers) self.setSelectionMode(QTreeView.ExtendedSelection) m = QStandardItemModel() m.setColumnCount(3) m.setHorizontalHeaderLabels(["name", "value", "size"]) r = m.invisibleRootItem() S = self.setup_(p) if S: r.appendRow(S) self.setModel(m) self.m = m self.setUniformRowHeights(True) self.header().setSectionResizeMode(QHeaderView.ResizeToContents) self.header().setFont(f)
def create_itemview_tabwidget(self): result = QTabWidget() init_widget(result, "bottomLeftTabWidget") result.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Ignored) tree_view = QTreeView() init_widget(tree_view, "treeView") filesystem_model = QFileSystemModel(tree_view) filesystem_model.setRootPath(QDir.rootPath()) tree_view.setModel(filesystem_model) table_widget = QTableWidget() init_widget(table_widget, "tableWidget") table_widget.setRowCount(10) table_widget.setColumnCount(10) list_model = QStandardItemModel(0, 1, result) list_model.appendRow(QStandardItem(QIcon(DIR_OPEN_ICON), "Directory")) list_model.appendRow(QStandardItem(QIcon(COMPUTER_ICON), "Computer")) list_view = QListView() init_widget(list_view, "listView") list_view.setModel(list_model) icon_mode_listview = QListView() init_widget(icon_mode_listview, "iconModeListView") icon_mode_listview.setViewMode(QListView.IconMode) icon_mode_listview.setModel(list_model) result.addTab(embed_into_hbox_layout(tree_view), "Tree View") result.addTab(embed_into_hbox_layout(table_widget), "Table") result.addTab(embed_into_hbox_layout(list_view), "List") result.addTab(embed_into_hbox_layout(icon_mode_listview), "Icon Mode List") return result
def _set_header( model: QtGui.QStandardItemModel, columns: Sequence[Column]) -> None: header_labels = list([column.name for column in columns]) model.setHorizontalHeaderLabels(header_labels)
def __init__(self, persepolis_setting): super().__init__() # MainWindow self.persepolis_setting = persepolis_setting # add support for other languages locale = str(self.persepolis_setting.value('settings/locale')) QLocale.setDefault(QLocale(locale)) self.translator = QTranslator() if self.translator.load(':/translations/locales/ui_' + locale, 'ts'): QCoreApplication.installTranslator(self.translator) # set ui direction ui_direction = self.persepolis_setting.value('ui_direction') if ui_direction == 'rtl': self.setLayoutDirection(Qt.RightToLeft) elif ui_direction in 'ltr': self.setLayoutDirection(Qt.LeftToRight) icons = ':/' + \ str(self.persepolis_setting.value('settings/icons')) + '/' self.setWindowTitle( QCoreApplication.translate("mainwindow_ui_tr", "Persepolis Download Manager")) self.setWindowIcon( QIcon.fromTheme('persepolis', QIcon(':/persepolis.svg'))) self.centralwidget = QWidget(self) self.verticalLayout = QVBoxLayout(self.centralwidget) # enable drag and drop self.setAcceptDrops(True) # frame self.frame = QFrame(self.centralwidget) # download_table_horizontalLayout download_table_horizontalLayout = QHBoxLayout() horizontal_splitter = QSplitter(Qt.Horizontal) vertical_splitter = QSplitter(Qt.Vertical) # category_tree self.category_tree_qwidget = QWidget(self) category_tree_verticalLayout = QVBoxLayout() self.category_tree = CategoryTreeView(self) category_tree_verticalLayout.addWidget(self.category_tree) self.category_tree_model = QStandardItemModel() self.category_tree.setModel(self.category_tree_model) category_table_header = [ QCoreApplication.translate("mainwindow_ui_tr", 'Category') ] self.category_tree_model.setHorizontalHeaderLabels( category_table_header) self.category_tree.header().setStretchLastSection(True) self.category_tree.header().setDefaultAlignment(Qt.AlignCenter) # queue_panel self.queue_panel_widget = QWidget(self) queue_panel_verticalLayout_main = QVBoxLayout(self.queue_panel_widget) # queue_panel_show_button self.queue_panel_show_button = QPushButton(self) queue_panel_verticalLayout_main.addWidget(self.queue_panel_show_button) # queue_panel_widget_frame self.queue_panel_widget_frame = QFrame(self) self.queue_panel_widget_frame.setFrameShape(QFrame.StyledPanel) self.queue_panel_widget_frame.setFrameShadow(QFrame.Raised) queue_panel_verticalLayout_main.addWidget( self.queue_panel_widget_frame) queue_panel_verticalLayout = QVBoxLayout(self.queue_panel_widget_frame) queue_panel_verticalLayout_main.setContentsMargins(50, -1, 50, -1) # start_end_frame self.start_end_frame = QFrame(self) # start time start_verticalLayout = QVBoxLayout(self.start_end_frame) self.start_checkBox = QCheckBox(self) start_verticalLayout.addWidget(self.start_checkBox) self.start_frame = QFrame(self) self.start_frame.setFrameShape(QFrame.StyledPanel) self.start_frame.setFrameShadow(QFrame.Raised) start_frame_verticalLayout = QVBoxLayout(self.start_frame) self.start_time_qDataTimeEdit = MyQDateTimeEdit(self.start_frame) self.start_time_qDataTimeEdit.setDisplayFormat('H:mm') start_frame_verticalLayout.addWidget(self.start_time_qDataTimeEdit) start_verticalLayout.addWidget(self.start_frame) # end time self.end_checkBox = QCheckBox(self) start_verticalLayout.addWidget(self.end_checkBox) self.end_frame = QFrame(self) self.end_frame.setFrameShape(QFrame.StyledPanel) self.end_frame.setFrameShadow(QFrame.Raised) end_frame_verticalLayout = QVBoxLayout(self.end_frame) self.end_time_qDateTimeEdit = MyQDateTimeEdit(self.end_frame) self.end_time_qDateTimeEdit.setDisplayFormat('H:mm') end_frame_verticalLayout.addWidget(self.end_time_qDateTimeEdit) start_verticalLayout.addWidget(self.end_frame) self.reverse_checkBox = QCheckBox(self) start_verticalLayout.addWidget(self.reverse_checkBox) queue_panel_verticalLayout.addWidget(self.start_end_frame) # limit_after_frame self.limit_after_frame = QFrame(self) # limit_checkBox limit_verticalLayout = QVBoxLayout(self.limit_after_frame) self.limit_checkBox = QCheckBox(self) limit_verticalLayout.addWidget(self.limit_checkBox) # limit_frame self.limit_frame = QFrame(self) self.limit_frame.setFrameShape(QFrame.StyledPanel) self.limit_frame.setFrameShadow(QFrame.Raised) limit_verticalLayout.addWidget(self.limit_frame) limit_frame_verticalLayout = QVBoxLayout(self.limit_frame) # limit_spinBox limit_frame_horizontalLayout = QHBoxLayout() self.limit_spinBox = QDoubleSpinBox(self) self.limit_spinBox.setMinimum(1) self.limit_spinBox.setMaximum(1023) limit_frame_horizontalLayout.addWidget(self.limit_spinBox) # limit_comboBox self.limit_comboBox = QComboBox(self) self.limit_comboBox.addItem("") self.limit_comboBox.addItem("") limit_frame_horizontalLayout.addWidget(self.limit_comboBox) limit_frame_verticalLayout.addLayout(limit_frame_horizontalLayout) # limit_pushButton self.limit_pushButton = QPushButton(self) limit_frame_verticalLayout.addWidget(self.limit_pushButton) # after_checkBox self.after_checkBox = QCheckBox(self) limit_verticalLayout.addWidget(self.after_checkBox) # after_frame self.after_frame = QFrame(self) self.after_frame.setFrameShape(QFrame.StyledPanel) self.after_frame.setFrameShadow(QFrame.Raised) limit_verticalLayout.addWidget(self.after_frame) after_frame_verticalLayout = QVBoxLayout(self.after_frame) # after_comboBox self.after_comboBox = QComboBox(self) self.after_comboBox.addItem("") after_frame_verticalLayout.addWidget(self.after_comboBox) # after_pushButton self.after_pushButton = QPushButton(self) after_frame_verticalLayout.addWidget(self.after_pushButton) queue_panel_verticalLayout.addWidget(self.limit_after_frame) category_tree_verticalLayout.addWidget(self.queue_panel_widget) # keep_awake_checkBox self.keep_awake_checkBox = QCheckBox(self) queue_panel_verticalLayout.addWidget(self.keep_awake_checkBox) self.category_tree_qwidget.setLayout(category_tree_verticalLayout) horizontal_splitter.addWidget(self.category_tree_qwidget) # download table widget self.download_table_content_widget = QWidget(self) download_table_content_widget_verticalLayout = QVBoxLayout( self.download_table_content_widget) # download_table self.download_table = DownloadTableWidget(self) vertical_splitter.addWidget(self.download_table) horizontal_splitter.addWidget(self.download_table_content_widget) self.download_table.setColumnCount(13) self.download_table.setSelectionBehavior(QAbstractItemView.SelectRows) self.download_table.setEditTriggers(QAbstractItemView.NoEditTriggers) self.download_table.verticalHeader().hide() # hide column of GID and column of link. self.download_table.setColumnHidden(8, True) self.download_table.setColumnHidden(9, True) download_table_header = [ QCoreApplication.translate("mainwindow_ui_tr", 'File Name'), QCoreApplication.translate("mainwindow_ui_tr", 'Status'), QCoreApplication.translate("mainwindow_ui_tr", 'Size'), QCoreApplication.translate("mainwindow_ui_tr", 'Downloaded'), QCoreApplication.translate("mainwindow_ui_tr", 'Percentage'), QCoreApplication.translate("mainwindow_ui_tr", 'Connections'), QCoreApplication.translate("mainwindow_ui_tr", 'Transfer Rate'), QCoreApplication.translate("mainwindow_ui_tr", 'Estimated Time Left'), 'Gid', QCoreApplication.translate("mainwindow_ui_tr", 'Link'), QCoreApplication.translate("mainwindow_ui_tr", 'First Try Date'), QCoreApplication.translate("mainwindow_ui_tr", 'Last Try Date'), QCoreApplication.translate("mainwindow_ui_tr", 'Category') ] self.download_table.setHorizontalHeaderLabels(download_table_header) # fixing the size of download_table when window is Maximized! self.download_table.horizontalHeader().setSectionResizeMode( QHeaderView.ResizeMode.Interactive) self.download_table.horizontalHeader().setStretchLastSection(True) horizontal_splitter.setStretchFactor(0, 3) # category_tree width horizontal_splitter.setStretchFactor(1, 10) # ratio of tables's width # video_finder_widget self.video_finder_widget = QWidget(self) video_finder_horizontalLayout = QHBoxLayout(self.video_finder_widget) self.muxing_pushButton = QPushButton(self) self.muxing_pushButton.setIcon(QIcon(icons + 'video_finder')) video_finder_horizontalLayout.addWidget(self.muxing_pushButton) video_finder_horizontalLayout.addSpacing(20) video_audio_verticalLayout = QVBoxLayout() self.video_label = QLabel(self) video_audio_verticalLayout.addWidget(self.video_label) self.audio_label = QLabel(self) video_audio_verticalLayout.addWidget(self.audio_label) video_finder_horizontalLayout.addLayout(video_audio_verticalLayout) status_muxing_verticalLayout = QVBoxLayout() self.video_finder_status_label = QLabel(self) status_muxing_verticalLayout.addWidget(self.video_finder_status_label) self.muxing_status_label = QLabel(self) status_muxing_verticalLayout.addWidget(self.muxing_status_label) video_finder_horizontalLayout.addLayout(status_muxing_verticalLayout) vertical_splitter.addWidget(self.video_finder_widget) download_table_content_widget_verticalLayout.addWidget( vertical_splitter) download_table_horizontalLayout.addWidget(horizontal_splitter) self.frame.setLayout(download_table_horizontalLayout) self.verticalLayout.addWidget(self.frame) self.setCentralWidget(self.centralwidget) # menubar self.menubar = QMenuBar(self) self.menubar.setGeometry(QRect(0, 0, 600, 24)) self.setMenuBar(self.menubar) fileMenu = self.menubar.addMenu( QCoreApplication.translate("mainwindow_ui_tr", '&File')) editMenu = self.menubar.addMenu( QCoreApplication.translate("mainwindow_ui_tr", '&Edit')) viewMenu = self.menubar.addMenu( QCoreApplication.translate("mainwindow_ui_tr", '&View')) downloadMenu = self.menubar.addMenu( QCoreApplication.translate("mainwindow_ui_tr", '&Download')) queueMenu = self.menubar.addMenu( QCoreApplication.translate("mainwindow_ui_tr", '&Queue')) videoFinderMenu = self.menubar.addMenu( QCoreApplication.translate("mainwindow_ui_tr", 'V&ideo Finder')) helpMenu = self.menubar.addMenu( QCoreApplication.translate("mainwindow_ui_tr", '&Help')) # viewMenu submenus sortMenu = viewMenu.addMenu( QCoreApplication.translate("mainwindow_ui_tr", 'Sort by')) # statusbar self.statusbar = QStatusBar(self) self.setStatusBar(self.statusbar) self.statusbar.showMessage( QCoreApplication.translate("mainwindow_ui_tr", "Persepolis Download Manager")) # toolBar self.toolBar2 = QToolBar(self) self.addToolBar(Qt.TopToolBarArea, self.toolBar2) self.toolBar2.setWindowTitle( QCoreApplication.translate("mainwindow_ui_tr", 'Menu')) self.toolBar2.setFloatable(False) self.toolBar2.setMovable(False) self.toolBar = QToolBar(self) self.addToolBar(Qt.TopToolBarArea, self.toolBar) self.toolBar.setWindowTitle( QCoreApplication.translate("mainwindow_ui_tr", 'Toolbar')) self.toolBar.setFloatable(False) self.toolBar.setMovable(False) #toolBar and menubar and actions self.persepolis_setting.beginGroup('settings/shortcuts') # videoFinderAddLinkAction self.videoFinderAddLinkAction = QAction( QIcon(icons + 'video_finder'), QCoreApplication.translate("mainwindow_ui_tr", 'Find Video Links...'), self, statusTip=QCoreApplication.translate( "mainwindow_ui_tr", 'Download video or audio from Youtube, Vimeo, etc.'), triggered=self.showVideoFinderAddLinkWindow) self.videoFinderAddLinkAction_shortcut = QShortcut( self.persepolis_setting.value('video_finder_shortcut'), self, self.showVideoFinderAddLinkWindow) videoFinderMenu.addAction(self.videoFinderAddLinkAction) # stopAllAction self.stopAllAction = QAction(QIcon(icons + 'stop_all'), QCoreApplication.translate( "mainwindow_ui_tr", 'Stop All Active Downloads'), self, statusTip='Stop All Active Downloads', triggered=self.stopAllDownloads) downloadMenu.addAction(self.stopAllAction) # sort_file_name_Action self.sort_file_name_Action = QAction(QCoreApplication.translate( "mainwindow_ui_tr", 'File Name'), self, triggered=self.sortByName) sortMenu.addAction(self.sort_file_name_Action) # sort_file_size_Action self.sort_file_size_Action = QAction(QCoreApplication.translate( "mainwindow_ui_tr", 'File Size'), self, triggered=self.sortBySize) sortMenu.addAction(self.sort_file_size_Action) # sort_first_try_date_Action self.sort_first_try_date_Action = QAction( QCoreApplication.translate("mainwindow_ui_tr", 'First Try Date'), self, triggered=self.sortByFirstTry) sortMenu.addAction(self.sort_first_try_date_Action) # sort_last_try_date_Action self.sort_last_try_date_Action = QAction(QCoreApplication.translate( "mainwindow_ui_tr", 'Last Try Date'), self, triggered=self.sortByLastTry) sortMenu.addAction(self.sort_last_try_date_Action) # sort_download_status_Action self.sort_download_status_Action = QAction(QCoreApplication.translate( "mainwindow_ui_tr", 'Download Status'), self, triggered=self.sortByStatus) sortMenu.addAction(self.sort_download_status_Action) # trayAction self.trayAction = QAction( QCoreApplication.translate("mainwindow_ui_tr", 'Show System Tray Icon'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Show/Hide system tray icon"), triggered=self.showTray) self.trayAction.setCheckable(True) viewMenu.addAction(self.trayAction) # showMenuBarAction self.showMenuBarAction = QAction( QCoreApplication.translate("mainwindow_ui_tr", 'Show Menubar'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Show Menubar'), triggered=self.showMenuBar) self.showMenuBarAction.setCheckable(True) viewMenu.addAction(self.showMenuBarAction) # showSidePanelAction self.showSidePanelAction = QAction( QCoreApplication.translate("mainwindow_ui_tr", 'Show Side Panel'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Show Side Panel'), triggered=self.showSidePanel) self.showSidePanelAction.setCheckable(True) viewMenu.addAction(self.showSidePanelAction) # minimizeAction self.minimizeAction = QAction( QIcon(icons + 'minimize'), QCoreApplication.translate("mainwindow_ui_tr", 'Minimize to System Tray'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Minimize to System Tray"), triggered=self.minMaxTray) self.minimizeAction_shortcut = QShortcut( self.persepolis_setting.value('hide_window_shortcut'), self, self.minMaxTray) viewMenu.addAction(self.minimizeAction) # addlinkAction self.addlinkAction = QAction( QIcon(icons + 'add'), QCoreApplication.translate("mainwindow_ui_tr", 'Add New Download Link...'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Add New Download Link"), triggered=self.addLinkButtonPressed) self.addlinkAction_shortcut = QShortcut( self.persepolis_setting.value('add_new_download_shortcut'), self, self.addLinkButtonPressed) fileMenu.addAction(self.addlinkAction) # importText self.addtextfileAction = QAction( QIcon(icons + 'file'), QCoreApplication.translate("mainwindow_ui_tr", 'Import Links from Text File...'), self, statusTip=QCoreApplication.translate( "mainwindow_ui_tr", 'Create a text file and put links in it, line by line!'), triggered=self.importText) self.addtextfileAction_shortcut = QShortcut( self.persepolis_setting.value('import_text_shortcut'), self, self.importText) fileMenu.addAction(self.addtextfileAction) # resumeAction self.resumeAction = QAction( QIcon(icons + 'play'), QCoreApplication.translate("mainwindow_ui_tr", 'Resume Download'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Resume Download"), triggered=self.resumeButtonPressed) downloadMenu.addAction(self.resumeAction) # pauseAction self.pauseAction = QAction( QIcon(icons + 'pause'), QCoreApplication.translate("mainwindow_ui_tr", 'Pause Download'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Pause Download"), triggered=self.pauseButtonPressed) downloadMenu.addAction(self.pauseAction) # stopAction self.stopAction = QAction( QIcon(icons + 'stop'), QCoreApplication.translate("mainwindow_ui_tr", 'Stop Download'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Stop/Cancel Download"), triggered=self.stopButtonPressed) downloadMenu.addAction(self.stopAction) # propertiesAction self.propertiesAction = QAction( QIcon(icons + 'setting'), QCoreApplication.translate("mainwindow_ui_tr", 'Properties'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Properties"), triggered=self.propertiesButtonPressed) downloadMenu.addAction(self.propertiesAction) # progressAction self.progressAction = QAction( QIcon(icons + 'window'), QCoreApplication.translate("mainwindow_ui_tr", 'Progress'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Progress"), triggered=self.progressButtonPressed) downloadMenu.addAction(self.progressAction) # openFileAction self.openFileAction = QAction( QIcon(icons + 'file'), QCoreApplication.translate("mainwindow_ui_tr", 'Open File...'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Open File...'), triggered=self.openFile) fileMenu.addAction(self.openFileAction) # openDownloadFolderAction self.openDownloadFolderAction = QAction( QIcon(icons + 'folder'), QCoreApplication.translate("mainwindow_ui_tr", 'Open Download Folder'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Open Download Folder'), triggered=self.openDownloadFolder) fileMenu.addAction(self.openDownloadFolderAction) # openDefaultDownloadFolderAction self.openDefaultDownloadFolderAction = QAction( QIcon(icons + 'folder'), QCoreApplication.translate("mainwindow_ui_tr", 'Open Default Download Folder'), self, statusTip=QCoreApplication.translate( "mainwindow_ui_tr", 'Open Default Download Folder'), triggered=self.openDefaultDownloadFolder) fileMenu.addAction(self.openDefaultDownloadFolderAction) # exitAction self.exitAction = QAction( QIcon(icons + 'exit'), QCoreApplication.translate("mainwindow_ui_tr", 'Exit'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Exit"), triggered=self.closeAction) self.exitAction_shortcut = QShortcut( self.persepolis_setting.value('quit_shortcut'), self, self.closeAction) fileMenu.addAction(self.exitAction) # clearAction self.clearAction = QAction(QIcon(icons + 'multi_remove'), QCoreApplication.translate( "mainwindow_ui_tr", 'Clear Download List'), self, statusTip=QCoreApplication.translate( "mainwindow_ui_tr", 'Clear all items in download list'), triggered=self.clearDownloadList) editMenu.addAction(self.clearAction) # removeSelectedAction self.removeSelectedAction = QAction( QIcon(icons + 'remove'), QCoreApplication.translate("mainwindow_ui_tr", 'Remove Selected Downloads from List'), self, statusTip=QCoreApplication.translate( "mainwindow_ui_tr", 'Remove Selected Downloads from List'), triggered=self.removeSelected) self.removeSelectedAction_shortcut = QShortcut( self.persepolis_setting.value('remove_shortcut'), self, self.removeSelected) editMenu.addAction(self.removeSelectedAction) self.removeSelectedAction.setEnabled(False) # deleteSelectedAction self.deleteSelectedAction = QAction( QIcon(icons + 'trash'), QCoreApplication.translate("mainwindow_ui_tr", 'Delete Selected Download Files'), self, statusTip=QCoreApplication.translate( "mainwindow_ui_tr", 'Delete Selected Download Files'), triggered=self.deleteSelected) self.deleteSelectedAction_shortcut = QShortcut( self.persepolis_setting.value('delete_shortcut'), self, self.deleteSelected) editMenu.addAction(self.deleteSelectedAction) self.deleteSelectedAction.setEnabled(False) # moveSelectedDownloadsAction self.moveSelectedDownloadsAction = QAction( QIcon(icons + 'folder'), QCoreApplication.translate( "mainwindow_ui_tr", 'Move Selected Download Files to Another Folder...'), self, statusTip=QCoreApplication.translate( "mainwindow_ui_tr", 'Move Selected Download Files to Another Folder'), triggered=self.moveSelectedDownloads) editMenu.addAction(self.moveSelectedDownloadsAction) self.moveSelectedDownloadsAction.setEnabled(False) # createQueueAction self.createQueueAction = QAction( QIcon(icons + 'add_queue'), QCoreApplication.translate("mainwindow_ui_tr", 'Create New Queue...'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Create new download queue'), triggered=self.createQueue) queueMenu.addAction(self.createQueueAction) # removeQueueAction self.removeQueueAction = QAction( QIcon(icons + 'remove_queue'), QCoreApplication.translate("mainwindow_ui_tr", 'Remove Queue'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Remove this queue'), triggered=self.removeQueue) queueMenu.addAction(self.removeQueueAction) # startQueueAction self.startQueueAction = QAction( QIcon(icons + 'start_queue'), QCoreApplication.translate("mainwindow_ui_tr", 'Start this queue'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Start Queue'), triggered=self.startQueue) queueMenu.addAction(self.startQueueAction) # stopQueueAction self.stopQueueAction = QAction( QIcon(icons + 'stop_queue'), QCoreApplication.translate("mainwindow_ui_tr", 'Stop this queue'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Stop Queue'), triggered=self.stopQueue) queueMenu.addAction(self.stopQueueAction) # moveUpSelectedAction self.moveUpSelectedAction = QAction( QIcon(icons + 'multi_up'), QCoreApplication.translate("mainwindow_ui_tr", 'Move Selected Items Up'), self, statusTip=QCoreApplication.translate( "mainwindow_ui_tr", 'Move currently selected items up by one row'), triggered=self.moveUpSelected) self.moveUpSelectedAction_shortcut = QShortcut( self.persepolis_setting.value('move_up_selection_shortcut'), self, self.moveUpSelected) queueMenu.addAction(self.moveUpSelectedAction) # moveDownSelectedAction self.moveDownSelectedAction = QAction( QIcon(icons + 'multi_down'), QCoreApplication.translate("mainwindow_ui_tr", 'Move Selected Items Down'), self, statusTip=QCoreApplication.translate( "mainwindow_ui_tr", 'Move currently selected items down by one row'), triggered=self.moveDownSelected) self.moveDownSelectedAction_shortcut = QShortcut( self.persepolis_setting.value('move_down_selection_shortcut'), self, self.moveDownSelected) queueMenu.addAction(self.moveDownSelectedAction) # preferencesAction self.preferencesAction = QAction( QIcon(icons + 'preferences'), QCoreApplication.translate("mainwindow_ui_tr", 'Preferences'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Preferences'), triggered=self.openPreferences, menuRole=QAction.MenuRole.PreferencesRole) editMenu.addAction(self.preferencesAction) # aboutAction self.aboutAction = QAction( QIcon(icons + 'about'), QCoreApplication.translate("mainwindow_ui_tr", 'About'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'About'), triggered=self.openAbout, menuRole=QAction.MenuRole.AboutRole) helpMenu.addAction(self.aboutAction) # issueAction self.issueAction = QAction( QIcon(icons + 'about'), QCoreApplication.translate("mainwindow_ui_tr", 'Report an Issue'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Report an issue'), triggered=self.reportIssue) helpMenu.addAction(self.issueAction) # updateAction self.updateAction = QAction( QIcon(icons + 'about'), QCoreApplication.translate("mainwindow_ui_tr", 'Check for Newer Version'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Check for newer release'), triggered=self.newUpdate) helpMenu.addAction(self.updateAction) # logAction self.logAction = QAction( QIcon(icons + 'about'), QCoreApplication.translate("mainwindow_ui_tr", 'Show Log File'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Help'), triggered=self.showLog) helpMenu.addAction(self.logAction) # helpAction self.helpAction = QAction( QIcon(icons + 'about'), QCoreApplication.translate("mainwindow_ui_tr", 'Help'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Help'), triggered=self.persepolisHelp) helpMenu.addAction(self.helpAction) self.persepolis_setting.endGroup() self.qmenu = MenuWidget(self) self.toolBar2.addWidget(self.qmenu) # labels self.queue_panel_show_button.setText( QCoreApplication.translate("mainwindow_ui_tr", "Hide Options")) self.start_checkBox.setText( QCoreApplication.translate("mainwindow_ui_tr", "Start Time")) self.end_checkBox.setText( QCoreApplication.translate("mainwindow_ui_tr", "End Time")) self.reverse_checkBox.setText( QCoreApplication.translate("mainwindow_ui_tr", "Download bottom of\n the list first")) self.limit_checkBox.setText( QCoreApplication.translate("mainwindow_ui_tr", "Limit Speed")) self.limit_comboBox.setItemText(0, "KiB/s") self.limit_comboBox.setItemText(1, "MiB/s") self.limit_pushButton.setText( QCoreApplication.translate("mainwindow_ui_tr", "Apply")) self.after_checkBox.setText( QCoreApplication.translate("mainwindow_ui_tr", "After download")) self.after_comboBox.setItemText( 0, QCoreApplication.translate("mainwindow_ui_tr", "Shut Down")) self.keep_awake_checkBox.setText( QCoreApplication.translate("mainwindow_ui_tr", "Keep System Awake!")) self.keep_awake_checkBox.setToolTip( QCoreApplication.translate( "mainwindow_ui_tr", "<html><head/><body><p>This option will prevent the system from going to sleep.\ It is necessary if your power manager is suspending the system automatically. </p></body></html>" )) self.after_pushButton.setText( QCoreApplication.translate("mainwindow_ui_tr", "Apply")) self.muxing_pushButton.setText( QCoreApplication.translate("mainwindow_ui_tr", "Start Mixing")) self.video_label.setText( QCoreApplication.translate("mainwindow_ui_tr", "<b>Video File Status: </b>")) self.audio_label.setText( QCoreApplication.translate("mainwindow_ui_tr", "<b>Audio File Status: </b>")) self.video_finder_status_label.setText( QCoreApplication.translate("mainwindow_ui_tr", "<b>Status: </b>")) self.muxing_status_label.setText( QCoreApplication.translate("mainwindow_ui_tr", "<b>Mixing status: </b>"))
def __init__(self, view): QStandardItemModel.__init__(self, view)
def __init__(self, arg=None): super(Table, self).__init__(arg) # 设置窗体的标题和初始大小 self.setWindowTitle("QTableView表格视图控件的例子") self.resize(500, 300) # 准备数据模型,设置数据层次结构为5行4列 self.model = QStandardItemModel(5, 4) # 设置数据栏名称 self.model.setHorizontalHeaderLabels(['标题1', '标题2', '标题3', '标题4']) for row in range(5): for column in range(4): item = QStandardItem("row %s, column %s" % (row, column)) self.model.setItem(row, column, item) # 实例化表格视图,设置模型为自定义的模型 self.tableView = QTableView() self.tableView.setModel(self.model) # 下面代码让表格 100% 的填满窗口 self.tableView.horizontalHeader().setStretchLastSection(True) self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) # 下面代码让表格禁止编辑 self.tableView.setEditTriggers(QAbstractItemView.NoEditTriggers) # 下面代码让表格设置选中背景色 self.tableView.setStyleSheet("selection-background-color:lightblue;") # 下面代码要限定只能选择整行 # self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows) # 设置只能选中整行 # self.tableView.setSelectionMode(QAbstractItemView.SingleSelection) # 设置只能选中一行 # 下面代码可以选中表格的多行 self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows) # 设置只能选中整行 self.tableView.setSelectionMode(QAbstractItemView.ExtendedSelection) # 设置只能选中多行 # 下面代码可以打印表格的1行2列数据 data = self.tableView.model().index(1, 2).data() print(data) # 下面代码去掉左边表格的行号 # headerView = self.tableView.verticalHeader() # headerView.setHidden(True) # 局部布局 vboxLayout = QVBoxLayout() vboxLayout.addWidget(self.tableView) self.add_btn = QPushButton("添加记录") # 连接信号槽,点击按钮 add_btn 绑定槽事件 self.add_btn.clicked.connect(self.add_records_btn_click) self.del_btn = QPushButton("删除多行记录") # 连接信号槽,点击按钮 del_btn 绑定槽事件 self.del_btn.clicked.connect(self.del_records_btn_click) # 局部布局 hboxLayout = QHBoxLayout() hboxLayout.addWidget(self.add_btn) hboxLayout.addWidget(self.del_btn) # 全局布局 wl = QVBoxLayout(self) wl.addLayout(vboxLayout) wl.addLayout(hboxLayout)
class Table(QWidget): def __init__(self, arg=None): super(Table, self).__init__(arg) # 设置窗体的标题和初始大小 self.setWindowTitle("QTableView表格视图控件的例子") self.resize(500, 300) # 准备数据模型,设置数据层次结构为5行4列 self.model = QStandardItemModel(5, 4) # 设置数据栏名称 self.model.setHorizontalHeaderLabels(['标题1', '标题2', '标题3', '标题4']) for row in range(5): for column in range(4): item = QStandardItem("row %s, column %s" % (row, column)) self.model.setItem(row, column, item) # 实例化表格视图,设置模型为自定义的模型 self.tableView = QTableView() self.tableView.setModel(self.model) # 下面代码让表格 100% 的填满窗口 self.tableView.horizontalHeader().setStretchLastSection(True) self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) # 下面代码让表格禁止编辑 self.tableView.setEditTriggers(QAbstractItemView.NoEditTriggers) # 下面代码让表格设置选中背景色 self.tableView.setStyleSheet("selection-background-color:lightblue;") # 下面代码要限定只能选择整行 # self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows) # 设置只能选中整行 # self.tableView.setSelectionMode(QAbstractItemView.SingleSelection) # 设置只能选中一行 # 下面代码可以选中表格的多行 self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows) # 设置只能选中整行 self.tableView.setSelectionMode(QAbstractItemView.ExtendedSelection) # 设置只能选中多行 # 下面代码可以打印表格的1行2列数据 data = self.tableView.model().index(1, 2).data() print(data) # 下面代码去掉左边表格的行号 # headerView = self.tableView.verticalHeader() # headerView.setHidden(True) # 局部布局 vboxLayout = QVBoxLayout() vboxLayout.addWidget(self.tableView) self.add_btn = QPushButton("添加记录") # 连接信号槽,点击按钮 add_btn 绑定槽事件 self.add_btn.clicked.connect(self.add_records_btn_click) self.del_btn = QPushButton("删除多行记录") # 连接信号槽,点击按钮 del_btn 绑定槽事件 self.del_btn.clicked.connect(self.del_records_btn_click) # 局部布局 hboxLayout = QHBoxLayout() hboxLayout.addWidget(self.add_btn) hboxLayout.addWidget(self.del_btn) # 全局布局 wl = QVBoxLayout(self) wl.addLayout(vboxLayout) wl.addLayout(hboxLayout) # 点击删除按钮响应方法, 删除选中的单行数据 def del_record_btn_click(self): # 第一种方法: 删除单行数据 indices = self.tableView.selectionModel().selectedRows() for index in sorted(indices): self.model.removeRow(index.row()) # 第二种方法: 删除单行数据 # index = self.tableView.currentIndex() # 取得当前选中行的index # if -1 == index.row(): # return # self.model.removeRow(index.row()) # 通过index的row()操作得到行数进行删除 # 点击删除按钮响应方法, 删除选中的多行数据 def del_records_btn_click(self): # 第一种方法:删除多行数据 index_list = [] for model_index in self.tableView.selectionModel().selectedRows(): index = QtCore.QPersistentModelIndex(model_index) index_list.append(index) if index_list: for index in index_list: self.model.removeRow(index.row()) else: MessageBox = QMessageBox() MessageBox.information(self.tableView, "标题", "没有选中表格中要删除的行") # 第二种方法:删除多行数据 # indexs = self.tableView.selectionModel().selectedRows() # temp_list = [] # 创建一个空队列用于存放需要删除的行号 # for index in indexs: # temp_list.append(index.row()) # 队列中保存需要删除的行号 # temp_list.sort(key=int, reverse=True) # 用sort方法将队列进行降序排列 # print(temp_list) # # if temp_list: # for i in temp_list: # 按照队列删除对应的行 # self.model.removeRow(i) # else: # MessageBox = QMessageBox() # MessageBox.information(self.tableView, "标题", "没有选中表格中要删除的行") # 点击添加按钮相应方法,添加数据 def add_records_btn_click(self): self.model.appendRow([ QStandardItem("row %s, column %s" % (5, 0)), QStandardItem("row %s, column %s" % (6, 1)), QStandardItem("row %s, column %s" % (7, 2)), QStandardItem("row %s, column %s" % (8, 3)), ])
## so we can fit more lines into it on the graphicsview ## side. class MyHeaderView(QHeaderView): def __init__(self, parent=None): super(MyHeaderView, self).__init__(Qt.Horizontal, parent) def sizeHint(self): s = super(MyHeaderView, self).sizeHint() s.setHeight(s.height() * 3) return s if __name__ == '__main__': app = QApplication(sys.argv) model = QStandardItemModel(1, 1) model.setHeaderData(0, Qt.Horizontal, "Task") ## A view with some alternative header labels view1 = View() grid1 = DateTimeGrid() #grid1.setUserDefinedUpperScale(DateTimeScaleFormatter(DateTimeScaleFormatter.Year, "yyyy", "In the year %1.", Qt.AlignLeft)) #grid1.setUserDefinedLowerScale(DateTimeScaleFormatter(DateTimeScaleFormatter.Month, "MMMM", "In the month %1.", Qt.AlignRight)) grid1.setScale(DateTimeGrid.ScaleUserDefined) grid1.setDayWidth(6.0) view1.setGrid(grid1) view1.setModel(model) view1.show() ## A view with header and vertical grid lines for every 10 minutes view2 = View()
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()))
if nesting_level == 0: return result for i in range(num_children): child = QStandardItem("Child num {}, nesting Level {}".format( i + 1, nesting_level)) if i == 0: child.appendRow(add_child(num_children, nesting_level - 1)) result.append(child) return result if __name__ == '__main__': app = QApplication(sys.argv) model_size = 100000 list = [] source_model = QStandardItemModel() horizontal_header_list = [ "First Column with spacing", "Second Column with spacing" ] source_model.setHorizontalHeaderLabels(horizontal_header_list) for i in range(model_size): first_item = QStandardItem("FancyTextNumber {}".format(i)) if i == 0: first_item.appendRow(add_child(2, 2)) second_item = QStandardItem("FancyRow2TextNumber {}".format(i)) if i % 2 == 0: first_item.setBackground(Qt.red) row = [first_item, second_item] source_model.invisibleRootItem().appendRow(row) list.append("FancyTextNumber {}".format(i))
class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self): super(MainWindow, self).__init__() #self.ui = () self.setupUi(self) self.p: QProcess = None dirname = r"K:\Media Library" processing_dirs = getBaseDirs(dirname) s: QtCore.QSize = self.treeView.iconSize() self.message(f'size = {s}') s = QtCore.QSize(16, 16) # Set icon sizing here self.treeView.setIconSize(s) style = QApplication.style() self.running_icon = style.standardIcon(QStyle.SP_BrowserReload) self.done_icon = style.standardIcon(QStyle.SP_DialogApplyButton) self.failed_icon = style.standardIcon(QStyle.SP_MessageBoxCritical) self.missing_icon = style.standardIcon( QStyle.SP_TitleBarContextHelpButton) self.added_icon = style.standardIcon(QStyle.SP_ArrowUp) self.warning_icon = style.standardIcon(QStyle.SP_MessageBoxWarning) self.checksum_sets = [] for dirname in processing_dirs: self.checksum_sets.append(ChecksumSet(dirname)) self.updateStats() self.populateModel() self.treeView.setModel(self.model) # self.treeView.setExpandsOnDoubleClick(True) # for x in self.iterItems(self.model.invisibleRootItem()): # y = x.data(role=DisplayRole) # z = x.data(role=UserRole) # self.message(f'y = {y}, z = {z}') # x.setText('hi') # y = x.data(role=QtCore.Qt.UserRole) # self.message(x.text()) # self.message('HI' + y.baseDirname) # self.message('HI' + str(y)) self.start_process() # model.setHorizontalHeaderLabels(['Title', 'Summary']) # rootItem = model.invisibleRootItem() #First top-level row and children # item0 = [QStandardItem('Title0'), QStandardItem('Summary0')] # item00 = [QStandardItem('Title00'), QStandardItem('Summary00')] # item01 = [QStandardItem('Title01'), QStandardItem('Summary01')] # rootItem.appendRow(item0) # item0[0].appendRow(item00) # item0[0].appendRow(item01) #Second top-level item and its children # item1 = [QStandardItem('Title1'), QStandardItem('Summary1')] # item10 = [QStandardItem('Title10'), QStandardItem('Summary10')] # item11 = [QStandardItem('Title11'), QStandardItem('Summary11')] # item12 = [QStandardItem('Title12'), QStandardItem('Summary12')] # rootItem.appendRow(item1) # item1[0].appendRow(item10) # item1[0].appendRow(item11) # item1[0].appendRow(item12) #Children of item11 (third level items) # item110 = [QStandardItem('Title110'), QStandardItem('Summary110')] # item111 = [QStandardItem('Title111'), QStandardItem('Summary111')] # item11[0].appendRow(item110) # item11[0].appendRow(item111) def toast_notification(self, title, text): AppID = 'zchecksum' XML = ToastNotificationManager.get_template_content( ToastTemplateType.TOAST_TEXT02) print(XML) t = XML.get_elements_by_tag_name("text") print(t) t[0].append_child(XML.create_text_node(title)) t[1].append_child(XML.create_text_node(text)) notifier = ToastNotificationManager.create_toast_notifier(AppID) notifier.show(ToastNotification(XML)) def iterItems(self, root): if root is not None: for row in range(root.rowCount()): row_item = root.child(row, 0) if row_item.hasChildren(): for childIndex in range(row_item.rowCount()): # Take second column from "child"-row child = row_item.child(childIndex, 1) yield child def format_size(self, size): if size < 0: raise Exception('Unknown file size') if size < 1024: return f'{size} Bytes' elif size < 1024 * 1024: size_kb = size / 1024 return f'{size_kb:.1f} KB' elif size < 1024 * 1024 * 1024: size_mb = size / 1024 / 1024 return f'{size_mb:.1f} MB' else: size_gb = size / 1024 / 1024 / 1024 return f'{size_gb:.1f} GB' def populateModel(self): self.model = QStandardItemModel() self.model.setHorizontalHeaderLabels([ 'Status', 'Directory / Filename', 'File Count', 'Size', 'Checksum', 'Last Verified' ]) rootItem = self.model.invisibleRootItem() for checksum_set in self.checksum_sets: last_verified = '' status = '' if not checksum_set.hasSha512File(): status = 'New' elif checksum_set.has_changes(): status = "Modified" self.toast_notification("Checksum set was modified.", checksum_set.baseDirname) else: time_delta = datetime.now( ) - checksum_set.sha512File.last_verified last_verified = f'{time_delta.days} days ago' if time_delta.days > good_days: status = 'Test' else: status = 'Good' if status == 'Test' or status == 'New' or status == 'Modified': item = [ QStandardItem(status), QStandardItem(checksum_set.baseDirname), QStandardItem(str(len(checksum_set.filenames))), QStandardItem(''), QStandardItem(''), QStandardItem(last_verified) ] for filename in checksum_set.filenames: checksum_text = '' file_status = '' if checksum_set.sha512File: checksum = checksum_set.sha512File.findChecksum( filename) if checksum: checksum_text = checksum else: file_status = 'Added' size = Path(checksum_set.baseDirname + filename).stat().st_size size_display = self.format_size(size) # sz = len(checksum_set.baseDirname) + len(filename) c_item = [ QStandardItem(file_status), QStandardItem(filename), QStandardItem(''), QStandardItem(size_display), QStandardItem(checksum_text) ] c_item[1].setData(checksum_set, QtCore.Qt.UserRole) if file_status == 'Added': c_item[0].setIcon(self.added_icon) if filename == 'Thumbs.db': c_item[0].setIcon(self.warning_icon) item[0].appendRow(c_item) if checksum_set.hasSha512File(): for filename in checksum_set.get_missing_from_dir(): checksum_text = checksum_set.sha512File.findChecksum( filename) c_item = [ QStandardItem('Missing'), QStandardItem(filename), QStandardItem(''), QStandardItem('n/a'), QStandardItem(checksum_text) ] c_item[1].setData(checksum_set, QtCore.Qt.UserRole) c_item[0].setIcon(self.missing_icon) item[0].appendRow(c_item) rootItem.appendRow(item) # self.showIssues() #TODO: FIX, NOT WORKING! def showIssues(self): self.treeView.expandAll() root = self.model.invisibleRootItem() if root is not None: for row in range(root.rowCount()): row_item = root.child(row, 0) if row_item.hasChildren(): for childIndex in range(row_item.rowCount()): # Take second column from "child"-row child = row_item.child(childIndex, 1) filename = child.text() print(f'f = {filename}, {row_item.index()}') if filename == 'Thumbs.db': self.treeView.setExpanded(row_item.index(), True) # c_item[0].setIcon(self.warning_icon) # item[0].setExpanded(True) # self.start_process() # header = self.treeWidget.header() # header # header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents) # header.setStretchLastSection(False) # header.setSectionResizeMode(5, QtWidgets.QHeaderView.Stretch) def updateStats(self): new_count = 0 modified_count = 0 test_count = 0 good_count = 0 # items = [] for checksum_set in self.checksum_sets: if not checksum_set.hasSha512File(): new_count += 1 elif checksum_set.has_changes(): modified_count += 1 else: time_delta = datetime.now( ) - checksum_set.sha512File.last_verified if time_delta.days > good_days: test_count += 1 else: good_count += 1 self.statusBar().showMessage( f'Total sets: {len(self.checksum_sets)} sets. New sets: {new_count} Modified sets: {modified_count} Test sets: {test_count} Good sets: {good_count}' ) def message(self, s): # self.statusBar().showMessage(s) self.listWidget.addItem(s) def start_process(self): root = self.model.invisibleRootItem() started_count = 0 if root is not None: for row in range(root.rowCount()): if self.p: break row_item = root.child(row, 0) # item = self.model.item(row) dir = root.child(row, 1) set_status = root.child(row, 0) # self.message(dir.text()) if (set_status.text() == "New" or set_status.text() == 'Test') and row_item.hasChildren(): for childIndex in range(row_item.rowCount()): # Take second column from "child"-row # status = row_item.child(childIndex, 0) # self.message(status.text()) # if status.text() == 'New': # self.message(dir.text()) status = row_item.child(childIndex, 0) filename = row_item.child(childIndex, 1) # self.message(filename.text()) # checksum_item = row_item.child(childIndex, 4) if status.text() != "Done": # parent = filename.parent() # self.message("parent " + str(parent)) self.run_process(dir.text(), filename.text(), row_item, childIndex) started_count += 1 if self.p: break self.message(f'start_process done. Started {started_count}') # yield child # for i in range(self.model.rowCount()): # item = self.model.item(i) # root = self.treeWidget.invisibleRootItem() # child_count = root.childCount() # for i in range(child_count): # item = ro # ot.child(i) # url = item.text(0) # text at first (0) column # item.setText(1, 'result from %s' % url) # update result column (1) def run_process(self, dir, filename, set_item, childIndex): if self.p is None: # No process running. self.p_item = dict(set_item=set_item, child_index=childIndex) self.p = QProcess( ) # Keep a reference to the QProcess (e.g. on self) while it's running. # item # self.p.readyReadStandardOutput.connect(self.handle_stdout) self.p.readyReadStandardError.connect(self.handle_stderr) self.p.stateChanged.connect(self.handle_state) self.p.finished.connect( self.process_finished) # Clean up once complete. # filename = r"J:\Media Library\TV\New\Family Guy\Misc\Family Guy S07E11.avi" filepath = dir + filename self.message("Executing process for file: " + filepath) if Path(filepath).stat().st_size > 0: args = ["-hashfile", filepath, "SHA512"] self.p.start("certutil", args) else: # CertUtil cannot compute hashes on empty files args = ["zero_sha512.py"] self.p.start("python", args) status = set_item.child(childIndex, 0) status.setIcon(self.running_icon) set_item.setIcon(self.running_icon) if status.text() == '': if set_item.text() != 'Test': set_item.child(childIndex, 0).setText('Running Pass 1/3') else: set_item.child(childIndex, 0).setText('Running') elif status.text() == 'Running Pass 1/3': set_item.child(childIndex, 0).setText('Running Pass 2/3') elif status.text() == 'Running Pass 2/3': set_item.child(childIndex, 0).setText('Running Pass 3/3') else: self.message("process already running!") def handle_stderr(self): data = self.p.readAllStandardError() stderr = bytes(data).decode("utf8") # Extract progress if it is in the data. # progress = simple_percent_parser(stderr) # if progress: # self.progress.setValue(progress) self.message(stderr) def handle_stdout(self): data = self.p.readAllStandardOutput() stdout = bytes(data).decode("utf8") self.message(stdout) def handle_state(self, state): states = { QProcess.NotRunning: 'Not running', QProcess.Starting: 'Starting', QProcess.Running: 'Running', } state_name = states[state] self.message(f"State changed: {state_name}") def process_finished(self, exitCode, exitStatus): row_item = self.p_item['set_item'] child_index = self.p_item['child_index'] status_item = row_item.child(child_index, 0) filename_item = row_item.child(child_index, 1) try: checksum_set = filename_item.data(QtCore.Qt.UserRole) if checksum_set is None: self.toast_notification( "Checksum set not found.", "No checksum set for file " + filename_item.text()) raise Exception("No checksum set found") self.message( f'Process finished. exitCode = {exitCode}, exitStatus = {exitStatus}, baseDirname = {checksum_set.baseDirname}' ) data = self.p.readAllStandardOutput() # stdout = bytes(data) # self.message("normal stdout: " + stdout) # .decode('iso-8859-1').encode('utf8') # stdout = bytes(data).decode("iso-8859-1") #try: stdout = bytes(data).decode('utf8', 'replace') # except: # stdout = bytes(data).decode('iso-8859-1') # self.message("row_item" + row_item) self.message("stdout: " + stdout) lines = stdout.splitlines() if exitCode != 0 or len(lines) != 3: msg = f'Compute SHA512 failed. stdout: ({stdout})' error_message = '' if len(lines) > 1 and lines[1].startswith("CertUtil: "): error_message = lines[1][10:] # msg = f'Error: Compute SHA512 failed for file {filename}. stdout: ({cp.stdout}) stderr: ({cp.stderr})' # print(msg) self.toast_notification( 'Failed to compute checksum. ' + error_message, filename_item.text()) raise Exception(msg) new_checksum = lines[1].replace(" ", "") checksum_item = row_item.child(child_index, 4) old_checksum = checksum_item.text() if old_checksum == "": checksum_item.setText(new_checksum) elif new_checksum != old_checksum: msg = f'Checksums do not match.' self.toast_notification('Checksums mismatch', filename_item.text()) raise Exception(msg) if status_item.text() == "Running Pass 3/3" or status_item.text( ) == 'Running': status_item.setText('Done') status_item.setIcon(self.done_icon) # checksum = QStandardItem() # row_item.setChild(0, 4, checksum) # my_item.setText() # my_item.setText(0, "Done") # self.message("my_item: " + my_item.text(1)) # if self.all_files_done_checksum(row_item.parent()): # self.create_sha_file(my_item.parent()) if self.all_files_done_checksum(row_item): row_item.setIcon(self.done_icon) row_item.setText("Done") if checksum_set.hasSha512File(): self.message( f'Updating {checksum_set.baseDirname} checksum file modified date' ) checksum_set.update_modified() else: self.create_sha_file(row_item) except Exception as e: status_item.setText("Failed") status_item.setIcon(self.failed_icon) row_item.setText("Failed") row_item.setIcon(self.failed_icon) self.message("Error: " + str(e)) finally: self.p = None self.start_process() def all_files_done_checksum(self, set_item): for childIndex in range(set_item.rowCount()): status_item = set_item.child(childIndex, 0) checksum_item = set_item.child(childIndex, 4) filename_item = set_item.child(childIndex, 1) self.message(filename_item.text()) if status_item.text() != 'Done' or checksum_item.text() == '': return False return True def create_sha_file(self, set_item): self.message("create_sha_file!!") # index = set_item.index() # self.message(f'index = {index}') # dirname = self.model.item(index, 1) # dirname = set_item.data(1) dir = self.model.invisibleRootItem().child(set_item.row(), 1) dirname = dir.text() self.message( f'create_sha_file2, dir = {dir.text()}, row = {set_item.row()}, set_item.columnCount = {set_item.columnCount()}' ) shaFile = Sha512(dirname) for childIndex in range(set_item.rowCount()): checksum_item = set_item.child(childIndex, 4) filename_item = set_item.child(childIndex, 1) self.message(filename_item.text()) shaFile.addFileAndChecksum(filename_item.text(), checksum_item.text()) shaFile.writeFile()
def populateModel(self): self.model = QStandardItemModel() self.model.setHorizontalHeaderLabels([ 'Status', 'Directory / Filename', 'File Count', 'Size', 'Checksum', 'Last Verified' ]) rootItem = self.model.invisibleRootItem() for checksum_set in self.checksum_sets: last_verified = '' status = '' if not checksum_set.hasSha512File(): status = 'New' elif checksum_set.has_changes(): status = "Modified" self.toast_notification("Checksum set was modified.", checksum_set.baseDirname) else: time_delta = datetime.now( ) - checksum_set.sha512File.last_verified last_verified = f'{time_delta.days} days ago' if time_delta.days > good_days: status = 'Test' else: status = 'Good' if status == 'Test' or status == 'New' or status == 'Modified': item = [ QStandardItem(status), QStandardItem(checksum_set.baseDirname), QStandardItem(str(len(checksum_set.filenames))), QStandardItem(''), QStandardItem(''), QStandardItem(last_verified) ] for filename in checksum_set.filenames: checksum_text = '' file_status = '' if checksum_set.sha512File: checksum = checksum_set.sha512File.findChecksum( filename) if checksum: checksum_text = checksum else: file_status = 'Added' size = Path(checksum_set.baseDirname + filename).stat().st_size size_display = self.format_size(size) # sz = len(checksum_set.baseDirname) + len(filename) c_item = [ QStandardItem(file_status), QStandardItem(filename), QStandardItem(''), QStandardItem(size_display), QStandardItem(checksum_text) ] c_item[1].setData(checksum_set, QtCore.Qt.UserRole) if file_status == 'Added': c_item[0].setIcon(self.added_icon) if filename == 'Thumbs.db': c_item[0].setIcon(self.warning_icon) item[0].appendRow(c_item) if checksum_set.hasSha512File(): for filename in checksum_set.get_missing_from_dir(): checksum_text = checksum_set.sha512File.findChecksum( filename) c_item = [ QStandardItem('Missing'), QStandardItem(filename), QStandardItem(''), QStandardItem('n/a'), QStandardItem(checksum_text) ] c_item[1].setData(checksum_set, QtCore.Qt.UserRole) c_item[0].setIcon(self.missing_icon) item[0].appendRow(c_item) rootItem.appendRow(item)
class KeyMgrDialog(QDialog, UIKeyMgrDialog): def __init__(self, parent=None, model=True) -> None: super().__init__() self.parent = parent self.setupUi(self) self.setWindowTitle('密钥管理') self.setModal(model) self.key_list = key_cache.get_key_list() self.list_model = None self.setup_key_list(self.key_list) self.tbCreate.clicked.connect(self.create_key_action) self.tbCreate.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.tbCreate.setIcon( qta.icon('ei.file-new', color=icon_color['color'], color_active=icon_color['active'])) self.tbAdd.clicked.connect(self.add_key_action) self.tbAdd.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.tbAdd.setIcon( qta.icon('fa.plus', color=icon_color['color'], color_active=icon_color['active'])) self.tbRemove.clicked.connect(self.remove_key_action) self.tbRemove.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.tbRemove.setIcon( qta.icon('fa.minus', color=icon_color['color'], color_active=icon_color['active'])) add_key_invalidate_callback(self.key_invalidate) def setup_key_list(self, key_list): self.keyListView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.list_model = QStandardItemModel(self.keyListView) for key in key_list: item = self.make_item(key) self.list_model.appendRow(item) self.keyListView.setModel(self.list_model) self.keyListView.setContextMenuPolicy(Qt.CustomContextMenu) self.keyListView.customContextMenuRequested[QPoint].connect( self.context_menu) def key_invalidate(self, invalidate_key: Key): for i in range(self.list_model.rowCount()): key = self.list_model.item(i).data() if key.id == invalidate_key.id: #self.list_model.item(i).setIcon(qta.icon('fa.warning', color=icon_color)) self.update_key_icon(self.list_model.item(i)) return def update_key_icon(self, item): key = item.data() if key.timeout: item.setIcon( qta.icon('fa.warning', scale_factor=0.8, color=icon_color['color'], color_active=icon_color['active'])) elif key_cache.is_cur_key(key): item.setIcon( qta.icon('fa.check', scale_factor=0.8, color=icon_color['color'], color_active=icon_color['active'])) else: item.setIcon( QIcon( qta.icon('fa5s.key', scale_factor=0.7, color=icon_color['color'], color_active=icon_color['active']))) def make_item(self, key): item = QStandardItem(key.name) item.setData(key) #item.setIcon(QIcon(qta.icon('fa5s.key', color=icon_color))) self.update_key_icon(item) return item def get_selected_item(self): selected = self.keyListView.selectedIndexes() if not selected: return index = selected[0] item = self.list_model.item(index.row()) return item def context_menu(self, point): selected = self.keyListView.selectedIndexes() if not selected: return index = selected[0] item = self.list_model.item(index.row()) key = item.data() pop_menu = QMenu() _set_default_key_action = pop_menu.addAction( qta.icon('fa.check', color=icon_color['color'], color_active=icon_color['active']), '设为默认') if key_cache.is_cur_key(key) or key.timeout: _set_default_key_action.setEnabled(False) pop_menu.addSeparator() _encrypt_files_action = pop_menu.addAction( qta.icon('ei.lock', color=icon_color['color'], color_active=icon_color['active']), "加密文件") _decrypt_files_action = pop_menu.addAction( qta.icon('ei.unlock', color=icon_color['color'], color_active=icon_color['active']), "解密文件") pop_menu.addSeparator() _reload_key_action = pop_menu.addAction( qta.icon('fa.refresh', color=icon_color['color'], color_active=icon_color['active']), "重新加载") _reload_key_action.setEnabled(key.timeout) selected_action = pop_menu.exec_(QCursor.pos()) if selected_action == _encrypt_files_action: self.encrypt_files_action() elif selected_action == _set_default_key_action: self.set_default_key_action() elif selected_action == _reload_key_action: self.reload_key_action() elif selected_action == _decrypt_files_action: self.decrypt_files_action() def set_default_key_action(self, item=None): item = self.get_selected_item() self._set_default_key(item) for i in range(self.list_model.rowCount()): self.update_key_icon(self.list_model.item(i)) #self.list_model.item(i).setIcon(qta.icon('fa5s.key', color=icon_color)) def create_key_action(self): dialog = KeyCreateDialog(parent=self) dialog.activateWindow() dialog.exec() key = dialog.key if key is None: return item = self.make_item(key) self._add_key(item, key) def _add_key(self, item, key): self.list_model.appendRow(item) key_cache.add_key(key) def _remove_key(self, index): self.list_model.removeRow(index) key_cache.remove_key(index) def _set_default_key(self, item): key_cache.set_current_key(item.data()) self.update_key_icon(item) def add_key_action(self): file_name, _ = QFileDialog.getOpenFileName(self, '选择密钥文件') if len(file_name.strip()) == 0: return password = None if Key.need_password(file_name): ok_pressed = True while ok_pressed: password, ok_pressed = QInputDialog.getText( self, "需要密码", "输入密码:", QLineEdit.Password, "") if ok_pressed: illegal, msg = Key.is_password_illegal(password) if illegal: QMessageBox.information(self, '错误', msg) continue break else: return key = Key() try: key.load(file_name, password) except Exception as e: QMessageBox.critical(self, '错误', '不是有效的密钥文件<br/>' + str(e)) return row_len = self.list_model.rowCount() for i in range(0, row_len): item: QStandardItem = self.list_model.item(i, 0) k: Key = item.data() if k.id == key.id: QMessageBox.information(self, '信息', '相同的密钥已经加载') return item = self.make_item(key) self._add_key(item, key) return def remove_key_action(self): selected = self.keyListView.selectedIndexes() if not selected: return index = selected[0] self._remove_key(index.row()) QMessageBox.information(self, '信息', '密钥已经移除') def encrypt_files_action(self): item = self.get_selected_item() key = item.data() ef_dialog = EncryptFileDialog(self) ef_dialog.set_key(key) ef_dialog.exec() def decrypt_files_action(self): item = self.get_selected_item() key = item.data() ef_dialog = EncryptFileDialog( self, win_title='解密文件', before_process=is_encrypt_data, processor=decrypt_data, success_msg='解密完成', select_file_dlg_title='选择需要解密的文件', ) ef_dialog.set_key(key) ef_dialog.exec() def reload_key_action(self): item = self.get_selected_item() key: Key = item.data() password = None if Key.need_password(key.path): ok_pressed = True while ok_pressed: password, ok_pressed = QInputDialog.getText( self, "需要密码", "输入密码:", QLineEdit.Password, "") if ok_pressed: illegal, msg = Key.is_password_illegal(password) if illegal: QMessageBox.information(self, '错误', msg) continue break else: return try: key.load(key.path, password) except Exception as e: QMessageBox.critical(self, '错误', '不是有效的密钥文件<br/>' + str(e)) return self.update_key_icon(item)
class App(QMainWindow): def __init__(self): super(App, self).__init__() self.treeView = None self.model = None self.pattern_delegate = None self.callee_delegate = None self.sig_trie = None self.searchResults = None self.searchIndex = -1 self.findNextAction = None self.findPrevAction = None # these two maps are used to make the hyperlinks work # mapping from href to FunctionNode self.hrefs_to_funcs = {} # mapping from FunctionNode to tree view element (QStandardItem) self.func_node_items = {} self.init_ui() def init_ui(self): self.setWindowTitle('Signature Explorer') self.resize(1000, 640) app_icon = QIcon() app_icon.addFile('icon.ico', QSize(48, 48)) self.setWindowIcon(app_icon) self.pattern_delegate = PatternDelegate() self.callee_delegate = CalleesDelegate() self.treeView = TrieView() # self.treeView.setAlternatingRowColors(True) self.model = QStandardItemModel(0, 7, self.treeView) self.model.setHeaderData(0, Qt.Horizontal, 'Signature') self.model.setHeaderData(1, Qt.Horizontal, 'Function') self.model.setHeaderData(2, Qt.Horizontal, 'Callees') self.model.setHeaderData(3, Qt.Horizontal, 'Offset Extra Pattern') self.model.setHeaderData(4, Qt.Horizontal, 'Extra Pattern') self.model.setHeaderData(5, Qt.Horizontal, 'Source Binary') self.model.setHeaderData(6, Qt.Horizontal, 'ID') self.treeView.setModel(self.model) self.treeView.setSelectionBehavior(QAbstractItemView.SelectRows) self.treeView.setColumnWidth(0, 400) self.treeView.setColumnWidth(1, 200) self.treeView.setColumnWidth(2, 250) self.treeView.setColumnWidth(3, 25) self.treeView.setColumnWidth(4, 100) self.treeView.setColumnWidth(5, 200) self.treeView.setColumnWidth(6, 75) self.treeView.setItemDelegateForColumn(0, self.pattern_delegate) self.treeView.setItemDelegateForColumn(2, self.callee_delegate) self.treeView.setItemDelegateForColumn(4, self.pattern_delegate) self.treeView.horizontalScrollBar().setEnabled(True) self.treeView.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.treeView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.treeView.linkActivated.connect(self.on_func_link_clicked) # self.treeView.expanded.connect(lambda x: self.treeView.resizeColumnToContents(1)) # self.treeView.collapsed.connect(lambda x: self.treeView.resizeColumnToContents(1)) main_layout = QVBoxLayout() main_layout.addWidget(self.treeView) panel = QWidget() panel.setLayout(main_layout) self.setCentralWidget(panel) menuBar = self.menuBar() fileMenu = QMenu("File") openAction = QAction("&Open", self) openAction.setShortcuts(QKeySequence.Open) openAction.triggered.connect(self.open_file) fileMenu.addAction(openAction) closeAction = QAction("&Close", self) closeAction.setShortcuts(QKeySequence.Close) closeAction.triggered.connect(self.close_file) fileMenu.addAction(closeAction) saveAsAction = QAction("Save As...", self) saveAsAction.setShortcuts(QKeySequence.Save) saveAsAction.triggered.connect(self.save_as) fileMenu.addAction(saveAsAction) menuBar.addMenu(fileMenu) editMenu = QMenu("Edit") findAction = QAction("&Find", self) findAction.setShortcuts(QKeySequence.Find) findAction.triggered.connect(self.search) editMenu.addAction(findAction) self.findNextAction = QAction("&Find Next", self) self.findNextAction.setShortcuts(QKeySequence.FindNext) self.findNextAction.triggered.connect(self.select_next) self.findNextAction.setEnabled(False) editMenu.addAction(self.findNextAction) self.findPrevAction = QAction("&Find Prev", self) self.findPrevAction.setShortcuts(QKeySequence.FindPrevious) self.findPrevAction.triggered.connect(self.select_prev) self.findPrevAction.setEnabled(False) editMenu.addAction(self.findPrevAction) menuBar.addMenu(editMenu) viewMenu = QMenu("View") expandAction = QAction("&Expand All", self) expandAction.triggered.connect(self.treeView.expandAll) viewMenu.addAction(expandAction) collapseAction = QAction("&Collapse All", self) collapseAction.triggered.connect(self.treeView.collapseAll) viewMenu.addAction(collapseAction) menuBar.addMenu(viewMenu) def search(self): query_string, ok = QInputDialog.getText(self, 'Find in Trie', 'Function name') if not ok or not query_string: return self.searchResults = self.model.findItems( query_string, Qt.MatchContains | Qt.MatchRecursive, 1) if self.searchResults: self.findNextAction.setEnabled(True) self.findPrevAction.setEnabled(True) self.searchIndex = 0 self.select_next() else: self.findNextAction.setEnabled(False) self.findPrevAction.setEnabled(False) self.searchIndex = -1 QMessageBox.warning(self, 'Find in Trie', 'No results found') def select_next(self): next_item = self.searchResults[self.searchIndex] self.searchIndex = (self.searchIndex + 1) % len(self.searchResults) self.select_tree_item(next_item) def select_prev(self): prev_item = self.searchResults[self.searchIndex] self.searchIndex = (self.searchIndex - 1) % len(self.searchResults) self.select_tree_item(prev_item) def select_tree_item(self, item): path = [] while item: path.insert(0, self.model.indexFromItem(item)) item = item.parent() # print(path) for index in path: self.treeView.setExpanded(index, True) self.treeView.selectionModel().select( path[-1], QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows) self.treeView.scrollTo(path[-1]) def close_file(self): self.model.removeRows(0, self.model.rowCount()) self.sig_trie = None self.hrefs_to_funcs = {} self.func_node_items = {} def open_file(self): sig_filter = 'Signature library (*.sig)' json_zlib_filter = 'Compressed JSON signature library (*.json.zlib)' json_filter = 'JSON signature library (*.json)' pkl_filter = 'Pickled signature library (*.pkl)' fname, filter = QFileDialog.getOpenFileName(self, 'Open file', filter=';;'.join([ sig_filter, json_zlib_filter, json_filter, pkl_filter ])) if filter and fname: print('Opening signature library %s' % (fname, )) if filter == json_zlib_filter: with open(fname, 'rb') as f: json_trie = zlib.decompress(f.read()).decode('utf-8') sig_trie = sig_serialize_json.deserialize(json.loads(json_trie)) elif filter == json_filter: with open(fname, 'r') as f: json_trie = f.read() sig_trie = sig_serialize_json.deserialize(json.loads(json_trie)) elif filter == sig_filter: with open(fname, 'rb') as f: fb_trie = f.read() sig_trie = sig_serialize_fb.SignatureLibraryReader().deserialize( fb_trie) elif filter == pkl_filter: with open(fname, 'rb') as f: sig_trie = pickle.load(f) else: return self.open_trie(sig_trie, os.path.basename(fname)) def save_as(self): sig_filter = 'Signature library (*.sig)' json_zlib_filter = 'Compressed JSON signature library (*.json.zlib)' json_filter = 'JSON signature library (*.json)' pkl_filter = 'Pickled signature library (*.pkl)' fname, filter = QFileDialog.getSaveFileName(self, 'Open file', filter=';;'.join([ sig_filter, json_zlib_filter, json_filter, pkl_filter ])) if filter == json_zlib_filter: with open(fname, 'wb') as f: f.write( zlib.compress( sig_serialize_json.serialize( self.sig_trie).encode('utf-8'))) elif filter == json_filter: with open(fname, 'w') as f: json.dump(sig_serialize_json.serialize(self.sig_trie), f, indent=4) elif filter == sig_filter: with open(fname, 'wb') as f: f.write(sig_serialize_fb.SignatureLibraryWriter().serialize( self.sig_trie)) elif filter == pkl_filter: with open(fname, 'wb') as f: pickle.dump(self.sig_trie, f) else: return print('Saved as ' + fname) @staticmethod def generate_href(func): return str(id(func)) def get_func_name(self, func_node): if func_node is None: return '<missing>' else: return '<a href="' + self.generate_href( func_node) + '">' + func_node.name + '</a>' # handles when the user clicks on a hyperlink to a function node def on_func_link_clicked(self, link): print('Hyperlink clicked: ' + link) self.select_tree_item(self.func_node_items[self.hrefs_to_funcs[link]]) # Generate treeview row for function (leaf) node in the trie def add_func_node(self, parent, pattern_col_item, func): self.hrefs_to_funcs[self.generate_href(func)] = func self.func_node_items[func] = pattern_col_item if not func.callees: func.callees = {} callees_text = '<br />'.join([ str(k) + ': ' + self.get_func_name(v) for k, v in func.callees.items() ]) callees_item = QStandardItem(callees_text) cols = [ pattern_col_item, QStandardItem(func.name), callees_item, QStandardItem(str(func.pattern_offset) if func.pattern else ''), QStandardItem(str(func.pattern) if func.pattern else ''), QStandardItem(func.source_binary), QStandardItem(self.generate_href(func)) ] boldface = cols[1].font() boldface.setBold(True) cols[1].setFont(boldface) parent.appendRow(cols) # Recursively add rows for this trie node and its children def add_trie_node(self, parent, pattern_text, node): left_item = QStandardItem(pattern_text) if not node.value: # Stem node parent.appendRow([left_item, QStandardItem('')]) else: # Leaf node self.add_func_node(parent, left_item, node.value[0]) for func in node.value[1:]: self.add_func_node(parent, QStandardItem(''), func) pairs = map(lambda node: (str(node.pattern), node), node.children.values()) pairs = sorted(pairs, key=lambda kv: kv[0].replace('?', '\xff')) for text, child in pairs: self.add_trie_node(left_item, text, child) return left_item # Add bridge nodes to a special node at the root def add_bridge_nodes(self, parent, sig_trie): bridge_item = QStandardItem('(bridge)') parent.appendRow([bridge_item, QStandardItem('')]) def visit(func, visited): if func is None or func in visited: return visited.add(func) if func.is_bridge: self.add_func_node(bridge_item, QStandardItem(''), func) for callee in func.callees.values(): visit(callee, visited) visited = set() for func in sig_trie.all_values(): visit(func, visited) def open_trie(self, sig_trie, filename): self.close_file() self.sig_trie = sig_trie root_node = self.add_trie_node(self.model, filename, sig_trie) self.add_bridge_nodes(root_node, sig_trie)
def init_ui(self): self.setWindowTitle('Signature Explorer') self.resize(1000, 640) app_icon = QIcon() app_icon.addFile('icon.ico', QSize(48, 48)) self.setWindowIcon(app_icon) self.pattern_delegate = PatternDelegate() self.callee_delegate = CalleesDelegate() self.treeView = TrieView() # self.treeView.setAlternatingRowColors(True) self.model = QStandardItemModel(0, 7, self.treeView) self.model.setHeaderData(0, Qt.Horizontal, 'Signature') self.model.setHeaderData(1, Qt.Horizontal, 'Function') self.model.setHeaderData(2, Qt.Horizontal, 'Callees') self.model.setHeaderData(3, Qt.Horizontal, 'Offset Extra Pattern') self.model.setHeaderData(4, Qt.Horizontal, 'Extra Pattern') self.model.setHeaderData(5, Qt.Horizontal, 'Source Binary') self.model.setHeaderData(6, Qt.Horizontal, 'ID') self.treeView.setModel(self.model) self.treeView.setSelectionBehavior(QAbstractItemView.SelectRows) self.treeView.setColumnWidth(0, 400) self.treeView.setColumnWidth(1, 200) self.treeView.setColumnWidth(2, 250) self.treeView.setColumnWidth(3, 25) self.treeView.setColumnWidth(4, 100) self.treeView.setColumnWidth(5, 200) self.treeView.setColumnWidth(6, 75) self.treeView.setItemDelegateForColumn(0, self.pattern_delegate) self.treeView.setItemDelegateForColumn(2, self.callee_delegate) self.treeView.setItemDelegateForColumn(4, self.pattern_delegate) self.treeView.horizontalScrollBar().setEnabled(True) self.treeView.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.treeView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.treeView.linkActivated.connect(self.on_func_link_clicked) # self.treeView.expanded.connect(lambda x: self.treeView.resizeColumnToContents(1)) # self.treeView.collapsed.connect(lambda x: self.treeView.resizeColumnToContents(1)) main_layout = QVBoxLayout() main_layout.addWidget(self.treeView) panel = QWidget() panel.setLayout(main_layout) self.setCentralWidget(panel) menuBar = self.menuBar() fileMenu = QMenu("File") openAction = QAction("&Open", self) openAction.setShortcuts(QKeySequence.Open) openAction.triggered.connect(self.open_file) fileMenu.addAction(openAction) closeAction = QAction("&Close", self) closeAction.setShortcuts(QKeySequence.Close) closeAction.triggered.connect(self.close_file) fileMenu.addAction(closeAction) saveAsAction = QAction("Save As...", self) saveAsAction.setShortcuts(QKeySequence.Save) saveAsAction.triggered.connect(self.save_as) fileMenu.addAction(saveAsAction) menuBar.addMenu(fileMenu) editMenu = QMenu("Edit") findAction = QAction("&Find", self) findAction.setShortcuts(QKeySequence.Find) findAction.triggered.connect(self.search) editMenu.addAction(findAction) self.findNextAction = QAction("&Find Next", self) self.findNextAction.setShortcuts(QKeySequence.FindNext) self.findNextAction.triggered.connect(self.select_next) self.findNextAction.setEnabled(False) editMenu.addAction(self.findNextAction) self.findPrevAction = QAction("&Find Prev", self) self.findPrevAction.setShortcuts(QKeySequence.FindPrevious) self.findPrevAction.triggered.connect(self.select_prev) self.findPrevAction.setEnabled(False) editMenu.addAction(self.findPrevAction) menuBar.addMenu(editMenu) viewMenu = QMenu("View") expandAction = QAction("&Expand All", self) expandAction.triggered.connect(self.treeView.expandAll) viewMenu.addAction(expandAction) collapseAction = QAction("&Collapse All", self) collapseAction.triggered.connect(self.treeView.collapseAll) viewMenu.addAction(collapseAction) menuBar.addMenu(viewMenu)