[PYTHON] Eliminate the inconveniences of QDock Widget with PySide

QDockWidget is a very nice widget that allows you to leave layout adjustments to the user. However, it is also true that there are many places where I wonder, "Can I do this?" (Qt seems to be out of reach of itchy places in the standard state as a whole)

Let's raise the inconvenience with QDockWidget.

--Icon does not appear in the title bar --Icon is not displayed on the tab when tabbed --Toolbar cannot be placed in DockWidget --Cannot be tabbed in Float state (this has been solved with Qt5.5)

Tabbing in the last Float state is supported by Qt5.5, so it is not supported here. Wait for the completion of PySide 2 for Qt5.

Let's solve it in order from the top.

Display an icon in the title bar (Abandoned)

** Suddenly abandoned. ** **

As the number of DockWidgets increases, the visibility of which Dock has which function decreases. It would be convenient if the icon set by QDockWidget :: setWindowIcon is displayed in the title bar.

But that seems difficult.

Draw your own Drop Indicator with PySide (realize QProxyStyle with black magic)

Hook the drawControl in the title bar with the ProxyStyle used in the article above.

def drawControl(self, element, option, painter, widget):
    if element == QStyle.CE_DockWidgetTitle:
        width = self.pixelMetric(QStyle.PM_ToolBarIconSize)
        margin = self.pixelMetric(QStyle.PM_DockWidgetTitleMargin)

        painter.drawPixmap(margin, margin, widget.windowIcon().pixmap(QSize(width / 2, width / 2)))
        option.rect.adjust(width, 0, 0, 0)
    return self._style.drawControl(element, option, painter, widget)

The icon is drawn by itself, the display range is shifted by the icon, and it is passed to the original drawing. However, the result is as follows: The background of the icon part is not filled.

2016-12-30_01h49_092.png

In order to avoid this situation, I read the Qt source, but it is hard-coded. If you do, you have to draw all the title bars yourself. In any case, the title bar in the Float state is a separate implementation after a hard implementation, so that also needs to be reimplemented.

I gave up because there were too few rewards for my hard work.

Iconon is not displayed on the tab when tabbed

To display the icon on the title bar, display the icon inside the dock. However, it is not good that the icon is not displayed on the tabbed tab. Since the contents of the dog cannot be seen, it can be recognized only by the characters on the tab, and there is a big problem in visibility.

When QDockWidget is tabbed, the tab is stored in QTabBar in QMainWindow. But that tab and the QDock Widget are completely separate things.

So let's solve it in a slightly rough way, take a look at the code below.

#! usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function, absolute_import
import sys
from PySide.QtCore import *
from PySide.QtGui import *


def main():
    app = QApplication(sys.argv)
    main_window = QMainWindow()

    warning_icon = QApplication.style().standardIcon(QStyle.SP_MessageBoxWarning)
    critical_icon = QApplication.style().standardIcon(QStyle.SP_MessageBoxCritical)

    dock_1 = QDockWidget("warning", main_window)
    dock_1.setWindowIcon(warning_icon)

    dock_2 = QDockWidget("critical", main_window)
    dock_2.setWindowIcon(critical_icon)

    main_window.addDockWidget(Qt.LeftDockWidgetArea, dock_1)
    main_window.addDockWidget(Qt.RightDockWidgetArea, dock_2)

    def onLocationChanged(area):
        icon_dict = {}
        for _dock in main_window.findChildren(QDockWidget):
            icon_dict[_dock.windowTitle()] = _dock.windowIcon()

        for tab_bar in main_window.findChildren(QTabBar):
            for idx in range(tab_bar.count()):
                if tab_bar.tabText(idx) in icon_dict:
                    tab_bar.setTabIcon(idx, icon_dict[tab_bar.tabText(idx)])

    for dock in main_window.findChildren(QDockWidget):
        dock.dockLocationChanged.connect(onLocationChanged)
    onLocationChanged(0)

    main_window.show()
    app.exec_()


if __name__ == "__main__":
    main()

The execution result is as follows, the icon is displayed on the tabbed DockWidget.

2016-12-30_02h31_53.png

What I'm doing is checking the tabs of QTabBar under the main window every time the layout of QDockWidget is changed, and setting the icon to the tab with the same title name.

It's a brute force method, but as far as I read the Qt source, there seems to be no other solution. The layout pointer is stored in QTabBar :: tabData, but the user program cannot determine the QDockWidget from the pointer. On the user program side, there is only an element that connects only the tab name and the title name of QDockWidget.

QDockWidget cannot be placed in the toolbar

You cannot place the toolbar of the main window inside the QDockWidget, but you can have a separate toolbar for each QDockWidget.

#! usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function, absolute_import
import sys
from PySide.QtCore import *
from PySide.QtGui import *


def main():
    app = QApplication(sys.argv)
    main_window = QMainWindow()

    home_icon = QApplication.style().standardIcon(QStyle.SP_DirHomeIcon)
    drive_icon = QApplication.style().standardIcon(QStyle.SP_DriveDVDIcon)
    home_action = QAction(home_icon, "Home", main_window)
    drive_action = QAction(drive_icon, "Drive", main_window)

    dock_1 = QDockWidget("warning", main_window)
    dock_2 = QDockWidget("critical", main_window)
    main_window.addDockWidget(Qt.LeftDockWidgetArea, dock_1)
    main_window.addDockWidget(Qt.RightDockWidgetArea, dock_2)

    def create_dock_toolbar(dock):
        __main_window = QMainWindow()
        __main_window.setParent(dock)
        dock.setWidget(__main_window)
        return __main_window

    _main_window = create_dock_toolbar(dock_1)
    bar = QToolBar(_main_window)
    bar.addActions([home_action, drive_action])
    _main_window.addToolBar(bar)

    _main_window = create_dock_toolbar(dock_2)
    bar = QToolBar(_main_window)
    bar.addActions([home_action, drive_action])
    _main_window.addToolBar(bar)

    main_window.show()
    app.exec_()


if __name__ == "__main__":
    main()
2016-12-30_02h48_55.png

You can see that each of the two QDock Widgets has a toolbar.

Only QMainWindow can be placed on the toolbar. So, each QDockWidget has a QMainWindow as a child, and the toolbar is placed there.

Summary

Qt is a great GUI tool, but the state of the element is inconvenient.

Since the drawing is done completely by Qt, anything can be freely implemented. However, there are too many situations that the user has to implement in full with a little expansion. (This is a lot of hard coding with no extensibility rather than Qt's design being too lax)

If you think that there is a smarter solution just because you lack knowledge, please teach us.

Recommended Posts

Eliminate the inconveniences of QDock Widget with PySide
Align the size of the colorbar with matplotlib
Check the existence of the file with python
The third night of the loop with for
The second night of the loop with for
Count the number of characters with echo
The story of doing deep learning with TPU
Note: Prepare the environment of CmdStanPy with docker
Prepare the execution environment of Python3 with Docker
2016 The University of Tokyo Mathematics Solved with Python
[Note] Export the html of the site with python.
See the behavior of drunkenness with reinforcement learning
Increase the font size of the graph with matplotlib
Check the date of the flag duty with Python
Challenge the Tower of Hanoi with recursion + stack
Rewrite the name of the namespaced tag with lxml
Fill the browser with the width of Jupyter Notebook
Predict the second round of summer 2016 with scikit-learn
Dump the contents of redis db with lua
Tucker decomposition of the hay process with HOOI
Create a color-specified widget with Python + Qt (PySide)
Find out the day of the week with datetime
The basis of graph theory with matplotlib animation
GUI creation with Pyside Part 2 <Use of class>
Visualize the behavior of the sorting algorithm with matplotlib
Convert the character code of the file with Python3
[Python] Determine the type of iris with SVM
Extract the table of image files with OneDrive & Python
Coordinates of the right end of Label made with tkinter
The story of stopping the production service with the hostname command
Learn Nim with Python (from the beginning of the year).
Find the sum of unique values with pandas crosstab
Play with the UI implementation of Pythonista3 [Super Super Introduction]
Add information to the bottom of the figure with Matplotlib
Get the sum of each of multiple columns with awk
Take a screenshot of the LCD with Python-LEGO Mindstorms
Visualize the range of interpolation and extrapolation with python
Take the value of SwitchBot thermo-hygrometer with Raspberry Pi
Predict the number of people infected with COVID-19 with Prophet
Log the value of SwitchBot thermo-hygrometer with Raspberry Pi
Find out the location of packages installed with pip
Predict the gender of Twitter users with machine learning
Try to get the contents of Word with Golang
Visualize the characteristic vocabulary of a document with D3.js
I measured the performance of 1 million documents with mongoDB
Preparing the execution environment of PyTorch with Docker November 2019
Read the power of the smart meter with M5StickC (BP35C0-J11-T01)
Manage the package version number of requirements.txt with pip-tools
Summary of the basic flow of machine learning with Python
Record of the first machine learning challenge with Keras
Get the operation status of JR West with Python
Visualize the appreciation status of art works with OpenCV
[For beginners] Quantify the similarity of sentences with TF-IDF
Extract the band information of raster data with python
Script that changes the length of the sound with REAPER
Adjust the ratio of multiple figures with the matplotlib gridspec
Calculate the product of matrices with a character expression?