Альтернатива методу setSortingEnabled(True)

У меня есть таблица QTableWidget основанная на этой базе данных, созданная с помощью PyQT5.

Мне нужно как-то отсортировывать столбцы, но при использовании self.tableWidget.setSortingEnabled(True), при сортировки чисел выходит такое:

1, 10, 100, 1000, 10000, ...

Как сделать так, чтобы все сортировалось нормально, по возрастанию/убыванию.

UI-файл, нужный для загрузки интерфейса:

import sqlite3
import sys
# from time import sleep
from PyQt5.QtCore import Qt
from PyQt5 import uic
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QMainWindow, QTableWidgetItem


class MyWidget(QMainWindow):
    def __init__(self):
        super().__init__()
        uic.loadUi("Project_main.ui", self)
        #self.dialog_delete = uic.loadUi("Project_Delete.ui")
        #self.dialog_add = uic.loadUi("Project_Add.ui")
        self.setFixedSize(self.size())
        self.clearButton.clicked.connect(self.clear_bd)
        self.AlphabetSortButton.clicked.connect(self.alphabet_sort)
        self.AntiAlphabetSortButton.clicked.connect(self.anti_alphabet_sort)
        self.returnButton.clicked.connect(self.main_bd)
        self.deleteButton.clicked.connect(self.delete_record)
        self.addButton.clicked.connect(self.add_record)
        self.SerchPushButton.clicked.connect(self.update_result)
        self.con = sqlite3.connect("films_db.sqlite")
        self.tableWidget.itemChanged.connect(self.item_changed)
        self.modified = {}
        self.titles = None
        self.main_bd()
        self.tableWidget.setSortingEnabled(True)

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Enter:
            self.update_result()

    def anti_alphabet_sort(self):
        self.QTableWidget.sortItems(self.QtCore.Qt.AscendingOrder, 0)

    def alphabet_sort(self):
        self.QTableWidget.sortItems(0, self.QtCore.Qt.AscendingOrder)

    def add_record(self):
        # self.dialog_add.show()
        pass

    def delete_record(self):
        # self.dialog_delete.show()
        pass

    def clear_bd(self):
        self.tableWidget.setRowCount(0)
        self.tableWidget.setColumnCount(0)

    def main_bd(self):
        cur = self.con.cursor()
        result = cur.execute("select * from films").fetchall()
        self.tableWidget.setRowCount(len(result))
        self.tableWidget.setColumnCount(len(result[0]))
        self.titles = [description[0] for description in cur.description]
        for i, elem in enumerate(result):
            for j, val in enumerate(elem):
                self.tableWidget.setItem(i, j, QTableWidgetItem(str(val)))

    def update_result(self):
        cur = self.con.cursor()
        # Получили результат запроса, который ввели в текстовое поле
        result = cur.execute("SELECT * FROM films WHERE title like '%" +
                             (item_title := self.lineEdit_search.text()) + "%'", ).fetchall()
        # Заполнили размеры таблицы
        self.tableWidget.setRowCount(len(result))
        # Если запись не нашлась, то не будем ничего делать
        if not result:
            self.statusBar().showMessage('Ничего не нашлось')
            self.tableWidget.setRowCount(0)
            self.tableWidget.setColumnCount(0)
            return
        else:
            self.statusBar().showMessage(f"Нашлась запись с названием - {item_title}")
        self.tableWidget.setColumnCount(len(result[0]))
        self.titles = [description[0] for description in cur.description]
        # Заполнили таблицу полученными элементами
        for i, elem in enumerate(result):
            for j, val in enumerate(elem):
                self.tableWidget.setItem(i, j, QTableWidgetItem(str(val)))
        self.modified = {}

    def item_changed(self, item):
        self.modified[self.titles[item.column()]] = item.text()

    def save_results(self):
        if self.modified:
            cur = self.con.cursor()
            que = "UPDATE films SET\n"
            que += ", ".join([f"{key}='{self.modified.get(key)}'"
                              for key in self.modified.keys()])
            que += "WHERE title = ?"
            print(que)
            cur.execute(que, (self.SearchPushButton.text(),))
            self.con.commit()
            self.modified.clear()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MyWidget()
    ex.show()
    sys.exit(app.exec())

Ответы (2 шт):

Автор решения: S. Nick

Я не могу проверить ваш пример, но я покажу вам возможную реализацию вашей задачи.

Класс QStyledItemDelegate предоставляет средства отображения и редактирования элементов данных из модели.

Больше https://doc.qt.io/qt-5/qstyleditemdelegate.html

Обратите внимание:

  • сортировка работает по любой колонке
  • редактирование возможно только данные в колонке с индексом 5
  • в данной реализации также выполняются манипуляции с подсветкой ячеек в зависимости от значения ее содержимого.

Попробуйте изменить данные в колонке с индексом 5 и проверьте сортировеу.


import sys
import random
from PyQt5.Qt import *


class Delegate(QStyledItemDelegate):
    color_default = QColor("#aaedff")
    colorChanged = pyqtSignal(int, int, int)
    
    def __init__(self, parent=None):
        QStyledItemDelegate.__init__(self, parent)
        self.parent = parent

    def createEditor(self, parent, options, index):
        if index.column() == 5:
            value = index.data()      
            editor = QLineEdit(parent)                                  # !!!
            return editor        

    def setEditorData(self, editor, index):
        if index.column() == 5:
            value = index.model().data(index, Qt.EditRole)
            try: value = int(value)
            except: value = 0
            editor.setText(f'{value:>10}')                              # !!!  

    def updateEditorGeometry(self, editor, options, index):
        editor.setGeometry(options.rect)

    def setModelData(self, editor, model, index):
        if index.column() == 5:
            try: value = int(editor.text())
            except: value = 0
            value = f'{value:>10}'   
            model.setData(index, value, Qt.EditRole)
            self.colorChanged.emit(index.row(), index.column(), int(value))              ############
            

class MyWidget(QWidget):
    def __init__(self):
        super().__init__()

        self.layout = QVBoxLayout(self)

        self.tableWidget = QTableWidget()
        self.tableWidget.setSortingEnabled(True)
        self.layout.addWidget(self.tableWidget)
        
        delegate = Delegate(self)                                        # !!!
        delegate.colorChanged.connect(self.color_changed)                # !!!
        self.tableWidget.setItemDelegate(delegate)                       # !!!         

        self.pb_refill = QPushButton("Refill")
        self.pb_refill.clicked.connect(self.on_click_pb_refill)
        self.layout.addWidget(self.pb_refill)
        
    def color_changed(self, row, col, value):
        if value > 777:
            color = QColor(qrand() % 256, qrand() % 256, qrand() % 256)
            self.tableWidget.item(row, col).setBackground(color)
            self.tableWidget.item(row, col).setFont(QFont("Consolas", 10, QFont.Bold))
        else:
            self.tableWidget.item(row, col).setBackground(QColor("#fff"))
            self.tableWidget.item(row, col).setFont(QFont("Consolas", 10))        

    @pyqtSlot()
    def on_click_pb_refill(self):
        self.tableWidget.clear()
        self.tableWidget.setSortingEnabled(False)                         # !!!
        rows_random = random.randint(1, 10)       
        
        self.tableWidget.setRowCount(rows_random)
        self.tableWidget.setColumnCount(6)
        self.tableWidget.setFont(QFont("Consolas", 10))  
        
        for row in range(rows_random):
            for col in range(6):
                number = random.randint(1, 1000)
                self.tableWidget.setItem(row, col, QTableWidgetItem(f'{number:>10}'))
                
                if col == 5:
                    item_text = int(self.tableWidget.item(row, col).text())
                    if item_text > 777:
                        color = QColor(qrand() % 256, qrand() % 256, qrand() % 256)
                        self.tableWidget.item(row, col).setBackground(color)
                        self.tableWidget.item(row, col).setFont(
                            QFont("Consolas", 10, QFont.Bold))
                    
        self.tableWidget.setSortingEnabled(True)                          # !!!


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MyWidget()
    ex.resize(700, 300)
    ex.show()
    sys.exit(app.exec_())

введите сюда описание изображения

введите сюда описание изображения

введите сюда описание изображения

введите сюда описание изображения

→ Ссылка
Автор решения: Sergey Tatarincev

Ваша проблема в том, что вы устанавливаете в качестве данных строку, а не число.

Qt довольно умный фреймворк, и в большинстве случаев хранит данные в виде QVariant, а соответственно сам знает как с ними работать.

Приведу пример как заполнить таблицу, чтобы ваш столбец сортировался как числа (и плюсом работа с датой)

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QGridLayout, QWidget, QTableWidget, QTableWidgetItem
from PyQt5.QtCore import QSize, Qt, QDate


class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setMinimumSize(QSize(400, 400))
        central_widget = QWidget(self)
        self.setCentralWidget(central_widget)
        grid_layout = QGridLayout()
        central_widget.setLayout(grid_layout)

        table = QTableWidget(self)
        table.setColumnCount(4)
        table.setRowCount(100)
        table.setHorizontalHeaderLabels(["text", "text1", "text2", "date"])
        table.setSortingEnabled(True)

        for i in range(0, 100):
            table.setItem(i, 0, QTableWidgetItem(str(i))) # Когда добавляете так, данные имеют тип строка
            item = QTableWidgetItem()
            item.setData(Qt.EditRole, str(i)) # так опять будет строка
            table.setItem(i,1,item)
            item = QTableWidgetItem()
            item.setData(Qt.EditRole, i) # а так будет число
            table.setItem(i,2,item)
            # И то же касается других типов. на пример даты
            item = QTableWidgetItem()
            item.setData(Qt.EditRole, QDate.currentDate().addDays(i))
            table.setItem(i,3,item)

        table.resizeColumnsToContents()
        grid_layout.addWidget(table, 0, 0)   # Добавляем таблицу в сетку


if __name__ == "__main__":
    app = QApplication(sys.argv)
    mw = MainWindow()
    mw.show()
    sys.exit(app.exec())
→ Ссылка