MORPH INTO: Gear Cutter
This commit is contained in:
parent
068e2fb2e4
commit
42b37369f5
|
|
@ -1,6 +1,6 @@
|
||||||
cmake_minimum_required(VERSION 4.2)
|
cmake_minimum_required(VERSION 4.2)
|
||||||
|
|
||||||
project(PROJECT-NAME VERSION 1.0.0 LANGUAGES CXX)
|
project(gear-cutter VERSION 1.0.0 LANGUAGES CXX)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
|
||||||
44
Cargo.lock
generated
44
Cargo.lock
generated
|
|
@ -2,28 +2,6 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "PROJECT-NAME"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"bytemuck",
|
|
||||||
"glam",
|
|
||||||
"pollster",
|
|
||||||
"wgpu",
|
|
||||||
"winit",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "PROJECT-NAME-impl"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"PROJECT-NAME",
|
|
||||||
"glam",
|
|
||||||
"pollster",
|
|
||||||
"raw-window-handle",
|
|
||||||
"wgpu",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ab_glyph"
|
name = "ab_glyph"
|
||||||
version = "0.2.32"
|
version = "0.2.32"
|
||||||
|
|
@ -489,6 +467,28 @@ dependencies = [
|
||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gear-cutter"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"glam",
|
||||||
|
"pollster",
|
||||||
|
"wgpu",
|
||||||
|
"winit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gear-cutter-impl"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"gear-cutter",
|
||||||
|
"glam",
|
||||||
|
"pollster",
|
||||||
|
"raw-window-handle",
|
||||||
|
"wgpu",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gethostname"
|
name = "gethostname"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
members = ["ui"]
|
members = ["ui"]
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "PROJECT-NAME"
|
name = "gear-cutter"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
|
|
|
||||||
25
README.md
25
README.md
|
|
@ -1,24 +1 @@
|
||||||
# Qt6+WGPU skeleton program
|
# Gear Cutter
|
||||||
|
|
||||||
This example combines Qt6 GUI (C++) with WGPU rendering (Rust). It doesn’t do much of either but it’s the bridging what’s relevant.
|
|
||||||
|
|
||||||
Currently only X11 (XCB) is supported but it should be easy to extend to other platforms.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
This program is intended as a starting point rather than a dependency, so just copy it.
|
|
||||||
|
|
||||||
1. Fetch this code:
|
|
||||||
* clone the repo: `git clone https://gitea.noteuclid.ru/noteuclid/qt-wgpu-skel.git`
|
|
||||||
* or, download and unpack the [tarball](https://gitea.noteuclid.ru/noteuclid/qt-wgpu-skel/archive/master.tar.gz).
|
|
||||||
2. Rename the directory to your liking
|
|
||||||
3. Run `init.sh` with the project name, like:
|
|
||||||
* `./init.sh 'My FANCY Project'`, or
|
|
||||||
* `./init.sh 'My FANCY Project' 'fancy-project'`.
|
|
||||||
|
|
||||||
Running `init.sh` without arguments prints the help.
|
|
||||||
|
|
||||||
If you want to use Qt5, change the version in both `CMakeLists.txt`, as well as the KF version.
|
|
||||||
If you don’t want to depend on KDE, remove the ECM and KF5 dependencies, as well as `KColorCombo inBackground` from the UI. You’ll need to replace `inBackground->color()` with something else though.
|
|
||||||
|
|
|
||||||
83
init.sh
83
init.sh
|
|
@ -1,83 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# set -x # debug
|
|
||||||
set -e # abort on errors
|
|
||||||
cd "${0%/*}" # where this script is located
|
|
||||||
|
|
||||||
usage() {
|
|
||||||
{
|
|
||||||
echo "Usage:"
|
|
||||||
echo
|
|
||||||
echo " ./init.sh 'Project Name' [ internal-name ]"
|
|
||||||
echo
|
|
||||||
echo "Project name may be anything you like, in any language, with spaces (but not other control characters), etc."
|
|
||||||
echo
|
|
||||||
echo "Technical name must be ASCII alphanumeric (a-z, 0-9, also _ and - are allowed), preferably all-lowercase. If omitted, it will be generated from the project name (but that only works if the latter is ASCII too)."
|
|
||||||
echo
|
|
||||||
echo "Running this script with no arguments shows this message"
|
|
||||||
} | fold -s 2>&1
|
|
||||||
}
|
|
||||||
|
|
||||||
name="$1"
|
|
||||||
[ -n "$name" ] || { usage; exit 0; } 2>&1
|
|
||||||
echo -n "$name" | LC_ALL=C grep '[[:cntrl:]]' && { echo 'Project name must not contain any control characters'; exit 2; } 2>&1
|
|
||||||
name="$(echo -n $name)" # normalize whitespace
|
|
||||||
|
|
||||||
techname="$2"
|
|
||||||
if [ -z "$techname" ]; then
|
|
||||||
techname=$(
|
|
||||||
echo -n $name |
|
|
||||||
LC_ALL=C tr ' ' '-' |
|
|
||||||
LC_ALL=C tr -d -c '[:alnum:]-_' |
|
|
||||||
LC_ALL=C tr '[:upper:]' '[:lower:]' |
|
|
||||||
cat
|
|
||||||
)
|
|
||||||
echo "Technical name not specified; autogenerated: \"$techname\"" >&2
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -n $techname | LC_ALL=C grep '[^0-9A-Za-z_-]' && { echo 'Technical name must be ASCII alphanumeric'; exit 2; } 2>&1
|
|
||||||
echo -n $techname | LC_ALL=C grep -q '^[A-Za-z]' || { echo 'Technical name must start with an ASCII letter'; exit 2; } 2>&1
|
|
||||||
echo -n $techname | LC_ALL=C grep -q '[A-Za-z0-9]$' || { echo 'Technical name must end with an ASCII letter or digit'; exit 2; } 2>&1
|
|
||||||
|
|
||||||
echo "Project name: $name"
|
|
||||||
echo "Internal name: $techname"
|
|
||||||
|
|
||||||
if ! [ -e .git ]; then
|
|
||||||
alias git='echo skipping:'
|
|
||||||
fi
|
|
||||||
|
|
||||||
# detach from the skeleton repository
|
|
||||||
# oldurl="$(git remote -v get-url origin)"
|
|
||||||
git remote remove origin
|
|
||||||
|
|
||||||
# remove this script from the project (but keep on disk for now)
|
|
||||||
git rm --cached init.sh
|
|
||||||
git rm -r skel-test
|
|
||||||
|
|
||||||
# replace the README with a stub
|
|
||||||
echo "# $name" >| README.md
|
|
||||||
git rm screenshot.png
|
|
||||||
|
|
||||||
# escape HTML special characters (&, <, and >), but also sed special characters (& again, \, and /)
|
|
||||||
htmlname=$(echo -n "$name" | sed 's/&/\&/g; s/</\</g; s/>/\>/g; s:[\\/&]:\\&:g')
|
|
||||||
sed -i "s/PROJECT NAME/$htmlname/" ui/src/main_window.ui
|
|
||||||
|
|
||||||
# escape Rust special characters (" and \; don't worry of the others as they are unlikely enough and easy to fix manually), and also sed special characters (&, \, and /)
|
|
||||||
quotname=$(echo -n "$name" | sed 's/"/\\"/g; s:[\\/&]:\\&:g')
|
|
||||||
sed -i "s/PROJECT NAME/$quotname/" src/main.rs
|
|
||||||
|
|
||||||
# $techname is alphanumeric, nothing to escape
|
|
||||||
grep -l -r PROJECT-NAME | xargs sed -i "s/PROJECT-NAME/$techname/g"
|
|
||||||
|
|
||||||
# replace - with _ to form a valid snake_case identifier
|
|
||||||
ident=$(echo -n $techname | LC_ALL=C tr '-' '_')
|
|
||||||
grep -l -r PROJECT_NAME | xargs sed -i "s/PROJECT_NAME/$ident/g"
|
|
||||||
|
|
||||||
# black magic to convert snake_case to PascalCase
|
|
||||||
pascal=$(echo -n $ident | LC_ALL=C tr '_' '\n' | LC_ALL=C awk -v FIELDWIDTHS='1 1000' -v ORS='' '{print toupper($1) $2}')
|
|
||||||
grep -l -r PROJECTNAME | xargs sed -i "s/PROJECTNAME/$pascal/g"
|
|
||||||
|
|
||||||
# another hack, to update Cargo.lock
|
|
||||||
cargo tree >/dev/null
|
|
||||||
|
|
||||||
git commit -a -m "MORPH INTO: $name"
|
|
||||||
BIN
screenshot.png
BIN
screenshot.png
Binary file not shown.
|
Before Width: | Height: | Size: 45 KiB |
|
|
@ -1,3 +0,0 @@
|
||||||
# Self-tests
|
|
||||||
|
|
||||||
This directory contains self-tests for the skeleton itself. It should be removed when starting a new project.
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -E
|
|
||||||
|
|
||||||
exec cargo check
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -x
|
|
||||||
set -E
|
|
||||||
|
|
||||||
mkdir build
|
|
||||||
cmake -S . -B build -G Ninja -D CMAKE_BUILD_TYPE=Debug
|
|
||||||
ninja -C build
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
#!/bin/zsh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
trap 'echo Test failed!' ERR
|
|
||||||
name='<Test/+> "Example"'
|
|
||||||
dir=${0%/*}
|
|
||||||
root=$dir/..
|
|
||||||
tmp=$(mktemp -d)
|
|
||||||
trap 'rm -rf $tmp' EXIT
|
|
||||||
rsync -a --exclude=/.git --exclude-from=$root/.gitignore $root/. $tmp/
|
|
||||||
cd $tmp
|
|
||||||
echo "Initializing in $tmp"
|
|
||||||
./init.sh $name
|
|
||||||
echo "Starting the shell in $tmp"
|
|
||||||
echo "Review the files, then exit the shell"
|
|
||||||
${SHELL:-/bin/sh} -i
|
|
||||||
echo "Review successful, cleaning up"
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
#!/bin/zsh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
trap 'echo Test failed!' ERR
|
|
||||||
name='<Test/+> "Example"'
|
|
||||||
root=$(realpath ${0%/*}/..)
|
|
||||||
tmp=$(mktemp -d)
|
|
||||||
trap 'rm -rf $tmp' EXIT
|
|
||||||
cd $tmp
|
|
||||||
git -C $root archive HEAD | tar -x
|
|
||||||
echo "Initializing in $tmp"
|
|
||||||
./init.sh $name
|
|
||||||
echo "Starting the shell in $tmp"
|
|
||||||
echo "Review the files, then exit the shell"
|
|
||||||
${SHELL:-/bin/sh} -i
|
|
||||||
echo "Review successful, cleaning up"
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
#!/bin/zsh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
trap 'echo Test failed!' ERR
|
|
||||||
name='<Test/+> "Example"'
|
|
||||||
dir=${0%/*}
|
|
||||||
root=$dir/..
|
|
||||||
tmp=$(mktemp -d)
|
|
||||||
trap 'rm -rf $tmp' EXIT
|
|
||||||
rsync -a --exclude-from=$root/.gitignore $root/. $tmp/
|
|
||||||
cd $tmp
|
|
||||||
git clean -fxdq
|
|
||||||
echo "Initializing in $tmp"
|
|
||||||
./init.sh $name
|
|
||||||
echo "Starting the shell in $tmp"
|
|
||||||
echo "Review the files, then exit the shell"
|
|
||||||
${SHELL:-/bin/sh} -i
|
|
||||||
echo "Review successful, cleaning up"
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use PROJECT_NAME::{Core, RedrawArgs, init_gpu_inner};
|
use gear_cutter::{Core, RedrawArgs, init_gpu_inner};
|
||||||
use glam::uvec2;
|
use glam::uvec2;
|
||||||
use winit::{
|
use winit::{
|
||||||
application::ApplicationHandler,
|
application::ApplicationHandler,
|
||||||
|
|
@ -9,7 +9,7 @@ use winit::{
|
||||||
window::Window,
|
window::Window,
|
||||||
};
|
};
|
||||||
|
|
||||||
const TITLE: &str = "PROJECT NAME";
|
const TITLE: &str = "Gear Cutter";
|
||||||
|
|
||||||
struct MainWindow {
|
struct MainWindow {
|
||||||
window: Arc<Window>,
|
window: Arc<Window>,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
include(impl.cmake)
|
include(impl.cmake)
|
||||||
|
|
||||||
qt_add_executable(PROJECT-NAME
|
qt_add_executable(gear-cutter
|
||||||
src/api.cxx
|
src/api.cxx
|
||||||
src/main.cxx
|
src/main.cxx
|
||||||
src/main_window.cxx
|
src/main_window.cxx
|
||||||
|
|
@ -8,6 +8,6 @@ qt_add_executable(PROJECT-NAME
|
||||||
src/viewport.cxx
|
src/viewport.cxx
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(PROJECT-NAME PRIVATE Qt6::Gui Qt6::Widgets)
|
target_link_libraries(gear-cutter PRIVATE Qt6::Gui Qt6::Widgets)
|
||||||
target_link_libraries(PROJECT-NAME PRIVATE PROJECT_NAME_impl)
|
target_link_libraries(gear-cutter PRIVATE gear_cutter_impl)
|
||||||
target_include_directories(PROJECT-NAME PRIVATE src)
|
target_include_directories(gear-cutter PRIVATE src)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "PROJECT-NAME-impl"
|
name = "gear-cutter-impl"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
|
|
@ -7,7 +7,7 @@ edition = "2024"
|
||||||
crate-type = ["staticlib"]
|
crate-type = ["staticlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
PROJECT-NAME = {path = "../"}
|
gear-cutter = {path = "../"}
|
||||||
|
|
||||||
glam = { version = "0.30" }
|
glam = { version = "0.30" }
|
||||||
pollster = "0.4.0"
|
pollster = "0.4.0"
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ language = "C++"
|
||||||
|
|
||||||
[parse]
|
[parse]
|
||||||
parse_deps = true
|
parse_deps = true
|
||||||
include = ["PROJECT-NAME"]
|
include = ["gear-cutter"]
|
||||||
|
|
||||||
[export]
|
[export]
|
||||||
exclude = ["Box"]
|
exclude = ["Box"]
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
# build the Rust side as a static library
|
# build the Rust side as a static library
|
||||||
set(impl_basename "${CARGO_TARGET_DIR}/release/libPROJECT_NAME_impl")
|
set(impl_basename "${CARGO_TARGET_DIR}/release/libgear_cutter_impl")
|
||||||
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${impl_basename}.a
|
OUTPUT ${impl_basename}.a
|
||||||
COMMAND env CARGO_TARGET_DIR=${CARGO_TARGET_DIR} ${CARGO} build --release --package PROJECT-NAME-impl
|
COMMAND env CARGO_TARGET_DIR=${CARGO_TARGET_DIR} ${CARGO} build --release --package gear-cutter-impl
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
DEPFILE ${impl_basename}.d
|
DEPFILE ${impl_basename}.d
|
||||||
USES_TERMINAL
|
USES_TERMINAL
|
||||||
|
|
@ -17,9 +17,9 @@ add_custom_target(build_impl
|
||||||
DEPENDS ${impl_basename}.a
|
DEPENDS ${impl_basename}.a
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(PROJECT_NAME_impl STATIC IMPORTED)
|
add_library(gear_cutter_impl STATIC IMPORTED)
|
||||||
|
|
||||||
set_target_properties(PROJECT_NAME_impl PROPERTIES
|
set_target_properties(gear_cutter_impl PROPERTIES
|
||||||
IMPORTED_LOCATION ${impl_basename}.a
|
IMPORTED_LOCATION ${impl_basename}.a
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -37,9 +37,9 @@ add_custom_command(
|
||||||
DEPENDS_EXPLICIT_ONLY
|
DEPENDS_EXPLICIT_ONLY
|
||||||
)
|
)
|
||||||
|
|
||||||
target_sources(PROJECT_NAME_impl INTERFACE
|
target_sources(gear_cutter_impl INTERFACE
|
||||||
${bindings_basename}.hxx
|
${bindings_basename}.hxx
|
||||||
)
|
)
|
||||||
target_include_directories(PROJECT_NAME_impl INTERFACE
|
target_include_directories(gear_cutter_impl INTERFACE
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/include"
|
"${CMAKE_CURRENT_BINARY_DIR}/include"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{ffi::c_void, num::NonZero, ptr::NonNull};
|
use std::{ffi::c_void, num::NonZero, ptr::NonNull};
|
||||||
|
|
||||||
use PROJECT_NAME::{Core, RedrawArgs, init_gpu_inner};
|
use gear_cutter::{Core, RedrawArgs, init_gpu_inner};
|
||||||
use glam::{UVec2, uvec2};
|
use glam::{UVec2, uvec2};
|
||||||
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, XcbDisplayHandle, XcbWindowHandle};
|
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, XcbDisplayHandle, XcbWindowHandle};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ int main(int argc, char* argv[]) {
|
||||||
|
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
auto w = new PROJECTNAME;
|
auto w = new GearCutter;
|
||||||
w->show();
|
w->show();
|
||||||
|
|
||||||
return app.exec();
|
return app.exec();
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
#include "ui_main_window.h"
|
#include "ui_main_window.h"
|
||||||
|
|
||||||
PROJECTNAME::PROJECTNAME(QWidget* parent)
|
GearCutter::GearCutter(QWidget* parent)
|
||||||
: QMainWindow(parent),
|
: QMainWindow(parent),
|
||||||
m_ui(new Ui::MainWindow) {
|
m_ui(new Ui::MainWindow) {
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
updateView();
|
updateView();
|
||||||
}
|
}
|
||||||
|
|
||||||
PROJECTNAME::~PROJECTNAME() = default;
|
GearCutter::~GearCutter() = default;
|
||||||
|
|
||||||
void PROJECTNAME::updateView() {
|
void GearCutter::updateView() {
|
||||||
RedrawArgs args{
|
RedrawArgs args{
|
||||||
.cam_yaw = qDegreesToRadians((float)m_ui->camYaw->value()),
|
.cam_yaw = qDegreesToRadians((float)m_ui->camYaw->value()),
|
||||||
.cam_pitch = qDegreesToRadians((float)m_ui->camPitch->value()),
|
.cam_pitch = qDegreesToRadians((float)m_ui->camPitch->value()),
|
||||||
|
|
@ -21,7 +21,7 @@ void PROJECTNAME::updateView() {
|
||||||
m_ui->viewport->setView(args);
|
m_ui->viewport->setView(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PROJECTNAME::updateViewIf(bool update) {
|
void GearCutter::updateViewIf(bool update) {
|
||||||
if (update)
|
if (update)
|
||||||
updateView();
|
updateView();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@ namespace Ui {
|
||||||
class MainWindow;
|
class MainWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PROJECTNAME : public QMainWindow {
|
class GearCutter : public QMainWindow {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit PROJECTNAME(QWidget* parent = nullptr);
|
explicit GearCutter(QWidget* parent = nullptr);
|
||||||
~PROJECTNAME() override;
|
~GearCutter() override;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void updateView();
|
void updateView();
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>PROJECT NAME</string>
|
<string>Gear Cutter</string>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="centralwidget">
|
<widget class="QWidget" name="centralwidget">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user