From 3a101821b790cf5d82c50009de4a1185bd70bc02 Mon Sep 17 00:00:00 2001 From: Kseninia Mikhaylova Date: Mon, 14 Oct 2024 13:50:28 +0300 Subject: [PATCH] test gui --- gui_test.py | 97 +++++++++++++++++++++++++++++++++++++++++++++++ juce_init.py | 101 +++++++++++++++++++++++++++++++++++++++++++++++++ main.py | 19 ++++++++++ poetry.lock | 26 ++++++++++++- pyproject.toml | 1 + 5 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 gui_test.py create mode 100644 juce_init.py create mode 100644 main.py diff --git a/gui_test.py b/gui_test.py new file mode 100644 index 0000000..477f0e6 --- /dev/null +++ b/gui_test.py @@ -0,0 +1,97 @@ +from juce_init import START_JUCE_COMPONENT +import popsicle as juce + + +class SidePanel(juce.Component): + backgroundColour = juce.Colours.lightblue + textColour = juce.Colours.black + + robots = [] + + robotsLabel = juce.Label("", "Выберите робота") + robotsRadio = [] + robotsControls = [robotsLabel] + robotsRadio + + robotButtonsId = 1001 + + def __init__(self, robots=[]): + super().__init__() + self.robots = robots + + for r in self.robots: + self.robotsRadio.append( + juce.ToggleButton(r["name"]), + ) + self.robotsControls = [self.robotsLabel] + self.robotsRadio + + for s in self.robotsControls: + self.addAndMakeVisible(s) + + for s in self.robotsRadio: + s.setRadioGroupId(self.robotButtonsId, juce.dontSendNotification) + s.onClick = self.updateToggleState + s.setClickingTogglesState(True) + + def paint(self, g: juce.Graphics): + g.fillAll(self.backgroundColour) + + for i, s in enumerate(self.robotsControls): + s.setColour(s.textColourId, self.textColour) + if hasattr(s, "tickDisabledColourId"): + s.setColour(s.tickColourId, self.textColour) + s.setColour(s.tickDisabledColourId, self.textColour) + + s.setBounds(10 if i == 0 else 20, (i * 20), self.getWidth() - 20, 20) + + def updateToggleState(self): + for i, s in enumerate(self.robotsRadio): + if s.getToggleState() == True: + print(self.robots[i]) + break; + + +class MainPanel(juce.Component): + sliders = [] + + def __init__(self): + super().__init__() + + for _ in range(5): + slider = juce.Slider() + slider.setTextBoxStyle( + juce.Slider.TextEntryBoxPosition.NoTextBox, True, 0, 0 + ) + + self.sliders.append(slider) + self.addAndMakeVisible(slider) + + def paint(self, g): + g.fillAll(juce.Colours.hotpink) + + +class MainContentComponent(juce.Component): + def __init__(self, robots): + super().__init__() + + self.rightPanel = SidePanel(robots) + self.mainPanel = MainPanel() + + self.addAndMakeVisible(self.rightPanel) + self.addAndMakeVisible(self.mainPanel) + + self.setSize(800, 600) + # self.setResizable(False, False) + + def paint(self, g): + g.fillAll( + self.getLookAndFeel().findColour(juce.ResizableWindow.backgroundColourId) + ) + + def resized(self): + bounds = self.getLocalBounds() + self.rightPanel.setBounds(bounds.removeFromRight(self.proportionOfWidth(0.25))) + self.mainPanel.setBounds(bounds) + + +if __name__ == "__main__": + START_JUCE_COMPONENT(MainContentComponent, name="ROBOT GUI") diff --git a/juce_init.py b/juce_init.py new file mode 100644 index 0000000..113412d --- /dev/null +++ b/juce_init.py @@ -0,0 +1,101 @@ +import os +import sys +import glob +import time +import traceback +from pathlib import Path +from functools import wraps + + +try: + import popsicle as juce + +except ImportError: + folder = (Path(__file__).parent.parent / "build") + for ext in ["*.so", "*.pyd"]: + path_to_search = folder / "**" / ext + for f in glob.iglob(str(path_to_search), recursive=True): + if os.path.isfile(f): + sys.path.append(str(Path(f).parent)) + break + + import popsicle as juce + + +def START_JUCE_COMPONENT(ComponentClass, name, **kwargs): + class DefaultWindow(juce.DocumentWindow): + component = None + + def __init__(self): + super().__init__( + juce.JUCEApplication.getInstance().getApplicationName(), + juce.Desktop.getInstance().getDefaultLookAndFeel() + .findColour(juce.ResizableWindow.backgroundColourId), + juce.DocumentWindow.allButtons, + True) + + self.component = ComponentClass(kwargs.get("robots", [])) + + self.setResizable(True, True) + self.setContentNonOwned(self.component, True) + self.centreWithSize(self.component.getWidth(), self.component.getHeight() + self.getTitleBarHeight()) + self.setAlwaysOnTop(kwargs.get("alwaysOnTop", False)) + self.setVisible(True) + + def __del__(self): + self.clearContentComponent() + + if self.component: + self.component.setVisible(False) + del self.component + + def closeButtonPressed(self): + juce.JUCEApplication.getInstance().systemRequestedQuit() + + class DefaultApplication(juce.JUCEApplication): + window = None + + def __init__(self): + super().__init__() + + def getApplicationName(self): + return name + + def getApplicationVersion(self): + return "1.0" + + def initialise(self, commandLineParameters): + self.window = DefaultWindow() + + juce.MessageManager.callAsync(lambda: juce.Process.makeForegroundProcess()) + + def shutdown(self): + if self.window: + del self.window + + def systemRequestedQuit(self): + self.quit() + + # def unhandledException(self, ex: Exception, file: str, line: int): + # if hasattr(ex, "__traceback__"): + # print("Traceback (most recent call last):") + # traceback.print_tb(ex.__traceback__) + # print(ex) + # if isinstance(ex, KeyboardInterrupt): + # juce.JUCEApplication.getInstance().systemRequestedQuit() + + juce.START_JUCE_APPLICATION( + DefaultApplication, + catchExceptionsAndContinue=kwargs.get("catchExceptionsAndContinue", False)) + + +def timeit(func): + @wraps(func) + def timeit_wrapper(*args, **kwargs): + start_time = time.perf_counter() + result = func(*args, **kwargs) + total_time = time.perf_counter() - start_time + print(f'Function {func.__name__} Took {total_time:.4f} seconds') # {args} {kwargs} + return result + + return timeit_wrapper \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..7dbb860 --- /dev/null +++ b/main.py @@ -0,0 +1,19 @@ +import threading +from juce_init import START_JUCE_COMPONENT +from gui_test import MainContentComponent + +class MyApp: + robots = [ + {"name": "big", "host": "192.168.70.55", "slave_id": 11}, + {"name": "small", "host": "192.168.70.65", "slave_id": 22}, + ] + + def __init__(self): + robot = self.robots[0] + self.start_gui() + + def start_gui(self): + START_JUCE_COMPONENT(MainContentComponent, name="ROBOT GUI", robots=self.robots) + +if __name__ == "__main__": + MyApp() diff --git a/poetry.lock b/poetry.lock index 28407f5..d9e0b1d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -45,6 +45,30 @@ files = [ {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, ] +[[package]] +name = "popsicle" +version = "0.9.6" +description = "popsicle: Python integration for JUCE with pybind11." +optional = false +python-versions = ">=3.10" +files = [ + {file = "popsicle-0.9.6-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:0abedd3790cfe63b14ac3aa46263993e8d859b2c1f769dad8e8685d9995e8e2e"}, + {file = "popsicle-0.9.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10ef070c120e782d852eb47a55a187cf7276e8a759110bbbec713e5bbd0faf6a"}, + {file = "popsicle-0.9.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e2d51bd173da55f84aec6c2a3b5c4b8c0ac3742491a20110531fcc3a3f308bc"}, + {file = "popsicle-0.9.6-cp310-cp310-win_amd64.whl", hash = "sha256:c55c6f840269a49b395626ce600c65078aeeda8c727d305d1dca7bded5cf587b"}, + {file = "popsicle-0.9.6-cp310-cp310-win_arm64.whl", hash = "sha256:1e22ffb9aca2b468915d7c4b5bd7ce82cdf52ce86e1f4d54eacf6a033dd13c98"}, + {file = "popsicle-0.9.6-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:d013f08cfd2c491d3f4a35b6477592bd58ece3d59c37a513b6d1e620f50145f1"}, + {file = "popsicle-0.9.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cafbace5fb0df51aac0336cce47fc5208335c43670a103d1a7f8ee0f3094f118"}, + {file = "popsicle-0.9.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95063399a42258b56b469ae31a380d6c2ddb4ab01d729a52300ee3e55ecececb"}, + {file = "popsicle-0.9.6-cp311-cp311-win_amd64.whl", hash = "sha256:f30b6309b015f3a26a461223c19d7c8f962f42868575af076f15f71f53b58496"}, + {file = "popsicle-0.9.6-cp311-cp311-win_arm64.whl", hash = "sha256:0d0eafff83b08b6ada954e897a0e07b17f6994398c1860a5a9560af402c8f6e9"}, + {file = "popsicle-0.9.6-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:a42d53580ddbaca15e64e34c4971f83a862a95c5e50d3e1be5b127c4ff056e48"}, + {file = "popsicle-0.9.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4429eeee549cf340b3bcc60e7244881b08b8357aeafc88a2a9aee9dd1e3b9fc1"}, + {file = "popsicle-0.9.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11de3ccfb5a8f422f1532aaca835ab6907affcbf91ff45312ebcb1d424b4723f"}, + {file = "popsicle-0.9.6-cp312-cp312-win_amd64.whl", hash = "sha256:ce7f79069151860a1bd43ba50e786d57c8db3e2f6e8538db45bb0ac67adba7f6"}, + {file = "popsicle-0.9.6-cp312-cp312-win_arm64.whl", hash = "sha256:c40d8301b08690b041ed5710f0e9a63bf83c48ab51f5cf015b6fb7cf95921744"}, +] + [[package]] name = "pybullet" version = "3.2.6" @@ -94,4 +118,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "3.10.12" -content-hash = "fe8f4d7980373c9220c8910d6eb71c5f4dd23b77fc6879a939a317637ef349d4" +content-hash = "7bff17d98584d2b65789f0713e4ac29f8948fc013c65a192ab11ef9e3bfcd9d6" diff --git a/pyproject.toml b/pyproject.toml index 249bc7b..e9abd16 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,7 @@ pymodbus = "^3.7.0" tk = "^0.1.0" pybullet = "^3.2.6" numpy = "^1.20" +popsicle = "^0.9.6" [build-system]