I figured it out finally, some things around OOP are still very confusing for me, I guess I need time and more experience to settle all down. Below is a functional example of simple window with two QToolButtons - a regular one and modified one.
@JonB: I think that there is no difference between using PyQt and PySide, except for import statements, and loading ui, I think that PyQt cannot load ui file directly, it must be converted to py, maybe I'm wrong.
Now there are four things that bother me:
how to use regular QtToolButton as a classic button with drop-down menu (see my example), it seems that triggered signal is always called. Is it designed to work that way? If I don't add signal in code than button does nothing?
are there any major flaws in my example that should be corrected/fixed?
how to "inject" my custom made object into ui at wanted location? In my example I just inserted it inside grid_layout. Can I make a placeholder somehow? Let's say I have a more complicated window with many widgets, for example:
window.jpg
and I want to replace that Add Something button with custom one. What would be the best procedure to do this?
4) is calling GUI directly from a ui XML file smart thing to do or it would be better to convert it to py first?
My example, program.py, and form.ui:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Docs
"""
import os, sys
from PySide2 import QtWidgets, QtUiTools
class CustomToolButton(QtWidgets.QToolButton):
''' CustomToolButton description '''
def __init__(self, parent=None):
''' Constructor '''
super(CustomToolButton, self).__init__(parent)
self.setPopupMode(self.MenuButtonPopup)
self.triggered.connect(self.setDefaultAction)
return None
class Program(QtWidgets.QWidget):
''' Program description '''
def __init__(self, parent=None):
''' Constructor '''
super(Program, self).__init__(parent)
self.program_path = os.path.dirname(os.path.realpath(__file__))
self.loader = QtUiTools.QUiLoader()
ui_file_path = os.path.join(self.program_path, 'form.ui')
self.window = self.loader.load(ui_file_path, parent)
self.setup_ui(self.window)
return None
def setup_ui(self, window):
self.grid_layout = window.gridLayout
# tool button created in Qt Designer
self.tool_btn = window.toolButton
### THIS RUNS EVERY TIME BUTTON IS CLICKED NO MATTER
### WHAT ACTION YOU CHOOSE FROM DROP-DOWN MENU
### IT SEEMS THAT IN THIS CASE YOU CANNOT USE BUTTON LIKE
### CLASSIC BUTTON,
### THEREFORE IT SEEMS THAT "setDefaultAction" MAKES NO SENSE
self.tool_btn.triggered.connect(self.clicked_tool_btn)
# custom tool button
self.custom_tool_btn = CustomToolButton()
self.custom_tool_btn.setText('Custom button')
self.custom_tool_btn.setMinimumSize(150,50)
self.custom_tool_btn.setMaximumSize(150,50)
self.grid_layout.addWidget(self.custom_tool_btn)
self.define_ui_elements()
def define_ui_elements(self):
# tool button created in Qt Designer
tool_btn_menu = QtWidgets.QMenu(self)
tool_btn_menu.addAction('Action 1', self.action1_activated)
tool_btn_menu.addAction('Action 2', self.action2_activated)
tool_btn_menu.addAction('Action 3', self.action3_activated)
tool_btn_default_action = QtWidgets.QAction('Action 1',self)
self.tool_btn.setMenu(tool_btn_menu)
self.tool_btn.setDefaultAction(tool_btn_default_action)
# custom tool button
''' 1) creating actions '''
action1 = QtWidgets.QAction('Action 1', self)
action2 = QtWidgets.QAction('Action 2', self)
action3 = QtWidgets.QAction('Action 3', self)
''' 2) creating connections '''
action1.triggered.connect(self.action1_activated)
action2.triggered.connect(self.action2_activated)
action3.triggered.connect(self.action3_activated)
''' 3) creating btn menu '''
custom_tool_btn_menu = QtWidgets.QMenu(self)
custom_tool_btn_menu.addAction(action1)
custom_tool_btn_menu.addAction(action2)
custom_tool_btn_menu.addAction(action3)
''' 4) setting up menu and default action '''
self.custom_tool_btn.setMenu(custom_tool_btn_menu)
self.custom_tool_btn.setDefaultAction(action1)
### THIS RUNS EVERY TIME BUTTON IS CLICKED NO MATTER WHAT ACTION YOU CHOOSE FROM DROP-DOWN MENU
def clicked_tool_btn(self):
# calling action1_activated() because it is default option
self.action1_activated()
print('Qt Designer button is clicked.')
def action1_activated(self):
print('Action 1 activated.')
def action2_activated(self):
print('Action 2 activated.')
def action3_activated(self):
print('Action 3 activated.')
if __name__ == '__main__':
program = QtWidgets.QApplication(sys.argv)
main_window = Program()
main_window.window.show()
sys.exit(program.exec_())
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>400</width>
<height>300</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>400</width>
<height>300</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QToolButton" name="toolButton">
<property name="minimumSize">
<size>
<width>150</width>
<height>50</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>150</width>
<height>50</height>
</size>
</property>
<property name="text">
<string>QtDsgnrBtn</string>
</property>
<property name="popupMode">
<enum>QToolButton::MenuButtonPopup</enum>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>