kcjengr / probe_basic

User interface for the LinuxCNC machine control.
https://kcjengr.github.io/probe_basic/
GNU General Public License v3.0
90 stars 58 forks source link

Auto load of Dynamic User Tabs #84

Closed dpslwk closed 11 months ago

dpslwk commented 11 months ago

The name (objectName in QTDesigner) of the root QWidget is used as the tab button name (underscores are converted to space) and a dynamic property (bool) of sidebar is used to flag if the usertab should be loaded into the sidebar (only one side bar user tab is allowed)

To enable add USER_TABS_PATH to your ini [DISPLAY] section, each sub folder in the path will be loaded, multiple USER_TABS_PATH entries can be added

[DISPLAY]
USER_TABS_PATH = user_tabs/
[DISPLAY]
USER_TABS_PATH = /home/{{your_user_name}}/linuxcnc/configs/{{your_machine_name}}/user_tabs/
USER_TABS_PATH = /home/{{your_user_name}}/linuxcnc/user_tabs/

each sub folder in user_tabs should have a .py and a .ui file all named the same and the python file must have a UserTab class extending a QWidget

user_tabs/
    main_camera/
        main_camera.py
        main_camera.ui
    main_enclosure/
        main_enclosure.py
        main_enclosure.ui
    side_buttons/
        side_buttons.py
        side_buttons.ui

template for a main tab and side bar tab are included in the sample configs/probe_basic/user_tabs/ folder

Screenshot 2023-11-09 at 00 36 29 Screenshot 2023-11-09 at 01 15 51 SideBarUserTab
dpslwk commented 11 months ago

This is a python only (no .ui need) example of a sidebar user tab that will auto add SubCallButton's based on INI [MACROS] MACRO lines It does not support dynamic user parameters that gmoccapy has, but the normal SubCallButton widget named parameter passing should still work

Not sure if I should include this in the PR with the other templates, since we can only show one sidebar user tab

user_tabs/macros_sidebar/macros_sidebar.py

import os
import linuxcnc

from qtpy.QtWidgets import QWidget, QVBoxLayout

from qtpyvcp.utilities import logger
from qtpyvcp.widgets.button_widgets.subcall_button import SubCallButton

LOG = logger.getLogger(__name__)

INI_FILE = linuxcnc.ini(os.getenv('INI_FILE_NAME'))

class UserTab(QWidget):
    """Macro SubCall sidebar user tab.

    The label is set after the comma. The symbols \n adds a line break.
    If no label is given then

    [MACROS]
    MACRO = go_to_zero
    MACRO = unclamptool,Unclamp the\nTool

    Args:
        parent (QWidget, optional) : The parent widget of the button, or None.
    """

    sub_call_buttons = []
    style_sheet = """
    QPushButton {
            color: white;
            border-color: black;
            border-style: solid;
            border-radius: 5px;
            border-width: 2px;
            background: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(213, 218, 216, 255), stop:0.169312 rgba(82, 82, 83, 255), stop:0.328042 rgba(72, 70, 73, 255), stop:0.492063 rgba(78, 77, 79, 255), stop:0.703704 rgba(72, 70, 73, 255), stop:0.86 rgba(82, 82, 83, 255), stop:1 rgba(213, 218, 216, 255));
        }

        QPushButton {
            font-family: "Bebas Kai";
            font-size: 16pt;
        }

        QPushButton:disabled {
            border-color: gray;
        }

        QPushButton:hover {
            background:  qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                                             stop: 0 #A19E9E, stop: 1.0 #5C5959);
        }

        QPushButton:pressed {
            background:  qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(85, 85, 238, 255), stop:0.544974 rgba(90, 91, 239, 255), stop:1 rgba(126, 135, 243, 255));
        }

        QPushButton:checked[option="true"] {
            background:  qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(85, 85, 238, 255), stop:0.544974 rgba(90, 91, 239, 255), stop:1 rgba(126, 135, 243, 255));
        }

        QPushButton:checked {
            background:  qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(85, 85, 238, 255), stop:0.544974 rgba(90, 91, 239, 255), stop:1 rgba(126, 135, 243, 255));
        }
    """

    def __init__(self, parent=None):
        super(UserTab, self).__init__(parent)

        self.setObjectName("macros")
        self.setWindowTitle("Macro User Tab")
        self.setGeometry(0, 0, 179, 511)
        self.setMaximumWidth(179)
        self.setMaximumHeight(511)
        self.setProperty("sidebar", True)

        self.macro_button_layout = QVBoxLayout(self)

        macros = INI_FILE.findall("MACROS", "MACRO")

        for m in macros:
            for num,k in enumerate(m.split(',')):
                if num == 0:
                    macro = k
                    if len(m.split(',')) <2:
                        label = k.replace("_", " ").title()
                else:
                    label = k.replace(r'\n', '\n')

            button = SubCallButton(None, filename=macro)
            button.setText(label)
            button.setStyleSheet(self.style_sheet)
            button.setToolTip("o<{}> call".format(macro))
            self.sub_call_buttons.append(button)
            self.macro_button_layout.addWidget(self.sub_call_buttons[-1])

        self.macro_button_layout.addStretch()
Macro Sidebar Example
dpslwk commented 11 months ago

A variation on the above example

this uses MDIButton rather than SubCallButton and uses the same INI format as QtDragon [MDI_COMMAND_LIST] MDI_COMMAND

user_tabs/mdi_command_list_sidebar/mdi_command_list_sidebar.py

import os
import linuxcnc

from qtpy.QtWidgets import QWidget, QVBoxLayout

from qtpyvcp.utilities import logger
from qtpyvcp.widgets.button_widgets.mdi_button import MDIButton

LOG = logger.getLogger(__name__)

INI_FILE = linuxcnc.ini(os.getenv('INI_FILE_NAME'))

class UserTab(QWidget):
    """MDI SubCall sidebar user tab.

    The label is set after the comma. The symbols \n adds a line break.

    [MDI_COMMAND_LIST]
    # for macro buttons
    MDI_COMMAND = G0 Z25;X0 Y0;Z0, Goto\nUser\nZero
    MDI_COMMAND = G53 G0 Z0;G53 G0 X0 Y0,Goto\nMachn\nZero
    MDI_COMMAND = o<unclamptool> call,Uncalmp Tool

    Args:
        parent (QWidget, optional) : The parent widget of the button, or None.
    """

    mdi_call_buttons = []
    style_sheet = """
    QPushButton {
            color: white;
            border-color: black;
            border-style: solid;
            border-radius: 5px;
            border-width: 2px;
            background: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(213, 218, 216, 255), stop:0.169312 rgba(82, 82, 83, 255), stop:0.328042 rgba(72, 70, 73, 255), stop:0.492063 rgba(78, 77, 79, 255), stop:0.703704 rgba(72, 70, 73, 255), stop:0.86 rgba(82, 82, 83, 255), stop:1 rgba(213, 218, 216, 255));
        }

        QPushButton {
            font-family: &quot;Bebas Kai&quot;;
            font-size: 16pt;
        }

        QPushButton:disabled {
            border-color: gray;
        }

        QPushButton:hover {
            background:  qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                                             stop: 0 #A19E9E, stop: 1.0 #5C5959);
        }

        QPushButton:pressed {
            background:  qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(85, 85, 238, 255), stop:0.544974 rgba(90, 91, 239, 255), stop:1 rgba(126, 135, 243, 255));
        }

        QPushButton:checked[option=&quot;true&quot;] {
            background:  qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(85, 85, 238, 255), stop:0.544974 rgba(90, 91, 239, 255), stop:1 rgba(126, 135, 243, 255));
        }

        QPushButton:checked {
            background:  qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(85, 85, 238, 255), stop:0.544974 rgba(90, 91, 239, 255), stop:1 rgba(126, 135, 243, 255));
        }
    """

    def __init__(self, parent=None):
        super(UserTab, self).__init__(parent)

        self.setObjectName("macros")
        self.setWindowTitle("MDI Macro User Tab")
        self.setGeometry(0, 0, 179, 511)
        self.setMaximumWidth(179)
        self.setMaximumHeight(511)
        self.setProperty("sidebar", True)

        self.macro_button_layout = QVBoxLayout(self)

        macros = INI_FILE.findall("MDI_COMMAND_LIST", "MDI_COMMAND")

        for m in macros:
            for num,k in enumerate(m.split(',')):
                if num == 0:
                    macro = k
                    if len(m.split(',')) <2:
                        label = "MDI Macro {}".format(len(self.mdi_call_buttons) + 1)
                else:
                    label = k.replace(r'\n', '\n')

            button = MDIButton(None, command=macro)
            button.setText(label)
            button.setStyleSheet(self.style_sheet)
            button.setToolTip(macro.replace(';', '\n'))
            self.mdi_call_buttons.append(button)
            self.macro_button_layout.addWidget(self.mdi_call_buttons[-1])

        self.macro_button_layout.addStretch()