Recent Posts

How to detect QTreeWidgetItem is double-clicked in PySide

GOAL

Today’s goal is to execute a function when QTreeWidgetItem is double clicked.

Environment

Windows 10
Python 3.8.7
PySide2 (5.15.2)

Method

First I created a sample QTreeWidget as below.

import sys
from PySide2.QtWidgets import *


class MyMainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MyMainWindow, self).__init__(parent)
        self._generate_ui()
        self._init_tree_widget()

    def _generate_ui(self):
        main_widget = QWidget()
        main_layout = QVBoxLayout()
        main_widget.setLayout(main_layout)
        self.setCentralWidget(main_widget)
        self.tree_widget = QTreeWidget()
        self.tree_widget.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.tree_widget.setSelectionBehavior(QAbstractItemView.SelectItems)
        main_layout.addWidget(self.tree_widget)

    def _init_tree_widget(self):
        headers = ["header1", "header2"]
        self.tree_widget.setHeaderLabels(headers)
        tree_widget_item1 = QTreeWidgetItem(["group1"])
        tree_widget_item1.addChild(QTreeWidgetItem(["item1_1", "item1_2"]))
        self.tree_widget.addTopLevelItem(tree_widget_item1)
        tree_widget_item2 = QTreeWidgetItem(["group2"])
        self.tree_widget.addTopLevelItem(tree_widget_item2)
        tree_widget_item2.addChild(QTreeWidgetItem(["item2_1", "item2_2"]))
        tree_widget_item2.addChild(QTreeWidgetItem(["item3_1", "item3_2"]))


def launch():
    app = QApplication.instance()
    if not app:
        app = QApplication(sys.argv)
    widget = MyMainWindow()
    widget.show()
    app.exec_()


launch()

1. How to detect that item is double clicked

Connect the function to QTreeWidget.itemDoubleClicked signal.

class MyMainWindow(QMainWindow):
    def __init__(self, parent=None):
        # omitted
    def _generate_ui(self):
        main_widget = QWidget()
        main_layout = QVBoxLayout()
        main_widget.setLayout(main_layout)
        self.setCentralWidget(main_widget)
        self.tree_widget = QTreeWidget()
        self.tree_widget.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.tree_widget.setSelectionBehavior(QAbstractItemView.SelectItems)
        self.tree_widget.itemDoubleClicked.connect(self._doubleClicked) # connect function to the signal itemDoubleClicked
        main_layout.addWidget(self.tree_widget)

    def _init_tree_widget(self):
        # omitted
        
    def _doubleClicked(self): # this function is executed when an item is double clicked
        print("double clicked")

2. How to get double clicked item

Use QTreeWidget.currentItem(), QTreeWidget.currentColumn() or QAbstractItemView.currentIndex().

    def _doubleClicked(self):
        column = self.tree_widget.currentColumn()
        text = self.tree_widget.currentItem().text(column)
        print("double clicked item is " + text)

# output when item1_2 clicked => double clicked item is item1_2
# output when group1 clicked => double clicked item is grounp1

How To Clear Layout in PySide

I’d like to clear and redraw widgets in my custom PySide2 UI.

GOAL

Today’s goal is to remove and delete all children widgets from the layout.

Environment

Windows 10
Python 3.8.7
PySide2 (5.15.2)

Method

I created sample widget with clear-all button.

import sys
from PySide2.QtWidgets import *
from PySide2.QtCore import Qt, QEventLoop

class MyWidget(QWidget):
    def __init__(self, parent=None):
        super(MyWidget, self).__init__(parent)
        self.main_layout = QVBoxLayout()
        self.setLayout(self.main_layout)
        self._generateUI()
    def _generateUI(self):
        clear_button = QPushButton("Clear All")
        clear_button.clicked.connect(self._clearall)
        self.main_layout.addWidget(clear_button)
        label = QLabel("Label")
        self.main_layout.addWidget(label)
        combobox = QComboBox()
        combobox.addItems(["comboBox1", "comboBox2"])
        self.main_layout.addWidget(combobox)
        buttons_widget = QWidget()
        buttons_layout = QHBoxLayout()
        buttons_widget.setLayout(buttons_layout)
        for i in range(3):
            button = QPushButton("button"+str(i))
            buttons_layout.addWidget(button)
        self.main_layout.addWidget(buttons_widget)
    
    def _clearall(self): # the function to clear widgets
        pass

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

1. Get all children

To get the list of children in the layout, use count() and itemAt(). Because the itemAt() function returns QLayoutItem not Qwidget, we should get widget by using QLayoutItem.widget() method.

    def _clearall(self):
        children = []
        for i in range(self.main_layout.count()):
            child = self.main_layout.itemAt(i).widget()
            if child:
                children.append(child)
                print(child)

# output
# <class 'PySide2.QtWidgets.QPushButton'>
# <class 'PySide2.QtWidgets.QLabel'>
# <class 'PySide2.QtWidgets.QComboBox'>
# <class 'PySide2.QtWidgets.QWidget'>

2. Remove each child item

Use deleteLater() to delete widget.

    def _clearall(self):
        children = []
        for i in range(self.main_layout.count()):
            child = self.main_layout.itemAt(i).widget()
            if child:
                children.append(child)
        for child in children:
            child.deleteLater()

* There are many ways to remove widget from the parent layout. For example, some people use setParent(None) or something. However don’t use removeWidget() because it just removes widget without destroying it. Please refer to the discussion below.

Other Examples

There are another methods to implement _clearall() function.

The method in which items deleted in for loop

It works well because the deleteLater() destroys items after returning control from the function.

    def _clearall(self):
        for i in range(self.main_layout.count()):
            child = self.main_layout.itemAt(i).widget()
            child.widget().deleteLater()

This is a wrong example. The itemAt() function can’t find the child because when an item is deleted with setParent(None), other items will be renumbered.

    def _clearall(self): #wrong example
        for i in range(self.main_layout.count()):
            child = self.main_layout.itemAt(i).widget()
            child.setParent(None)
# An error occurred: AttributeError: 'NoneType' object has no attribute 'widget'

The method by using children() or findChildren() function

You can get children of widget with children() function.

    def _clearall_4(self): #wrong
        for child in self.children():
            print(child.__class__)
            child.deleteLater()
# output
# <class 'PySide2.QtWidgets.QVBoxLayout'> # children() returns layout too
# <class 'PySide2.QtWidgets.QPushButton'>
# <class 'PySide2.QtWidgets.QLabel'>
# <class 'PySide2.QtWidgets.QComboBox'>
# <class 'PySide2.QtWidgets.QWidget'>

You can get children of widget with findChild() function.

    def _clearall_4(self): #wrong
        for child in self.findChildren(QWidget):
            print(child.__class__)
            child.deleteLater()
# output
# <class 'PySide2.QtWidgets.QPushButton'>
# <class 'PySide2.QtWidgets.QLabel'>
# <class 'PySide2.QtWidgets.QComboBox'>
# <class 'PySide2.QtWidgets.QWidget'>
# <class 'PySide2.QtWidgets.QPushButton'>
# <class 'PySide2.QtWidgets.QPushButton'>
# <class 'PySide2.QtWidgets.QPushButton'>

This is a wrong example. The children() of layout returns [].

    def _clearall_4(self): #wrong example
        for child in self.main_layout.children():
            child.deleteLator()
        print("first layout children", self.main_layout.children()) # output => []

How To Create On/Off Button In PySide

GOAL

Today’s goal is to create On/Off button in my PySide UI as below.

Environment

Windows 10
Python 3.8.7
PySide2 (5.15.2)

Method

1. Create a normal QPushButton

import sys
from PySide2.QtWidgets import *

class MyWidget(QWidget):
    def __init__(self):
        super(MyWidget, self).__init__()
        self.layout = QVBoxLayout()
        self.on_off_button = QPushButton("on/off Button")
        self.on_off_button.setCheckable(True)
        #self.layout.addWidget(self.on_off_button)
        self.setLayout(self.layout)

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

2. Use setCheckable() method

QAbstractButton has a method setCheckable().

class MyWidget(QWidget):
    def __init__(self):
        super(MyWidget, self).__init__()
        self.layout = QVBoxLayout()
        self.on_off_button = QPushButton("on/off Button")
        self.on_off_button.setCheckable(True) # set Checkable True
        self.layout.addWidget(self.on_off_button)
        self.setLayout(self.layout)

How to change the status, checked or unchecked

To change the status manually, use the setChecked() method of QAbstractButton.

For example, reset button can be implemented by using the function to set the button unchecked.

class MyWidget(QWidget):
    def __init__(self):
        super(MyWidget, self).__init__()
        self.layout = QGridLayout()
        self.reset_button = QPushButton("Reset")
        self.reset_button.clicked.connect(self._reset)
        self.layout.addWidget(self.reset_button,0,0,1,3)

        self.on_off_button1 = QPushButton("on/off 1")
        self.on_off_button1.setCheckable(True)
        self.layout.addWidget(self.on_off_button1,1,0,1,1)

        self.on_off_button2 = QPushButton("on/off 2")
        self.on_off_button2.setCheckable(True)
        self.layout.addWidget(self.on_off_button2,1,1,1,1)

        self.on_off_button3 = QPushButton("on/off 3")
        self.on_off_button3.setCheckable(True)
        self.layout.addWidget(self.on_off_button3,1,2,1,1)
        self.setLayout(self.layout)

    def _reset(self):
        self.on_off_button1.setChecked(False)
        self.on_off_button2.setChecked(False)
        self.on_off_button3.setChecked(False)
When the reset button is clicked, all on/off buttons are set off

Categories

AfterEffects Algorithm Artificial Intelligence Blender C++ Computer Graphics Computer Science Daily Life DataAnalytics Event Game ImageProcessing JavaScript Kotlin mathematics Maya PHP Python SoftwareEngineering Tips Today's paper Tools TroubleShooting Unity Visual Sudio Web Windows WordPress 未分類