Compare commits
10 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4c2a4790c0 | ||
![]() |
1eac3f6a83 | ||
![]() |
87db38b08e | ||
![]() |
f802708af4 | ||
![]() |
738c8805f9 | ||
|
66937e2cfc | ||
![]() |
cd67e8ddd7 | ||
![]() |
b6fb6ca7d4 | ||
![]() |
73aa61a13b | ||
![]() |
8ad659de76 |
|
@ -28,6 +28,7 @@ find_package(PkgConfig REQUIRED)
|
||||||
pkg_check_modules(EISMULIPLEXER REQUIRED eismuliplexer)
|
pkg_check_modules(EISMULIPLEXER REQUIRED eismuliplexer)
|
||||||
find_package(Qt6 REQUIRED COMPONENTS Widgets)
|
find_package(Qt6 REQUIRED COMPONENTS Widgets)
|
||||||
find_package(Qt6 REQUIRED COMPONENTS Core)
|
find_package(Qt6 REQUIRED COMPONENTS Core)
|
||||||
|
# find_package(Python3 REQUIRED) - Removed since we're using QProcess instead of embedding Python
|
||||||
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
set(CMAKE_AUTOUIC ON)
|
set(CMAKE_AUTOUIC ON)
|
||||||
|
@ -41,15 +42,17 @@ add_executable(${PROJECT_NAME}
|
||||||
mainwindow.h
|
mainwindow.h
|
||||||
mainwindow.cpp
|
mainwindow.cpp
|
||||||
mainwindow.ui
|
mainwindow.ui
|
||||||
multiplexer.h
|
|
||||||
multiplexer.cpp
|
|
||||||
triggerwidget.cpp
|
triggerwidget.cpp
|
||||||
triggerwidget.h
|
triggerwidget.h
|
||||||
|
pythonrunner.cpp
|
||||||
|
pythonrunner.h
|
||||||
)
|
)
|
||||||
set_target_properties(${PROJECT_NAME} PROPERTIES WIN32_EXECUTABLE ON)
|
set_target_properties(${PROJECT_NAME} PROPERTIES WIN32_EXECUTABLE ON)
|
||||||
target_compile_options(${PROJECT_NAME} PUBLIC "-Wall")
|
target_compile_options(${PROJECT_NAME} PUBLIC "-Wall")
|
||||||
target_include_directories(${PROJECT_NAME} PUBLIC QCodeEditor)
|
target_include_directories(${PROJECT_NAME} PUBLIC QCodeEditor)
|
||||||
|
# target_include_directories(${PROJECT_NAME} PRIVATE ${Python3_INCLUDE_DIRS}) - Removed since we're using QProcess instead of embedding Python
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Widgets Qt6::Core ${EISMULIPLEXER_LIBRARIES} QCodeEditor)
|
target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Widgets Qt6::Core ${EISMULIPLEXER_LIBRARIES} QCodeEditor)
|
||||||
|
# ${Python3_LIBRARIES} - Removed since we're using QProcess instead of embedding Python
|
||||||
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/resources/eismultiplexerqt.png PROPERTIES QT_RESOURCE_ALIAS eismultiplexerqt.png)
|
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/resources/eismultiplexerqt.png PROPERTIES QT_RESOURCE_ALIAS eismultiplexerqt.png)
|
||||||
qt_add_resources(${PROJECT_NAME} "resources" PREFIX "/" FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/eismultiplexerqt.png )
|
qt_add_resources(${PROJECT_NAME} "resources" PREFIX "/" FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/eismultiplexerqt.png )
|
||||||
install(TARGETS ${PROJECT_NAME} DESTINATION bin)
|
install(TARGETS ${PROJECT_NAME} DESTINATION bin)
|
||||||
|
|
|
@ -108,7 +108,6 @@ void ChannelWidget::onGangComboChanged(int index)
|
||||||
gangedChannelNumber = -1;
|
gangedChannelNumber = -1;
|
||||||
checkbox.setEnabled(true);
|
checkbox.setEnabled(true);
|
||||||
checkbox.setChecked(false);
|
checkbox.setChecked(false);
|
||||||
checkbox.setChecked(false);
|
|
||||||
} else {
|
} else {
|
||||||
// A ganged channel was selected
|
// A ganged channel was selected
|
||||||
QString currentText = gangcombo.currentText();
|
QString currentText = gangcombo.currentText();
|
||||||
|
@ -128,7 +127,7 @@ void ChannelWidget::onGangComboChanged(int index)
|
||||||
void ChannelWidget::onOtherChannelStateChanged(uint16_t deviceSerial, uint16_t channelNumber, bool checked)
|
void ChannelWidget::onOtherChannelStateChanged(uint16_t deviceSerial, uint16_t channelNumber, bool checked)
|
||||||
{
|
{
|
||||||
// If this channel is ganged to the channel that changed state
|
// If this channel is ganged to the channel that changed state
|
||||||
if (gangedDeviceSerial == deviceSerial && gangedChannelNumber == channelNumber) {
|
if (this->deviceSerial == deviceSerial && this->channelNumber == channelNumber) {
|
||||||
// Update our checkbox state to follow the ganged channel
|
// Update our checkbox state to follow the ganged channel
|
||||||
checkbox.blockSignals(true);
|
checkbox.blockSignals(true);
|
||||||
checkbox.setChecked(checked);
|
checkbox.setChecked(checked);
|
||||||
|
@ -163,3 +162,9 @@ void ChannelWidget::setGangedChannel(uint16_t gangedDeviceSerial, uint16_t gange
|
||||||
updateCheckboxState();
|
updateCheckboxState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChannelWidget::replaceMultiplexer(std::shared_ptr<struct eismultiplexer> multiplexer, int serial)
|
||||||
|
{
|
||||||
|
if(serial == deviceSerial || serial == -1)
|
||||||
|
this->multiplexer = multiplexer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ public slots:
|
||||||
private slots:
|
private slots:
|
||||||
void onChannelToggled(bool checked);
|
void onChannelToggled(bool checked);
|
||||||
void onGangComboChanged(int index);
|
void onGangComboChanged(int index);
|
||||||
|
void replaceMultiplexer(std::shared_ptr<struct eismultiplexer> multiplexer, int serial = -1);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void channelAboutToBeTurnedOn(uint16_t deviceSerial, uint16_t channelNumber);
|
void channelAboutToBeTurnedOn(uint16_t deviceSerial, uint16_t channelNumber);
|
||||||
|
|
2
external/QCodeEditor/CMakeLists.txt
vendored
2
external/QCodeEditor/CMakeLists.txt
vendored
|
@ -1,4 +1,4 @@
|
||||||
cmake_minimum_required(VERSION 3.6)
|
cmake_minimum_required(VERSION 3.14)
|
||||||
project(QCodeEditor)
|
project(QCodeEditor)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
|
146
mainwindow.cpp
146
mainwindow.cpp
|
@ -1,6 +1,6 @@
|
||||||
#include <eismultiplexer.h>
|
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <set>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
@ -12,11 +12,12 @@ MainWindow::MainWindow(QWidget *parent):
|
||||||
QMainWindow(parent),
|
QMainWindow(parent),
|
||||||
ui(new Ui::MainWindow),
|
ui(new Ui::MainWindow),
|
||||||
codeEditor(this),
|
codeEditor(this),
|
||||||
|
pythonOutput(this),
|
||||||
|
pythonRunner(&pythonOutput),
|
||||||
currentFilePath(""),
|
currentFilePath(""),
|
||||||
isFileModified(false)
|
isFileModified(false)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
enumerateDevices();
|
|
||||||
|
|
||||||
codeEditor.setAutoIndentation(true);
|
codeEditor.setAutoIndentation(true);
|
||||||
codeEditor.setAutoParentheses(true);
|
codeEditor.setAutoParentheses(true);
|
||||||
|
@ -26,7 +27,10 @@ MainWindow::MainWindow(QWidget *parent):
|
||||||
font.setStyleHint(QFont::TypeWriter);
|
font.setStyleHint(QFont::TypeWriter);
|
||||||
codeEditor.setFont(font);
|
codeEditor.setFont(font);
|
||||||
|
|
||||||
ui->codeLayout->addWidget(&codeEditor);
|
ui->codeLayout->addWidget(&codeEditor, 1);
|
||||||
|
|
||||||
|
pythonOutput.setReadOnly(true);
|
||||||
|
ui->codeLayout->addWidget(&pythonOutput);
|
||||||
|
|
||||||
// Set up keyboard shortcuts
|
// Set up keyboard shortcuts
|
||||||
ui->actionOpen->setShortcut(QKeySequence::Open);
|
ui->actionOpen->setShortcut(QKeySequence::Open);
|
||||||
|
@ -42,8 +46,15 @@ MainWindow::MainWindow(QWidget *parent):
|
||||||
// Connect text changed signal to track modifications
|
// Connect text changed signal to track modifications
|
||||||
connect(&codeEditor, &QTextEdit::textChanged, this, [this]() {
|
connect(&codeEditor, &QTextEdit::textChanged, this, [this]() {
|
||||||
isFileModified = true;
|
isFileModified = true;
|
||||||
updateTitle();
|
updateStatus();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Connect Run and Stop buttons
|
||||||
|
connect(ui->pushButtonRun, &QPushButton::clicked, this, &MainWindow::runScript);
|
||||||
|
connect(ui->pushButtonStop, &QPushButton::clicked, this, &MainWindow::stopScript);
|
||||||
|
connect(&pythonRunner, &PythonRunner::scriptFinished, this, &MainWindow::stopScript);
|
||||||
|
enumerateDevices();
|
||||||
|
generateExample();
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
|
@ -51,16 +62,40 @@ MainWindow::~MainWindow()
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::updateTitle()
|
void MainWindow::readState()
|
||||||
|
{
|
||||||
|
for (auto& multiplexer : multiplexers) {
|
||||||
|
channel_t channelstate = eismultiplexer_get_connected(multiplexer.second.get());
|
||||||
|
for (auto& channel : channels) {
|
||||||
|
for(size_t i = 0; i < 16; ++i) {
|
||||||
|
channel_t mask = static_cast<channel_t>(1 << i);
|
||||||
|
channel->onOtherChannelStateChanged(multiplexer.first, i, channelstate & mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int triggerCount = eismultiplexer_get_trigger_count(multiplexer.second.get());
|
||||||
|
for(int i = 0; i < triggerCount; ++i) {
|
||||||
|
bool level;
|
||||||
|
trigger_state_t state;
|
||||||
|
int ret = eismultiplexer_get_trigger_state(multiplexer.second.get(), i, &state, &level);
|
||||||
|
if (ret < 0) {
|
||||||
|
for (auto& trigger : triggers)
|
||||||
|
trigger->updateSate(multiplexer.first, i, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::updateStatus()
|
||||||
{
|
{
|
||||||
QString windowTitle = "EisMultiplexer-Qt";
|
|
||||||
if (!currentFilePath.isEmpty()) {
|
if (!currentFilePath.isEmpty()) {
|
||||||
windowTitle = QString("%1 - %2").arg(currentFilePath);
|
QString status = "EisMultiplexer-Qt";
|
||||||
}
|
status = QString("%1").arg(currentFilePath);
|
||||||
if (isFileModified) {
|
if (isFileModified) {
|
||||||
windowTitle += "[*]";
|
status += " - [Unsaved Changes]";
|
||||||
|
}
|
||||||
|
ui->statusbar->showMessage(status);
|
||||||
}
|
}
|
||||||
setWindowTitle(windowTitle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onActionOpenTriggered()
|
void MainWindow::onActionOpenTriggered()
|
||||||
|
@ -86,7 +121,7 @@ void MainWindow::onActionOpenTriggered()
|
||||||
codeEditor.setPlainText(content);
|
codeEditor.setPlainText(content);
|
||||||
currentFilePath = filePath;
|
currentFilePath = filePath;
|
||||||
isFileModified = false;
|
isFileModified = false;
|
||||||
updateTitle();
|
updateStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onActionSaveTriggered()
|
void MainWindow::onActionSaveTriggered()
|
||||||
|
@ -107,7 +142,7 @@ void MainWindow::onActionSaveTriggered()
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
isFileModified = false;
|
isFileModified = false;
|
||||||
updateTitle();
|
updateStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onActionSaveAsTriggered()
|
void MainWindow::onActionSaveAsTriggered()
|
||||||
|
@ -120,13 +155,6 @@ void MainWindow::onActionSaveAsTriggered()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the file has a .py extension if it's a Python file
|
|
||||||
if (filePath.endsWith(".py", Qt::CaseInsensitive)) {
|
|
||||||
// File already has .py extension
|
|
||||||
} else {
|
|
||||||
filePath += ".py";
|
|
||||||
}
|
|
||||||
|
|
||||||
QFile file(filePath);
|
QFile file(filePath);
|
||||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||||
QMessageBox::warning(this, tr("Error"), tr("Could not save file: %1").arg(filePath));
|
QMessageBox::warning(this, tr("Error"), tr("Could not save file: %1").arg(filePath));
|
||||||
|
@ -139,13 +167,16 @@ void MainWindow::onActionSaveAsTriggered()
|
||||||
|
|
||||||
currentFilePath = filePath;
|
currentFilePath = filePath;
|
||||||
isFileModified = false;
|
isFileModified = false;
|
||||||
updateTitle();
|
updateStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::enumerateDevices()
|
void MainWindow::enumerateDevices()
|
||||||
{
|
{
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
uint16_t* serials = eismultiplexer_list_available_devices(&count);
|
uint16_t* serials = eismultiplexer_list_available_devices(&count);
|
||||||
|
if (ui->scrollArea->layout())
|
||||||
|
delete ui->scrollArea->layout();
|
||||||
|
QVBoxLayout* channelLayout = new QVBoxLayout(ui->scrollArea);
|
||||||
|
|
||||||
if (!serials || count == 0)
|
if (!serials || count == 0)
|
||||||
{
|
{
|
||||||
|
@ -164,6 +195,7 @@ void MainWindow::enumerateDevices()
|
||||||
int ret = eismultiplexer_connect(multiplexer.get(), serial);
|
int ret = eismultiplexer_connect(multiplexer.get(), serial);
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
{
|
{
|
||||||
|
multiplexers.push_back({serial, multiplexer});
|
||||||
uint16_t channelCount = 0;
|
uint16_t channelCount = 0;
|
||||||
qDebug()<<"Adding channels from device "<<serial;
|
qDebug()<<"Adding channels from device "<<serial;
|
||||||
if (eismultiplexer_get_channel_count(multiplexer.get(), &channelCount) >= 0)
|
if (eismultiplexer_get_channel_count(multiplexer.get(), &channelCount) >= 0)
|
||||||
|
@ -173,7 +205,7 @@ void MainWindow::enumerateDevices()
|
||||||
std::shared_ptr<ChannelWidget> widget(new ChannelWidget(serial, channel, multiplexer));
|
std::shared_ptr<ChannelWidget> widget(new ChannelWidget(serial, channel, multiplexer));
|
||||||
qDebug()<<"Added widget from device "<<serial<<" channel "<<channel;
|
qDebug()<<"Added widget from device "<<serial<<" channel "<<channel;
|
||||||
channels.push_back(widget);
|
channels.push_back(widget);
|
||||||
ui->channelLayout->addWidget(widget.get());
|
channelLayout->addWidget(widget.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,7 +219,7 @@ void MainWindow::enumerateDevices()
|
||||||
std::shared_ptr<TriggerWidget> triggerWidget(new TriggerWidget(serial, trigger, multiplexer));
|
std::shared_ptr<TriggerWidget> triggerWidget(new TriggerWidget(serial, trigger, multiplexer));
|
||||||
qDebug()<<"Added trigger widget from device "<<serial<<" trigger "<<trigger;
|
qDebug()<<"Added trigger widget from device "<<serial<<" trigger "<<trigger;
|
||||||
triggers.push_back(triggerWidget);
|
triggers.push_back(triggerWidget);
|
||||||
ui->channelLayout->addWidget(triggerWidget.get());
|
channelLayout->addWidget(triggerWidget.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,7 +230,8 @@ void MainWindow::enumerateDevices()
|
||||||
qWarning()<<"Failed to connect to device with serial"<<serial<<"eismultiplexer_connect returned"<<ret;
|
qWarning()<<"Failed to connect to device with serial"<<serial<<"eismultiplexer_connect returned"<<ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ui->channelLayout->addStretch();
|
|
||||||
|
channelLayout->addStretch();
|
||||||
|
|
||||||
// Second pass: populate gang combos and connect signals for channels
|
// Second pass: populate gang combos and connect signals for channels
|
||||||
for (const auto& widget : channels) {
|
for (const auto& widget : channels) {
|
||||||
|
@ -222,20 +255,79 @@ void MainWindow::enumerateDevices()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readState();
|
||||||
|
|
||||||
ui->statusbar->showMessage("Ready");
|
ui->statusbar->showMessage("Ready");
|
||||||
|
|
||||||
free(serials);
|
free(serials);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::runScript() {
|
||||||
|
QString scriptContent = codeEditor.toPlainText();
|
||||||
|
pythonRunner.runScript(scriptContent);
|
||||||
|
ui->pushButtonRun->setEnabled(false);
|
||||||
|
ui->pushButtonStop->setEnabled(true);
|
||||||
|
codeEditor.setEnabled(false);
|
||||||
|
ui->scrollArea->setEnabled(false);
|
||||||
|
disconnectDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::stopScript() {
|
||||||
|
pythonRunner.stopScript();
|
||||||
|
channels.clear();
|
||||||
|
triggers.clear();
|
||||||
|
ui->pushButtonRun->setEnabled(true);
|
||||||
|
ui->pushButtonStop->setEnabled(false);
|
||||||
|
codeEditor.setEnabled(true);
|
||||||
|
ui->scrollArea->setEnabled(true);
|
||||||
|
enumerateDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::disconnectDevices()
|
||||||
|
{
|
||||||
|
for(auto& multiplexer : multiplexers)
|
||||||
|
eismultiplexer_disconnect(multiplexer.second.get());
|
||||||
|
multiplexers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::generateExample()
|
void MainWindow::generateExample()
|
||||||
{
|
{
|
||||||
QString example =
|
QString example =
|
||||||
"import eismultiplexer\n\n"
|
"# This is an example script to show you how\n# to drive eismultiplexer using the python api\n"
|
||||||
"from time import sleep";
|
"import eismultiplexer as multi\n"
|
||||||
|
"from time import sleep\n\n"
|
||||||
|
"# First initalize the device(s)\n";
|
||||||
|
|
||||||
for (const auto& channel : channels)
|
std::set<uint16_t> serials;
|
||||||
|
for (size_t i = 0; i < channels.size(); ++i)
|
||||||
|
serials.insert(channels[i]->getDeviceSerial());
|
||||||
|
|
||||||
|
size_t i = 0;
|
||||||
|
for (uint16_t serial : serials)
|
||||||
{
|
{
|
||||||
example.append(QString("eismultiplexer"));
|
example.append(QString("multiplexer_") + QString::number(i) + " = multi.Multiplexer(serial=" + QString::number(serial) + ")\n");
|
||||||
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
example.append("\nprint('\\nListing the nummber of channels per unit')\n");
|
||||||
|
for (size_t i = 0; i < serials.size(); ++i)
|
||||||
|
{
|
||||||
|
QString printLine = "print(f'Found unit with serial number {" + QString::number(channels[i]->getDeviceSerial()) + "} and {multiplexer_" + QString::number(i) + ".getChannelCount()} channels')\n";
|
||||||
|
example.append(printLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
example.append("\nprint('Connecting the first and second channel on the first unit')\n");
|
||||||
|
example.append("multiplexer_0.connectChannel(multi.Channel.A)\n");
|
||||||
|
example.append("multiplexer_0.connectChannel(multi.Channel.B)\n\n");
|
||||||
|
example.append("print('Waiting for half a second for something to happen')\n");
|
||||||
|
example.append("sleep(0.5)\n\n");
|
||||||
|
example.append("print('Disconnect first channel')\n");
|
||||||
|
example.append("multiplexer_0.disconnectChannel(multi.Channel.A)\n\n");
|
||||||
|
example.append("print('Waiting up to 5000 milliseconds for a trigger')\n");
|
||||||
|
example.append("multiplexer_0.setTriggerState(0, multi.TriggerState.INPUT)\n");
|
||||||
|
example.append("multiplexer_0.waitTrigger(0, multi.TriggerState.HIGHLEVEL, 5000)\n\n");
|
||||||
|
example.append("print('Disconnecting all channels')\n");
|
||||||
|
example.append("multiplexer_0.clear()\n");
|
||||||
|
codeEditor.setText(example);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
17
mainwindow.h
17
mainwindow.h
|
@ -1,16 +1,16 @@
|
||||||
|
|
||||||
|
|
||||||
#ifndef MAINWINDOW_H
|
#ifndef MAINWINDOW_H
|
||||||
#define MAINWINDOW_H
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
|
#include <eismultiplexer.h>
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <QCodeEditor>
|
#include <QCodeEditor>
|
||||||
#include <QPythonCompleter>
|
#include <QPythonCompleter>
|
||||||
#include <QPythonHighlighter>
|
#include <QPythonHighlighter>
|
||||||
|
#include <QProgressBar>
|
||||||
#include "channelwidget.h"
|
#include "channelwidget.h"
|
||||||
#include "triggerwidget.h"
|
#include "triggerwidget.h"
|
||||||
|
#include "pythonrunner.h"
|
||||||
|
|
||||||
namespace Ui
|
namespace Ui
|
||||||
{
|
{
|
||||||
|
@ -20,12 +20,15 @@ class MainWindow;
|
||||||
class MainWindow : public QMainWindow
|
class MainWindow : public QMainWindow
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
std::vector<std::shared_ptr<ChannelWidget>> channels;
|
|
||||||
std::vector<std::shared_ptr<TriggerWidget>> triggers;
|
std::vector<std::shared_ptr<TriggerWidget>> triggers;
|
||||||
|
std::vector<std::shared_ptr<ChannelWidget>> channels;
|
||||||
|
std::vector<std::pair<uint16_t, std::shared_ptr<struct eismultiplexer>>> multiplexers;
|
||||||
Ui::MainWindow *ui;
|
Ui::MainWindow *ui;
|
||||||
QCodeEditor codeEditor;
|
QCodeEditor codeEditor;
|
||||||
QPythonHighlighter highligter;
|
QPythonHighlighter highligter;
|
||||||
QPythonCompleter completer;
|
QPythonCompleter completer;
|
||||||
|
QTextEdit pythonOutput;
|
||||||
|
PythonRunner pythonRunner;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void channelStateChanged(uint16_t device, uint16_t channel);
|
void channelStateChanged(uint16_t device, uint16_t channel);
|
||||||
|
@ -38,11 +41,15 @@ private slots:
|
||||||
void onActionOpenTriggered();
|
void onActionOpenTriggered();
|
||||||
void onActionSaveTriggered();
|
void onActionSaveTriggered();
|
||||||
void onActionSaveAsTriggered();
|
void onActionSaveAsTriggered();
|
||||||
|
void runScript();
|
||||||
|
void stopScript();
|
||||||
|
void readState();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void enumerateDevices();
|
void enumerateDevices();
|
||||||
void generateExample();
|
void generateExample();
|
||||||
void updateTitle();
|
void updateStatus();
|
||||||
|
void disconnectDevices();
|
||||||
QString currentFilePath;
|
QString currentFilePath;
|
||||||
bool isFileModified;
|
bool isFileModified;
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,9 +16,14 @@
|
||||||
<widget class="QWidget" name="centralwidget">
|
<widget class="QWidget" name="centralwidget">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
|
<widget class="QSplitter" name="splitter">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="">
|
||||||
<layout class="QVBoxLayout" name="codeLayout">
|
<layout class="QVBoxLayout" name="codeLayout">
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
<number>10</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
@ -48,9 +53,14 @@
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</widget>
|
||||||
<item>
|
|
||||||
<widget class="QScrollArea" name="scrollArea">
|
<widget class="QScrollArea" name="scrollArea">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>400</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
<property name="widgetResizable">
|
<property name="widgetResizable">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
|
@ -59,15 +69,12 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>595</width>
|
<width>504</width>
|
||||||
<height>537</height>
|
<height>537</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
<layout class="QVBoxLayout" name="verticalLayout_3"/>
|
||||||
<item>
|
</widget>
|
||||||
<layout class="QVBoxLayout" name="channelLayout"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
#include "multiplexer.h"
|
|
||||||
|
|
||||||
Multiplexer::Multiplexer(QObject *parent)
|
|
||||||
: QObject{parent}
|
|
||||||
{}
|
|
||||||
|
|
||||||
Multiplexer::~Multiplexer()
|
|
||||||
{
|
|
||||||
for(auto& multiplexer : multiplexers)
|
|
||||||
eismultiplexer_disconnect(multiplexer.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Multiplexer::probe()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
#ifndef MULTIPLEXER_H
|
|
||||||
#define MULTIPLEXER_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include "eismultiplexer.h"
|
|
||||||
|
|
||||||
class Multiplexer : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
std::vector<std::shared_ptr<struct eismultiplexer>> multiplexers;
|
|
||||||
std::vector<channel_t> channelStates;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit Multiplexer(QObject *parent = nullptr);
|
|
||||||
~Multiplexer();
|
|
||||||
void probe();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void foundDevice(std::shared_ptr<struct eismultiplexer>);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // MULTIPLEXER_H
|
|
56
pythonrunner.cpp
Normal file
56
pythonrunner.cpp
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#include "pythonrunner.h"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
PythonRunner::PythonRunner(QTextEdit* outputWidget, QObject* parent)
|
||||||
|
: QObject(parent), m_outputWidget(outputWidget), m_process(nullptr) {
|
||||||
|
m_process = new QProcess(this);
|
||||||
|
connect(m_process, &QProcess::readyReadStandardOutput, this, &PythonRunner::onOutputAvailable);
|
||||||
|
connect(m_process, &QProcess::readyReadStandardError, this, &PythonRunner::onErrorAvailable);
|
||||||
|
connect(m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &PythonRunner::onProcessFinished);
|
||||||
|
}
|
||||||
|
|
||||||
|
PythonRunner::~PythonRunner() {
|
||||||
|
if (m_process) {
|
||||||
|
m_process->terminate();
|
||||||
|
m_process->waitForFinished(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PythonRunner::runScript(const QString& scriptContent) {
|
||||||
|
if (m_process->state() == QProcess::Running) {
|
||||||
|
m_process->terminate();
|
||||||
|
m_process->waitForFinished(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_outputWidget->clear();
|
||||||
|
m_process->start("python3", QStringList() << "-u" << "-c" << scriptContent);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PythonRunner::stopScript() {
|
||||||
|
if (m_process && m_process->state() == QProcess::Running) {
|
||||||
|
m_process->terminate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PythonRunner::onOutputAvailable() {
|
||||||
|
QByteArray output = m_process->readAllStandardOutput();
|
||||||
|
m_outputWidget->append(output);
|
||||||
|
m_outputWidget->moveCursor(QTextCursor::End);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PythonRunner::onErrorAvailable() {
|
||||||
|
QByteArray error = m_process->readAllStandardError();
|
||||||
|
m_outputWidget->append(error);
|
||||||
|
m_outputWidget->moveCursor(QTextCursor::End);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PythonRunner::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) {
|
||||||
|
if (exitStatus == QProcess::NormalExit && exitCode != 0) {
|
||||||
|
m_outputWidget->append(QString("Process exited with code %1\n").arg(exitCode));
|
||||||
|
} else if (exitStatus == QProcess::CrashExit) {
|
||||||
|
m_outputWidget->append("Python was stopped\n");
|
||||||
|
}
|
||||||
|
emit scriptFinished(exitCode);
|
||||||
|
}
|
31
pythonrunner.h
Normal file
31
pythonrunner.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QTextEdit>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTemporaryDir>
|
||||||
|
|
||||||
|
class PythonRunner : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
PythonRunner(QTextEdit* outputWidget, QObject* parent = nullptr);
|
||||||
|
~PythonRunner();
|
||||||
|
|
||||||
|
bool runScript(const QString& scriptContent);
|
||||||
|
void stopScript();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onOutputAvailable();
|
||||||
|
void onErrorAvailable();
|
||||||
|
void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void scriptFinished(int code);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QTextEdit* m_outputWidget;
|
||||||
|
QProcess* m_process;
|
||||||
|
bool ready;
|
||||||
|
QTemporaryDir dir;
|
||||||
|
};
|
|
@ -70,9 +70,46 @@ void TriggerWidget::onLevelToggled(bool checked)
|
||||||
updateTriggerState();
|
updateTriggerState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TriggerWidget::updateSate(uint16_t serial, uint16_t trigger, trigger_state_t state)
|
||||||
|
{
|
||||||
|
if (serial == deviceSerial && trigger == triggerNumber) {
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case TRIGGER_INPUT:
|
||||||
|
levelCheckbox.blockSignals(true);
|
||||||
|
levelCheckbox.setChecked(false);
|
||||||
|
levelCheckbox.setEnabled(false);
|
||||||
|
levelCheckbox.blockSignals(false);
|
||||||
|
inputCheckbox.blockSignals(true);
|
||||||
|
inputCheckbox.setChecked(true);
|
||||||
|
inputCheckbox.blockSignals(false);
|
||||||
|
break;
|
||||||
|
case TRIGGER_HIGH:
|
||||||
|
inputCheckbox.blockSignals(true);
|
||||||
|
inputCheckbox.setChecked(false);
|
||||||
|
inputCheckbox.blockSignals(false);
|
||||||
|
levelCheckbox.blockSignals(true);
|
||||||
|
levelCheckbox.setChecked(true);
|
||||||
|
levelCheckbox.setEnabled(true);
|
||||||
|
levelCheckbox.blockSignals(false);
|
||||||
|
break;
|
||||||
|
case TRIGGER_LOW:
|
||||||
|
inputCheckbox.blockSignals(true);
|
||||||
|
inputCheckbox.setChecked(false);
|
||||||
|
inputCheckbox.blockSignals(false);
|
||||||
|
levelCheckbox.blockSignals(true);
|
||||||
|
levelCheckbox.setChecked(true);
|
||||||
|
levelCheckbox.setEnabled(false);
|
||||||
|
levelCheckbox.blockSignals(false);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TriggerWidget::updateTriggerState()
|
void TriggerWidget::updateTriggerState()
|
||||||
{
|
{
|
||||||
trigger_state_t state;
|
trigger_state_t state = TRIGGER_INPUT;
|
||||||
if(inputCheckbox.isChecked())
|
if(inputCheckbox.isChecked())
|
||||||
{
|
{
|
||||||
levelCheckbox.blockSignals(true);
|
levelCheckbox.blockSignals(true);
|
||||||
|
|
|
@ -22,6 +22,7 @@ public:
|
||||||
|
|
||||||
uint16_t getDeviceSerial() const;
|
uint16_t getDeviceSerial() const;
|
||||||
uint16_t getTriggerNumber() const;
|
uint16_t getTriggerNumber() const;
|
||||||
|
void updateSate(uint16_t serial, uint16_t trigger, trigger_state_t state);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onInputToggled(bool checked);
|
void onInputToggled(bool checked);
|
||||||
|
|
Loading…
Reference in a new issue