如此丑陋的程序谁能帮我?

本想找一个拖放实现分类的app,竟然没有找到,现在想用QTableWidget表格实现,
用到了全局数据,实在太丑了了;在列和列之间拖动单元格

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QDropEvent
from PyQt5.QtWidgets import QTableWidget, QAbstractItemView, QTableWidgetItem, QWidget, QHBoxLayout,QGridLayout, QApplication,QCheckBox
from PyQt5 import QtCore, QtGui, QtWidgets

def transpose_2dA(data,padd=0):
    if padd:
        ln=[len(row) for row in data]
        ln=max(ln)
        #transposed = [[row[col] if col<len(row) else '' for row in data] for col in range(ln)]
        data2=[data[ii]+['']*(ln-len(data[ii])) for ii in range(len(data))]
        transposed = [[row[col] for row in data2] for col in range(ln)]
    else:
        transposed = [[row[col] for row in data] for col in range(len(data[0]))]
    return transposed

def maxminfun(vv,mn,mx):
    if vv<mn:
        return mn
    if vv>mx:
        return mx
    return vv

globaldata=[]
fgMoveAction=0
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.SelectItems)#(QAbstractItemView.SelectRows)
        self.setDragDropMode(QAbstractItemView.InternalMove)

    def dropEvent(self, event: QDropEvent):
        global globaldata,fgMoveAction
        if not event.isAccepted() and event.source() == self:
            drop_row,drop_col = self.drop_on(event)
            if drop_col<0: return
            cc=list(set(item.column() for item in self.selectedItems()))
            if len(cc)>1: return #单列
            drag_col=cc[0]
            rows = sorted(set(item.row() for item in self.selectedItems()))
            if rows[-1]!=rows[0]+len(rows)-1: return #连续单元 简化算法
            drag_row=rows[0]
            
            data=transpose_2dA(globaldata,padd=1)
            if drag_col==drop_col:
                datacol=data[drop_col]
                if drag_row>drop_row:
                    datacol=datacol[:drop_row]+datacol[rows[0]:rows[-1]+1]+datacol[drop_row:rows[0]]+datacol[rows[-1]+1:]
                else:
                    datacol=datacol[:rows[0]]+datacol[rows[-1]+1:drop_row]+datacol[rows[0]:rows[-1]+1]+datacol[drop_row:]
                data[drop_col]=datacol
            else:
                datacol=data[drop_col]
                datacol=datacol[:drop_row]+data[drag_col][rows[0]:rows[-1]+1]+datacol[drop_row:]
                data[drop_col]=datacol
                if fgMoveAction:
                    datacol=data[drag_col]
                    datacol=datacol[:rows[0]]+datacol[rows[-1]+1:]
                    data[drag_col]=datacol
            globaldata=transpose_2dA(data,padd=1)
            #debug_trace()
        
            event.accept()
            print(globaldata)
            self.setRowCount(len(globaldata))
            for row, data in enumerate(globaldata):
                for col, txt in enumerate(data):
                    self.setItem(row, col, QTableWidgetItem(txt))
        super().dropEvent(event)
        #self.show()

    def drop_on(self, event):
        index = self.indexAt(event.pos())
        cc=index.column()
        if cc>=self.columnCount():
            cc=-1
        rr=maxminfun(index.row(),0,self.rowCount())
        return rr,cc

    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()


class Window(QWidget):
    def __init__(self):
        global globaldata,fgMoveAction
        super(Window, self).__init__()

        self.hlayout = QGridLayout()#QHBoxLayout()
        self.setLayout(self.hlayout)

        self.table_widget = TableWidgetDragRows()
        self.btnmyquit = QtWidgets.QPushButton('quit')
        self.btnmyquit.clicked.connect(self.myquit_fun)
        self.btnadditems = QtWidgets.QPushButton('add items')
        self.btnadditems.clicked.connect(self.btnadditems_fun)
        self.btnloadlst = QtWidgets.QPushButton('loadlist')
        self.btnloadlst.clicked.connect(self.btnloadlst_fun)
        self.btnloadfile = QtWidgets.QPushButton('loadfile')
        self.btnloadfile.clicked.connect(self.btnloadfile_fun)
        self.movecheckbox=QCheckBox('move') #
        self.movecheckbox.stateChanged.connect(self.movecheckboxfun)
        self.hlayout.addWidget(self.table_widget, 1, 0,15,36)
        self.hlayout.addWidget(self.btnloadlst, 36, 0,1,1)
        self.hlayout.addWidget(self.btnadditems, 36, 2,1,1)
        self.hlayout.addWidget(self.btnloadfile, 36, 3,1,1)
        self.hlayout.addWidget(self.movecheckbox, 36, 5,1,1)
        self.hlayout.addWidget(self.btnmyquit, 36, 6,1,1)

        # setup table widget
        self.table_widget.setColumnCount(2)
        self.table_widget.setHorizontalHeaderLabels(['Type', 'Name'])

        self.data = [['Red', 'Toyota'], ['Blue', 'RV'], ['Green', 'Beetle'], ['Silver', 'Chevy'], ['Black', 'BMW']]
        self.table_widget.setRowCount(len(self.data))
        globaldata=self.data
        for i, [color, model] in enumerate(self.data):
            self.table_widget.setItem(i, 0, QTableWidgetItem(color))
            self.table_widget.setItem(i, 1, QTableWidgetItem(model))

        self.resize(400, 400)
        self.show()

    def movecheckboxfun(self):
        global fgMoveAction
        fgMoveAction=self.movecheckbox.isChecked()
    def btnadditems_fun(self):
        pass
    def btnloadlst_fun(self):
        pass
    def btnloadfile_fun(self):
        pass

    def myquit_fun(self):
        app.instance().quit()
        sys.exit()#退出 quit
        #自定义右键按钮

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Window()
    sys.exit(app.exec_())
阅读 2k
1 个回答

其实一开始不必太在意代码的美丑,能跑起来,能满足需求是最重要的。

跑起来之后,可以开始思考如何让代码更优雅简洁,这一步通常也称为重构,重构完成意味着更好的可维护性,有时也意味着更好的性能。

开始重构之前,最好先写一些测试用例,避免重构完成之后出现意想不到的bug。有了测试用例,重构起来也会更有信心,不会束手束脚。

重构一般可以专注于这几个方面:

  • 特别长的代码块,可以拆分成几个小的函数,函数的命名最好可以描述出它的功能,这样也起到了文档的作用。
  • 最好不要使用全局变量。可以使用 class 新建一个对象,将变量放在这个对象里面。
  • 大量重复的代码可以考虑使用列表来保存其中不同的部分,并且动态地调用,比如用 apply 之类的方法。
本文参与了SegmentFault 思否面试闯关挑战赛,欢迎正在阅读的你也加入。
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进