Сохраните элемент Qtreewidget и восстановите его с выделением - PullRequest
0 голосов
/ 30 августа 2018

Мне нужна поддержка моего небольшого программного обеспечения с Qwidget и QTreeWidgets. см. рисунок ниже.

enter image description here

Вот так выглядит мой Qwidget. Я хочу восстановить элементы Qtreewidget и виджет при закрытии окна и восстановить его с предыдущим выбором. как отмечено на рисунке ниже. Как вы видите в моих сценариях ниже, я использовал Qsettings, я пытался исправить это с помощью pickle, но он не работает.

enter image description here

Любое улучшение в кодировании приветствуется.

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

iconroot = QFileInfo(__file__).absolutePath()
ORGANIZATION_NAME = 'Circularcolumn App'
ORGANIZATION_DOMAIN = 'Circular shape'
APPLICATION_NAME = 'QSettings program'
SETTINGS_TRAY = 'settings/tray'

class usedcircularshape(QDialog):
    def __init__(self, parent=None):
    super().__init__(parent)

    self.setWindowTitle("Frequently used shape")
    self.setWindowIcon(QIcon(iconroot+"/images/circularcolumnnorebar.png"))
    #self.setStyleSheet("background-color:#f2f2f2")

    self.addbutton = QPushButton("Add")
    self.addbutton.clicked.connect(self.add)
    self.deletebutton = QPushButton("Delete")
    self.deletebutton.clicked.connect(self.delete)

    self.okbutton = QPushButton("Ok")
    self.okbutton.setCursor(Qt.PointingHandCursor)
    #self.okbutton.clicked.connect(self.hidethiswindow)
    self.okbutton.clicked.connect(self.savesetting)

    self.cancelbutton = QPushButton("Cancel")
    self.cancelbutton.setCursor(Qt.PointingHandCursor)
    self.cancelbutton.clicked.connect(self.loadsetting)
    #self.cancelbutton.clicked.connect(self.close)

    self.addimage()
    self.qlabeltodefinesection()
    self.treewidget()

    self.sectionnamecircular = QLabel('Section name: ')
    self.sectionnamecircularindata = QLineEdit('Define en name to section')
    self.sectionnamecircularindata.setObjectName("sectionnamecircularindata")
    self.sectionnamecircular.setBuddy(self.sectionnamecircularindata)
    self.sectionnamecircular.setFocus()
    self.grid_sectionname = QHBoxLayout()
    self.grid_sectionname.addWidget(self.sectionnamecircular)
    self.grid_sectionname.addWidget(self.sectionnamecircularindata)

    self.boxlayout = QGridLayout()
    self.boxlayout.addLayout(self.grid_sectionname,0,0,1,2)
    self.boxlayout.addWidget(self.treewidget,1,0,5,2)
    self.boxlayout.addWidget(self.addbutton,2,2)
    self.boxlayout.addWidget(self.deletebutton,3,2)
    self.boxlayout.addWidget(self.imagelabel,6,0)
    self.boxlayout.addLayout(self.qlabelhboxgrid ,6,1)
    self.boxlayout.addWidget(self.okbutton,8,1)
    self.boxlayout.addWidget(self.cancelbutton,8,2)
    self.setLayout(self.boxlayout)
    try:
        self.loadsetting()
    except ( ValueError, TypeError):
        pass

def treewidget(self):
    self.treewidget = QTreeWidget(self)
    self.treewidget.setColumnCount(1)
    self.treewidget.setColumnWidth(1,20)
    self.treewidget.setHeaderItem(QTreeWidgetItem(['Standard Section Library']))
    #self.treewidget.addTopLevelItem(QTreeWidgetItem(['Standard Sectiontype']))
    self.treewidget.setRootIsDecorated(True)

    self.firstparentitem = QTreeWidgetItem(self.treewidget)
    self.firstparentitem.setText(0,'Circular shapes')
    self.firstparentitem.setIcon(0,QIcon(iconroot+"/images/circularcolumnnorebar.png"))

    standardsectionlist = ["D100","D150","D200","D250","D300","D350","D400","D450","D500","D550","D600","D650"
                           ,"D700","D750","D800","D850","D900","D950","D1000"]

    for i in standardsectionlist:
        self.firstparentitem.addChild(QTreeWidgetItem(["%s"%i]))

    self.secondparentitem = QTreeWidgetItem(self.treewidget)
    self.secondparentitem.setText(0,'Customized')
    self.secondparentitem.setIcon(0,QIcon(iconroot+"/images/circularcolumnnorebar.png"))    
    self.secondchilditem = QTreeWidgetItem(["D235"])    
    self.secondparentitem.insertChild(0,self.secondchilditem)
    self.secondchilditem.setChildIndicatorPolicy(QTreeWidgetItem.DontShowIndicator)
    self.treewidget.move(15,15)
    self.treewidget.setGeometry(15,15,200,600)
    self.treewidget.setAlternatingRowColors(True)
    self.treewidget.expandItem ( self.firstparentitem )
    self.show() 

    print(self.treewidget.headerItem().text(0))
    print(self.treewidget.columnCount())
    print(self.treewidget.currentColumn())

    print(self.treewidget.indexFromItem(self.firstparentitem).row())
    print(self.firstparentitem.childCount())
    print(self.firstparentitem.child(1).text(0))
    print(self.firstparentitem.text(0))
    print(self.treewidget.headerItem().text(0))
    print(self.treewidget.topLevelItem(0).text(0))
    print(self.firstparentitem.isSelected())
    print(self.treewidget.selectedItems())
    print(self.secondchilditem.text(1))

    branchstyle = '''QTreeWidget {border:none;} 

    QTreeView::branch:has-siblings:!adjoins-item {
        border-image: url(images/vline.png) 0;}

    QTreeView::branch:has-siblings:adjoins-item {
        border-image: url(images/branch-more.png) 0;}

    QTreeView::branch:!has-children:!has-siblings:adjoins-item {
        border-image: url(images/branch-end.png) 0;}

    QTreeView::branch:has-children:!has-siblings:closed,
    QTreeView::branch:closed:has-children:has-siblings {
        border-image: none;
        image: url(images/branch-closed.png);}

    QTreeView::branch:open:has-children:!has-siblings,
    QTreeView::branch:open:has-children:has-siblings {
        border-image: none;
        image: url(images/branch-open.png);}'''

    self.treewidget.setStyleSheet(branchstyle)
    self.treewidget.itemClicked.connect(self.currentitem)
    self.treewidget.currentItemChanged.connect(self.current_item_changed)

   #@QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem, QtWidgets.QTreeWidgetItem)
def current_item_changed(self, current, previous):
   #print('\ncurrent: {}, \nprevious: {}'.format(current, previous))
    print(current.text(0),previous)


def add(self):
    text, ok = QInputDialog.getText(self, "Add custom section", "Enter section geometry f.ex as D325 or just 325 in mm: ")

    if ok:
        self.secondchilditem = QTreeWidgetItem(["%s"% text])
        self.secondparentitem.insertChild(0,self.secondchilditem)
        self.treewidget.expandItem ( self.secondparentitem )
        self.gettext = text

    print(self.secondparentitem.child(0), self.gettext)


def delete(self):
    self.secondparentitem.takeChild(0)

def currentitem(self):
    print(self.treewidget.currentItem().text(0),self.treewidget.selectedItems())
    self.itemtext = self.treewidget.currentItem().text(0)

    if self.itemtext == self.firstparentitem.text(0) or self.itemtext == self.secondparentitem.text(0):
        return None
    elif self.itemtext == self.treewidget.topLevelItem(0).text(0):
        return None
    elif self.itemtext == None:
        return None
    else:
        self.select_circular_section = int(self.itemtext.translate({ord('D'):None}))   
        print(self.itemtext, self.treewidget.selectedItems, self.select_circular_section)

        area = str(format(3.1416/4*(self.select_circular_section)**2,'0.2E'))
        inerti = str(format(3.1416/64*pow(self.select_circular_section,4),'0.2E'))

        self.qlabelcirculardiameterselected = QLabel('')
        qlabelcircularareaselected = QLabel('')
        qlabelcircularinertimomentselected = QLabel("")
        emptylabel1 = QLabel('     ')

        self.qlabelcirculardiameterselected.setText('%s    mm '% self.select_circular_section)
        qlabelcircularareaselected.setText('{}    mm2 ' .format(area))
        qlabelcircularinertimomentselected.setText("%s    mm4 " %(inerti))

        qlabelhboxgridselected = QGridLayout()
        qlabelhboxgridselected.addWidget(emptylabel1,0,0)
        qlabelhboxgridselected.addWidget(self.qlabelcirculardiameterselected,1,0)
        qlabelhboxgridselected.addWidget(qlabelcircularareaselected,2,0)
        qlabelhboxgridselected.addWidget(qlabelcircularinertimomentselected,3,0)
        qlabelhboxgridselected.addWidget(emptylabel1,4,0,5,0)       

        return print(self.itemtext, self.treewidget.selectedItems, self.select_circular_section), self.boxlayout.addLayout(qlabelhboxgridselected ,6,2),self.qlabelcirculardiameterselected     

def addimage(self):
    self.imagelabel = QLabel()
    self.circularimage = QPixmap(iconroot+"/images/circularcolumnnorebard.png").scaled(230,230,Qt.KeepAspectRatio)
    self.imagelabel.setPixmap(self.circularimage) 
    self.imagelabel.setGeometry(15,15,15,15)

def hidethiswindow(self):   
    if self.itemtext == self.firstparentitem.text(0) or self.itemtext == self.secondparentitem.text(0):
        QMessageBox.about(self,'Error selection','Please, select a section not a text')
    elif self.itemtext == self.treewidget.topLevelItem(0).text(0):
        QMessageBox.about(self,'Error selection','Please, select a section not a text')
    elif self.itemtext == None:
        QMessageBox.about(self,'Error selection','Please, select a section not a text')
    else:
        self.savesetting()
        self.hide() 

def qlabeltodefinesection(self):    
    self.qlabelcirculardiameter = QLabel('    D = ')
    self.qlabelcirculararea = QLabel('    A = ')
    self.qlabelcircularinertimoment = QLabel("    I = ")
    self.emptylabel = QLabel('     ')   
    self.qlabelhboxgrid = QGridLayout()
    self.qlabelhboxgrid.addWidget(self.emptylabel,0,0)
    self.qlabelhboxgrid.addWidget(self.qlabelcirculardiameter,1,0)
    self.qlabelhboxgrid.addWidget(self.qlabelcirculararea,2,0)
    self.qlabelhboxgrid.addWidget(self.qlabelcircularinertimoment,3,0)
    self.qlabelhboxgrid.addWidget(self.emptylabel,4,0,5,0)


def savesetting(self):
    settings = QSettings(ORGANIZATION_NAME,APPLICATION_NAME)
    #settings = QSettings('config.ini',QSettings.IniFormat)
    settings.beginGroup('D')
    settings.setValue(SETTINGS_TRAY,self.geometry())
    settings.setValue("LineEdit",self.sectionnamecircularindata.text())
    settings.setValue("Selectitem",self.treewidget.currentItem())
    settings.setValue("Label",self.qlabelcirculardiameterselected)
    settings.endGroup()
    print('Saved', )
    #self.hide()   

def loadsetting(self):
    settings = QSettings(ORGANIZATION_NAME,APPLICATION_NAME)
    #settings = QSettings('config.ini',QSettings.IniFormat)
    settings.beginGroup('D')
    myrect = settings.value(SETTINGS_TRAY)
    restorelineEdit = settings.value("LineEdit",'')
    restoreselectsection = settings.value("Selectitem",)
    restoreqlabel =  settings.value("Label",'')
    self.setGeometry(myrect)
    self.sectionnamecircularindata.setText(restorelineEdit)
    self.treewidget.setCurrentItem(restoreselectsection)
    settings.endGroup()        

if __name__ == "__main__":

QCoreApplication.setApplicationName(ORGANIZATION_NAME)
QCoreApplication.setOrganizationDomain(ORGANIZATION_DOMAIN)
QCoreApplication.setApplicationName(APPLICATION_NAME)

app = QApplication(sys.argv)
subwindow=usedcircularshape()
subwindow.show()
app.exec()

Что делает код?

Это программное обеспечение для конкретных колонок, идея в том, что пользователь должен иметь возможность выбрать стандартный раздел, например D350, D означает диаметр, а 350 - диаметр круглой бетонной колонны в мм. Пользователь имеет возможность добавлять собственные формы круглых столбцов. И когда пользователь щелкает и выбирает в Qtreewidget раздел, этот раздел должен оставаться глобальным и доступным для дальнейшего расчета, который ей не показан. Это виджет для определения геометрии в большом конкретном программном обеспечении. Я бы ниже пояснил код. Сначала я создаю Qdialog и создаю Qtreewidget, изображение для уточнения и Qlabel на основе выбора элемента. В Qtreewidget я сначала создаю родительский элемент и называю его «Круглые формы»

self.firstparentitem = QTreeWidgetItem(self.treewidget)
self.firstparentitem.setText(0,'Circular shapes')

Затем у родительского элемента есть дочерние элементы, для их определения и добавления сначала создается список со стандартными разделами

    standardsectionlist = ["D100","D150","D200","D250","D300","D350","D400","D450","D500","D550","D600","D650"
                       ,"D700","D750","D800","D850","D900","D950","D1000"]

И выполнить условие для добавления детей в родительский элемент.

    for i in standardsectionlist:
    self.firstparentitem.addChild(QTreeWidgetItem(["%s"%i]))

позже я определяю второй родительский элемент, даю имя «Customized» и определяю список, затем добавляю элемент списка как дочерний элемент ко второму родительскому элементу. Чтобы пользователь мог добавить и удалить дочерний элемент из второго родительского элемента, я создаю 2 кнопки

self.addbutton = QPushButton("Add")
self.deletebutton = QPushButton("Delete")

В Addbutton есть функция для добавления дочернего элемента во второй родительский элемент. Кнопка Delete имеет функцию для удаления первого дочернего элемента из второго родительского элемента.

def currentitem(self):

функция currentitem имеет функцию, когда пользователь щелкает и выбирает элемент в Qtreewidget, он берет текст currentitem и удаляет из него букву D и преобразует его в int, затем отображает его как D = Diameter, A = Площадь ЭСТ…

def hidethiswindow(self)

Функция скрытия этого окна позволяет пользователю выбрать элемент или отменить виджет. В случае, если пользователь по ошибке выбрал элемент header, появится сообщение об ошибке и будет сказано выбрать дочерний элемент. Это не мощно!

Остальной код должен отображать и Qsettings. Надеюсь, что это объясняет код.

1 Ответ

0 голосов
/ 31 августа 2018

Что вам нужно сделать, это сохранить данные каждого элемента, для этого я создал метод itemToTuple, который возвращает кортеж сохраняемых данных, также учитывая, что сохраненный кортеж вы должны установить эти свойства и для этого вы используете Функция tupleToItem (если вы хотите добавить больше информации об элементах, вам нужно только изменить эти методы). Но чтобы сохранить все элементы QTreeWidget, нужно пройти через все дерево. Для этого используется метод dataFromChild, который возвращает данные элемента и его дочерних элементов. Обратный процесс, то есть, если данные должны быть установлены во всех элементах, должен использоваться dataToChild, который устанавливает соответствующие данные для элемента и его дочерних элементов.

Также я разделил приложение на несколько классов, чтобы оно было отсортировано, если вы хотите сохранить информацию виджета, вы должны создать метод writeSettings (...) с форматом, аналогичным формату, который я показываю тогда. приложение вызовет метод closeEvent (...). Точно так же вы можете создать метод readSettings (...) и вызвать его в своем конструкторе.

import os
import sys
from PyQt5 import QtCore, QtGui, QtWidgets

iconroot = os.path.dirname(__file__)
ORGANIZATION_NAME = 'Circularcolumn App'
ORGANIZATION_DOMAIN = 'Circular shape'
APPLICATION_NAME = 'QSettings program'
SETTINGS_TRAY = 'settings/tray'

QSS = """
QTreeWidget{
    border:none;
 } 

QTreeView::branch:has-siblings:!adjoins-item {
   border-image: url(images/vline.png) 0;
}

QTreeView::branch:has-siblings:adjoins-item {
    border-image: url(images/branch-more.png) 0;
}

QTreeView::branch:!has-children:!has-siblings:adjoins-item {
    border-image: url(images/branch-end.png) 0;
}

QTreeView::branch:has-children:!has-siblings:closed,
QTreeView::branch:closed:has-children:has-siblings {
    border-image: none;
    image: url(images/branch-closed.png);
}

QTreeView::branch:open:has-children:!has-siblings,
QTreeView::branch:open:has-children:has-siblings {
    border-image: none;
    image: url(images/branch-open.png);
}
"""


class TreeWidget(QtWidgets.QTreeWidget):
    currentTextChanged = QtCore.pyqtSignal(str)

    def __init__(self, parent=None):
        super(TreeWidget, self).__init__(parent)
        self.currentItemChanged.connect(self.onCurrentItemChanged)
        self.setHeaderLabel('Standard Section Library')
        self.setRootIsDecorated(True)
        self.setAlternatingRowColors(True)
        self.readSettings()
        self.expandAll()

    def onCurrentItemChanged(self, current, previous):
        if current not in [self.topLevelItem(ix) for ix in range(self.topLevelItemCount())]:
            self.currentTextChanged.emit(current.text(0))

    def readSettings(self):
        settings = QtCore.QSettings()
        settings.beginGroup("TreeWidget")
        values = settings.value("items")
        if values is None:
            self.loadDefault()
        else:
            TreeWidget.dataToChild(values, self.invisibleRootItem())
            self.customized_item = None
            for ix in range(self.topLevelItemCount()):
                tlevel_item = self.topLevelItem(ix)
                if tlevel_item.text(0) == "Customized":
                    self.customized_item = tlevel_item
        settings.endGroup()

    def writeSettings(self):
        settings = QtCore.QSettings()
        settings.beginGroup("TreeWidget")
        settings.setValue("items", TreeWidget.dataFromChild(self.invisibleRootItem()))
        settings.endGroup()

    def loadDefault(self):
        standardsectionlist = ["D100","D150","D200","D250","D300","D350","D400","D450","D500",
        "D550","D600","D650","D700","D750","D800","D850","D900","D950","D1000"]
        rootItem = QtWidgets.QTreeWidgetItem(self, ['Circular shapes'])
        rootItem.setIcon(0, QtGui.QIcon(os.path.join(iconroot,"images/circularcolumnnorebar.png")))
        for element in standardsectionlist:
            rootItem.addChild(QtWidgets.QTreeWidgetItem([element]))

        self.customized_item = QtWidgets.QTreeWidgetItem(self, ["Customized"])
        self.customized_item.setIcon(0, QtGui.QIcon(os.path.join(iconroot,"images/circularcolumnnorebar.png")))

    @staticmethod
    def dataToChild(info, item):
        TreeWidget.tupleToItem(info["data"], item)
        for val in info["childrens"]:
            child = QtWidgets.QTreeWidgetItem()
            item.addChild(child)
            TreeWidget.dataToChild(val, child)

    @staticmethod
    def tupleToItem(t, item):
        # set values to item
        ba, isSelected = t
        ds = QtCore.QDataStream(ba)
        ds >> item
        item.setSelected(isSelected) 

    @staticmethod
    def dataFromChild(item):
        l = []
        for i in range(item.childCount()):
            child = item.child(i)
            l.append(TreeWidget.dataFromChild(child))
        return {"childrens": l, "data": TreeWidget.itemToTuple(item)}

    @staticmethod
    def itemToTuple(item):
        # return values from item
        ba = QtCore.QByteArray()
        ds = QtCore.QDataStream(ba, QtCore.QIODevice.WriteOnly)
        ds << item
        return ba, item.isSelected()


class InfoWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(InfoWidget, self).__init__(parent)
        hlay = QtWidgets.QHBoxLayout(self)
        plabel = QtWidgets.QLabel()
        pixmap = QtGui.QPixmap(os.path.join(iconroot, "images/circularcolumnnorebard.png"))\
                    .scaled(230, 230, QtCore.Qt.KeepAspectRatio)
        plabel.setPixmap(pixmap)
        hlay.addWidget(plabel)
        self.ilabel = QtWidgets.QLabel()
        hlay.addWidget(self.ilabel)
        hlay.addStretch()
        self.readSettings()

    @QtCore.pyqtSlot(str)
    def setData(self, text):
        try:
            circular_section = int(text.translate({ord('D'): ""}))
            area = (3.1416/4)*(circular_section**2)
            inertia = (3.1416/64)*circular_section**4
            fmt = "D = {}mm\nA = {:0.2E}mm2\n I  = {:0.2E}mm4"
            self.ilabel.setText(fmt.format(circular_section, area, inertia))
        except ValueError:
            pass

    def readSettings(self):
        settings = QtCore.QSettings()
        settings.beginGroup("InfoWidget")
        self.ilabel.setText(settings.value("text", ""))
        settings.endGroup()

    def writeSettings(self):
        settings = QtCore.QSettings()
        settings.beginGroup("InfoWidget")
        settings.setValue("text", self.ilabel.text())
        settings.endGroup()


class CircularDialog(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super(CircularDialog, self).__init__(parent)
        grid = QtWidgets.QGridLayout(self)

        self.tree = TreeWidget()
        self.infoWidget = InfoWidget()

        section_lay = QtWidgets.QHBoxLayout()
        section_label = QtWidgets.QLabel("Section name: ")
        section_edit = QtWidgets.QLineEdit('Define en name to section')
        section_lay.addWidget(section_label)
        section_lay.addWidget(section_edit)

        self.tree.currentTextChanged.connect(self.infoWidget.setData)

        button_layout = QtWidgets.QVBoxLayout()
        add_button = QtWidgets.QPushButton("Add")
        add_button.clicked.connect(self.addItem)
        delete_button = QtWidgets.QPushButton("Delete")
        delete_button.clicked.connect(self.removeItem)
        button_layout.addWidget(add_button, alignment=QtCore.Qt.AlignBottom)
        button_layout.addWidget(delete_button, alignment=QtCore.Qt.AlignTop)

        buttonBox = QtWidgets.QDialogButtonBox()
        buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
        buttonBox.accepted.connect(self.accept) 
        buttonBox.rejected.connect(self.reject)

        # for windows, posible bug
        self.accepted.connect(self.write_all_data)
        self.rejected.connect(self.write_all_data)

        grid.addLayout(section_lay, 0, 0)
        grid.addWidget(self.tree, 1, 0)
        grid.addLayout(button_layout, 1, 1)
        grid.addWidget(self.infoWidget, 2, 0, 1, 2)
        grid.addWidget(buttonBox, 3, 0, 1, 2)
        self.readSettings()

    def readSettings(self):
        settings = QtCore.QSettings()
        settings.beginGroup("CircularDialog")
        self.setGeometry(settings.value("geometry", QtCore.QRect(300, 300, 400, 600)))
        settings.endGroup()

    def writeSettings(self):
        settings = QtCore.QSettings()
        settings.beginGroup("CircularDialog")
        settings.setValue("geometry", self.geometry())
        settings.endGroup()

    def closeEvent(self, event):
        self.write_all_data()
        super(CircularDialog, self).closeEvent(event)

    def write_all_data(self):
        for children in self.findChildren(QtWidgets.QWidget) + [self]:
            if hasattr(children, "writeSettings"):
                children.writeSettings()

    def addItem(self):
        text, ok = QtWidgets.QInputDialog.getText(self, "Add custom section", 
            "Enter section geometry f.ex as D325 or just 325 in mm: ")
        if ok:
            it = QtWidgets.QTreeWidgetItem([text])
            self.tree.customized_item.addChild(it)

    def removeItem(self):
        it = self.tree.customized_item.takeChild(0)
        del it


if __name__ == '__main__':
    QtCore.QCoreApplication.setApplicationName(ORGANIZATION_NAME)
    QtCore.QCoreApplication.setOrganizationDomain(ORGANIZATION_DOMAIN)
    QtCore.QCoreApplication.setApplicationName(APPLICATION_NAME)

    app = QtWidgets.QApplication(sys.argv)
    app.setStyleSheet(QSS)
    w = CircularDialog()
    w.show()
    sys.exit(app.exec_())
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...