qtablewidget의 필터에 사용되는 메뉴 모음을 pyqt5에서 스크롤 가능하게 만드는 방법은 무엇입니까?

앨빈로 레자 스

나는 pyqt를 처음 접 했고이 질문은 어리석은 일입니다. 어떤 도움이라도 정말 감사하겠습니다. 이 페이지에서 qtablewidget을 필터링하는 코드가있는 코드가 있습니다. 이 코드는 원하는 필터링 출력에 완벽하게 작동합니다. 그러나 테이블이 있고 행이 많기 때문에 필터링에 사용되는 메뉴 모음을 행에 모든 고유 한 내용을 표시하는 대신 스크롤 할 수 있기를 원합니다. 메뉴 바의 높이를 고정하고 싶습니다.

다음은 코드입니다.

import csv
import sys
from PyQt5 import QtCore

from PyQt5 import QtGui, QtWidgets


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent=parent)
        self.verticalLayout = QtWidgets.QVBoxLayout(self)
        self.table = QtWidgets.QTableWidget(self)
        self.table.setColumnCount(0)
        self.table.setRowCount(0)
        self.verticalLayout.addWidget(self.table)

        self.loadAll()
        self.horizontalHeader = self.table.horizontalHeader()
        self.horizontalHeader.sectionClicked.connect(self.on_view_horizontalHeader_sectionClicked)
        self.keywords = dict([(i, []) for i in range(self.table.columnCount())])
        self.checkBoxs = []
        self.col = None

    def slotSelect(self, state):

        for checkbox in self.checkBoxs:
            checkbox.setChecked(QtCore.Qt.Checked == state)

    def on_view_horizontalHeader_sectionClicked(self, index):
        self.menu = QtWidgets.QMenu()
        self.col = index

        data_unique = []
        self.checkBoxs = []

        checkBox = QtWidgets.QCheckBox("Select all", self.menu)
        checkableAction = QtWidgets.QWidgetAction(self.menu)
        checkableAction.setDefaultWidget(checkBox)
        self.menu.addAction(checkableAction)
        checkBox.setChecked(True)
        checkBox.stateChanged.connect(self.slotSelect)

        for i in range(self.table.rowCount()):
            if not self.table.isRowHidden(i):
                item = self.table.item(i, index)
                if item.text() not in data_unique:
                    data_unique.append(item.text())
                    checkBox = QtWidgets.QCheckBox(item.text(), self.menu)
                    checkBox.setChecked(True)
                    checkableAction = QtWidgets.QWidgetAction(self.menu)
                    checkableAction.setDefaultWidget(checkBox)
                    self.menu.addAction(checkableAction)
                    self.checkBoxs.append(checkBox)

        btn = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel,
                                     QtCore.Qt.Horizontal, self.menu)
        btn.accepted.connect(self.menuClose)
        btn.rejected.connect(self.menu.close)
        checkableAction = QtWidgets.QWidgetAction(self.menu)
        checkableAction.setDefaultWidget(btn)
        self.menu.addAction(checkableAction)

        headerPos = self.table.mapToGlobal(self.horizontalHeader.pos())

        posY = headerPos.y() + self.horizontalHeader.height()
        posX = headerPos.x() + self.horizontalHeader.sectionPosition(index)
        self.menu.exec_(QtCore.QPoint(posX, posY))


    def menuClose(self):
        self.keywords[self.col] = []
        for element in self.checkBoxs:
            if element.isChecked():
                self.keywords[self.col].append(element.text())
        self.filterdata()
        self.menu.close()

    def loadAll(self):
        with open("pokemon_data.csv", "r") as inpfil:
            reader = csv.reader(inpfil, delimiter=',')
            csheader = next(reader)
            ncol = len(csheader)
            data = list(reader)
            row_count = len(data)

            self.table.setRowCount(row_count)
            self.table.setColumnCount(ncol)
            self.table.setHorizontalHeaderLabels(('%s' % ', '.join(map(str, csheader))).split(","))

            for ii in range(0, row_count):
                mainins = data[ii]
                for var in range(0, ncol):
                    self.table.setItem(ii, var, QtWidgets.QTableWidgetItem(mainins[var]))

    def clearFilter(self):
        for i in range(self.table.rowCount()):
            self.table.setRowHidden(i, False)

    def filterdata(self):

        columnsShow = dict([(i, True) for i in range(self.table.rowCount())])

        for i in range(self.table.rowCount()):
            for j in range(self.table.columnCount()):
                item = self.table.item(i, j)
                if self.keywords[j]:
                    if item.text() not in self.keywords[j]:
                        columnsShow[i] = False
        for key, value in columnsShow.items():
            self.table.setRowHidden(key, not value)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())

현재는 다음과 같습니다. 여기에 이미지 설명 입력

헤더를 클릭하여 필터링하려고하면 아래 이미지와 같이 테이블에 행이 너무 많기 때문에 모든 데스크톱 창이 채워집니다.

여기에 이미지 설명 입력

i just need a better size of the menubar.

I tried searching related queries to this but I just couldn't find that I can integrate it to this code. Please be gentle with your response as Im really new to this. Thank you so much!

musicamante

The easiest solution is to use an undocumented stylesheet property (as proposed in the unaccepted answer of this post).

    def on_view_horizontalHeader_sectionClicked(self, index):
        self.menu = QtWidgets.QMenu()
        self.menu.setStyleSheet('QMenu { menu-scrollable: true; }')
        # ...

Alternatively (if, for any reason, that behavior doesn't work as expected or its support is removed in the future) you can create a QProxyStyle subclass, implement its styleHint and return True if the given hint is SH_Menu_Scrollable.

class ProxyStyle(QtWidgets.QProxyStyle):
    def styleHint(self, hint, option, widget, data):
        if hint == self.SH_Menu_Scrollable:
            return True
        return super().styleHint(hint, option, widget, data)

# ...

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    app.setStyle(ProxyStyle())
    w = Widget()
    w.show()
    sys.exit(app.exec_())

If you want to keep that behavior only for that menu, you can apply the proxy style to the menu instead of doing it for the whole application:

    def on_view_horizontalHeader_sectionClicked(self, index):
        self.menu = QtWidgets.QMenu()
        self.menu.setStyle(ProxyStyle())
        # ...

그건 그렇고, 당신은 메뉴 바가 아니라 메뉴를 사용하고 있습니다 . 메뉴 바는 일반적으로 창 상단에 배치되는 위젯으로, 각 항목이 (아마도) 메뉴 인 다른 항목을 포함합니다.

최신 정보

항목이기 때문에 많이 사용하여 QMenu를 여러 가지 이유로위한 좋은 솔루션이 아닙니다. 더 나은 방법은 QListWidget과 버튼 상자를 포함하는 QWidget을 사용하는 것입니다. 메뉴와 유사한 동작을 유지하려면 (외부에서 클릭이 발생하면 닫아야 함) Popup창 플래그를 추가 할 수 있습니다 .

class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        # ...
        self.dialog = QtWidgets.QWidget()
        self.dialog.setWindowFlags(
            self.dialog.windowFlags() | QtCore.Qt.Popup | QtCore.Qt.FramelessWindowHint)
        self.dialog.setMaximumHeight(300)
        layout = QtWidgets.QVBoxLayout(self.dialog)
        self.dialogList = QtWidgets.QListWidget()
        layout.addWidget(self.dialogList)
        self.dialogList.itemChanged.connect(self.slotSelect)

        buttonBox = QtWidgets.QDialogButtonBox(
           QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel)
        layout.addWidget(buttonBox)
        buttonBox.accepted.connect(self.menuClose)
        buttonBox.rejected.connect(self.dialog.hide)

    def on_view_horizontalHeader_sectionClicked(self, index):
        self.dialogList.clear()
        self.selectAllItem = QtWidgets.QListWidgetItem('Select all')
        self.selectAllItem.setCheckState(QtCore.Qt.Unchecked)
        self.dialogList.addItem(self.selectAllItem)

        self.col = index
        self.itemList = []
        data_unique = []
        for i in range(self.table.rowCount()):
            if not self.table.isRowHidden(i):
                item = self.table.item(i, index)
                if item == self.selectAllItem:
                    continue
                if item.text() not in data_unique:
                    item = QtWidgets.QListWidgetItem(item.text())
                    item.setCheckState(QtCore.Qt.Unchecked)
                    self.dialogList.addItem(item)
                    self.itemList.append(item)

        self.dialog.move(QtGui.QCursor.pos())
        self.dialog.show()

    def slotSelect(self, item):
        # temporally disconnect the signal to avoid recursion
        self.dialogList.itemChanged.disconnect(self.slotSelect)
        if item == self.selectAllItem:
            state = item.checkState()
            for i in self.itemList:
                i.setCheckState(state)
        else:
            states = [i.checkState() for i in self.itemList]
            if all(states):
                self.selectAllItem.setCheckState(QtCore.Qt.Checked)
            elif not any(states):
                self.selectAllItem.setCheckState(QtCore.Qt.Unchecked)
            else:
                self.selectAllItem.setCheckState(QtCore.Qt.PartiallyChecked)

        # reconnect the signal back again
        self.dialogList.itemChanged.connect(self.slotSelect)

    def menuClose(self):
        self.dialog.hide()
        self.keywords[self.col] = []
        for item in self.itemList:
            if item.checkState():
                self.keywords[self.col].append(item.text())
        self.filterdata()

이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.

침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제

에서 수정
0

몇 마디 만하겠습니다

0리뷰
로그인참여 후 검토

관련 기사

Related 관련 기사

뜨겁다태그

보관