QTableWidget icin DragandDrop uygulamasinda sorun

Kendimin yazmadigi aagidaki kodla bir QTableWidget’de satirlari Drag and Drop kullanarak yeniden siraliyorum. Kod calisiyor. Her satira bir PushButton ekledim. Bir satiri mouse’la tutup asagi yukari ittirdigimde PushButton’un diisindaki tüm cell’ler aktariliyor, PushButton’un yeri aktarilmadigi icin bos kaliyor.

Kodda widget’leri kaydirmak icin su satirlar var. Bunlari hata vermemesi icin kapattim. Bunlari kendi ihtiyacima uyarlayamiyorum.

                        pass #Program takilmasin diye ekledim.
                        # for others we call the parents callback function to get the widget
                        #_widget = self._parent.get_table_widget(column_data["item"])
                        #if _widget is not None:
                        #    self.setCellWidget(row_index, column_index, _widget)

Burada istenen _parent adli variable’i tanimlamak ve get_table_widget adli fonksiyonu yazmak gerek. Bunu basaramiyorum.
Bu konuda yardimlarinizi rica ediyorum. Baska bir cözüm de olabilir.

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'TabzuSQL_2.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets
import sys
from PyQt5.QtWidgets import *

from PyQt5.QtCore import Qt
from PyQt5.QtGui import QDropEvent
import sqlite3
import os  
from functools import partial
  

class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        #Form.setWindowFlags(Qt.WindowType.FramelessWindowHint)
        Form.setStyleSheet("QPushButton {\n"
"     background-color:#fff1a3;\n"
"     border-top-width: 1;\n"
"     border-left-width: 1;     \n"
"     border-bottom-width: 3;\n"
"     border-right-width: 3;\n"
"     border-style: solid;\n"
"     border-color:black;\n"
"     min-width: 9ex;\n"
"     min-height: 2.5ex;\n"
"     font-size:12pt;\n"
"     color:black;\n"
"}\n"
"QPushButton:hover {\n"
"   background-color:#ffff7f;\n"
"    padding-left: 10px;\n"
"    padding-top: 3px;\n"
"    margin:1px;\n"
"    font-size:13pt;\n"
"}\n"
"QHeaderView::section {\n"
"	background:rgb(250, 255, 193);\n"
"   font-size:11pt;\n"
"   font: bold;\n"
"   color: black;\n"
"}\n"
"QHeaderView::section:vertical { \n"
"        border: none;\n"
"   	padding-left:5px;\n"
"   font-size:11pt;\n"
"   font: normal;\n"
"    }\n"
"QHeaderView::section:horizontal { \n"
"        border: none;\n" 
"    }\n"
"")

        Form.resize(340, 360)
        self.horizontalLayoutWidget = QtWidgets.QWidget(Form)
        self.horizontalLayoutWidget.setGeometry(QtCore.QRect(10, 60, 320, 241))
        self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.table_widget = QtWidgets.QTableWidget(self.horizontalLayoutWidget)
        self.table_widget.setObjectName("table_widget")
        self.table_widget = TableWidgetDragRows()
        self.horizontalLayout.addWidget(self.table_widget)

        # setup table widget
        self.table_widget.setColumnCount(3)
        self.table_widget.setColumnWidth(0, 30)
        self.table_widget.setColumnWidth(1, 140)
        self.table_widget.setColumnWidth(2, 30)
 # setup table widget
        self.table_widget.setHorizontalHeaderLabels(["Type", "Name", "Sec"])
        items = [
            ("Red", "Toyota"),
            ("Blue", "RV"),
            ("Green", "Beetle"),
            ("Silver", "Chevy"),
            ("Black", "BMW"),
        ]
        self.table_widget.setRowCount(len(items))
        for i, (color, model) in enumerate(items):
            self.table_widget.setItem(i, 0, QTableWidgetItem(color))
            self.table_widget.setItem(i, 1, QTableWidgetItem(model))
            self.pBGid_TurSil= QtWidgets.QPushButton('')
            self.pBGid_TurSil.setStyleSheet("border-color:white;")
            self.pBGid_TurSil.setObjectName("pBGid_TurSil")
            self.pBGid_TurSil.setText("Push")
            self.table_widget.setCellWidget(i,2,self.pBGid_TurSil)

        #self.button1 = QtWidgets.QPushButton(Form)
        #self.button1.setGeometry(QtCore.QRect(90, 320, 158, 30))
        #self.button1.setMinimumSize(QtCore.QSize(0, 30))
        #self.button1.setObjectName("button1")
        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        #self.button1.setText(_translate("Form", "Yeni sırayı kaydet"))
        self.pBGid_TurSil.setText(_translate("Form", "Push"))
#----------------------------------------------
class TableWidgetDragRows(QTableWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.setDragEnabled(True)
        self.setAcceptDrops(True)
        self.viewport().setAcceptDrops(True)
        self.setDragDropOverwriteMode(False)
        self.setDropIndicatorShown(True)

        self.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.setDragDropMode(QAbstractItemView.InternalMove)
        self.setAlternatingRowColors(True)
    def dropEvent(self, event):

        if not event.isAccepted() and event.source() == self:
            drop_row = self.drop_on(event)
            rows = sorted(set(item.row() for item in self.selectedItems()))
            rows_to_move = []
            for row_index in rows:
                items = dict()
                for column_index in range(self.columnCount()):
                    # get the widget or item of current cell
                    widget = self.cellWidget(row_index, column_index)
                    if isinstance(widget, type(None)):
                        # if widget is NoneType, it is a QTableWidgetItem
                        items[column_index] = {"kind": "QTableWidgetItem",
                                               "item": QTableWidgetItem(self.item(row_index, column_index))}
                    else:
                        # otherwise it is any other kind of widget. So we catch the widgets unique (hopefully) objectname
                        items[column_index] = {"kind": "QWidget",
                                               "item": widget.objectName()}
                rows_to_move.append(items)

            for row_index in reversed(rows):
                self.removeRow(row_index)
                if row_index < drop_row:
                    drop_row -= 1

            for row_index, data in enumerate(rows_to_move):
                row_index += drop_row
                self.insertRow(row_index)

                for column_index, column_data in data.items():
                    if column_data["kind"] == "QTableWidgetItem":
                        # for QTableWidgetItem we can re-create the item directly
                        self.setItem(row_index, column_index, column_data["item"])
                    else:
                        pass
                        # for others we call the parents callback function to get the widget
                        #_widget = self._parent.get_table_widget(column_data["item"])
                        #if _widget is not None:
                        #    self.setCellWidget(row_index, column_index, _widget)

            event.accept()

        super().dropEvent(event)

    def drop_on(self, event):
        index = self.indexAt(event.pos())
        if not index.isValid():
            return self.rowCount()

        return index.row() + 1 if self.is_below(event.pos(), index) else index.row()
    def is_below(self, pos, index):
        rect = self.visualRect(index)
        margin = 2
        if pos.y() - rect.top() < margin:
            return False
        elif rect.bottom() - pos.y() < margin:
            return True
        # noinspection PyTypeChecker
        return rect.contains(pos, True) and not (int(self.model().flags(index)) & Qt.ItemIsDropEnabled) and pos.y() >= rect.center().y()



if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Form = QtWidgets.QWidget()
    ui = Ui_Form()
    ui.setupUi(Form)
    Form.show()
    sys.exit(app.exec_())

Merhaba, malum yılbaşı. Sabahın 03.43 ünde yazıyorum. Hafif demliyim. Hatam olursa affola.

Kodunuzu bu kafayla detaylı inceleyemedim. Bir chatgpt ile sohbet ettik. Bir yerde zorlandı. Ben de madem bu kadar önemli, Githup copilota da sorayım dedim.

Benzer şekilde o da çuvalladı.

Aslında çok basit isteiğiniz. Satırları sürükle bırak, butonlar da beraber gitsin.

Her iki AI de kod önerdi ve her ikisinin de bütün kodları sürükle bıraktan sonra hatası uygulamayı kapattı.

Gece gece QT nin butonla neden birleştirip yeniden oluşturamadığını incelemek ve kodun bir bugdan mı kaynaklandığını araştırmaktan kaçmak için yaklaşım değiştirmeye karar verdim.

İmkansız diye bir şey yoktur, ama bence, butonu listeye eklemek QT için bir sıkıntı ve bazan çökmelere neden olabiliyor. Debug yapmak yada nedenini derinlemesine araştırmak isteyen varsa ona da ayrı bir gün bakabiliriz.

Ben de her zaman yaptığım gibi alternatif fikirler üzerinden devam ettim.

Alt tarafı arayüz tasarımı, buton kullanamıyorsam, buton taklidi yaparım. Gridin biri buton taklidi yapsın ne olacak ki diye yaklaştım olaya.

Aksi halde sizin dragdrop event ının tersine mühendisliğini yapıp nerede buton eklemek gerekiyor diye uzunca aramakla uğraşacaktım.

Ama alternatif olsun diye bu yöntemi işleri hızlandırır diye öneriyorum.

Izgaranın biri buton taklidi yapsın dedim, copilot yazdı.

Şöyle bir yaklaşım önerdim. Buton id leri hep sıralamada ilk neredeyse o id ile tanımlansın.

Ki siz o id yi hep sabit kullanabilin. Ama isterseniz id leri satır ile de indeksleyebilirsiniz.

Sonuçta buton gibi davranır.

Ama alışkanlık hep id leri sabit kalsın bende.

Çok uzattım,

Ona göre yazdırdığım kod aşağıda denedim bir sorun göremedim. Umarım ihtiyacınızı karşılar. Kolay gelsin.

# -*- coding: utf-8 -*-

from PyQt5 import QtCore, QtGui, QtWidgets
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt

class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.setStyleSheet("QPushButton {\n"
                           "     background-color:#fff1a3;\n"
                           "     border-top-width: 1;\n"
                           "     border-left-width: 1;     \n"
                           "     border-bottom-width: 3;\n"
                           "     border-right-width: 3;\n"
                           "     border-style: solid;\n"
                           "     border-color:black;\n"
                           "     min-width: 9ex;\n"
                           "     min-height: 2.5ex;\n"
                           "     font-size:12pt;\n"
                           "     color:black;\n"
                           "}\n"
                           "QPushButton:hover {\n"
                           "   background-color:#ffff7f;\n"
                           "    padding-left: 10px;\n"
                           "    padding-top: 3px;\n"
                           "    margin:1px;\n"
                           "    font-size:13pt;\n"
                           "}\n"
                           "QHeaderView::section {\n"
                           "	background:rgb(250, 255, 193);\n"
                           "   font-size:11pt;\n"
                           "   font: bold;\n"
                           "   color: black;\n"
                           "}\n"
                           "QHeaderView::section:vertical { \n"
                           "        border: none;\n"
                           "   	padding-left:5px;\n"
                           "   font-size:11pt;\n"
                           "   font: normal;\n"
                           "    }\n"
                           "QHeaderView::section:horizontal { \n"
                           "        border: none;\n"
                           "    }\n"
                           "")

        Form.resize(340, 360)
        self.horizontalLayoutWidget = QtWidgets.QWidget(Form)
        self.horizontalLayoutWidget.setGeometry(QtCore.QRect(10, 60, 320, 241))
        self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.table_widget = TableWidgetDragRows(self.horizontalLayoutWidget)
        self.table_widget.setObjectName("table_widget")
        self.horizontalLayout.addWidget(self.table_widget)

        # setup table widget
        self.table_widget.setColumnCount(3)
        self.table_widget.setColumnWidth(0, 100)
        self.table_widget.setColumnWidth(1, 140)
        self.table_widget.setColumnWidth(2, 100)
        self.table_widget.setHorizontalHeaderLabels(["Type", "Name", "Sec"])
        items = [
            ("Red", "Toyota", "btn1"),
            ("Blue", "RV", "btn2"),
            ("Green", "Beetle", "btn3"),
            ("Silver", "Chevy", "btn4"),
            ("Black", "BMW", "btn5"),
        ]
        self.table_widget.setRowCount(len(items))
        for i, (color, model, btn_id) in enumerate(items):
            self.table_widget.setItem(i, 0, QTableWidgetItem(color))
            self.table_widget.setItem(i, 1, QTableWidgetItem(model))
            
            # Create a clickable item for the 'button'
            button_item = QTableWidgetItem("Push")
            button_item.setTextAlignment(Qt.AlignCenter)
            button_item.setBackground(QtGui.QColor(255, 241, 163))  # Simulate button color
            button_item.setData(Qt.UserRole, btn_id)  # Set a unique ID for each button item
            self.table_widget.setItem(i, 2, button_item)

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))

class TableWidgetDragRows(QTableWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.setDragEnabled(True)
        self.setAcceptDrops(True)
        self.viewport().setAcceptDrops(True)
        self.setDragDropOverwriteMode(False)
        self.setDropIndicatorShown(True)

        self.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.setDragDropMode(QAbstractItemView.InternalMove)
        self.setAlternatingRowColors(True)

        self.cellClicked.connect(self.handleCellClicked)

    def handleCellClicked(self, row, column):
        if column == 2:
            item = self.item(row, column)
            if item:
                item_id = item.data(Qt.UserRole)
                print(f"Button in row {row} with ID {item_id} clicked")

    def dropEvent(self, event):
        drop_row = self.drop_on(event)
        rows = sorted(set(item.row() for item in self.selectedItems()))
        rows_to_move = []
        for row_index in rows:
            items = []
            for column_index in range(self.columnCount()):
                items.append(self.takeItem(row_index, column_index))
            rows_to_move.append(items)

        for row_index in reversed(rows):
            self.removeRow(row_index)
            if row_index < drop_row:
                drop_row -= 1

        for row_index, data in enumerate(rows_to_move):
            row_index += drop_row
            self.insertRow(row_index)
            for column_index, item in enumerate(data):
                self.setItem(row_index, column_index, item)

        event.accept()

        super().dropEvent(event)

    def drop_on(self, event):
        index = self.indexAt(event.pos())
        if not index.isValid():
            return self.rowCount()
        return index.row() + 1 if self.is_below(event.pos(), index) else index.row()

    def is_below(self, pos, index):
        rect = self.visualRect(index)
        margin = 2
        if pos.y() - rect.top() < margin:
            return False
        elif rect.bottom() - pos.y() < margin:
            return True
        return rect.contains(pos, True) and not (int(self.model().flags(index)) & Qt.ItemIsDropEnabled) and pos.y() >= rect.center().y()

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    Form = QtWidgets.QWidget()
    ui = Ui_Form()
    ui.setupUi(Form)
    Form.show()
    sys.exit(app.exec_())

Yine de satıra indexli kodu da bırakayım. Belki size başka türlü gereklidir:

# -*- coding: utf-8 -*-

from PyQt5 import QtCore, QtGui, QtWidgets
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt

class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.setStyleSheet("QPushButton {\n"
                           "     background-color:#fff1a3;\n"
                           "     border-top-width: 1;\n"
                           "     border-left-width: 1;     \n"
                           "     border-bottom-width: 3;\n"
                           "     border-right-width: 3;\n"
                           "     border-style: solid;\n"
                           "     border-color:black;\n"
                           "     min-width: 9ex;\n"
                           "     min-height: 2.5ex;\n"
                           "     font-size:12pt;\n"
                           "     color:black;\n"
                           "}\n"
                           "QPushButton:hover {\n"
                           "   background-color:#ffff7f;\n"
                           "    padding-left: 10px;\n"
                           "    padding-top: 3px;\n"
                           "    margin:1px;\n"
                           "    font-size:13pt;\n"
                           "}\n"
                           "QHeaderView::section {\n"
                           "	background:rgb(250, 255, 193);\n"
                           "   font-size:11pt;\n"
                           "   font: bold;\n"
                           "   color: black;\n"
                           "}\n"
                           "QHeaderView::section:vertical { \n"
                           "        border: none;\n"
                           "   	padding-left:5px;\n"
                           "   font-size:11pt;\n"
                           "   font: normal;\n"
                           "    }\n"
                           "QHeaderView::section:horizontal { \n"
                           "        border: none;\n"
                           "    }\n"
                           "")

        Form.resize(340, 360)
        self.horizontalLayoutWidget = QtWidgets.QWidget(Form)
        self.horizontalLayoutWidget.setGeometry(QtCore.QRect(10, 60, 320, 241))
        self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.table_widget = TableWidgetDragRows(self.horizontalLayoutWidget)
        self.table_widget.setObjectName("table_widget")
        self.horizontalLayout.addWidget(self.table_widget)

        # setup table widget
        self.table_widget.setColumnCount(3)
        self.table_widget.setColumnWidth(0, 100)
        self.table_widget.setColumnWidth(1, 140)
        self.table_widget.setColumnWidth(2, 100)
        self.table_widget.setHorizontalHeaderLabels(["Type", "Name", "Sec"])
        items = [
            ("Red", "Toyota"),
            ("Blue", "RV"),
            ("Green", "Beetle"),
            ("Silver", "Chevy"),
            ("Black", "BMW"),
        ]
        self.table_widget.setRowCount(len(items))
        for i, (color, model) in enumerate(items):
            self.table_widget.setItem(i, 0, QTableWidgetItem(color))
            self.table_widget.setItem(i, 1, QTableWidgetItem(model))
            
            # Create a clickable item for the 'button'
            button_item = QTableWidgetItem("Push")
            button_item.setTextAlignment(Qt.AlignCenter)
            button_item.setBackground(QtGui.QColor(255, 241, 163))  # Simulate button color
            button_item.setData(Qt.UserRole, "same_id")  # Set the same ID for each button item
            self.table_widget.setItem(i, 2, button_item)

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))

class TableWidgetDragRows(QTableWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.setDragEnabled(True)
        self.setAcceptDrops(True)
        self.viewport().setAcceptDrops(True)
        self.setDragDropOverwriteMode(False)
        self.setDropIndicatorShown(True)

        self.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.setDragDropMode(QAbstractItemView.InternalMove)
        self.setAlternatingRowColors(True)

        self.cellClicked.connect(self.handleCellClicked)

    def handleCellClicked(self, row, column):
        if column == 2:
            item = self.item(row, column)
            if item:
                item_id = item.data(Qt.UserRole)
                print(f"Button in row {row} with ID {item_id} clicked")

    def dropEvent(self, event):
        drop_row = self.drop_on(event)
        rows = sorted(set(item.row() for item in self.selectedItems()))
        rows_to_move = []
        for row_index in rows:
            items = []
            for column_index in range(self.columnCount()):
                items.append(self.takeItem(row_index, column_index))
            rows_to_move.append(items)

        for row_index in reversed(rows):
            self.removeRow(row_index)
            if row_index < drop_row:
                drop_row -= 1

        for row_index, data in enumerate(rows_to_move):
            row_index += drop_row
            self.insertRow(row_index)
            for column_index, item in enumerate(data):
                self.setItem(row_index, column_index, item)

        event.accept()

        super().dropEvent(event)

    def drop_on(self, event):
        index = self.indexAt(event.pos())
        if not index.isValid():
            return self.rowCount()
        return index.row() + 1 if self.is_below(event.pos(), index) else index.row()

    def is_below(self, pos, index):
        rect = self.visualRect(index)
        margin = 2
        if pos.y() - rect.top() < margin:
            return False
        elif rect.bottom() - pos.y() < margin:
            return True
        return rect.contains(pos, True) and not (int(self.model().flags(index)) & Qt.ItemIsDropEnabled) and pos.y() >= rect.center().y()

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    Form = QtWidgets.QWidget()
    ui = Ui_Form()
    ui.setupUi(Form)
    Form.show()
    sys.exit(app.exec_())

Cok tesekkür eder, yeni yilda bol saglik ve basari dilerim.
Birkac kadehin bazan zihni acmasi ve parlak fikirlerin bulunmasi icin yardimci oldugunu ben de saptiyorum.
Kod calisiyor ve görevini tam yerine getiriyor. Gorevi hizasindaki satiri veritabanindan silecek bir fonksiyonu cagirmak. Ekledigimiz handleCellClicked ve icindeki item_id bu iste kullanilmak icin cok uygun.

Button taklidinin tek sakincasi gördügüm ve okudugum kadariyla bir item’e stylesheet uygulanamamasi. Unicode’la cagrilan ◁ isaretini ( ◁ SIL ) seklinde kullanarak oranib tiklamabilecek bir alan oldugunu daha iyi dile getirmeye calisacagim.

Selam ve saygilar

1 Beğeni