【问题标题】:PyQt5, Can't use mouse wheel to scroll within a QTableView displaying a pandas dataframePyQt5,不能使用鼠标滚轮在显示熊猫数据框的 QTableView 中滚动
【发布时间】:2020-07-21 06:18:51
【问题描述】:

我使用类似于this one 的 QtableView。一切正常。 我的问题是,我不能用鼠标滚轮滚动。按下鼠标左键滚动并移动滚动条也不起作用。

我只能使用箭头上下滚动。

代码如下:

class PandasModel(QAbstractTableModel):

    def __init__(self, data, parent=None):
        """

        :param data: a pandas dataframe
        :param parent: 
        """
        QtCore.QAbstractTableModel.__init__(self, parent)
        self._data = data

    def rowCount(self, parent=None):
        return len(self._data.values)

    def columnCount(self, parent=None):
        return self._data.columns.size

    def data(self, index, role=QtCore.Qt.DisplayRole):
        current_column = index.column()
        current_row = index.row()

        gold_color = QColor(235, 201, 52)
        lightred_color = QColor('#FF5858')
        knallrot = QColor('#FF0000')
        shine_yellow = QColor('#FFFF00')
        greeny = QColor(92, 235, 52)

        white_color = QColor(QtCore.Qt.white)
        black_color = QColor(QtCore.Qt.black)

        if index.isValid():
            if role == QtCore.Qt.DisplayRole:
                return str(self._data.iloc[index.row(), index.column()])

        if role == Qt.BackgroundColorRole:   # The role for backgrounbd color of a cell
            if current_column == 7:
                it = self._data.iloc[index.row(), current_column]  # Finds the specific data (second column) to test and assigns it to the variable "it"
                if 20 > it > 10:  
                    return QBrush(gold_color)  
                if it >= 20: 
                    return QBrush(shine_yellow)  

        if role == Qt.BackgroundColorRole:  # The role for backgrounbd color of a cell
            if current_column == 5:
                it = self._data.iloc[index.row(), current_column]  # Finds the specific data (second column) to test and assigns it to the variable "it"
                if  it > 100000:  
                    return QBrush(greeny)
                if 10000 > it >= 1000:  
                    return QBrush(lightred_color) 
                if it < 1000: 
                    return QBrush(knallrot) 

            if current_column == 2:
                it = self._data.iloc[index.row(), current_column]  # Finds the specific data (second column) to test and assigns it to the variable "it"
                if  it > 100000:  
                    return QBrush(greeny)
                if 10000 > it >= 1000:  
                    return QBrush(lightred_color) 
                if it < 1000: 
                    return QBrush(knallrot)    

        if role == Qt.TextColorRole:
            return QColor(39, 68, 209)

        if role == Qt.BackgroundColorRole and index == 7:
            return QColor(235, 201, 52)

        if role == Qt.FontRole and index == 7:
            return QFont("Helvetica", 12, QFont.Bold, );

        return None

    def headerData(self, rowcol, orientation, role):

        if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
            return self._data.columns[rowcol]
        if orientation == QtCore.Qt.Vertical and role == QtCore.Qt.DisplayRole:
            return self._data.index[rowcol]

        if role == Qt.BackgroundColorRole and rowcol == 7:
            return QColor(235, 201, 52) #gold
        elif role == Qt.BackgroundColorRole and rowcol == 6:
            return QColor(235, 67, 52) #red
        elif role == Qt.BackgroundColorRole and rowcol == 5:
            return QColor(235, 67, 52) #red
        elif role == Qt.BackgroundColorRole and rowcol == 4:
            return QColor(235, 67, 52) #red
        elif role == Qt.BackgroundColorRole and rowcol == 3:
            return QColor(92, 235, 52) #green
        elif role == Qt.BackgroundColorRole and rowcol == 2:
            return QColor(92, 235, 52) #green
        elif role == Qt.BackgroundColorRole and rowcol == 1:
            return QColor(92, 235, 52) #green
        return None

    def flags(self, index):
        flags = super(self.__class__, self).flags(index)
        #flags |= QtCore.Qt.ItemIsEditable
        flags |= QtCore.Qt.ItemIsSelectable
        flags |= QtCore.Qt.ItemIsEnabled
        flags |= QtCore.Qt.ItemIsDragEnabled
        flags |= QtCore.Qt.ItemIsDropEnabled
        return flags

    def sort(self, Ncol, order):
        """Sort table by given column number.
        """
        try:
            self.layoutAboutToBeChanged.emit()
            self._data = self._data.sort_values(self._data.columns[Ncol], ascending=not order)
            self.layoutChanged.emit()
        except Exception as e:
            print(e)

在 main.py 中它是这样调用的:

        self.tabCrawledresult = QTabWidget()
        self.tabCrawledresult.layout = QGridLayout() 
        self.tabCrawledresult.setLayout(self.tabCrawledresult.layout)
        self.tabs.addTab(self.tabCrawledresult, "Results")

        self.view_minmax = QTableView(self.tabCrawledresult)
        self.modelminmax = gui_pandasModel_sort.PandasModel(df)
        self.view_minmax.setModel(self.modelminmax)  
        self.view_minmax.resize(1000,500)  # is it possible to fit the size to width of headers?  
        self.view_minmax.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)    
        self.view_minmax.setSortingEnabled(True)
        self.view_minmax.sortByColumn(7, Qt.DescendingOrder)
        self.view_minmax.setAlternatingRowColors(True)
        self.view_minmax.setStyleSheet("alternate-background-color: rgb(209, 209, 209)"
                           "; background-color: rgb(244, 244, 244);")

        self.view_minmax.show()

如何使 QTableView 的大小适合标题的宽度?

【问题讨论】:

  • 使用您的代码,我无法重现您的问题。请提供完整的minimal, reproducible example(可能带有显示当前行为的基本测试模型)。关于尺寸,首先你不应该在同一篇文章中问两个不相关的问题;无论如何,只需在 SO 上搜索,因为有很多关于此的问题和答案。
  • hmm,当我用数据框填充 PandasModel 并且它太大时,会显示滚动条,但我无法使用鼠标滚轮,也无法使用滚动条。
  • 很遗憾,如果您不提供 MRE,我们真的无法帮助您。至少分享完整的类,包括它们的方法,否则你的问题将得不到解答。
  • 感谢您提供与 MRE 的链接。使用最小的可重现示例我无法重现“神秘”行为。但是这样做,我清理了一些与我的 PandasModel 重叠的 QWidget。因此,我的鼠标滚轮无法与 QTableView 正确交互。现在一切都很好:) -.Closed.-
  • 很好,这也是需要编写良好示例的实际原因之一:大多数时候,您只需尝试以正确的方式向他人解释问题,就可以意识到问题出在哪里。这通常被认为是Rubber duck debugging,这是一种众所周知的自行解决问题的方法。无论如何,了解问题的根源仍然很有用,并且您可能会发现它也很有用,就像将来其他人一样。我建议您寻找它并与我们分享作为您自己的答案。

标签: python-3.x pandas dataframe pyqt5 qtableview


【解决方案1】:

首先。类 PandasModel 是正确的,没有麻烦。我在 main.py 中所做的事情是错误的。我在 QTabWidget 中显示 TableView 并希望在其底部有一个导出按钮。所以我丑陋的解决方案是,在选项卡上放置一个空 QLabelWidgets 的“层”,然后将 TableView 放在它上面。 这是我的完整示例,其中包含一些数据。

import sys
from PyQt5 import QtCore
from PyQt5.QtCore import QAbstractTableModel, Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QTabWidget, QFileDialog, 
    QGridLayout, QPushButton, QTableView, QLabel, QHeaderView
import numpy as np
import pandas as pd
from functools import partial

def createDF():
    df = pd.DataFrame(np.random.randint(0,11,size=(50, 5)), columns=list(['A','B','C', 
            'D','Tree']))
    print(df)
    return df

trees = {  1: 'Arborvitae (Thuja occidentalis)',
             2: 'Black Ash (Fraxinus nigra)',
             3: 'White Ash (Fraxinus americana)',     
             4: 'Bigtooth Aspen (Populus grandidentata)',
             5: 'Quaking Aspen (Populus tremuloides)',
             6: 'Basswood (Tilia americana)', 
             7: 'American Beech (Fagus grandifolia)',  
             8: 'Black Birch (Betula lenta)',  
             9: 'Gray Birch (Betula populifolia)',  
             10: 'Paper Birch (Betula papyrifera)'} 

class PandasModel(QAbstractTableModel):

    def __init__(self, data, parent=None):
        """
        :param data: a pandas dataframe
        :param parent: 
        """
        QtCore.QAbstractTableModel.__init__(self, parent)        
        self._data = data

    def rowCount(self, parent=None):
        return len(self._data.values)

    def columnCount(self, parent=None):
        return self._data.columns.size

    def data(self, index, role=QtCore.Qt.DisplayRole):

        if index.isValid():
            if role == QtCore.Qt.DisplayRole:
                return str(self._data.iloc[index.row(), index.column()])    
        return None

    def headerData(self, rowcol, orientation, role):

        if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
            return self._data.columns[rowcol]
        if orientation == QtCore.Qt.Vertical and role == QtCore.Qt.DisplayRole:
            return self._data.index[rowcol]


    def flags(self, index):
        flags = super(self.__class__, self).flags(index)
        #flags |= QtCore.Qt.ItemIsEditable
        flags |= QtCore.Qt.ItemIsSelectable
        flags |= QtCore.Qt.ItemIsEnabled
        flags |= QtCore.Qt.ItemIsDragEnabled
        flags |= QtCore.Qt.ItemIsDropEnabled
        return flags

    def sort(self, Ncol, order):
        """Sort table by given column number.
        """
        try:
            self.layoutAboutToBeChanged.emit()
            self._data = self._data.sort_values(self._data.columns[Ncol], 
                ascending=not order)
            self.layoutChanged.emit()
        except Exception as e:
            print(e)


class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        self.resize(400, 600)        

        self.tabs = QTabWidget()
        self.tabs.layout = QGridLayout() 
        self.tabs.setLayout(self.tabs.layout)

        self.grid = QGridLayout()
        self.grid.addWidget(self.tabs)
        self.setLayout(self.grid)
        self.setCentralWidget(self.tabs)  

        self.df = createDF()

        self.displayDF()

    def displayDF(self):

        self.result = QTabWidget()
        self.result.layout = QGridLayout() 
        self.result.setLayout(self.result.layout)
        self.tabs.addTab(self.result, 'Dataframe')

        #empty space to put the export button in the bottom of the TableView
        positions = [(i,j) for i in range(20) for j in range(10)]
        space = '              '
        i = 0
        for position, leer in zip(positions, space): 
            emptyLabels = QLabel(leer)
            self.result.layout.addWidget(emptyLabels, *position)

        #export button     
        self.buttonExport = QPushButton("Export", self)
        self.buttonExport.clicked.connect(partial(self.writeToCSV, self.df))
        self.result.layout.addWidget(self.buttonExport, 21, 0)

        # QTableView
        self.view_minmax = QTableView(self.result)
        self.modelminmax = PandasModel(self.df)
        self.view_minmax.setModel(self.modelminmax)
        self.view_minmax.resize(360,500)    
        self.view_minmax.clicked.connect(self.onClickedRow)
        self.view_minmax.sortByColumn(4, Qt.DescendingOrder)
        self.view_minmax.show()    

    def onClickedRow(self, index=None):
        print("Click !")
        print(index.data())

    def writeToCSV(self, df):
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        fileName, _ = QFileDialog.getSaveFileName(self,"Export results to a\ 
                csv","","CSV (*.csv);;Text Files (*.txt)", options=options)
        if fileName:
            print(fileName)
            df.to_csv (fileName, index = False, header=True) 
        self.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setStyle('Fusion')
    mw = MainWindow()
    mw.show()
    sys.exit(app.exec_())

因此,当我在 #QTableView 部分之后创建#empty space QLabelWidget 和导出按钮时,就像这样,滚动条出现奇怪的行为,因为鼠标信号发送到 QLabelWidgets 而不是 QTabelView

        # QTableView
        self.view_minmax = QTableView(self.result)
        self.modelminmax = PandasModel(self.df)
        self.view_minmax.setModel(self.modelminmax)
        self.view_minmax.resize(360,500)    
        self.view_minmax.clicked.connect(self.onClickedRow)
        self.view_minmax.sortByColumn(4, Qt.DescendingOrder)
        self.view_minmax.show() 

        #empty space to put the export button in the bottom of the TableView
        positions = [(i,j) for i in range(20) for j in range(10)]
        space = '              '
        i = 0
        for position, leer in zip(positions, space): 
            emptyLabels = QLabel(leer)
            self.result.layout.addWidget(emptyLabels, *position)

        #export button     
        self.buttonExport = QPushButton("Export", self)
        self.buttonExport.clicked.connect(partial(self.writeToCSV, self.df))
        self.result.layout.addWidget(self.buttonExport, 21, 0)

也许它会帮助某人。

【讨论】:

    猜你喜欢
    • 2019-05-19
    • 1970-01-01
    • 2023-03-06
    • 2012-06-07
    • 1970-01-01
    • 1970-01-01
    • 2013-04-20
    • 1970-01-01
    • 2011-09-29
    相关资源
    最近更新 更多