diff --git a/.github/workflows/build-osx.yml b/.github/workflows/build-osx.yml
index eaec902c0..8c97a7d7d 100644
--- a/.github/workflows/build-osx.yml
+++ b/.github/workflows/build-osx.yml
@@ -267,7 +267,7 @@ jobs:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_APPSPEC_PASS: ${{ secrets.APPLE_APPSPEC_PASS }}
APPLE_DIST_STORE: true
- APPLE_UPLOAD_STORE: startsWith(github.ref, 'refs/tags/1.') || startsWith(github.ref, 'refs/tags/2.')
+ APPLE_UPLOAD_STORE: ${{ startsWith(github.ref, 'refs/tags/1.') || startsWith(github.ref, 'refs/tags/2.') }}
run: make dist
# Upload
diff --git a/.github/workflows/build-win.yml b/.github/workflows/build-win.yml
index ea51cdd7a..30535aa54 100644
--- a/.github/workflows/build-win.yml
+++ b/.github/workflows/build-win.yml
@@ -142,6 +142,10 @@ jobs:
env:
BRANDING: ${{ matrix.branding }}
steps:
+
+ # Install WIX
+ - name: Install WIX
+ run: dotnet tool install --global wix --version 4.0.1
# Checkout
- name: Checkout
@@ -213,13 +217,13 @@ jobs:
run: |
fsutil volume diskfree d:
- # Upload ZIP
- - name: Upload ZIP
+ # Upload MSI
+ - name: Upload MSI
uses: actions/upload-artifact@v3
with:
- name: Packages ZIP (${{ matrix.os }}-${{ matrix.branding }})
+ name: Packages MSI (${{ matrix.os }}-${{ matrix.branding }})
path: |
- ./dist/win-10/*.zip
+ ./dist/win-10/*.msi
# Upload Packages
- name: Upload App
@@ -236,4 +240,4 @@ jobs:
if: startsWith(github.ref, 'refs/tags/') && (matrix.branding == 'neuromore')
with:
token: ${{ secrets.PAT_GITHUB_ACTIONS }}
- files: ./dist/win-10/*.zip
+ files: ./dist/win-10/*.msi
diff --git a/build/make/Engine.mk b/build/make/Engine.mk
index d7fcb5bbf..ca19b9e71 100644
--- a/build/make/Engine.mk
+++ b/build/make/Engine.mk
@@ -410,9 +410,9 @@ build: pch $(OBJS) $(OBJSPRIV)
$(AR) $(ARFLAGS) $(LIBDIR)/$(NAME)$(SUFFIX)$(EXTLIB) $(OBJS) $(OBJSPRIV)
clean:
- $(call deletefiles,$(OBJDIR),*.o)
- $(call deletefiles,$(OBJDIR),*.op)
- $(call deletefiles,$(OBJDIR),$(PCH).pch)
- $(call deletefiles,$(LIBDIR),$(NAME)$(SUFFIX)$(EXTLIB))
+ -$(call deletefiles,$(OBJDIR),*.o)
+ -$(call deletefiles,$(OBJDIR),*.op)
+ -$(call deletefiles,$(OBJDIR),$(PCH).pch)
+ -$(call deletefiles,$(LIBDIR),$(NAME)$(SUFFIX)$(EXTLIB))
.DEFAULT_GOAL := build
diff --git a/build/make/EngineJNI.mk b/build/make/EngineJNI.mk
index c99201075..5b800e53b 100644
--- a/build/make/EngineJNI.mk
+++ b/build/make/EngineJNI.mk
@@ -192,8 +192,8 @@ build: $(OBJDIR)/neuromoreEngine.o $(OBJS) $(JOBJS)
$(JAR) cf $(LIBDIR)/$(NAME)$(SUFFIX).jar -C $(OBJDIR)/Java .
clean:
- $(call deletefiles,$(OBJDIR),*.o)
- $(call deletefiles,$(OBJDIR),*.class)
- $(call deletefiles,$(LIBDIR),$(NAME)$(SUFFIX)$(EXTDLL))
- $(call deletefiles,$(LIBDIR),$(NAME)$(SUFFIX)$(EXTPDB))
- $(call deletefiles,$(LIBDIR),$(NAME)$(SUFFIX).jar)
+ -$(call deletefiles,$(OBJDIR),*.o)
+ -$(call deletefiles,$(OBJDIR),*.class)
+ -$(call deletefiles,$(LIBDIR),$(NAME)$(SUFFIX)$(EXTDLL))
+ -$(call deletefiles,$(LIBDIR),$(NAME)$(SUFFIX)$(EXTPDB))
+ -$(call deletefiles,$(LIBDIR),$(NAME)$(SUFFIX).jar)
diff --git a/build/make/EngineLIB.mk b/build/make/EngineLIB.mk
index ae799986a..4609ed249 100644
--- a/build/make/EngineLIB.mk
+++ b/build/make/EngineLIB.mk
@@ -175,6 +175,6 @@ build: $(OBJDIR)/neuromoreEngine.o $(OBJS)
$(LINK) $(LINKFLAGS) $(LINKPATH) $(OBJDIR)/neuromoreEngine.o $(OBJS) $(LINKLIBS) -o $(LIBDIR)/$(NAME)$(SUFFIX)$(EXTDLL)
clean:
- $(call deletefiles,$(OBJDIR),*.o)
- $(call deletefiles,$(LIBDIR),$(NAME)$(SUFFIX)$(EXTDLL))
- $(call deletefiles,$(LIBDIR),$(NAME)$(SUFFIX)$(EXTPDB))
+ -$(call deletefiles,$(OBJDIR),*.o)
+ -$(call deletefiles,$(LIBDIR),$(NAME)$(SUFFIX)$(EXTDLL))
+ -$(call deletefiles,$(LIBDIR),$(NAME)$(SUFFIX)$(EXTPDB))
diff --git a/build/make/QtBase.mk b/build/make/QtBase.mk
index 8ddfef5cd..ef649c20a 100644
--- a/build/make/QtBase.mk
+++ b/build/make/QtBase.mk
@@ -538,19 +538,19 @@ build: pch $(PRES) $(OBLS)
$(AR) $(ARFLAGS) $(LIBDIR)/$(NAME)$(SUFFIX)$(EXTLIB) $(OBLS)
clean:
- $(call deletefiles,$(MOCDIR),*.cpp)
- $(call deletefiles,$(MOCDIR),*.moc)
- $(call deletefiles,$(MOCDIR),*.mocmm)
- $(call deletefiles,$(RCCDIR),*.cpp)
- $(call deletefiles,$(RCCDIR),*.cppp)
- $(call deletefiles,$(UICDIR),*.h)
- $(call deletefiles,$(OBJDIR),$(PCH).pch)
- $(call deletefiles,$(OBJDIR),*.o)
- $(call deletefiles,$(OBJDIR),*.op)
- $(call deletefiles,$(OBJDIR),*.oc)
- $(call deletefiles,$(OBJDIR),*.ocp)
- $(call deletefiles,$(OBJDIR),*.omm)
- $(call deletefiles,$(OBJDIR),*.ommp)
- $(call deletefiles,$(OBJDIR),*.omoc)
- $(call deletefiles,$(OBJDIR),*.orcc)
- $(call deletefiles,$(LIBDIR),$(NAME)$(SUFFIX)$(EXTLIB))
+ -$(call deletefiles,$(MOCDIR),*.cpp)
+ -$(call deletefiles,$(MOCDIR),*.moc)
+ -$(call deletefiles,$(MOCDIR),*.mocmm)
+ -$(call deletefiles,$(RCCDIR),*.cpp)
+ -$(call deletefiles,$(RCCDIR),*.cppp)
+ -$(call deletefiles,$(UICDIR),*.h)
+ -$(call deletefiles,$(OBJDIR),$(PCH).pch)
+ -$(call deletefiles,$(OBJDIR),*.o)
+ -$(call deletefiles,$(OBJDIR),*.op)
+ -$(call deletefiles,$(OBJDIR),*.oc)
+ -$(call deletefiles,$(OBJDIR),*.ocp)
+ -$(call deletefiles,$(OBJDIR),*.omm)
+ -$(call deletefiles,$(OBJDIR),*.ommp)
+ -$(call deletefiles,$(OBJDIR),*.omoc)
+ -$(call deletefiles,$(OBJDIR),*.orcc)
+ -$(call deletefiles,$(LIBDIR),$(NAME)$(SUFFIX)$(EXTLIB))
diff --git a/build/make/Studio.mk b/build/make/Studio.mk
index 66095933d..a21bd3eb6 100644
--- a/build/make/Studio.mk
+++ b/build/make/Studio.mk
@@ -393,6 +393,18 @@ DEFINES := $(DEFINES) \
OBJSPRIV := $(OBJSPRIV)
RCCHPRIV := $(RCCHPRIV)
RCCO := $(RCCO)
+ifeq ($(TARGET_ARCH),x86)
+APPGUIDPLAT = ba60729d-dace-4746-9c78-829d9b943f67
+endif
+ifeq ($(TARGET_ARCH),x64)
+APPGUIDPLAT = 2bcdb126-dc80-42ba-985e-48836ce23193
+endif
+ifeq ($(TARGET_ARCH),arm)
+APPGUIDPLAT = 9e9ffa9f-f21a-4125-bb87-bff0d2866af7
+endif
+ifeq ($(TARGET_ARCH),arm64)
+APPGUIDPLAT = badb6403-11c5-4dd4-8964-9fda8a470314
+endif
else
include ../../priv/build/make/StudioBranding.mk
endif
@@ -796,27 +808,27 @@ build: pch $(PRES) $(OBLS) $(RESO)
@-$(call copyfiles,$(BINDIRDEP)/*$(EXTDLL),$(BINDIR))
clean:
- $(call deletefiles,$(MOCDIR),*.cpp)
- $(call deletefiles,$(MOCDIR),*.moc)
- $(call deletefiles,$(MOCDIR),*.mocmm)
- $(call deletefiles,$(RCCDIR),*.cpp)
- $(call deletefiles,$(RCCDIR),*.cppp)
- $(call deletefiles,$(UICDIR),*.h)
- $(call deletefiles,$(OBJDIR),$(PCH).pch)
- $(call deletefiles,$(OBJDIR),*.o)
- $(call deletefiles,$(OBJDIR),*.op)
- $(call deletefiles,$(OBJDIR),*.oc)
- $(call deletefiles,$(OBJDIR),*.ocp)
- $(call deletefiles,$(OBJDIR),*.omm)
- $(call deletefiles,$(OBJDIR),*.ommp)
- $(call deletefiles,$(OBJDIR),*.omoc)
- $(call deletefiles,$(OBJDIR),*.orcc)
- $(call deletefiles,$(OBJDIR),*.res)
- $(call deletefiles,$(LIBDIR),$(NAME)$(SUFFIX)$(EXTLIB))
- $(call deletefiles,$(BINDIR),$(NAME)$(SUFFIX)$(EXTLIB))
- $(call deletefiles,$(BINDIR),$(NAME)$(SUFFIX)$(EXTBIN))
- $(call deletefiles,$(BINDIR),$(NAME)$(SUFFIX)$(EXTPDB))
- $(call deletefiles,$(BINDIR),*.$(EXTDLL))
+ -$(call deletefiles,$(MOCDIR),*.cpp)
+ -$(call deletefiles,$(MOCDIR),*.moc)
+ -$(call deletefiles,$(MOCDIR),*.mocmm)
+ -$(call deletefiles,$(RCCDIR),*.cpp)
+ -$(call deletefiles,$(RCCDIR),*.cppp)
+ -$(call deletefiles,$(UICDIR),*.h)
+ -$(call deletefiles,$(OBJDIR),$(PCH).pch)
+ -$(call deletefiles,$(OBJDIR),*.o)
+ -$(call deletefiles,$(OBJDIR),*.op)
+ -$(call deletefiles,$(OBJDIR),*.oc)
+ -$(call deletefiles,$(OBJDIR),*.ocp)
+ -$(call deletefiles,$(OBJDIR),*.omm)
+ -$(call deletefiles,$(OBJDIR),*.ommp)
+ -$(call deletefiles,$(OBJDIR),*.omoc)
+ -$(call deletefiles,$(OBJDIR),*.orcc)
+ -$(call deletefiles,$(OBJDIR),*.res)
+ -$(call deletefiles,$(LIBDIR),$(NAME)$(SUFFIX)$(EXTLIB))
+ -$(call deletefiles,$(BINDIR),$(NAME)$(SUFFIX)$(EXTLIB))
+ -$(call deletefiles,$(BINDIR),$(NAME)$(SUFFIX)$(EXTBIN))
+ -$(call deletefiles,$(BINDIR),$(NAME)$(SUFFIX)$(EXTPDB))
+ -$(call deletefiles,$(BINDIR),*.$(EXTDLL))
################################################################################################
diff --git a/deps/build/make/platforms/dist.mk b/deps/build/make/platforms/dist.mk
index 90ff888ce..2344d8a46 100644
--- a/deps/build/make/platforms/dist.mk
+++ b/deps/build/make/platforms/dist.mk
@@ -65,18 +65,21 @@ dist-prep:
chcp 1252
echo [PUB] $(PUBLISHER)
echo [PFX] $(SIGN_PFX_FILE)
+ echo [RMD] $(DISTDIR)/$(NAME)
+ -$(call rmdir,$(DISTDIR)/$(NAME))
+ -$(call deletefiles,$(DISTDIR),$(NAME)*.zip)
+ -$(call deletefiles,$(DISTDIR),$(NAME)*.msi)
+ -$(call deletefiles,$(DISTDIR),$(NAME)*.wixpdb)
+ -$(call deletefiles,$(DISTDIR),$(NAME)*.appx)
+ -$(call deletefiles,$(DISTDIR),$(NAME)*.appxbundle)
+ -$(call deletefiles,$(DISTDIR),$(NAME)*.appxupload)
echo [MKD] $(DISTDIR)/$(NAME)
- $(call rmdir,$(DISTDIR)/$(NAME))
- $(call deletefiles,$(DISTDIR),$(NAME)*.zip)
- $(call deletefiles,$(DISTDIR),$(NAME)*.appx)
- $(call deletefiles,$(DISTDIR),$(NAME)*.appxbundle)
- $(call deletefiles,$(DISTDIR),$(NAME)*.appxupload)
- $(call mkdir,$(DISTDIR)/$(NAME))
- $(call mkdir,$(DISTDIR)/$(NAME)/resources)
- $(call mkdir,$(DISTDIR)/$(NAME)/upload)
- $(call mkdir,$(DISTDIR)/$(NAME)/x64)
- $(call mkdir,$(DISTDIR)/$(NAME)/x86)
- $(call mkdir,$(DISTDIR)/$(NAME)/arm64)
+ -$(call mkdir,$(DISTDIR)/$(NAME))
+ -$(call mkdir,$(DISTDIR)/$(NAME)/resources)
+ -$(call mkdir,$(DISTDIR)/$(NAME)/upload)
+ -$(call mkdir,$(DISTDIR)/$(NAME)/x64)
+ -$(call mkdir,$(DISTDIR)/$(NAME)/x86)
+ -$(call mkdir,$(DISTDIR)/$(NAME)/arm64)
$(call copyfiles,$(DISTDIR)/$(NAME).appxmanifest,$(DISTDIR)/$(NAME)/AppxManifest.xml)
$(call replace,$(DISTDIR)/$(NAME)/AppxManifest.xml,{PUBLISHER},$(PUBLISHER),$(DISTDIR)/$(NAME)/AppxManifest.xml)
$(call replace,$(DISTDIR)/$(NAME)/AppxManifest.xml,{PUBLISHERID},$(PUBLISHERID),$(DISTDIR)/$(NAME)/AppxManifest.xml)
@@ -91,37 +94,38 @@ dist-prep:
$(call copyfiles,$(APPICON)-44x44.png,$(DISTDIR)/$(NAME)/resources/app-44x44.png)
$(call copyfiles,$(APPICON)-50x50.png,$(DISTDIR)/$(NAME)/resources/app-50x50.png)
$(call copyfiles,$(APPICON)-150x150.png,$(DISTDIR)/$(NAME)/resources/app-150x150.png)
+ $(call copyfiles,$(APPICON).ico,$(DISTDIR)/$(NAME)/resources/app.ico)
dist-vis-%: dist-prep
echo [VIS] $*
$(call mkdir,$(DISTDIR)/$(NAME)/x64/Visualizations/$*/)
$(call copyfiles,$(DISTDIR)/../../visualizations/$*/Info.json,$(DISTDIR)/$(NAME)/x64/Visualizations/$*/)
$(call copyfiles,$(DISTDIR)/../../visualizations/$*/Thumbnail.png,$(DISTDIR)/$(NAME)/x64/Visualizations/$*/)
- $(call copyfilesrecursive,$(DISTDIR)/../../visualizations/$*/win-x64/*,$(DISTDIR)/$(NAME)/x64/Visualizations/$*/)
+ -$(call copyfilesrecursive,$(DISTDIR)/../../visualizations/$*/win-x64/*,$(DISTDIR)/$(NAME)/x64/Visualizations/$*/)
$(call sleep,3)
ifeq ($(SIGN_PFX_PASS),)
- $(call sign,$(DISTDIR)/$(NAME)/x64/Visualizations/$*/$*.exe,$(SIGN_PFX_FILE))
+ -$(call sign,$(DISTDIR)/$(NAME)/x64/Visualizations/$*/$*.exe,$(SIGN_PFX_FILE))
else
- $(call signp,$(DISTDIR)/$(NAME)/x64/Visualizations/$*/$*.exe,$(SIGN_PFX_FILE),$(SIGN_PFX_PASS))
+ -$(call signp,$(DISTDIR)/$(NAME)/x64/Visualizations/$*/$*.exe,$(SIGN_PFX_FILE),$(SIGN_PFX_PASS))
endif
$(call mkdir,$(DISTDIR)/$(NAME)/x86/Visualizations/$*/)
$(call copyfiles,$(DISTDIR)/../../visualizations/$*/Info.json,$(DISTDIR)/$(NAME)/x86/Visualizations/$*/)
$(call copyfiles,$(DISTDIR)/../../visualizations/$*/Thumbnail.png,$(DISTDIR)/$(NAME)/x86/Visualizations/$*/)
- $(call copyfilesrecursive,$(DISTDIR)/../../visualizations/$*/win-x86/*,$(DISTDIR)/$(NAME)/x86/Visualizations/$*/)
+ -$(call copyfilesrecursive,$(DISTDIR)/../../visualizations/$*/win-x86/*,$(DISTDIR)/$(NAME)/x86/Visualizations/$*/)
$(call sleep,3)
ifeq ($(SIGN_PFX_PASS),)
- $(call sign,$(DISTDIR)/$(NAME)/x86/Visualizations/$*/$*.exe,$(SIGN_PFX_FILE))
+ -$(call sign,$(DISTDIR)/$(NAME)/x86/Visualizations/$*/$*.exe,$(SIGN_PFX_FILE))
else
- $(call signp,$(DISTDIR)/$(NAME)/x86/Visualizations/$*/$*.exe,$(SIGN_PFX_FILE),$(SIGN_PFX_PASS))
+ -$(call signp,$(DISTDIR)/$(NAME)/x86/Visualizations/$*/$*.exe,$(SIGN_PFX_FILE),$(SIGN_PFX_PASS))
endif
$(call mkdir,$(DISTDIR)/$(NAME)/arm64/Visualizations/$*/)
$(call copyfiles,$(DISTDIR)/../../visualizations/$*/Info.json,$(DISTDIR)/$(NAME)/arm64/Visualizations/$*/)
$(call copyfiles,$(DISTDIR)/../../visualizations/$*/Thumbnail.png,$(DISTDIR)/$(NAME)/arm64/Visualizations/$*/)
- $(call copyfilesrecursive,$(DISTDIR)/../../visualizations/$*/win-arm64/*,$(DISTDIR)/$(NAME)/arm64/Visualizations/$*/)
+ -$(call copyfilesrecursive,$(DISTDIR)/../../visualizations/$*/win-arm64/*,$(DISTDIR)/$(NAME)/arm64/Visualizations/$*/)
$(call sleep,3)
ifeq ($(SIGN_PFX_PASS),)
- $(call sign,$(DISTDIR)/$(NAME)/arm64/Visualizations/$*/$*.exe,$(SIGN_PFX_FILE))
+ -$(call sign,$(DISTDIR)/$(NAME)/arm64/Visualizations/$*/$*.exe,$(SIGN_PFX_FILE))
else
- $(call signp,$(DISTDIR)/$(NAME)/arm64/Visualizations/$*/$*.exe,$(SIGN_PFX_FILE),$(SIGN_PFX_PASS))
+ -$(call signp,$(DISTDIR)/$(NAME)/arm64/Visualizations/$*/$*.exe,$(SIGN_PFX_FILE),$(SIGN_PFX_PASS))
endif
dist-dll-x64: dist-prep
echo [DLL] Copy X64 DLL
@@ -142,7 +146,6 @@ dist-dll-arm64: dist-prep
echo [DLL] Copy ARM64 DLL
dist-bin-%: dist-prep dist-dll-%
echo [BIN] $(DISTDIR)/$(NAME)/$*
- $(call mkdir,$(DISTDIR)/$(NAME)/$*)
$(call copyfiles,./bin/win-$*/$(NAME)$(EXTBIN),$(DISTDIR)/$(NAME)/$*/$(NAME)$(EXTBIN))
$(call copyfiles,./bin/win-$*/$(NAME)$(EXTPDB),$(DISTDIR)/$(NAME)/$*/$(NAME)$(EXTPDB))
$(call sleep,3)
@@ -157,8 +160,26 @@ endif
echo [SYM] $(DISTDIR)/$(NAME)/upload/$(NAME)-$*.appxsym
$(ZIPPER) $(DISTDIR)/$(NAME)/upload/$(NAME)-$*.appxsym.zip $(DISTDIR)/$(NAME)/$*/$(NAME)$(EXTPDB)
$(call move,$(DISTDIR)/$(NAME)/upload/$(NAME)-$*.appxsym.zip,$(DISTDIR)/$(NAME)/upload/$(NAME)-$*.appxsym)
- echo [ZIP] $(DISTDIR)/$(NAME)-$(VERSION3)-win-10-$*.zip
- $(ZIPPER) $(DISTDIR)/$(NAME)-$(VERSION3)-win-10-$*.zip $(DISTDIR)/$(NAME)/$*/*
+ echo [MSI] $(DISTDIR)/$(NAME)-$(VERSION3)-win-10-$*.msi
+ powershell -command "& { \
+ Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass; \
+ . $(DISTDIR)/Harvest.ps1 -in $(DISTDIR)/$(NAME).wxs -out $(DISTDIR)/$(NAME)/MSI.$*.wxs -path $(DISTDIR)/$(NAME)/$*; }"
+ wix extension add WixToolset.Firewall.wixext
+ wix build -arch $* \
+ -ext WixToolset.Firewall.wixext \
+ -d APPNAME="$(APPNAME)" \
+ -d APPSHORTNAME="$(APPSHORTNAME)" \
+ -d APPGUIDPLAT="$(APPGUIDPLAT)" \
+ -d APPCOMPANY="$(APPCOMPANY)" \
+ -d VERSION=$(VERSION4) \
+ -b $(DISTDIR)/$(NAME) \
+ $(DISTDIR)/$(NAME)/MSI.$*.wxs \
+ -out $(DISTDIR)/$(NAME)-$(VERSION3)-win-10-$*.msi
+ifeq ($(SIGN_PFX_PASS),)
+ $(call sign,$(DISTDIR)/$(NAME)-$(VERSION3)-win-10-$*.msi,$(SIGN_PFX_FILE))
+else
+ $(call signp,$(DISTDIR)/$(NAME)-$(VERSION3)-win-10-$*.msi,$(SIGN_PFX_FILE),$(SIGN_PFX_PASS))
+endif
dist: dist-prep dist-vis dist-bin-x64 dist-bin-x86 dist-bin-arm64
echo [BDL] $(DISTDIR)/$(NAME)-$(VERSION3)-win-10.appxbundle
$(call makepkg,$(DISTDIR)/$(NAME)/Layout.xml,$(DISTDIR))
diff --git a/deps/build/make/platforms/win-all.mk b/deps/build/make/platforms/win-all.mk
index 2c13cb043..f065435bd 100644
--- a/deps/build/make/platforms/win-all.mk
+++ b/deps/build/make/platforms/win-all.mk
@@ -2,27 +2,27 @@
# Delete Files in Folder by Pattern
define deletefiles
- cmd.exe /C "del /s /q $(subst /,\,$(1))\$(2) >nul 2>&1" & exit 0
+ powershell "Remove-Item $(1)/$(2) -Force"
endef
# Copy Files between Folders by Pattern
define copyfiles
- cmd.exe /C "copy /Y $(subst /,\,$(1)) $(subst /,\,$(2)) >nul 2>&1" & exit 0
+ powershell "Copy-Item $(1) -Destination $(2) -Force"
endef
# Copy Files recursively
define copyfilesrecursive
- cmd.exe /C "xcopy /Y /E /H $(subst /,\,$(1)) $(subst /,\,$(2)) >nul 2>&1" & exit 0
+ powershell "Copy-Item $(1) -Destination $(2) -Force -Recurse"
endef
# Recursively remove folder
-define rmdir
- cmd.exe /C "if exist $(subst /,\,$(1)) rd /s /q $(subst /,\,$(1)) >nul 2>&1"
+define rmdir
+ powershell "Remove-Item $(1) -Force -Recurse"
endef
# Create folder and all sub folders
define mkdir
- cmd.exe /C "if not exist $(subst /,\,$(1)) mkdir $(subst /,\,$(1)) >nul 2>&1"
+ powershell "New-Item $(1) -ItemType Directory -Force"
endef
# Replace string occurrences in file
@@ -32,40 +32,40 @@ endef
# Create APPX unencrypted
define makepkg
- cmd.exe /C "MakeAppx.exe build /o /h SHA256 /f $(subst /,\,$(1)) /op $(subst /,\,$(2))"
+ MakeAppx.exe build /o /h SHA256 /f $(subst /,\,$(1)) /op $(subst /,\,$(2))
endef
# Create APPXBUNDLE
define makebundle
- cmd.exe /C "MakeAppx.exe bundle /o /d $(subst /,\,$(1)) /p $(subst /,\,$(2)) >nul 2>&1"
+ MakeAppx.exe bundle /o /d $(subst /,\,$(1)) /p $(subst /,\,$(2))
endef
# Sign File with pfx without password
define sign
- cmd.exe /C "if exist $(subst /,\,$(1)) SignTool.exe sign /a /fd SHA256 /td SHA256 /tr http://timestamp.digicert.com /f $(subst /,\,$(2)) $(subst /,\,$(1)) >nul 2>&1"
+ SignTool.exe sign /a /fd SHA256 /td SHA256 /tr http://timestamp.digicert.com /f $(subst /,\,$(2)) $(subst /,\,$(1))
endef
# Sign File with pfx with password
define signp
- cmd.exe /C "if exist $(subst /,\,$(1)) SignTool.exe sign /a /fd SHA256 /td SHA256 /tr http://timestamp.digicert.com /f $(subst /,\,$(2)) /p $(3) $(subst /,\,$(1))"
+ SignTool.exe sign /a /fd SHA256 /td SHA256 /tr http://timestamp.digicert.com /f $(subst /,\,$(2)) /p $(3) $(subst /,\,$(1))
endef
# Create .pri resources
define makepri
- cmd.exe /C "MakePri.exe new /v /o /cf $(subst /,\,$(1)) /pr $(subst /,\,$(2)) /of $(subst /,\,$(3)) /IndexName $(4)"
+ MakePri.exe new /v /o /cf $(subst /,\,$(1)) /pr $(subst /,\,$(2)) /of $(subst /,\,$(3)) /IndexName $(4)
endef
# Create ZIP
define makezip
- cmd.exe /C "powershell Compress-Archive -Force $(1) $(2)"
+ powershell Compress-Archive -Force $(1) $(2)
endef
# Move Folder or File
define move
- cmd.exe /C "powershell Move-Item -Force -Path $(1) -Destination $(2)"
+ powershell Move-Item -Force -Path $(1) -Destination $(2)
endef
# Sleep n seconds
define sleep
- cmd.exe /C "powershell Start-Sleep -Seconds $(1)"
+ powershell Start-Sleep -Seconds $(1)
endef
diff --git a/dist/win-10/.gitignore b/dist/win-10/.gitignore
index 4ca5d88c6..17a6b692d 100644
--- a/dist/win-10/.gitignore
+++ b/dist/win-10/.gitignore
@@ -5,3 +5,5 @@
*.appxbundle
*.appxupload
*.appx
+*.msi
+*.wixpdb
diff --git a/dist/win-10/Harvest.ps1 b/dist/win-10/Harvest.ps1
new file mode 100644
index 000000000..0fe35672f
--- /dev/null
+++ b/dist/win-10/Harvest.ps1
@@ -0,0 +1,82 @@
+param (
+ [Parameter(Mandatory)] [System.String] $in,
+ [Parameter(Mandatory)] [System.String] $out,
+ [Parameter(Mandatory)] [System.String] $path
+)
+
+# Load WXS
+$xml = [xml](Get-Content -Path $in)
+$featurenode = $xml.Wix.Package.Feature
+
+###############################################################################################
+
+function process
+{
+ [CmdletBinding()]
+ param(
+ [Parameter()]
+ [System.String] $path,
+ [System.Xml.XmlNode] $dirnode,
+ [System.Boolean] $rootfolder
+ )
+
+ $newguid = [guid]::NewGuid()
+ $newcompid = ("-" + $newguid).Replace('-', '_')
+
+ $newcomponent = $xml.CreateElement("Component", "http://wixtoolset.org/schemas/v4/wxs")
+ $newcomponent.SetAttribute("Id", $newcompid)
+ $newcomponent.SetAttribute("Guid", $newguid)
+
+ $contents = (Get-ChildItem -Path $path)
+ foreach ($c in $contents)
+ {
+ $plainname = (Split-Path $c -leaf)
+ if ($c -Is [System.IO.DirectoryInfo])
+ {
+ $newfolder = $xml.CreateElement("Directory", "http://wixtoolset.org/schemas/v4/wxs")
+ $newfolder.SetAttribute("Name", $plainname)
+ $dirnode.AppendChild($newfolder) | Out-Null
+
+ process $c.FullName $newfolder 0
+ }
+ else
+ {
+ $newfile = $xml.CreateElement("File", "http://wixtoolset.org/schemas/v4/wxs")
+# $newfile.SetAttribute("Source", (Resolve-Path -Relative $c.FullName))
+ $newfile.SetAttribute("Source", $c.FullName)
+
+ $ext = [System.IO.Path]::GetExtension($c)
+ if ($ext -eq ".exe") {
+ if ($rootfolder) {
+ $newfile.SetAttribute("Id", $plainname)
+ }
+ $newrule = $xml.CreateElement(
+ "fire:FirewallException",
+ "http://wixtoolset.org/schemas/v4/wxs/firewall")
+ $newrule.SetAttribute("Name", $plainname)
+ $newrule.SetAttribute("Profile", "private")
+ $newrule.SetAttribute("Scope", "any")
+ $newfile.AppendChild($newrule) | Out-Null
+ }
+ $newcomponent.AppendChild($newfile) | Out-Null
+ }
+ }
+ if ($newcomponent.ChildNodes.Count)
+ {
+ $dirnode.AppendChild($newcomponent) | Out-Null
+ $newcompref = $xml.CreateElement("ComponentRef", "http://wixtoolset.org/schemas/v4/wxs")
+ $newcompref.SetAttribute("Id", $newcompid)
+ $featurenode.AppendChild($newcompref) | Out-Null
+ }
+}
+
+###############################################################################################
+
+$node = $xml.Wix.Package.StandardDirectory | where {$_.Id -eq 'ProgramFiles6432Folder'}
+$node = $node.Directory | where {$_.Id -eq 'INSTALLFOLDER'}
+
+# Modify WXS
+process $path $node 1
+
+# Save WXS
+$xml.save($out)
diff --git a/dist/win-10/Studio.wxs b/dist/win-10/Studio.wxs
new file mode 100644
index 000000000..f3a33ab07
--- /dev/null
+++ b/dist/win-10/Studio.wxs
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/priv b/priv
index 3ec133a70..b63a15c1a 160000
--- a/priv
+++ b/priv
@@ -1 +1 @@
-Subproject commit 3ec133a70c9d2f1ab97f4f57debdeb2b0d3af9f5
+Subproject commit b63a15c1ac2e7c6dc29dea08aded573e54102c46
diff --git a/src/Engine/BciDevice.cpp b/src/Engine/BciDevice.cpp
index e76ea1f0f..78c403e05 100644
--- a/src/Engine/BciDevice.cpp
+++ b/src/Engine/BciDevice.cpp
@@ -73,6 +73,8 @@ void BciDevice::CreateSensors()
{
// create sensor with the neuro headset sample rate
Sensor* sensor = new Sensor(mElectrodes[i].GetName(), GetSampleRate());
+ sensor->GetInput()->SetBufferSizeInSeconds(DEFAULTBUFFERSIZEINSECONDS);
+ sensor->GetOutput()->SetBufferSizeInSeconds(DEFAULTBUFFERSIZEINSECONDS);
// set unique color for each sensor
sensor->GetChannel()->GetColor().SetUniqueColor(i);
diff --git a/src/Engine/BciDevice.h b/src/Engine/BciDevice.h
index 98d79097f..99db55680 100644
--- a/src/Engine/BciDevice.h
+++ b/src/Engine/BciDevice.h
@@ -38,6 +38,8 @@ class ENGINE_API BciDevice : public Device
public:
enum { BASE_TYPE_ID = 0x02 };
+ static constexpr const double DEFAULTBUFFERSIZEINSECONDS = 60.0;
+
// constructor & destructor
BciDevice(DeviceDriver* driver = NULL);
virtual ~BciDevice();
@@ -57,6 +59,9 @@ class ENGINE_API BciDevice : public Device
// true if device provides the contact quality for a sensor
virtual bool HasEegContactQualityIndicator() { return false; }
+ // true if the device has named electrodes with positions
+ virtual bool HasElectrodePositions() { return true; }
+
// get/set position of electrodes
const Core::Array& GetElectrodes() const { return mElectrodes; }
EEGElectrodes::Electrode GetElectrodePosition(uint32 neuroSensorIndex) const;
diff --git a/src/Engine/Branding.h b/src/Engine/Branding.h
index 05a87fa3a..c5e751433 100644
--- a/src/Engine/Branding.h
+++ b/src/Engine/Branding.h
@@ -34,7 +34,7 @@ class Branding
inline Branding() { }
public:
static constexpr const char* CompanyName = "neuromore"; // do not put Inc. behind this as this is also used as folder name
- static constexpr const char* DeveloperName = "neuromore Inc.";
+ static constexpr const char* DeveloperName = "neuromore co";
static constexpr const char* Website = "https://www.neuromore.com";
static constexpr const char* DocumentationUrl = "https://doc.neuromore.com";
static constexpr const char* AccountUrl = "https://account.neuromore.com";
@@ -51,10 +51,14 @@ class Branding
static constexpr const char* SplashImageName = ":/Images/SplashScreen-neuromore.png";
static constexpr const char* AboutImageName = ":/Images/About-neuromore.png";
static constexpr const bool LoginRemberMePrechecked = true;
+ static constexpr const bool HasColorCodesForChannels = false;
+ static constexpr const bool HasMinimalDeviceInfo = false;
static constexpr const bool DefaultAutoDetectionEnabled = true;
static constexpr const bool DefaultTestDeviceEnabled = true;
static constexpr const bool DefaultEemagineEnabled = true;
- static constexpr const bool DefaultBrainMasterEnabled = false;
+ static constexpr const bool DefaultBrainMasterEnabled = true;
+ static constexpr const bool DefaultOpenBCIEnabled = true;
+ static constexpr const bool DefaultBrainflowEnabled = true;
static constexpr const int DefaultServerPresetIdx = 0;
static constexpr const int DefaultAutoSelectType = 4; // = SELECT_FIRST_EIGHT (see ChannelMultiSelectionWidget.h)
};
diff --git a/src/Engine/Devices/Neurosity/CrownDevice.h b/src/Engine/Devices/Neurosity/CrownDevice.h
index e6398fd93..aeec41764 100644
--- a/src/Engine/Devices/Neurosity/CrownDevice.h
+++ b/src/Engine/Devices/Neurosity/CrownDevice.h
@@ -52,8 +52,8 @@ class ENGINE_API CrownDevice : public BciDevice
const char* GetTypeName() const override { return "crown"; }
double GetLatency() const override { return 0.1; }
double GetExpectedJitter() const override { return 0.2; }
- bool IsWireless() const override { return false; /* we do not know the connection quality of the muse; this removes the connection quality icon in the device manager */ }
- bool HasEegContactQualityIndicator() override { return true; }
+ bool IsWireless() const override { return false; }
+ bool HasEegContactQualityIndicator() override { return false; }
static const char* GetRuleName() { return "DEVICE_NeurosityNotion"; }
int32 GetOscPathDeviceId(const Core::String& address) const override;
diff --git a/src/Engine/Devices/Neurosity/NotionDevices.h b/src/Engine/Devices/Neurosity/NotionDevices.h
index 359e06fc8..7fe8a7e6f 100644
--- a/src/Engine/Devices/Neurosity/NotionDevices.h
+++ b/src/Engine/Devices/Neurosity/NotionDevices.h
@@ -59,8 +59,8 @@ class ENGINE_API NotionDevice : public BciDevice
const char* GetTypeName() const override { return "notion"; }
double GetLatency() const override { return 0.1; }
double GetExpectedJitter() const override { return 0.2; }
- bool IsWireless() const override { return false; /* we do not know the connection quality of the muse; this removes the connection quality icon in the device manager */ }
- bool HasEegContactQualityIndicator() override { return true; }
+ bool IsWireless() const override { return false; }
+ bool HasEegContactQualityIndicator() override { return false; }
static const char* GetRuleName() { return "DEVICE_NeurosityNotion"; }
int32 GetOscPathDeviceId(const Core::String& address) const override;
diff --git a/src/Engine/Devices/OpenBCI/OpenBCIDevices.cpp b/src/Engine/Devices/OpenBCI/OpenBCIDevices.cpp
index fdc344135..23d20e638 100644
--- a/src/Engine/Devices/OpenBCI/OpenBCIDevices.cpp
+++ b/src/Engine/Devices/OpenBCI/OpenBCIDevices.cpp
@@ -49,37 +49,24 @@ void OpenBCIDeviceBase::CreateSensors()
mAccForwardSensor->GetChannel()->SetMinValue(-2000.0);
mAccForwardSensor->GetChannel()->SetMaxValue(1996.1);
mAccForwardSensor->GetChannel()->SetUnit("mm/s^2");
+ mAccForwardSensor->GetChannel()->SetBufferSizeInSeconds(DEFAULTBUFFERSIZEINSECONDS);
AddSensor(mAccForwardSensor);
mAccUpSensor = new Sensor("Acc (Up)", 250);
mAccUpSensor->GetChannel()->SetMinValue(-2000.0);
mAccUpSensor->GetChannel()->SetMaxValue(1996.1);
mAccUpSensor->GetChannel()->SetUnit("mm/s^2");
+ mAccUpSensor->GetChannel()->SetBufferSizeInSeconds(DEFAULTBUFFERSIZEINSECONDS);
AddSensor(mAccUpSensor);
mAccLeftSensor = new Sensor("Acc (Left)", 250);
mAccLeftSensor->GetChannel()->SetMinValue(-2000.0);
mAccLeftSensor->GetChannel()->SetMaxValue(1996.1);
mAccLeftSensor->GetChannel()->SetUnit("mm/s^2");
+ mAccLeftSensor->GetChannel()->SetBufferSizeInSeconds(DEFAULTBUFFERSIZEINSECONDS);
AddSensor(mAccLeftSensor);
}
-void OpenBCIDeviceBase::StartTest()
-{
- if (mTesting)
- return;
-
- mTesting = true;
-}
-
-void OpenBCIDeviceBase::StopTest()
-{
- if (!mTesting)
- return;
-
- mTesting = false;
-}
-
//
// OpenBCI without Daisy module
@@ -111,14 +98,14 @@ void OpenBCIDevice::CreateElectrodes()
mElectrodes.Clear();
mElectrodes.Reserve(NUMELECTRODESCYTON);
- mElectrodes.Add(GetEEGElectrodes()->GetElectrodeByID("Fp1"));
- mElectrodes.Add(GetEEGElectrodes()->GetElectrodeByID("Fp2"));
- mElectrodes.Add(GetEEGElectrodes()->GetElectrodeByID("C5"));
- mElectrodes.Add(GetEEGElectrodes()->GetElectrodeByID("C6"));
- mElectrodes.Add(GetEEGElectrodes()->GetElectrodeByID("P7"));
- mElectrodes.Add(GetEEGElectrodes()->GetElectrodeByID("O1"));
- mElectrodes.Add(GetEEGElectrodes()->GetElectrodeByID("O2"));
- mElectrodes.Add(GetEEGElectrodes()->GetElectrodeByID("P8"));
+ mElectrodes.Add(GetEEGElectrodes()->GetElectrodeByID("1"));
+ mElectrodes.Add(GetEEGElectrodes()->GetElectrodeByID("2"));
+ mElectrodes.Add(GetEEGElectrodes()->GetElectrodeByID("3"));
+ mElectrodes.Add(GetEEGElectrodes()->GetElectrodeByID("4"));
+ mElectrodes.Add(GetEEGElectrodes()->GetElectrodeByID("5"));
+ mElectrodes.Add(GetEEGElectrodes()->GetElectrodeByID("6"));
+ mElectrodes.Add(GetEEGElectrodes()->GetElectrodeByID("7"));
+ mElectrodes.Add(GetEEGElectrodes()->GetElectrodeByID("8"));
}
diff --git a/src/Engine/Devices/OpenBCI/OpenBCIDevices.h b/src/Engine/Devices/OpenBCI/OpenBCIDevices.h
index bddcec9d9..07d7ef090 100644
--- a/src/Engine/Devices/OpenBCI/OpenBCIDevices.h
+++ b/src/Engine/Devices/OpenBCI/OpenBCIDevices.h
@@ -86,14 +86,13 @@ class ENGINE_API OpenBCIDeviceBase : public BciDevice
double GetLatency() const override { return 0.1; }
double GetExpectedJitter() const override { return 0.1; }
bool IsWireless() const override { return true; }
- bool HasTestMode() const override { return true; }
+ bool HasTestMode() const override { return false; }
void CreateSensors() override;
- void StartTest() override;
- void StopTest() override;
- inline bool IsTestRunning() override { return mTesting; }
- inline bool HasEegContactQualityIndicator() override { return mTesting; }
+ inline bool IsTestRunning() override { return true; }
+ inline bool HasEegContactQualityIndicator() override { return true; }
+ inline bool HasElectrodePositions() override { return false; }
virtual void AddSample(uint32 idx, double v) = 0;
diff --git a/src/Engine/EEGElectrodes.cpp b/src/Engine/EEGElectrodes.cpp
index 5c56c99f8..d5c874ded 100644
--- a/src/Engine/EEGElectrodes.cpp
+++ b/src/Engine/EEGElectrodes.cpp
@@ -432,14 +432,14 @@ void EEGElectrodes::Init()
// Others
// 127 electrodes
- //mElectrodes.Add( Electrode("1", 36.000, -22.000) );
- //mElectrodes.Add( Electrode("2", 47.000, -6.000) );
- //mElectrodes.Add( Electrode("3", 56.000, 10.000) );
- //mElectrodes.Add( Electrode("4", 72.000, 26.000) );
- //mElectrodes.Add( Electrode("5", 78.000, 42.000) );
- //mElectrodes.Add( Electrode("6", 90.000, 58.000) );
- //mElectrodes.Add( Electrode("7", 126.000, 74.000) );
- //mElectrodes.Add( Electrode("8", 54.000, -22.000) );
+ mElectrodes.Add( Electrode("1", 180.000, 10.000) );
+ mElectrodes.Add( Electrode("2", 180.000, 30.000) );
+ mElectrodes.Add( Electrode("3", 180.000, 50.000) );
+ mElectrodes.Add( Electrode("4", 180.000, 70.000) );
+ mElectrodes.Add( Electrode("5", 0.000, 70.000) );
+ mElectrodes.Add( Electrode("6", 0.000, 50.000) );
+ mElectrodes.Add( Electrode("7", 0.000, 30.000) );
+ mElectrodes.Add( Electrode("8", 0.000, 10.000) );
//mElectrodes.Add( Electrode("9", 64.000, -6.000) );
//mElectrodes.Add( Electrode("10", 73.000, 10.000) );
//mElectrodes.Add( Electrode("11", 90.000, 26.000) );
diff --git a/src/Engine/Graph/ChannelSelectorNode.cpp b/src/Engine/Graph/ChannelSelectorNode.cpp
index a3a14eea1..7bac7ede6 100644
--- a/src/Engine/Graph/ChannelSelectorNode.cpp
+++ b/src/Engine/Graph/ChannelSelectorNode.cpp
@@ -59,20 +59,25 @@ void ChannelSelectorNode::Init()
InitInputPorts(1);
GetInputPort(INPUTPORT_SET).SetupAsChannels("In", "x1", INPUTPORT_SET);
- const uint32 numPortsDefault = 0;
- InitOutputPorts(numPortsDefault);
+ // create temporary empty ports
+ InitOutputPorts(NUMPORTSDEFAULT);
+ for (uint32 i = 0; i < NUMPORTSDEFAULT; ++i)
+ {
+ mTempString.Format("y%i", i + 1);
+ GetOutputPort(i).SetupAsChannels("-", mTempString.AsChar(), i);
+ }
// ATTRIBUTES
// hidden port number attribute
Core::AttributeSettings* attribNumPorts = RegisterAttribute("", "numOutputPorts", "", Core::ATTRIBUTE_INTERFACETYPE_INTSPINNER);
- attribNumPorts->SetDefaultValue( Core::AttributeInt32::Create(numPortsDefault) );
+ attribNumPorts->SetDefaultValue( Core::AttributeInt32::Create(NUMPORTSDEFAULT) );
attribNumPorts->SetMinValue( Core::AttributeInt32::Create(0) );
attribNumPorts->SetMaxValue( Core::AttributeInt32::Create(INT_MAX) );
attribNumPorts->SetVisible(false);
// channel names
- Core::AttributeSettings* attribChannelNames = RegisterAttribute("Channel Names", "channels", "", Core::ATTRIBUTE_INTERFACETYPE_STRINGARRAY);
+ Core::AttributeSettings* attribChannelNames = RegisterAttribute("Channels", "channels", "Names or Indexes of Channels to use", Core::ATTRIBUTE_INTERFACETYPE_STRINGARRAY);
attribChannelNames->SetDefaultValue( Core::AttributeStringArray::Create("*") );
// single output attribute
@@ -109,7 +114,7 @@ void ChannelSelectorNode::ReInit(const Time& elapsed, const Time& delta)
{
if (inPort.GetChannels() != NULL && inPort.GetChannels()->GetNumChannels() == 0)
{
- mIsInitialized = false;
+ //mIsInitialized = false;
}
}
@@ -129,23 +134,29 @@ void ChannelSelectorNode::Update(const Time& elapsed, const Time& delta)
if (mIsInitialized == false)
return;
- CORE_ASSERT(mOutputChannels.Size() == mSelectedInputs.Size());
-
// forward all samples to the outputs
- const uint32 numChannels = mOutputChannels.Size();
+ const uint32 numChannels = mMapping.Size();
for (uint32 i=0; iAddSample(0.0);
+
+ // no input and no output, do nothing
+ if (!reader || !m.out)
+ continue;
+
+ // forward samples from input to output
const uint32 numSamples = reader->GetNumNewSamples();
for (uint32 s = 0; s < numSamples; ++s)
{
- double sample = reader->GetSample(s); // Note: don't pop samples here, just read them (as we might forward one channel multiple times to different outputs)
- output->AsType()->AddSample(sample);
+ // Note: don't pop samples here, just read them
+ // (as we might forward one channel multiple times to different outputs)
+ double sample = reader->GetSample(s);
+ m.out->AddSample(sample);
}
}
@@ -157,11 +168,6 @@ void ChannelSelectorNode::Update(const Time& elapsed, const Time& delta)
// an attribute has changed
void ChannelSelectorNode::OnAttributesChanged()
{
- // A little hacky? This codepath is only used to create output ports during loading of the classifier - the attribute is the only way we know how
- // many ports there should be created before loading the connection.
- if (GetNumOutputPorts() == 0)
- ReInitOutputPorts();
-
// always reinit if a node attribute was changed
ResetAsync();
}
@@ -173,6 +179,7 @@ void ChannelSelectorNode::Start(const Time& elapsed)
// remember number of ports in attribute
SetInt32Attribute("numOutputPorts", mInputReader.GetNumChannels());
+ DeleteOutputChannels();
CollectSelectedInputChannels(); // collect channels first
ReInitOutputPorts(); // init ports (empty, without channels)
ReInitOutputChannels(); // init output channels and connect them to the ports; also name the ports
@@ -189,8 +196,6 @@ void ChannelSelectorNode::CollectSelectedInputChannels()
const Array& names = GetStringArrayAttribute(ATTRIB_CHANNELNAMES, Array());
const bool hasNames = !names.IsEmpty();
const bool useSingleWildCardFeature = (hasNames && names[0].GetLength() == 1 && names[0].GetFirst() == StringCharacter::asterisk);
-
- mSelectedInputs.Clear();
const uint32 numInputChannels = mInputReader.GetNumChannels();
@@ -202,9 +207,12 @@ void ChannelSelectorNode::CollectSelectedInputChannels()
if (useSingleWildCardFeature == true)
{
// forward all channel
- for (uint32 i=0; i* ch = mInputReader.GetChannel(i)->AsType();
+ mMapping.Add(Mapping(ch, 0, ch->GetName()));
+ }
+
return;
}
@@ -220,8 +228,6 @@ void ChannelSelectorNode::CollectSelectedInputChannels()
name.GetFirst() == StringCharacter::asterisk &&
name.GetLast() == StringCharacter::asterisk);
- bool haveChannel = false;
-
// normal mode: exact name matching
if (useWildCardFeature == false && useSingleWildCardFeature == false)
{
@@ -231,7 +237,7 @@ void ChannelSelectorNode::CollectSelectedInputChannels()
for (uint32 j=0; jGetNameString().IsEqual(name) == true)
+ if ((name.IsValidInt() && name.ToInt() == (int)(j+1)) || channel->GetNameString().IsEqual(name))
{
// found it
channelIndex = j;
@@ -242,17 +248,17 @@ void ChannelSelectorNode::CollectSelectedInputChannels()
// add the channel
if (channelIndex != CORE_INVALIDINDEX32)
{
- mSelectedInputs.Add(channelIndex);
- haveChannel = true;
+ Channel* ch = mInputReader.GetChannel(channelIndex)->AsType();
+ mMapping.Add(Mapping(ch, 0, ch->GetName()));
}
-
- // node error when channel was not found (not in wild card mode, only in exact match mode)
- if (haveChannel == false)
+ else
{
+ mMapping.Add(Mapping(0, 0, name));
+
+ // node error when channel was not found (not in wild card mode, only in exact match mode)
String tmp; tmp.Format("Channel '%s' not found", name.AsChar());
SetError(ERROR_CHANNEL_NOT_FOUND, tmp.AsChar());
}
-
}
else // wildcard modes: use substring matching
{
@@ -266,13 +272,10 @@ void ChannelSelectorNode::CollectSelectedInputChannels()
if (channel->GetNameString().Contains(name) == true)
{
// found one, add it (don't break, keep processing all input channels)
- mSelectedInputs.Add(j);
- haveChannel = true;
+ mMapping.Add(Mapping(channel->AsType(), 0, channel->GetNameString()));
}
}
}
-
-
}
}
@@ -280,12 +283,14 @@ void ChannelSelectorNode::CollectSelectedInputChannels()
// use this to check if an input channel is forwarded by the node
bool ChannelSelectorNode::IsChannelSelected(const ChannelBase* channel) const
{
+ if (!channel)
+ return false;
+
// compare pointer against all selected input channels
- const uint32 numSelectedChannels = mSelectedInputs.Size();
+ const uint32 numSelectedChannels = mMapping.Size();
for (uint32 i=0; i(this)->mInputReader.GetChannel(index) == channel)
+ if (channel == mMapping[i].in)
return true;
}
@@ -297,7 +302,6 @@ bool ChannelSelectorNode::IsChannelSelected(const ChannelBase* channel) const
void ChannelSelectorNode::ReInitOutputPorts()
{
const bool singleOutput = GetBoolAttribute(ATTRIB_SINGLE_OUTPUT);
- uint32 numPortsAttr = GetInt32Attribute(ATTRIB_NUMOUTPUTPORTS); // previously remembered number of output ports
// get number of required output ports
if (singleOutput == true)
@@ -307,28 +311,15 @@ void ChannelSelectorNode::ReInitOutputPorts()
// update channel references
GetOutputPort(0).GetChannels()->Clear();
-
- // remember number of outputs (if it has changed)
- if (numPortsAttr != 1)
- SetInt32AttributeByIndex(ATTRIB_NUMOUTPUTPORTS, 1);
}
else
{
// get number of output channels we have to output
- const uint32 numChannels = mSelectedInputs.Size();
- uint32 numPorts;
-
- // use last port count only if there are no input channel present
- if (mInputReader.GetNumChannels() == 0 && GetNumOutputPorts() == 0)
- numPorts = numPortsAttr;
- else
- numPorts = numChannels;
-
- SetInt32AttributeByIndex(ATTRIB_NUMOUTPUTPORTS, numPorts);
+ const uint32 numChannels = mMapping.Size();
// create empty ports
- InitOutputPorts(numPorts);
- for (uint32 i = 0; i < numPorts; ++i)
+ InitOutputPorts(numChannels);
+ for (uint32 i = 0; i < numChannels; ++i)
{
mTempString.Format("y%i", i + 1);
GetOutputPort(i).SetupAsChannels("-", mTempString.AsChar(), i);
@@ -340,27 +331,22 @@ void ChannelSelectorNode::ReInitOutputPorts()
// create the output channels (one for each selected input channel) and connect them to the outputs
void ChannelSelectorNode::ReInitOutputChannels()
{
- DeleteOutputChannels();
-
// create output channel array, for the selected channels given
- const uint32 numChannels = mSelectedInputs.Size();
+ const uint32 numChannels = mMapping.Size();
for (uint32 i = 0; i < numChannels; ++i)
{
- CORE_ASSERT(mSelectedInputs[i] < mInputReader.GetNumChannels());
-
- Channel* channel = new Channel(); // TODO clone channel here so its independent of type
- Channel* inputChannel = mInputReader.GetChannel(mSelectedInputs[i])->AsType();
-
// configure output channel to same parameters as input channel
- channel->SetBufferSize(10); // set any buffersize > 0
- channel->SetName(inputChannel->GetName());
- channel->SetSampleRate(inputChannel->GetSampleRate());
- channel->SetMinValue(inputChannel->GetMinValue());
- channel->SetMaxValue(inputChannel->GetMaxValue());
- channel->SetColor(inputChannel->GetColor());
-
- mOutputChannels.Add(channel);
+ mMapping[i].out = new Channel();
+ mMapping[i].out->SetBufferSize(10); // set any buffersize > 0
+ mMapping[i].out->SetName(mMapping[i].name);
+ if (mMapping[i].in)
+ {
+ mMapping[i].out->SetSampleRate(mMapping[i].in->GetSampleRate());
+ mMapping[i].out->SetMinValue(mMapping[i].in->GetMinValue());
+ mMapping[i].out->SetMaxValue(mMapping[i].in->GetMaxValue());
+ mMapping[i].out->SetColor(mMapping[i].in->GetColor());
+ }
}
// now add connections to output ports
@@ -374,24 +360,24 @@ void ChannelSelectorNode::ReInitOutputChannels()
MultiChannel* multichannel = GetOutputPort(0).GetChannels();
multichannel->Clear();
for (uint32 i = 0; i < numChannels; ++i)
- multichannel->AddChannel(mOutputChannels[i]);
+ multichannel->AddChannel(mMapping[i].out);
// set the port name
GetOutputPort(0).SetName("Out");
}
else
{
- CORE_ASSERT(GetNumOutputPorts() == numChannels);
+ CORE_ASSERT(GetNumOutputPorts() >= numChannels);
// add each channel to its individual port
for (uint32 i = 0; i < numChannels; ++i)
{
MultiChannel* multichannel = GetOutputPort(i).GetChannels();
multichannel->Clear();
- multichannel->AddChannel(mOutputChannels[i]);
+ multichannel->AddChannel(mMapping[i].out);
// set the port name
- GetOutputPort(i).SetName(mOutputChannels[i]->GetName());
+ GetOutputPort(i).SetName(mMapping[i].name);
}
}
}
@@ -411,5 +397,11 @@ void ChannelSelectorNode::DeleteOutputChannels()
}
// delete the output channels
- DestructArray(mOutputChannels);
+ const uint32 numMappings = mMapping.Size();
+ for (uint32 i = 0; i < numMappings; i++)
+ {
+ delete mMapping[i].out;
+ mMapping[i].out = 0;
+ }
+ mMapping.Clear();
}
diff --git a/src/Engine/Graph/ChannelSelectorNode.h b/src/Engine/Graph/ChannelSelectorNode.h
index 1b2448971..89ff3bad7 100644
--- a/src/Engine/Graph/ChannelSelectorNode.h
+++ b/src/Engine/Graph/ChannelSelectorNode.h
@@ -33,6 +33,8 @@
class ENGINE_API ChannelSelectorNode : public SPNode
{
public:
+ static constexpr const uint32 NUMPORTSDEFAULT = 64;
+
enum { TYPE_ID = 0x0031 };
static const char* Uuid () { return "fd168f5a-047b-11e5-8418-1697f925ec7b"; }
@@ -44,8 +46,9 @@ class ENGINE_API ChannelSelectorNode : public SPNode
enum
{
ATTRIB_NUMOUTPUTPORTS = 0,
- ATTRIB_CHANNELNAMES,
- ATTRIB_SINGLE_OUTPUT,
+ ATTRIB_CHANNELNAMES = 1,
+ ATTRIB_SINGLE_OUTPUT = 2,
+ ATTRIB_QUICK_CONFIG = 3
};
enum EError
@@ -53,7 +56,15 @@ class ENGINE_API ChannelSelectorNode : public SPNode
ERROR_CHANNEL_NOT_FOUND = GraphObjectError::ERROR_CONFIGURATION | 0x01,
};
-
+ struct Mapping
+ {
+ Channel* in;
+ Channel* out;
+ Core::String name;
+ Mapping(Channel* in = 0, Channel* out = 0, const Core::String& name = "") :
+ in(in), out(out), name(name) { }
+ };
+
// constructor & destructor
ChannelSelectorNode(Graph* graph);
~ChannelSelectorNode();
@@ -77,7 +88,6 @@ class ENGINE_API ChannelSelectorNode : public SPNode
// check if input channels are selected (forwarded) to the output
bool IsChannelSelected(const ChannelBase* channel) const;
- bool IsChannelSelected(uint32 index) const { return mSelectedInputs.Contains(index); }
private:
void CollectSelectedInputChannels(); // collect all channels that match the provided selection into mSelectedInputChannels array
@@ -86,9 +96,7 @@ class ENGINE_API ChannelSelectorNode : public SPNode
void ReInitOutputChannels(); // create the output channels (one for each selected input channel) and connect them to the outputs
void DeleteOutputChannels();
- Core::Array mSelectedInputs; // indices of the selected channels in mInputChannels; one for each element of mOutputChannels
- Core::Array mOutputChannels; // the output channels where all samples are forwarded to (1:1 from mSelectedInputChannels)
-
+ Core::Array mMapping; // mapping between input and output channels
};
diff --git a/src/Engine/Graph/Classifier.cpp b/src/Engine/Graph/Classifier.cpp
index 50ae6be66..154b2329e 100644
--- a/src/Engine/Graph/Classifier.cpp
+++ b/src/Engine/Graph/Classifier.cpp
@@ -1092,3 +1092,18 @@ uint32 Classifier::SaveCloudParameters(CloudParameters& parameter) const
return numSaved;
}
+
+
+//
+ChannelSelectorNode* Classifier::FindMainChannelSelector() const
+{
+ const uint32 numNodes = GetNumNodes();
+ for (uint32 i = 0; i < numNodes; i++) {
+ Node* n = GetNode(i);
+ if (n->GetType() == ChannelSelectorNode::TYPE_ID && n->GetNumInputPorts() > 0) {
+ if (n->GetBoolAttribute(ChannelSelectorNode::ATTRIB_QUICK_CONFIG))
+ return (ChannelSelectorNode*)n;
+ }
+ }
+ return 0;
+}
diff --git a/src/Engine/Graph/Classifier.h b/src/Engine/Graph/Classifier.h
index c65515747..d4c59fbc6 100644
--- a/src/Engine/Graph/Classifier.h
+++ b/src/Engine/Graph/Classifier.h
@@ -43,7 +43,7 @@
#include "AnnotationNode.h"
#include "DeviceInputNode.h"
#include "ToneGeneratorNode.h"
-
+#include "ChannelSelectorNode.h"
class ENGINE_API Classifier : public Graph, public Core::EventHandler
{
@@ -126,6 +126,9 @@ class ENGINE_API Classifier : public Graph, public Core::EventHandler
ToneGeneratorNode* GetToneGeneratorNode(uint32_t index) const { return mToneGeneratorNodes[index]; }
uint32_t GetNumToneGeneratorNodes() const { return mToneGeneratorNodes.Size(); }
+ // find the first ChannelSelector with a DeviceInput at its input
+ ChannelSelectorNode* FindMainChannelSelector() const;
+
//
// Parameters
//
@@ -166,6 +169,7 @@ class ENGINE_API Classifier : public Graph, public Core::EventHandler
void CollectUsedSensors();
bool IsSensorUsed (Sensor* sensor) const { return mUsedSensors.Contains(sensor); }
+ uint32 GetNumUsedSensors() const { return mUsedSensors.Size(); }
//
// Classifier Control
diff --git a/src/Engine/Graph/CustomFeedbackNode.cpp b/src/Engine/Graph/CustomFeedbackNode.cpp
index 3748d37cf..55e62682e 100644
--- a/src/Engine/Graph/CustomFeedbackNode.cpp
+++ b/src/Engine/Graph/CustomFeedbackNode.cpp
@@ -191,14 +191,6 @@ void CustomFeedbackNode::Update(const Time& elapsed, const Time& delta)
}
}
-
-void CustomFeedbackNode::SetName(const char* name)
-{
- FeedbackNode::SetName(name);
- mChannels[0]->SetName(name);
-}
-
-
// attributes have changed
void CustomFeedbackNode::OnAttributesChanged()
{
diff --git a/src/Engine/Graph/CustomFeedbackNode.h b/src/Engine/Graph/CustomFeedbackNode.h
index e5c1fe97d..a569edfee 100644
--- a/src/Engine/Graph/CustomFeedbackNode.h
+++ b/src/Engine/Graph/CustomFeedbackNode.h
@@ -76,8 +76,6 @@ class ENGINE_API CustomFeedbackNode : public FeedbackNode
const char* GetRuleName() const override { return "NODE_Feedback"; }
GraphObject* Clone(Graph* graph) override { CustomFeedbackNode* clone = new CustomFeedbackNode(graph); return clone; }
- void SetName(const char* name) override;
-
// accessors
bool IsRanged() const { return GetBoolAttribute(ATTRIB_ISRANGED); }
double GetRangeMin() const override { return GetFloatAttribute(ATTRIB_RANGEMIN); }
diff --git a/src/Engine/Graph/Graph.cpp b/src/Engine/Graph/Graph.cpp
index a0815761b..b25391394 100644
--- a/src/Engine/Graph/Graph.cpp
+++ b/src/Engine/Graph/Graph.cpp
@@ -701,7 +701,7 @@ bool Graph::ApplySettings(const GraphSettings& settings)
const bool typeMatched = (settings.HasObjectType(i) == true && object->GetType() == settings.GetObjectType(i));
const bool nameMatched = (settings.HasObjectName(i) == true && object->GetUuidString().IsEqualNoCase(settings.GetObjectName(i)));
- if (uuidMatched || typeMatched || nameMatched)
+ if (typeMatched && (uuidMatched || nameMatched))
{
uint32 attributeIndex = object->FindAttributeIndexByInternalName(attributeName);
diff --git a/src/Engine/Graph/OutputNode.cpp b/src/Engine/Graph/OutputNode.cpp
index c340bf5a0..9bee2d9aa 100644
--- a/src/Engine/Graph/OutputNode.cpp
+++ b/src/Engine/Graph/OutputNode.cpp
@@ -57,6 +57,13 @@ void OutputNode::Init()
// upload checkbox
AttributeSettings* attributeUpload = RegisterAttribute("Upload", "upload", "Upload the data stream to neuromore Cloud after a successful session.", ATTRIBUTE_INTERFACETYPE_CHECKBOX);
attributeUpload->SetDefaultValue(AttributeBool::Create(false));
+
+ // output name
+ AttributeSettings* outputName = RegisterAttribute("Output Name", "outputName", "Select name of output/upload.", ATTRIBUTE_INTERFACETYPE_COMBOBOX);
+ outputName->ResizeComboValues(2);
+ outputName->SetComboValue(0, "Use Node Name");
+ outputName->SetComboValue(1, "Use Channel Name");
+ outputName->SetDefaultValue(AttributeInt32::Create(0));
}
@@ -77,6 +84,9 @@ void OutputNode::Start(const Time& elapsed)
const Time startTime = mInputReader.FindMinLastSampleTime();
mInputReader.Start(startTime);
+ // whether to use node or channel name for output
+ const bool usechannelname = this->GetInt32Attribute(ATTRIB_OUTPUTNAME) == 1;
+
int readerIndex = 0;
const uint32 numChannels = mChannels.Size();
for (uint32 i = 0; i < numChannels; ++i)
@@ -103,6 +113,7 @@ void OutputNode::Start(const Time& elapsed)
// set start times
mResamplers[i].SetStartTime(startTime);
mChannels[i]->SetStartTime(startTime);
+ mChannels[i]->SetName(usechannelname ? inputChannel->GetName() : this->GetName());
// finally reinit resampler
mResamplers[i].ReInit();
diff --git a/src/Engine/Graph/OutputNode.h b/src/Engine/Graph/OutputNode.h
index dc502ee2f..afa8c158b 100644
--- a/src/Engine/Graph/OutputNode.h
+++ b/src/Engine/Graph/OutputNode.h
@@ -43,7 +43,8 @@ class ENGINE_API OutputNode : public SPNode
{
ATTRIB_SIGNALRESOLUTION = 0,
ATTRIB_UPLOAD = 1,
- NUM_BASEATTRIBUTES = 2
+ ATTRIB_OUTPUTNAME = 2,
+ NUM_BASEATTRIBUTES = 3
};
enum SignalResolution
diff --git a/src/Engine/Graph/ParameterNode.cpp b/src/Engine/Graph/ParameterNode.cpp
index 3cd39f841..dfc3f4461 100644
--- a/src/Engine/Graph/ParameterNode.cpp
+++ b/src/Engine/Graph/ParameterNode.cpp
@@ -42,7 +42,7 @@ ParameterNode::ParameterNode(Graph* graph) : InputNode(graph)
mShowControls = false;
mShowInputs = false;
- mSampleRate = 128;
+ mSampleRate = 0.0;
mShowWidget = true;
mEnableWidget = true;
@@ -85,7 +85,7 @@ void ParameterNode::Init()
// sample rate
Core::AttributeSettings* attributeSampleRate = RegisterAttribute("Sample Rate", "sampleRate", "Sample rate of the output channel.", Core::ATTRIBUTE_INTERFACETYPE_FLOATSPINNER);
- attributeSampleRate->SetDefaultValue( Core::AttributeFloat::Create(mSampleRate) );
+ attributeSampleRate->SetDefaultValue( Core::AttributeFloat::Create(0.0) );
attributeSampleRate->SetMinValue( Core::AttributeFloat::Create(0) );
attributeSampleRate->SetMaxValue( Core::AttributeFloat::Create(DBL_MAX) );
@@ -166,6 +166,10 @@ void ParameterNode::ReInit(const Time& elapsed, const Time& delta)
void ParameterNode::Start(const Time& elapsed)
{
+ BciDevice* bci = GetEngine()->GetActiveBci();
+ mSampleRate = mSampleRate > 0.01 ? mSampleRate :
+ bci ? bci->GetSampleRate() : DEFAULTSAMPLERATE;
+
// create sensors before start
UpdateSensors();
@@ -173,7 +177,7 @@ void ParameterNode::Start(const Time& elapsed)
InputNode::Start(elapsed);
UpdateNames();
-
+
// configure clock and start it at current elapsed time
mClock.Reset();
mClock.SetFrequency(mSampleRate);
@@ -256,7 +260,9 @@ void ParameterNode::OnAttributesChanged()
{
ResetAsync();
- mSampleRate = sampleRate;
+ BciDevice* bci = GetEngine()->GetActiveBci();
+ mSampleRate = sampleRate > 0.01 ? sampleRate :
+ bci ? bci->GetSampleRate() : DEFAULTSAMPLERATE;
}
// update values if default value was changed
@@ -382,7 +388,6 @@ void ParameterNode::GenerateSamples()
void ParameterNode::UpdateSensors()
{
const uint32 numChannels = GetInt32Attribute(ATTRIB_NUMCHANNELS);
- const uint32 sampleRate = GetFloatAttribute(ATTRIB_SAMPLERATE);
mValues.Resize(numChannels);
mSensors.Resize(numChannels);
@@ -395,7 +400,7 @@ void ParameterNode::UpdateSensors()
mSensors[i].Reset();
mSensors[i].GetChannel()->SetBufferSize(10);
mSensors[i].SetDriftCorrectionEnabled(false);
- mSensors[i].GetChannel()->SetSampleRate(sampleRate);
+ mSensors[i].GetChannel()->SetSampleRate(mSampleRate);
mTempString.Format("%.2f", mValues[i]);
mSensors[i].GetChannel()->SetName(mTempString.AsChar());
GetOutputPort(OUTPUTPORT_VALUE).GetChannels()->AddChannel(mSensors[i].GetChannel());
diff --git a/src/Engine/Graph/ParameterNode.h b/src/Engine/Graph/ParameterNode.h
index 04fed47cb..11e33f48c 100644
--- a/src/Engine/Graph/ParameterNode.h
+++ b/src/Engine/Graph/ParameterNode.h
@@ -34,6 +34,8 @@
class ENGINE_API ParameterNode : public InputNode
{
public:
+ static constexpr const double DEFAULTSAMPLERATE = 128.0;
+
enum { TYPE_ID = 0x0003 };
static const char* Uuid () { return "0a2f1004-bb6b-11e4-8dfc-aa07a5b093db"; }
diff --git a/src/Engine/Graph/SPNode.cpp b/src/Engine/Graph/SPNode.cpp
index 8e9eca3d6..cb8305ec5 100644
--- a/src/Engine/Graph/SPNode.cpp
+++ b/src/Engine/Graph/SPNode.cpp
@@ -177,7 +177,7 @@ void SPNode::ReInit(const Time& elapsed, const Time& delta)
{
if (mInputChannels.GetChannel(i)->GetSampleRate() <= 0)
{
- mIsInitialized = false;
+ //mIsInitialized = false;
SetError(ERROR_INPUT_CONSTANT_SAMPLERATE, "Input must have a valid sample rate.");
return;
}
@@ -191,7 +191,7 @@ void SPNode::ReInit(const Time& elapsed, const Time& delta)
{
if (mInputReader.HasUniformSampleRate() == false)
{
- mIsInitialized = false;
+ //mIsInitialized = false;
SetError(ERROR_INPUT_MATCHING_SAMPLERATES, "Input sample rates are incompatible.");
return;
}
diff --git a/src/Engine/Version.h b/src/Engine/Version.h
index 7be6d6864..8604894e2 100644
--- a/src/Engine/Version.h
+++ b/src/Engine/Version.h
@@ -26,6 +26,6 @@
#define NEUROMORE_ENGINE_VERSION_MAJOR 1
#define NEUROMORE_ENGINE_VERSION_MINOR 7
-#define NEUROMORE_ENGINE_VERSION_PATCH 1
+#define NEUROMORE_ENGINE_VERSION_PATCH 2
#endif
diff --git a/src/QtBase/AttributeWidgets/AttributeWidgets.cpp b/src/QtBase/AttributeWidgets/AttributeWidgets.cpp
index 13fed3245..ffc77438c 100644
--- a/src/QtBase/AttributeWidgets/AttributeWidgets.cpp
+++ b/src/QtBase/AttributeWidgets/AttributeWidgets.cpp
@@ -632,6 +632,7 @@ void StringArrayAttributeWidget::OnStringChange()
assert( sender()->inherits("QLineEdit") == true );
QLineEdit* widget = qobject_cast( sender() );
String widgetText;
+ String attribText;
// get the number of attributes and iterate through them
const uint32 numAttributes = mAttributes.Size();
@@ -639,7 +640,7 @@ void StringArrayAttributeWidget::OnStringChange()
for (uint32 i=0; i(mAttributes[i]);
- const char* attribText = attribute->AsString().AsChar();
+ attribText = attribute->AsString();
widgetText = FromQtString(widget->text());
if (widgetText.Compare(attribText) != 0)
diff --git a/src/QtBase/Networking/OscServer.cpp b/src/QtBase/Networking/OscServer.cpp
index ceffcb1d2..4e1b1abf2 100644
--- a/src/QtBase/Networking/OscServer.cpp
+++ b/src/QtBase/Networking/OscServer.cpp
@@ -84,16 +84,14 @@ void OscServer::Init()
// initialize the socket bind mode, reuse UDP adresses
QAbstractSocket::BindMode bindMode = QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint;
- // bind the udp sockets
- //if (mLocalEndpoint.isNull() == true)
- mUdpSocket->bind(mUdpPort, bindMode);
- //else
- // mUdpSocket->bind(mLocalEndpoint, mUdpPort, bindMode);
-
-
- connect(mUdpSocket, SIGNAL(readyRead()), this, SLOT(OnReceiveUdpDatagram()));
-
- mUdpOutSocket->bind(mLocalEndpoint, mRemoteUdpPort, bindMode);
+ // setup listen udp port
+ if (mUdpSocket->bind(mUdpPort, bindMode))
+ connect(mUdpSocket, SIGNAL(readyRead()), this, SLOT(OnReceiveUdpDatagram()));
+ else
+ LogError("Failed to bind UDP socket.");
+
+ // setup output udp port (no need to bind!)
+ //mUdpOutSocket->bind(mLocalEndpoint, mRemoteUdpPort, bindMode);
const uint32 FPS = 100;
mTimer = new QTimer(this);
diff --git a/src/QtBase/Version.h b/src/QtBase/Version.h
index 39051e9e9..9d085dd5e 100644
--- a/src/QtBase/Version.h
+++ b/src/QtBase/Version.h
@@ -9,6 +9,6 @@
#define NEUROMORE_QTBASE_VERSION_MAJOR 1
#define NEUROMORE_QTBASE_VERSION_MINOR 7
-#define NEUROMORE_QTBASE_VERSION_PATCH 1
+#define NEUROMORE_QTBASE_VERSION_PATCH 2
#endif
\ No newline at end of file
diff --git a/src/Studio/Devices/BrainFlow/BrainFlowDriver.cpp b/src/Studio/Devices/BrainFlow/BrainFlowDriver.cpp
index 25bdf86e1..cf962d961 100644
--- a/src/Studio/Devices/BrainFlow/BrainFlowDriver.cpp
+++ b/src/Studio/Devices/BrainFlow/BrainFlowDriver.cpp
@@ -33,7 +33,7 @@
using namespace Core;
-BrainFlowDriver::BrainFlowDriver() : DeviceDriver(true)
+BrainFlowDriver::BrainFlowDriver() : DeviceDriver(Branding::DefaultBrainflowEnabled)
{
CORE_EVENTMANAGER.AddEventHandler(this);
AddSupportedDevice(BrainFlowDevice::TYPE_ID);
diff --git a/src/Studio/Devices/OpenBCI/OpenBCIDriver.cpp b/src/Studio/Devices/OpenBCI/OpenBCIDriver.cpp
index b3ed01a15..f69d61d0c 100644
--- a/src/Studio/Devices/OpenBCI/OpenBCIDriver.cpp
+++ b/src/Studio/Devices/OpenBCI/OpenBCIDriver.cpp
@@ -34,7 +34,7 @@
using namespace Core;
// constructor
-OpenBCIDriver::OpenBCIDriver() : DeviceDriver(false), EventHandler()
+OpenBCIDriver::OpenBCIDriver() : DeviceDriver(Branding::DefaultOpenBCIEnabled), EventHandler()
{
LogInfo("Constructing OpenBCI device driver ...");
diff --git a/src/Studio/Plugins/Devices/BciDeviceWidget.cpp b/src/Studio/Plugins/Devices/BciDeviceWidget.cpp
index 03b81eef2..1a3abc45a 100644
--- a/src/Studio/Plugins/Devices/BciDeviceWidget.cpp
+++ b/src/Studio/Plugins/Devices/BciDeviceWidget.cpp
@@ -29,12 +29,6 @@
using namespace Core;
-#if defined(NEUROMORE_BRANDING_ANT) && defined(PRODUCTION_BUILD)
-#define NEUROMORE_MINIMAL_DEVICE_STATS true
-#else
-#define NEUROMORE_MINIMAL_DEVICE_STATS false
-#endif
-
// constructor
BciDeviceWidget::BciDeviceWidget(BciDevice* device, QWidget* parent) : DeviceWidget(device, parent)
{
@@ -56,9 +50,13 @@ void BciDeviceWidget::Init()
// init base
DeviceWidget::Init();
+ // layouts from base class
+ QHBoxLayout* primaryLayout = GetPrimaryDeviceInfoLayout();
+ QHBoxLayout* secondaryLayout = GetSecondaryDeviceInfoLayout();
+
// add signal quality widget
mSignalQualityWidget = new SignalQualityWidget(this);
- GetSecondaryDeviceInfoLayout()->addWidget(mSignalQualityWidget);
+ secondaryLayout->addWidget(mSignalQualityWidget);
// hide/show signal quality widget (if device is not wireless)
if (mBciDevice->HasWirelessIndicator())
@@ -66,19 +64,131 @@ void BciDeviceWidget::Init()
else
mSignalQualityWidget->hide();
+ // impedance grid widget (container)
+ mImpedanceGridWidget = new QWidget(this);
+
+ // impedance grid
+ mImpedanceGrid = new QGridLayout(mImpedanceGridWidget);
+ mImpedanceGrid->setAlignment(Qt::AlignCenter);
+ mImpedanceGrid->setSizeConstraint(QLayout::SetMinimumSize);
+ mImpedanceGrid->setContentsMargins(QMargins(16, 0, 0, 0));
+
+ const QSize lblsize(30, 20);
+ const QSize valsize(50, 20);
+
+ QLabel* headlbl = new QLabel();
+ headlbl->setFixedSize(lblsize);
+ headlbl->setText("CH");
+ headlbl->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ headlbl->setStyleSheet("background-color: black;");
+ headlbl->setAlignment(Qt::AlignCenter);
+
+ QLabel* headval = new QLabel();
+ headval->setFixedSize(valsize);
+ headval->setText("kOhm");
+ headval->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ headval->setStyleSheet("background-color: black;");
+ headval->setAlignment(Qt::AlignCenter);
+
+ mImpedanceGrid->addWidget(headlbl, 0, 0);
+ mImpedanceGrid->addWidget(headval, 0, 1);
+
+ if (mBciDevice)
+ {
+ const uint32 numSensors = std::min(8U, mBciDevice->GetNumNeuroSensors());
+ for (uint32_t i = 0; i < numSensors; i++)
+ {
+ Sensor* sensor = mBciDevice->GetNeuroSensor(i);
+
+ // name label
+ QLabel* lbl = new QLabel();
+ lbl->setFixedSize(lblsize);
+ lbl->setText(sensor->GetName());
+ lbl->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ lbl->setAlignment(Qt::AlignCenter);
+
+ if (Branding::HasColorCodesForChannels)
+ {
+ switch (i)
+ {
+ case 0: lbl->setStyleSheet("color: brown; background-color: black;"); break;
+ case 1: lbl->setStyleSheet("color: red; background-color: black;"); break;
+ case 2: lbl->setStyleSheet("color: orange; background-color: black;"); break;
+ case 3: lbl->setStyleSheet("color: yellow; background-color: black;"); break;
+ case 4: lbl->setStyleSheet("color: green; background-color: black;"); break;
+ case 5: lbl->setStyleSheet("color: blue; background-color: black;"); break;
+ case 6: lbl->setStyleSheet("color: purple; background-color: black;"); break;
+ case 7: lbl->setStyleSheet("color: grey; background-color: black;"); break;
+ default: lbl->setStyleSheet("color: white; background-color: black;"); break;
+ }
+ }
+ else
+ lbl->setStyleSheet("color: white; background-color: black;");
+
+ // value label
+ QLabel* val = new QLabel();
+ val->setFixedSize(valsize);
+ val->setText(QString().sprintf("%5.1f", mBciDevice->GetImpedance(i)));
+ val->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ val->setAlignment(Qt::AlignCenter);
+
+ mImpedanceGrid->addWidget(lbl, i+1, 0);
+ mImpedanceGrid->addWidget(val, i+1, 1);
+ }
+
+ for (uint32_t i = 0; i < 3; i++)
+ {
+ // name label
+ QLabel* lbl = new QLabel();
+ lbl->setFixedSize(lblsize);
+ lbl->setText("AVG");
+ lbl->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ lbl->setAlignment(Qt::AlignCenter);
+ lbl->setStyleSheet("color: white; background-color: black;");
+
+ // name
+ switch (i)
+ {
+ case 0: lbl->setText("MIN"); break;
+ case 1: lbl->setText("AVG"); break;
+ case 2: lbl->setText("MAX"); break;
+ default: break;
+ }
+
+ // value label
+ QLabel* val = new QLabel();
+ val->setFixedSize(valsize);
+ val->setText(QString().sprintf("%5.1f", 0.0));
+ val->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ val->setAlignment(Qt::AlignCenter);
+ val->setStyleSheet("background-color: black;");
+
+ mImpedanceGrid->addWidget(lbl, i+numSensors+1, 0);
+ mImpedanceGrid->addWidget(val, i+numSensors+1, 1);
+ }
+ }
+
+ primaryLayout->addWidget(mImpedanceGridWidget);
+
+ // electrode widget container
+ mEEGElectrodeWidgetContainer = new QWidget(this);
+ mEEGElectrodeWidgetContainer->setMinimumSize(256, 256);
+ mEEGElectrodeWidgetContainer->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+
// electrode widget
- mEEGElectrodeWidget = new EEGElectrodesWidget(this);
+ mEEGElectrodeWidget = new EEGElectrodesWidget(mEEGElectrodeWidgetContainer);
// set minimuim size and expanding
- mEEGElectrodeWidget->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed);
+ mEEGElectrodeWidget->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);
mEEGElectrodeWidget->setFixedSize(256,256);
- // add it to primary layout
- QVBoxLayout* primaryLayout = GetPrimaryDeviceInfoLayout();
- primaryLayout ->addWidget(mEEGElectrodeWidget);
+ primaryLayout ->addWidget(mEEGElectrodeWidgetContainer);
mBciDevice = static_cast(mDevice);
mEEGElectrodeWidget->SetDevice(mBciDevice);
+
+ mImpedanceGridWidget->hide();
+ mEEGElectrodeWidgetContainer->hide();
}
@@ -97,6 +207,67 @@ void BciDeviceWidget::UpdateInterface()
// update impedance test widget
if (mDeviceTestWidget != NULL && mDeviceTestWidget->isVisible())
static_cast(mDeviceTestWidget)->UpdateInterface();
+
+ // update impedance grid
+ if (mBciDevice && mImpedanceGridWidget && mImpedanceGrid && mImpedanceGrid->rowCount() > 0)
+ {
+ Classifier* classifier = GetEngine()->GetActiveClassifier();
+ mImpedanceGridWidget->setVisible(mBciDevice->HasEegContactQualityIndicator());
+ mEEGElectrodeWidgetContainer->setVisible(mBciDevice->HasElectrodePositions());
+ const uint32 numSensors = std::min(
+ (uint32)mImpedanceGrid->rowCount()-4U,
+ std::min(8U, mBciDevice->GetNumNeuroSensors()));
+ const uint32 numUsedSensors = classifier ? classifier->GetNumUsedSensors() : 0U;
+ double max = DBL_MIN;
+ double avg = 0.0;
+ double min = DBL_MAX;
+ uint32 cnt = 0;
+ for (uint32_t i = 0; i < numSensors; i++)
+ {
+ Sensor* sensor = mBciDevice->GetNeuroSensor(i);
+ double impedance = mBciDevice->GetImpedance(i);
+ Color color = sensor->GetContactQualityColor();
+ const bool isused = classifier && classifier->IsSensorUsed(sensor);
+
+ // value label in grid
+ QLabel* item = (QLabel*)mImpedanceGrid->itemAtPosition(i + 1, 1)->widget();
+
+ if (impedance >= 0.1)
+ item->setText(QString().sprintf("%5.1f", impedance));
+ else
+ item->setText("");
+
+ if (numUsedSensors == 0 || isused)
+ {
+ if (impedance > 0.1)
+ {
+ max = impedance > max ? impedance : max;
+ min = impedance < min ? impedance : min;
+ avg += impedance;
+ cnt++;
+ }
+ }
+ else
+ color *= 0.4; // same factor as in EEGElectrodesWidget.cpp
+
+ item->setStyleSheet(QString("color: black; background-color: %1;").arg(
+ color.ToHexString().AsChar()));
+ }
+
+ if (cnt) avg /= cnt;
+
+ QLabel* lblmin = (QLabel*)mImpedanceGrid->itemAtPosition(numSensors + 1, 1)->widget();
+ if (min >= 0.1 && min < DBL_MAX) lblmin->setText(QString().sprintf("%5.1f", min));
+ else lblmin->setText("");
+
+ QLabel* lblavg = (QLabel*)mImpedanceGrid->itemAtPosition(numSensors + 2, 1)->widget();
+ if (avg >= 0.1) lblavg->setText(QString().sprintf("%5.1f", avg));
+ else lblavg->setText("");
+
+ QLabel* lblmax = (QLabel*)mImpedanceGrid->itemAtPosition(numSensors + 3, 1)->widget();
+ if (max >= 0.1) lblmax->setText(QString().sprintf("%5.1f", max));
+ else lblmax->setText("");
+ }
}
@@ -137,12 +308,12 @@ void BciDeviceWidget::AddSensorItems(QTreeWidgetItem* parent)
// add signal quality subitem
item = new QTreeWidgetItem(sensorItem);
item->setText(0, "Signal Quality");
- item->setHidden(NEUROMORE_MINIMAL_DEVICE_STATS);
+ item->setHidden(Branding::HasMinimalDeviceInfo);
// add current value subitem
item = new QTreeWidgetItem(sensorItem);
item->setText(0, "Current Value");
- item->setHidden(NEUROMORE_MINIMAL_DEVICE_STATS);
+ item->setHidden(Branding::HasMinimalDeviceInfo);
// add sample rate subitem
item = new QTreeWidgetItem(sensorItem);
@@ -151,17 +322,16 @@ void BciDeviceWidget::AddSensorItems(QTreeWidgetItem* parent)
// add drift correction subitem
item = new QTreeWidgetItem(sensorItem);
item->setText(0, "Drift (Corrected)");
- item->setHidden(NEUROMORE_MINIMAL_DEVICE_STATS);
+ item->setHidden(Branding::HasMinimalDeviceInfo);
// add burst size subitem
item = new QTreeWidgetItem(sensorItem);
item->setText(0, "Max/Avg Burst Size");
- item->setHidden(NEUROMORE_MINIMAL_DEVICE_STATS);
// add burst size subitem
item = new QTreeWidgetItem(sensorItem);
item->setText(0, "Max Latency");
- item->setHidden(NEUROMORE_MINIMAL_DEVICE_STATS);
+ item->setHidden(Branding::HasMinimalDeviceInfo);
// add sample counter subitem
item = new QTreeWidgetItem(sensorItem);
@@ -174,7 +344,7 @@ void BciDeviceWidget::AddSensorItems(QTreeWidgetItem* parent)
// add total memory subitem
item = new QTreeWidgetItem(sensorItem);
item->setText(0, "Memory Used");
- item->setHidden(NEUROMORE_MINIMAL_DEVICE_STATS);
+ item->setHidden(Branding::HasMinimalDeviceInfo);
}
}
diff --git a/src/Studio/Plugins/Devices/BciDeviceWidget.h b/src/Studio/Plugins/Devices/BciDeviceWidget.h
index bbe1daf56..092280f56 100644
--- a/src/Studio/Plugins/Devices/BciDeviceWidget.h
+++ b/src/Studio/Plugins/Devices/BciDeviceWidget.h
@@ -53,8 +53,11 @@ class BciDeviceWidget : public DeviceWidget
private:
BciDevice* mBciDevice; // reference to BciDevice
+ QWidget* mEEGElectrodeWidgetContainer;
EEGElectrodesWidget* mEEGElectrodeWidget; // eeg sensor status
- SignalQualityWidget* mSignalQualityWidget;
+ SignalQualityWidget* mSignalQualityWidget;
+ QWidget* mImpedanceGridWidget;
+ QGridLayout* mImpedanceGrid;
};
#endif
diff --git a/src/Studio/Plugins/Devices/DeviceWidget.cpp b/src/Studio/Plugins/Devices/DeviceWidget.cpp
index cd0bcedbb..0df621f98 100644
--- a/src/Studio/Plugins/Devices/DeviceWidget.cpp
+++ b/src/Studio/Plugins/Devices/DeviceWidget.cpp
@@ -42,8 +42,7 @@ DeviceWidget::DeviceWidget(Device* device, QWidget* parent) : QWidget(parent)
mDeviceTestWidget = NULL;
mDevice = device;
-
- setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Maximum );
+ setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding);
}
@@ -60,6 +59,7 @@ void DeviceWidget::Init()
mLayout = new QGridLayout();
mLayout->setMargin(0);
//mLayout->setSpacing(0);
+ mLayout->setAlignment(Qt::AlignTop);
//mLayout->addWidget(QLabel( mDevice->GetName().AsChar() ));
@@ -72,29 +72,34 @@ void DeviceWidget::Init()
if (existsFileTest.exists() == true)
{
mDeviceIcon = new QLabel();
- mDeviceIcon->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+ mDeviceIcon->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
mLayout->addWidget(mDeviceIcon, 0, 0);
// scale and apply icon
QPixmap icon(iconFilename.AsChar());
- icon = icon.scaledToWidth(130, Qt::TransformationMode::SmoothTransformation);
+ icon = icon.scaledToWidth(160, Qt::TransformationMode::SmoothTransformation);
mDeviceIcon->setPixmap(icon);
}
else
mDeviceIcon = NULL;
// middle: secondary device info widgets (battery, signal etc)
- mSecondaryDeviceInfoLayout = new QVBoxLayout();
- mSecondaryDeviceInfoLayout->setMargin(0);
+ mSecondaryDeviceInfoLayout = new QHBoxLayout();
+ mSecondaryDeviceInfoLayout->setContentsMargins(0, 8, 0, 8);
+ mSecondaryDeviceInfoLayout->setAlignment(Qt::AlignLeft);
+ mSecondaryDeviceInfoLayout->setSizeConstraint(QLayout::SetMinimumSize);
mBatteryStatusWidget = new BatteryStatusWidget(this);
mBatteryStatusWidget->setVisible(mDevice->HasBatteryIndicator());
+
mSecondaryDeviceInfoLayout->addWidget(mBatteryStatusWidget);
- mLayout->addLayout(mSecondaryDeviceInfoLayout, 1, 0);
+ mLayout->addLayout(mSecondaryDeviceInfoLayout, 1, 0, Qt::AlignLeft | Qt::AlignVCenter);
// bottom: info/test buttons
QSize buttonSize = QSize(65, 25);
QHBoxLayout* buttonLayout = new QHBoxLayout();
+ buttonLayout->setAlignment(Qt::AlignLeft);
+ buttonLayout->setSizeConstraint(QLayout::SetMinimumSize);
mDeviceInfoButton = new QPushButton("Info");
mDeviceInfoButton->setCheckable(true);
mDeviceInfoButton->setIcon(GetQtBaseManager()->FindIcon("/Images/Icons/Info.png"));
@@ -115,29 +120,26 @@ void DeviceWidget::Init()
mDeviceTestButton->setFixedSize(buttonSize);
connect(mDeviceTestButton, SIGNAL(clicked()), this, SLOT(OnDeviceTestButtonPressed()));
buttonLayout->addWidget(mDeviceTestButton);
- InitDeviceTestWidget();
}
else
{
mDeviceTestButton = NULL;
}
-
- // add spacer widget
- QWidget* spacerWidget = new QWidget();
- spacerWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Ignored);
- buttonLayout->addWidget(spacerWidget);
+
+ InitDeviceTestWidget();
// add hor. button layout to main layout
- mLayout->addLayout(buttonLayout, 2, 0);
+ mLayout->addLayout(buttonLayout, 2, 0, Qt::AlignLeft);
// 2) right half of widget area (primary device info)
- mPrimaryDeviceInfoLayout = new QVBoxLayout();
+ mPrimaryDeviceInfoLayout = new QHBoxLayout();
mPrimaryDeviceInfoLayout->setMargin(0);
- mLayout->addLayout(mPrimaryDeviceInfoLayout, 0, 1, 3, 1);
+ mLayout->addLayout(mPrimaryDeviceInfoLayout, 0, 1, 4, 1, Qt::AlignTop);
// 3) device information tree (lower half of main layout)
mDeviceInfoTree = new QTreeWidget();
- mDeviceInfoTree->setFixedHeight(200);
+ mDeviceInfoTree->setMinimumHeight(70);
+ mDeviceInfoTree->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
mDeviceInfoTree->setSelectionMode( QAbstractItemView::SingleSelection);
mDeviceInfoTree->setSortingEnabled(false);
mDeviceInfoTree->setAlternatingRowColors(true);
@@ -156,7 +158,7 @@ void DeviceWidget::Init()
mDeviceInfoTree->hide();
// add tree
- mLayout->addWidget(mDeviceInfoTree, 3, 0, 1, 2);
+ mLayout->addWidget(mDeviceInfoTree, 4, 0, 1, 2);
setLayout(mLayout);
}
@@ -220,21 +222,24 @@ void DeviceWidget::InitDeviceInfoWidget()
void DeviceWidget::InitDeviceTestWidget()
{
- if (mDevice->HasTestMode() == false || mDeviceTestWidget != NULL)
+ if ((!mDevice->HasTestMode() && !mDevice->IsTestRunning()) || mDeviceTestWidget != NULL)
return;
mDeviceTestWidget = CreateDeviceTestWidget();
- mLayout->addWidget(mDeviceTestWidget, 4, 0, 1, 2);
+ mLayout->addWidget(mDeviceTestWidget, 3, 0, 1, 1);
- if (mDevice->IsTestRunning())
- {
- mDeviceTestWidget->setVisible(true);
- mDeviceTestButton->setText("Close");
- }
- else
+ if (mDevice->HasTestMode())
{
- mDeviceTestWidget->setVisible(false);
- mDeviceTestButton->setText("Test");
+ if (mDevice->IsTestRunning())
+ {
+ mDeviceTestWidget->setVisible(true);
+ mDeviceTestButton->setText("Stop");
+ }
+ else
+ {
+ mDeviceTestWidget->setVisible(false);
+ mDeviceTestButton->setText("Test");
+ }
}
}
@@ -246,12 +251,14 @@ void DeviceWidget::AddDeviceItems()
item->setText(0, "Device Name");
item->setText(1, mDevice->GetName().AsChar());
mDeviceInfoTree->addTopLevelItem(item);
+ item->setHidden(Branding::HasMinimalDeviceInfo);
// Memory usage
item = new QTreeWidgetItem();
item->setText(0, "Memory Used");
item->setText(1, "0 KB");
mDeviceInfoTree->addTopLevelItem(item);
+ item->setHidden(Branding::HasMinimalDeviceInfo);
}
@@ -442,8 +449,10 @@ void DeviceWidget::OnDeviceInfoButtonPressed()
mDeviceInfoTree->setVisible(mShowInfoWidget);
// update Button text
- if (mShowInfoWidget == true)
+ if (mShowInfoWidget == true)
+ {
mDeviceInfoButton->setText("Close");
+ }
else
mDeviceInfoButton->setText("Info");
@@ -487,7 +496,7 @@ void DeviceWidget::OnDeviceTestButtonPressed()
if (mDevice->IsTestRunning())
{
mDeviceTestWidget->setVisible(true);
- mDeviceTestButton->setText("Close");
+ mDeviceTestButton->setText("Stop");
}
else
{
diff --git a/src/Studio/Plugins/Devices/DeviceWidget.h b/src/Studio/Plugins/Devices/DeviceWidget.h
index e46cd86b0..ce4e740e6 100644
--- a/src/Studio/Plugins/Devices/DeviceWidget.h
+++ b/src/Studio/Plugins/Devices/DeviceWidget.h
@@ -73,8 +73,8 @@ class DeviceWidget : public QWidget
// UI elements
QGridLayout* mLayout; // main layout
- QVBoxLayout* mPrimaryDeviceInfoLayout; // the right half of the device status panel
- QVBoxLayout* mSecondaryDeviceInfoLayout; // the area on the left under the device icon
+ QHBoxLayout* mPrimaryDeviceInfoLayout; // the right half of the device status panel
+ QHBoxLayout* mSecondaryDeviceInfoLayout; // the area on the left under the device icon
QLabel* mDeviceIcon; // photographic icon (large size)
QPushButton* mDeviceInfoButton; // show/hide button for device information tree
QPushButton* mDeviceTestButton; // show/hide button for device information test
@@ -93,8 +93,8 @@ class DeviceWidget : public QWidget
// shared ui elements
QWidget* mDeviceTestWidget;
- inline QVBoxLayout* GetPrimaryDeviceInfoLayout() { return mPrimaryDeviceInfoLayout; }
- inline QVBoxLayout* GetSecondaryDeviceInfoLayout() { return mSecondaryDeviceInfoLayout; }
+ inline QHBoxLayout* GetPrimaryDeviceInfoLayout() { return mPrimaryDeviceInfoLayout; }
+ inline QHBoxLayout* GetSecondaryDeviceInfoLayout() { return mSecondaryDeviceInfoLayout; }
void AddDeviceInfo (const char* name, QWidget* value);
// helpers
diff --git a/src/Studio/Plugins/Experience/ExperienceWidget.cpp b/src/Studio/Plugins/Experience/ExperienceWidget.cpp
index 2a87511f7..60ed672d1 100644
--- a/src/Studio/Plugins/Experience/ExperienceWidget.cpp
+++ b/src/Studio/Plugins/Experience/ExperienceWidget.cpp
@@ -502,7 +502,7 @@ void ExperienceWidget::paintEvent(QPaintEvent* event)
font.setPixelSize( textScaler );
painter.setFont( font );
- const int usedheight = MAINLAYOUTPADDING + mTextEdit->isVisible() ? mTextEdit->height() : 0;
+ const int usedheight = MAINLAYOUTPADDING + (mTextEdit->isVisible() ? mTextEdit->height() : 0);
painter.drawText( QRect( 0, 0, width(), height()-usedheight), Qt::AlignHCenter | Qt::AlignVCenter | Qt::TextWordWrap, mText );
painter.end();
diff --git a/src/Studio/Plugins/ExperienceSelection/ExperienceSelectionWidget.cpp b/src/Studio/Plugins/ExperienceSelection/ExperienceSelectionWidget.cpp
index 349b756a8..247c2f76c 100644
--- a/src/Studio/Plugins/ExperienceSelection/ExperienceSelectionWidget.cpp
+++ b/src/Studio/Plugins/ExperienceSelection/ExperienceSelectionWidget.cpp
@@ -221,16 +221,19 @@ void ExperienceSelectionWidget::CreateWidgetsForFolders(bool downloadAssets)
}
}
} else {
- button->setText(item.GetName());
+
+ QLabel* lbl = new QLabel(button);
+ lbl->setWordWrap(true);
+ lbl->setAlignment(Qt::AlignCenter);
+ lbl->setGeometry(QRect(24, 30, tileSize-48, tileSize));
+ lbl->setStyleSheet("QLabel { color: black; font-size: 18px; }");
+ lbl->setText(item.GetName());
button->setStyleSheet(QString("QPushButton {"
"width: %1px;"
"height: %1px;"
"border-image: url(:/Images/Icons/ExperienceFolder.png);"
- "text-align: center bottom;"
- "font-size: 18px;"
- "color: #FFFFFF;"
- "}").arg(tileSize - 18));
+ "}").arg(tileSize));
}
connect(button, &ImageButton::clicked, this, &ExperienceSelectionWidget::OnItemButtonClicked);
diff --git a/src/Studio/Plugins/SessionControl/ClientInfoWidget.cpp b/src/Studio/Plugins/SessionControl/ClientInfoWidget.cpp
index 7545c5d13..12a428212 100644
--- a/src/Studio/Plugins/SessionControl/ClientInfoWidget.cpp
+++ b/src/Studio/Plugins/SessionControl/ClientInfoWidget.cpp
@@ -47,9 +47,9 @@ bool ClientInfoWidget::Init()
// clients table
mClientTable = new QTableWidget();
- //mClientTable->setMaximumHeight(50);
+ mClientTable->setFixedHeight(100);
//gridLayout->addWidget(new QLabel("Clients"), row, 0);
- gridLayout->addWidget(mClientTable, row, 1);
+ gridLayout->addWidget(mClientTable, row, 1, Qt::AlignTop);
row++;
setLayout(gridLayout);
diff --git a/src/Studio/Plugins/SessionControl/PreSessionWidget.cpp b/src/Studio/Plugins/SessionControl/PreSessionWidget.cpp
index d925d2e77..cb5185789 100644
--- a/src/Studio/Plugins/SessionControl/PreSessionWidget.cpp
+++ b/src/Studio/Plugins/SessionControl/PreSessionWidget.cpp
@@ -43,14 +43,18 @@ void PreSessionWidget::Init()
{
setObjectName("TransparentWidget");
setMinimumHeight(mStartButtonSize);
- setSizePolicy(QSizePolicy::Ignored, QSizePolicy::MinimumExpanding);
+ setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
QHBoxLayout* hLayout = new QHBoxLayout();
hLayout->setMargin(0);
setLayout(hLayout);
+ constexpr int labelwidth = 64;
+
// create the back to selection button
- mBackToSelectionButton = new QPushButton("Back to selection");
+ mBackToSelectionButton = new QPushButton("Back");
+ mBackToSelectionButton->setFixedSize(QSize(mStartButtonSize, mStartButtonSize));
+
hLayout->addWidget(mBackToSelectionButton);
connect( mBackToSelectionButton, &QPushButton::clicked, this, [=]
{
@@ -69,21 +73,19 @@ void PreSessionWidget::Init()
// ------------------------------------------
- QVBoxLayout* vLayout = new QVBoxLayout();
- vLayout->setMargin(0);
- hLayout->addLayout(vLayout);
-
// right side of hLayout: grid layout
- QGridLayout* gLayout = new QGridLayout();
+ gLayout = new QGridLayout();
gLayout->setMargin(0);
- vLayout->addLayout(gLayout);
+
+ hLayout->addLayout(gLayout);
//
// first row of gridlayout
//
// add the level selection label
- mVisSelectionLabel = new QLabel(" Visualization:");
+ mVisSelectionLabel = new QLabel("Visualization:");
+ mVisSelectionLabel->setFixedWidth(labelwidth);
gLayout->addWidget( mVisSelectionLabel, 0, 0 );
// add the level selection button (for local ones)
@@ -92,7 +94,7 @@ void PreSessionWidget::Init()
if (GetManager()->GetVisualizationManager()->GetNumVisualizations() == 0)
mVisSelectionButton->setEnabled(false);
- mVisSelectionButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
+ mVisSelectionButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
mVisSelectionButton->setIcon( GetQtBaseManager()->FindIcon("Images/Icons/Eye.png") );
connect( mVisSelectionButton, &QPushButton::clicked, this, &PreSessionWidget::OnSelectVisualizationClicked);
gLayout->addWidget(mVisSelectionButton, 0, 1);
@@ -100,68 +102,55 @@ void PreSessionWidget::Init()
//
// second row of gridlayout
//
-/*
- // add the total time label
- mTotalTimeLabel = new QLabel(" Duration:");
- gLayout->addWidget( mTotalTimeLabel, 1, 0 );
-
- // row 1, col 1: horizontal layout: spinner+label
- QHBoxLayout* hLayoutBottom = new QHBoxLayout();
- hLayoutBottom->setMargin(0);
- gLayout->addLayout( hLayoutBottom, 1, 1 );
-
- // add the total time spinbox
- mTotalTimeSpinbox = new IntSpinBox();
- mTotalTimeSpinbox->setRange(0, INT_MAX);
- const int defaultTotalTime = (int)GetEngine()->GetSession()->GetTotalTime().InSeconds();
- mTotalTimeSpinbox->setValue(defaultTotalTime);
- hLayoutBottom->addWidget(mTotalTimeSpinbox);
- connect( mTotalTimeSpinbox, SIGNAL(valueChanged(double)), this, SLOT(OnTotalTimeChanged(double)) );
-
- // NOTE disabled it for now
- mTotalTimeSpinbox->setEnabled(false);
-
- // add seconds label
- mTotalTimeSecondsLabel = new QLabel(" seconds");
- hLayoutBottom->addWidget(mTotalTimeSecondsLabel);
-
- // add toolbar spacer widget
- QWidget* spacerWidget = new QWidget();
- spacerWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
- hLayoutBottom->addWidget(spacerWidget);
- */
+ // add the level selection label
+ mElectrodeSelectionLabel = new QLabel("Channels:");
+ mElectrodeSelectionLabel->setFixedWidth(labelwidth);
+ gLayout->addWidget( mElectrodeSelectionLabel, 1, 0 );
+
+ QHBoxLayout* chLayout = new QHBoxLayout();
+ chLayout->setAlignment(Qt::AlignLeft);
+
+ for (uint32_t i = 0; i < NUMELECTRODESELECT; i++)
+ {
+ mElectrodeSelections[i] = new QComboBox();
+ mElectrodeSelections[i]->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+ mElectrodeSelections[i]->setEditable(false);
+ mElectrodeSelections[i]->setAutoCompletion(false);
+
+ connect(mElectrodeSelections[i], &QComboBox::currentTextChanged, this, &PreSessionWidget::OnChannelSelected);
+ chLayout->addWidget(mElectrodeSelections[i]);
+ }
+ gLayout->addLayout(chLayout, 1, 1, Qt::AlignLeft);
+
+ if (Classifier* c = GetEngine()->GetActiveClassifier())
+ UpdateChannels(c->FindMainChannelSelector());
+ else
+ UpdateChannels(0);
//
// third row of gridlayout
//
// add the user label
- mSelectUserLabel = new QLabel(" User:");
- gLayout->addWidget( mSelectUserLabel, 1, 0 );
+ mSelectUserLabel = new QLabel("User:");
+ mSelectUserLabel->setFixedWidth(labelwidth);
+ gLayout->addWidget( mSelectUserLabel, 2, 0 );
// add select user button
- mSelectUserButton = new QPushButton(GetUser()->CreateDisplayableName().AsChar());
- mSelectUserButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
+ mSelectUserButton = new QPushButton(GetSessionUser()->CreateDisplayableName().AsChar());
+ mSelectUserButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
mSelectUserButton->setIcon( GetQtBaseManager()->FindIcon("Images/Icons/Users.png") );
connect( mSelectUserButton, &QPushButton::clicked, this, &PreSessionWidget::OnSelectUserClicked);
- gLayout->addWidget( mSelectUserButton, 1, 1 );
+ gLayout->addWidget( mSelectUserButton, 2, 1);
// show report button
mShowReportButton = new QPushButton("Show Report");
+ mShowReportButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
mShowReportButton->setIcon(GetQtBaseManager()->FindIcon("/Images/Icons/Report.png"));
mShowReportButton->setVisible(false);
- vLayout->addWidget(mShowReportButton);
+ gLayout->addWidget(mShowReportButton, 3, 0, 1, 2);
connect( mShowReportButton, SIGNAL(clicked()), mPlugin, SLOT(ShowReport()) );
-
- if (GetUser()->FindRule("STUDIO_SETTING_EasyWorkflow") != NULL)
- {
- mVisSelectionLabel->hide();
- mVisSelectionButton->hide();
-
- mSelectUserLabel->hide();
- mSelectUserButton->hide();
- }
}
@@ -257,12 +246,98 @@ void PreSessionWidget::ReInit()
}
-void PreSessionWidget::ShowSelectUserButton(bool show)
+void PreSessionWidget::UpdateChannels(ChannelSelectorNode* chs)
{
- mSelectUserLabel->setVisible(show);
- mSelectUserButton->setVisible(show);
+ // update comboboxes values
+ for (uint32_t i = 0; i < NUMELECTRODESELECT; i++)
+ {
+ QComboBox* box = mElectrodeSelections[i];
+
+ // block signals
+ const bool oldState = box->blockSignals(true);
+
+ // clear first
+ box->clear();
+
+ // enable/disable and try set value from channelselector output
+ if (chs && chs->GetNumInputPorts() && chs->GetNumOutputPorts())
+ {
+ const bool singleoutput = chs->GetBoolAttribute(ChannelSelectorNode::ATTRIB_SINGLE_OUTPUT);
+
+ InputPort& firstportin = chs->GetInputPort(0);
+ OutputPort& firstportout = chs->GetOutputPort(0);
+
+ MultiChannel* firstmchin = firstportin.GetChannels();
+ MultiChannel* firstmchout = firstportout.GetChannels();
+
+ // fill choices from first single input multichannel
+ if (firstmchin)
+ {
+ uint32 numchs = firstmchin->GetNumChannels();
+ QStringList qs;
+ for (uint32_t j = 0; j < numchs; j++)
+ qs.push_back(firstmchin->GetChannel(j)->GetName());
+ box->addItems(qs);
+ }
+
+ // set selected text from channel name in multichannel
+ if (singleoutput && firstmchout && i < firstmchout->GetNumChannels())
+ {
+ box->setCurrentText(firstmchout->GetChannel(i)->GetName());
+ box->setEnabled(true);
+ }
+ // set selected text from port name
+ else if (!singleoutput && i < chs->GetNumOutputPorts())
+ {
+ box->setCurrentText(chs->GetOutputPort(i).GetName());
+ box->setEnabled(true);
+ }
+ else
+ {
+ box->setCurrentText("");
+ box->setEnabled(false);
+ }
+ }
+ else
+ {
+ box->setCurrentText("");
+ box->setEnabled(false);
+ }
+
+ // restore signals
+ box->blockSignals(oldState);
+ }
}
+Core::String PreSessionWidget::GetSelectedChannels()
+{
+ Core::String s;
+ for (uint32_t i = 0; i < NUMELECTRODESELECT; i++)
+ {
+ if (!mElectrodeSelections[i]->isEnabled())
+ continue;
+
+ if (!s.IsEmpty())
+ s += ',';
+
+ const QString& cur = mElectrodeSelections[i]->currentText();
+
+ if (cur.isEmpty())
+ return "";
+
+ s += cur.toLatin1().data();
+ }
+ return s;
+}
+
+void PreSessionWidget::OnChannelSelected(const QString& text)
+{
+ QComboBox* box = (QComboBox*)QObject::sender();
+ Core::String chs = GetSelectedChannels();
+ if (chs.IsEmpty())
+ return;
+ emit SelectedChannelsChanged();
+}
// called when the total session time got changed
void PreSessionWidget::OnTotalTimeChanged(double value)
diff --git a/src/Studio/Plugins/SessionControl/PreSessionWidget.h b/src/Studio/Plugins/SessionControl/PreSessionWidget.h
index 65d5fb9a0..e9fdae56a 100644
--- a/src/Studio/Plugins/SessionControl/PreSessionWidget.h
+++ b/src/Studio/Plugins/SessionControl/PreSessionWidget.h
@@ -45,6 +45,8 @@ class PreSessionWidget : public QWidget
{
Q_OBJECT
public:
+ static constexpr const uint32_t NUMELECTRODESELECT = 4;
+
PreSessionWidget(SessionControlPlugin* plugin, QWidget* parent, int buttonSize);
virtual ~PreSessionWidget() {}
@@ -55,20 +57,31 @@ class PreSessionWidget : public QWidget
QPushButton* GetShowReportButton() { return mShowReportButton; }
QPushButton* GetSelectUserButton() { return mSelectUserButton; }
- void ShowSelectUserButton(bool show = true);
+ void UpdateChannels(ChannelSelectorNode* chs = 0);
+ Core::String GetSelectedChannels();
+ inline QString GetSelectedChannel(uint32 idx) { return idx < NUMELECTRODESELECT ? mElectrodeSelections[idx]->currentText() : ""; }
private slots:
void OnTotalTimeChanged(double value);
void OnSelectVisualizationClicked();
void OnSelectUserClicked() { GetMainWindow()->SelectSessionUser(); }
+ void OnChannelSelected(const QString& text);
+
+ signals:
+ void SelectedChannelsChanged();
private:
SessionControlPlugin* mPlugin;
ImageButton* mStartButton;
uint32 mStartButtonSize;
+ QGridLayout* gLayout;
+
QPushButton* mBackToSelectionButton;
+ QLabel* mElectrodeSelectionLabel;
+ QComboBox* mElectrodeSelections[NUMELECTRODESELECT];
+
QLabel* mVisSelectionLabel;
QPushButton* mVisSelectionButton;
diff --git a/src/Studio/Plugins/SessionControl/SessionControlPlugin.cpp b/src/Studio/Plugins/SessionControl/SessionControlPlugin.cpp
index 39e9243c6..e6fe48f25 100644
--- a/src/Studio/Plugins/SessionControl/SessionControlPlugin.cpp
+++ b/src/Studio/Plugins/SessionControl/SessionControlPlugin.cpp
@@ -66,63 +66,65 @@ bool SessionControlPlugin::Init()
QWidget* mainWidget = new QWidget();
QVBoxLayout* mainLayout = new QVBoxLayout();
+ mainLayout->setAlignment(Qt::AlignTop);
mainWidget->setLayout( mainLayout );
+ mainWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
const int32 buttonSize = IMAGEBUTTONSIZE_BIG;
const int32 startButtonSize = IMAGEBUTTONSIZE_BIG;
- // create the dummy widget and a layout
- QWidget* dummyWidget = new QWidget();
- QVBoxLayout* vLayout = new QVBoxLayout();
QHBoxLayout* hLayout = new QHBoxLayout();
- vLayout->setMargin(0);
+ hLayout->setAlignment(Qt::AlignTop);
hLayout->setMargin(0);
- vLayout->addLayout(hLayout);
- dummyWidget->setLayout(vLayout);
// pre-session widget
mPreSessionWidget = new PreSessionWidget(this, NULL, startButtonSize);
+ mPreSessionWidget->setFixedHeight(128);
+ mPreSessionWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
mPreSessionWidget->ReInit();
+
hLayout->addWidget(mPreSessionWidget);
connect(mPreSessionWidget->GetStartButton(), &QPushButton::clicked, this, &SessionControlPlugin::OnStart);
-
- // show user button only if user is allowed to select a client
- if (GetUser()->FindRule("STUDIO_SETTING_SelectUser") == NULL)
- mPreSessionWidget->ShowSelectUserButton(false);
+ connect(mPreSessionWidget, &PreSessionWidget::SelectedChannelsChanged, this, &SessionControlPlugin::OnSelectedChannelsChanged);
// while-session widget
mWhileSessionWidget = new WhileSessionWidget(NULL, buttonSize);
+ mWhileSessionWidget->setFixedHeight(128);
+ mWhileSessionWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
hLayout->addWidget(mWhileSessionWidget);
connect( mWhileSessionWidget->GetStopButton(), SIGNAL(clicked()), this, SLOT(OnStop()) );
connect( mWhileSessionWidget->GetPauseButton(), SIGNAL(clicked()), this, SLOT(OnPause()) );
connect( mWhileSessionWidget->GetContinueButton(), SIGNAL(clicked()), this, SLOT(OnContinue()) );
+ mainLayout->addLayout(hLayout);
+
+ hLayout = new QHBoxLayout();
+ hLayout->setAlignment(Qt::AlignTop);
+
// session-info widget
mSessionInfoWidget = new SessionInfoWidget();
- mSessionInfoWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
- vLayout->addWidget(mSessionInfoWidget);
+ mSessionInfoWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
+ hLayout->addWidget(mSessionInfoWidget);
+
+ mainLayout->addLayout(hLayout);
// add session widget
- dummyWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
- mainLayout->addWidget(dummyWidget);
+ hLayout = new QHBoxLayout();
+ hLayout->setAlignment(Qt::AlignTop);
// add stage widget
- mStageControlWidget = new StageControlWidget(mainWidget);
- mStageControlWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
- mainLayout->addWidget(mStageControlWidget);
+ mStageControlWidget = new StageControlWidget(0);
+ mStageControlWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
+ hLayout->addWidget(mStageControlWidget);
+ mainLayout->addLayout(hLayout);
// add client info widget
#ifndef PRODUCTION_BUILD
- mClientInfoWidget = new ClientInfoWidget(mainWidget);
- mClientInfoWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
+ mClientInfoWidget = new ClientInfoWidget();
+ mClientInfoWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
mainLayout->addWidget(mClientInfoWidget);
#endif
- // add spacer widget
- QWidget* spacerWidget = new QWidget(mainWidget);
- spacerWidget->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::MinimumExpanding);
- mainLayout->addWidget(spacerWidget);
-
// connect to server signals so the interface gets updated
NetworkServer* networkServer = GetNetworkServer();
connect( networkServer, SIGNAL( ClientAdded ( NetworkServerClient* ) ), this, SLOT( OnClientChanged( NetworkServerClient* ) ) );
@@ -227,8 +229,8 @@ void SessionControlPlugin::UpdateWidgets()
const bool isRunning = GetEngine()->GetSession()->IsRunning();
if (isRunning || isPreparing)
{
- mWhileSessionWidget->show();
mPreSessionWidget->hide();
+ mWhileSessionWidget->show();
}
else
{
@@ -263,6 +265,44 @@ void SessionControlPlugin::OnSessionUserChanged(const User& user)
userButton->setText(name.AsChar());
}
+void SessionControlPlugin::OnActiveBciChanged(BciDevice* device)
+{
+
+}
+
+void SessionControlPlugin::OnActiveClassifierChanged(Classifier* classifier)
+{
+ UpdateStartButton();
+ mPreSessionWidget->UpdateChannels(0);
+}
+
+void SessionControlPlugin::OnNodeStarted(Graph* graph, SPNode* node)
+{
+ if (mPreSessionWidget)
+ if (node->GetType() == ChannelSelectorNode::TYPE_ID)
+ if (node->GetBoolAttribute(ChannelSelectorNode::ATTRIB_QUICK_CONFIG))
+ mPreSessionWidget->UpdateChannels((ChannelSelectorNode*)node);
+}
+
+void SessionControlPlugin::OnRemoveNode(Graph* graph, Node* node)
+{
+ if (mPreSessionWidget)
+ if (node->GetType() == ChannelSelectorNode::TYPE_ID)
+ if (node->GetBoolAttribute(ChannelSelectorNode::ATTRIB_QUICK_CONFIG))
+ mPreSessionWidget->UpdateChannels(0);
+}
+
+void SessionControlPlugin::OnAttributeUpdated(Graph* graph, GraphObject* object, Core::Attribute* attribute)
+{
+ if (mPreSessionWidget)
+ if (object->GetType() == ChannelSelectorNode::TYPE_ID)
+ if (attribute == object->GetAttributeValue(ChannelSelectorNode::ATTRIB_QUICK_CONFIG))
+ if (object->GetBoolAttribute(ChannelSelectorNode::ATTRIB_QUICK_CONFIG))
+ mPreSessionWidget->UpdateChannels((ChannelSelectorNode*)object);
+ else
+ mPreSessionWidget->UpdateChannels(0);
+}
+
void SessionControlPlugin::OnRemoveDevice(Device* device)
{
if (GetSession()->IsRunning() == true)
@@ -337,6 +377,10 @@ void SessionControlPlugin::OnMessageReceived( NetworkServerClient* client, Netwo
delete message;
}
+void SessionControlPlugin::OnSelectedChannelsChanged()
+{
+ ApplySettings();
+}
void SessionControlPlugin::CheckStartRequirements()
{
@@ -360,6 +404,14 @@ void SessionControlPlugin::CheckStartRequirements()
const bool deviceBatteryLow = (GetDeviceManager()->IsDevicePowerOk() == false);
const bool deviceMissing = (classifier != NULL && classifier->HasError (DeviceInputNode::ERROR_DEVICE_NOT_FOUND) == true);
+
+ // not runninng session as patient
+ const char* noPatientInfo = "No patient selected.";
+ if (GetUser()->GetIdString() == GetSessionUser()->GetIdString() && (GetUser()->FindRule("ROLE_ClinicClinician") || GetUser()->FindRule("ROLE_ClinicOperator")))
+ mSessionInfoWidget->ShowInfo(SessionInfoWidget::TYPE_WARNING, noPatientInfo, "Please select a patient before starting a session.");
+ else
+ mSessionInfoWidget->RemoveInfo(noPatientInfo);
+
// classifier or statemachine has error
const bool designError = (classifier != NULL && classifier->HasError() == true) || (stateMachine != NULL && stateMachine->HasError() == true);
@@ -381,11 +433,11 @@ void SessionControlPlugin::CheckStartRequirements()
mSessionInfoWidget->RemoveInfo( noPermissionInfo );
// device test
- const char* testModeInfo = "Device is in test mode";
+ /*const char* testModeInfo = "Device is in test mode";
if (deviceTestRunning == true)
mSessionInfoWidget->ShowInfo( SessionInfoWidget::TYPE_INFO, testModeInfo, "Please stop the device test first.");
else
- mSessionInfoWidget->RemoveInfo( testModeInfo );
+ mSessionInfoWidget->RemoveInfo( testModeInfo );*/
// device not connected
const char* noDevicePower = "Device battery is almost empty";
@@ -456,6 +508,9 @@ void SessionControlPlugin::OnStart()
StateMachine* activeStateMachine = GetEngine()->GetActiveStateMachine();
Experience* activeExperience = GetEngine()->GetActiveExperience();
+ if (!activeClassifier)
+ return;
+
// gather all errors an compose them into a string
/* if (activeClassifier != NULL && activeClassifier->HasError() == true)
{
@@ -483,15 +538,18 @@ void SessionControlPlugin::OnStart()
// BEGIN
+ // make sure no session is running
+ CORE_ASSERT(GetSession()->IsRunning() == false);
+
// switch to first stage
EMIT_EVENT( OnSwitchStage(0) );
+ //
+ ApplySettings();
+
// TODO do checks on statemachine / experience?
mPreSessionWidget->setEnabled(false);
- // make sure no session is running
- CORE_ASSERT( GetSession()->IsRunning() == false );
-
GetEngine()->Reset(); // reset engine (also resets session)
GetEngine()->SoftPause(); // but keep it paused
@@ -530,6 +588,41 @@ void SessionControlPlugin::OnParametersLoaded(bool success)
}
+void SessionControlPlugin::ApplySettings()
+{
+ Classifier* activeClassifier = GetEngine()->GetActiveClassifier();
+ StateMachine* activeStateMachine = GetEngine()->GetActiveStateMachine();
+ Experience* activeExperience = GetEngine()->GetActiveExperience();
+
+ if (!activeClassifier)
+ return;
+
+ // get selected channels for session before disabling ui
+ const Core::String selectedChannels = mPreSessionWidget->GetSelectedChannels();
+
+ // apply channel selection on main channel selector if found
+ if (ChannelSelectorNode* chselector = activeClassifier->FindMainChannelSelector())
+ {
+ // remember
+ const bool dirty = activeClassifier->IsDirty();
+ const bool settingsmode = activeClassifier->GetUseSettings();
+
+ // clone classifier settings from experience or create new
+ GraphSettings set = activeExperience ? activeExperience->GetClassifierSettings() : GraphSettings();
+
+ // set the channels on the main selector node
+ AttributeSettings* attrib = chselector->GetAttribute(ChannelSelectorNode::ATTRIB_CHANNELNAMES);
+ set.Add(chselector, attrib->GetInternalName(), selectedChannels);
+
+
+ // apply modified settings and restore state
+ activeClassifier->ApplySettings(set);
+ activeClassifier->SetIsDirty(dirty);
+ activeClassifier->SetUseSettings(settingsmode);
+ }
+}
+
+
void SessionControlPlugin::Start()
{
GetSession()->Reset();
@@ -751,7 +844,7 @@ void SessionControlPlugin::ShowReport()
// called after switching layout
void SessionControlPlugin::OnAfterLoadLayout()
{
- if (GetUser()->FindRule("STUDIO_SETTING_EasyWorkflow") != NULL)
+ /*if (GetUser()->FindRule("STUDIO_SETTING_EasyWorkflow") != NULL)
{
// open visualization select window if one is available and none is running
VisualizationManager* vizManager = GetManager()->GetVisualizationManager();
@@ -760,5 +853,5 @@ void SessionControlPlugin::OnAfterLoadLayout()
VisualizationSelectWindow selectVizWindow(GetMainWindow());
selectVizWindow.exec();
}
- }
+ }*/
}
diff --git a/src/Studio/Plugins/SessionControl/SessionControlPlugin.h b/src/Studio/Plugins/SessionControl/SessionControlPlugin.h
index cf16b0942..570e90a96 100644
--- a/src/Studio/Plugins/SessionControl/SessionControlPlugin.h
+++ b/src/Studio/Plugins/SessionControl/SessionControlPlugin.h
@@ -72,14 +72,20 @@ class SessionControlPlugin : public Plugin, private Core::EventSource, public Co
// update widget visibility
void UpdateWidgets();
+ // apply quick settings from ui on nodes
+ void ApplySettings();
// EVENTS
void OnPreparedSession() override final;
void OnSessionUserChanged(const User& user) override final;
+ void OnActiveBciChanged(BciDevice* device) override final;
void OnActiveExperienceChanged(Experience* experience) override final { UpdateStartButton(); }
- void OnActiveClassifierChanged(Classifier* experience) override final { UpdateStartButton(); }
+ void OnActiveClassifierChanged(Classifier* classifier) override final;
void OnActiveStateMachineChanged(StateMachine* experience) override final { UpdateStartButton(); }
+ void OnNodeStarted(Graph* graph, SPNode* node) override final;
+ void OnRemoveNode(Graph* graph, Node* node) override final;
void OnRemoveDevice(Device* device) override final;
+ void OnAttributeUpdated(Graph* graph, GraphObject* object, Core::Attribute* attribute) override final;
public slots:
void ShowReport();
@@ -89,6 +95,7 @@ class SessionControlPlugin : public Plugin, private Core::EventSource, public Co
void OnStop();
void OnPause();
void OnContinue();
+ void OnSelectedChannelsChanged();
// client and network message callabacks
void OnClientChanged ( NetworkServerClient* client );
diff --git a/src/Studio/Plugins/SessionControl/SessionInfoWidget.cpp b/src/Studio/Plugins/SessionControl/SessionInfoWidget.cpp
index d9b6b12be..575af105c 100644
--- a/src/Studio/Plugins/SessionControl/SessionInfoWidget.cpp
+++ b/src/Studio/Plugins/SessionControl/SessionInfoWidget.cpp
@@ -40,6 +40,7 @@ SessionInfoWidget::SessionInfoWidget(QWidget* parent) : QWidget(parent)
mGridLayout = new QGridLayout();
mGridLayout->setColumnStretch(1,100);
+ mGridLayout->setAlignment(Qt::AlignTop);
setLayout(mGridLayout);
// load icons
@@ -252,7 +253,7 @@ void SessionInfoWidget::UpdateLayout()
// add to row i
mGridLayout->addWidget(info.mIcon, i, 0);
- mGridLayout->addWidget(info.mLabel, i, 1, Qt::AlignLeft);
+ mGridLayout->addWidget(info.mLabel, i, 1);
}
if (numInfos > 0)
diff --git a/src/Studio/Plugins/SessionControl/StageControlWidget.cpp b/src/Studio/Plugins/SessionControl/StageControlWidget.cpp
index bde51a5b0..d1364e64e 100644
--- a/src/Studio/Plugins/SessionControl/StageControlWidget.cpp
+++ b/src/Studio/Plugins/SessionControl/StageControlWidget.cpp
@@ -63,10 +63,11 @@ bool StageControlWidget::Init()
mStageTable->setSelectionBehavior( QAbstractItemView::SelectRows );
mStageTable->setSelectionMode( QAbstractItemView::NoSelection );
//mStageTable->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum);
+ mStageTable->setFixedHeight(80);
connect( mStageTable, SIGNAL( cellDoubleClicked ( int, int ) ), this, SLOT( OnStageTableDoubleClick( int, int ) ) );
- gridLayout->addWidget(mStageTable, row, 0);
+ gridLayout->addWidget(mStageTable, row, 0, Qt::AlignTop);
row++;
setLayout(gridLayout);
diff --git a/src/Studio/Plugins/SessionControl/WhileSessionWidget.cpp b/src/Studio/Plugins/SessionControl/WhileSessionWidget.cpp
index 9fa7a549c..dd4fd6e92 100644
--- a/src/Studio/Plugins/SessionControl/WhileSessionWidget.cpp
+++ b/src/Studio/Plugins/SessionControl/WhileSessionWidget.cpp
@@ -35,7 +35,7 @@ WhileSessionWidget::WhileSessionWidget(QWidget* parent, int buttonSize) : QWidge
setObjectName("TransparentWidget");
setMinimumHeight(buttonSize);
- setSizePolicy(QSizePolicy::Ignored, QSizePolicy::MinimumExpanding);
+ setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
QVBoxLayout* mainLayout = new QVBoxLayout();
mainLayout->setMargin(0);
diff --git a/src/Studio/Version.h b/src/Studio/Version.h
index c33a8c95b..5e8f50e50 100644
--- a/src/Studio/Version.h
+++ b/src/Studio/Version.h
@@ -26,7 +26,7 @@
#define NEUROMORE_STUDIO_VERSION_MAJOR 1
#define NEUROMORE_STUDIO_VERSION_MINOR 7
-#define NEUROMORE_STUDIO_VERSION_PATCH 1
+#define NEUROMORE_STUDIO_VERSION_PATCH 2
// Macros
diff --git a/src/Studio/Widgets/BatteryStatusWidget.cpp b/src/Studio/Widgets/BatteryStatusWidget.cpp
index a14a8f8a0..9b0b70e96 100644
--- a/src/Studio/Widgets/BatteryStatusWidget.cpp
+++ b/src/Studio/Widgets/BatteryStatusWidget.cpp
@@ -32,7 +32,7 @@ using namespace Core;
// constructor
BatteryStatusWidget::BatteryStatusWidget(QWidget* parent) : QWidget(parent)
{
- mPixmapHeight = 24;
+ mPixmapHeight = 18;
setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum );
@@ -57,7 +57,7 @@ BatteryStatusWidget::BatteryStatusWidget(QWidget* parent) : QWidget(parent)
mTextLabel = new QLabel();
mTextLabel->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
QFont timeFont = mTextLabel->font();
- timeFont.setPixelSize(20);
+ timeFont.setPixelSize(14);
mTextLabel->setFont(timeFont);
mainLayout->addWidget( mTextLabel, 0, Qt::AlignLeft | Qt::AlignHCenter );
@@ -153,7 +153,7 @@ void BatteryStatusWidget::SetStatus(double normalizedLevel, EStatus status)
if (mBatteryStatus == STATUS_UNKNOWN)
mTempString = "";
else
- mTempString.Format( "%.0f %%", mBatteryLevel * 100.0 );
+ mTempString.Format( "%.0f%%", mBatteryLevel * 100.0 );
mTextLabel->setText( mTempString.AsChar() );
diff --git a/src/Studio/Widgets/ImpedanceTestWidget.cpp b/src/Studio/Widgets/ImpedanceTestWidget.cpp
index a2efdef67..8b857de53 100644
--- a/src/Studio/Widgets/ImpedanceTestWidget.cpp
+++ b/src/Studio/Widgets/ImpedanceTestWidget.cpp
@@ -38,12 +38,14 @@ ImpedanceTestWidget::Threshold ImpedanceTestWidget::Thresholds[] = {
// constructor
ImpedanceTestWidget::ImpedanceTestWidget(BciDevice* device, QWidget* parent) : QWidget(parent),
- mImpedanceThreshold(&ImpedanceTestWidget::Thresholds[DEFAULTPROFILE])
+ mImpedanceThreshold(&ImpedanceTestWidget::Thresholds[DEFAULTPROFILE]),
+ mPixmapPass(GetQtBaseManager()->FindIcon("Images/Icons/SuccessCircled.png").pixmap(20, 20)),
+ mPixmapFail(GetQtBaseManager()->FindIcon("Images/Icons/FailCircled.png").pixmap(20, 20))
{
mDevice = device;
// expand width but not height
- setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
+ setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum);
// add the main widget
@@ -53,7 +55,7 @@ ImpedanceTestWidget::ImpedanceTestWidget(BciDevice* device, QWidget* parent) : Q
vMainLayout->setMargin(4);
vMainLayout->addWidget(mainWidget);
setLayout(vMainLayout);
- vMainLayout->setAlignment(Qt::AlignLeft);
+ vMainLayout->setAlignment(Qt::AlignLeft | Qt::AlignTop);
// Main Grid Layout
// add the main widget w/ grid layout
@@ -61,65 +63,38 @@ ImpedanceTestWidget::ImpedanceTestWidget(BciDevice* device, QWidget* parent) : Q
mainLayout->setMargin(2);
mainWidget->setLayout(mainLayout);
- // Column 1
+ // Row 1
QLabel* label = new QLabel("Impedance Test");
mainLayout->addWidget(label, 0, 0, 1, 2);
+ // Row 2
label = new QLabel("Threshold:");
- mainLayout->addWidget(label, 1, 0, 2, 1);
+ mainLayout->addWidget(label, 1, 0, 1, 1);
mThresholdComboBox = new QComboBox();
- mThresholdComboBox->setEditable(true);
+ mThresholdComboBox->setEditable(false);
mThresholdComboBox->setMaxCount(NUMPROFILES);
for (size_t i = 0; i < NUMPROFILES; i++)
mThresholdComboBox->addItem(Thresholds[i].name);
connect(mThresholdComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(OnThresholdSelected(int)));
connect(mThresholdComboBox, SIGNAL(editTextChanged(const QString&)), this, SLOT(OnThresholdEdited(const QString&)));
- mThresholdComboBox->setFixedWidth(75);
- mainLayout->addWidget(mThresholdComboBox, 1, 1, 2, 1);
+ mThresholdComboBox->setFixedWidth(100);
+ mainLayout->addWidget(mThresholdComboBox, 1, 1, 1, 1);
// select default threshold
mThresholdComboBox->setCurrentIndex(DEFAULTPROFILE);
OnThresholdSelected(DEFAULTPROFILE);
- // Column 2
- // expanding spacer widget
- QWidget* spacerWidget = new QWidget();
- spacerWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
- mainLayout->addWidget(spacerWidget, 0, 2, 3, 1);
-
- // Column 3
- // min/max/average
- label = new QLabel("Min:"); mainLayout->addWidget(label, 0, 3);
- label = new QLabel("Max:"); mainLayout->addWidget(label, 1, 3);
- label = new QLabel("Avg:"); mainLayout->addWidget(label, 2, 3);
-
- mMinImpedanceLabel = new QLabel("-"); mainLayout->addWidget(mMinImpedanceLabel, 0, 4);
- mMaxImpedanceLabel = new QLabel("-"); mainLayout->addWidget(mMaxImpedanceLabel, 1, 4);
- mAvgImpedanceLabel = new QLabel("-"); mainLayout->addWidget(mAvgImpedanceLabel, 2, 4);
-
- // Column 4
- // fixed size spacer widget
- spacerWidget = new QWidget();
- spacerWidget->setFixedWidth(15);
- mainLayout->addWidget(spacerWidget, 0, 5, 3, 1);
-
- // Column 5
+ // Row 3
+ QLabel* passedlbl = new QLabel("Passed:");
+ mainLayout->addWidget(passedlbl, 2, 0, 1, 1);
+
// Pass/Fail icon
- mPassIconLabel = new QLabel();
- mFailIconLabel = new QLabel();
- const int pixmapSize = 50;
- mPassIconLabel->setPixmap(GetQtBaseManager()->FindIcon("Images/Icons/SuccessCircled.png").pixmap(pixmapSize, pixmapSize));
- mFailIconLabel->setPixmap(GetQtBaseManager()->FindIcon("Images/Icons/FailCircled.png").pixmap(pixmapSize, pixmapSize));
- mPassIconLabel->setVisible(false);
- mFailIconLabel->setVisible(false);
- mPassIconLabel->setToolTip("Passed");
- mFailIconLabel->setVisible("Failed");
-
- QHBoxLayout* hLayout = new QHBoxLayout();
- hLayout->addWidget(mPassIconLabel);
- hLayout->addWidget(mFailIconLabel);
- mainLayout->addLayout(hLayout, 0, 6, 3, 1);
+ mTestLabel = new QLabel();
+ mTestLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ //mTestLabel->setAlignment(Qt::AlignCenter);
+ //mTestLabel->setStyleSheet("color: white; background-color: black;");
+ mainLayout->addWidget(mTestLabel, 2, 1, 1, 1);
}
@@ -131,94 +106,47 @@ ImpedanceTestWidget::~ImpedanceTestWidget()
void ImpedanceTestWidget::UpdateInterface()
{
- Classifier* activeClassifier = GetEngine()->GetActiveClassifier();
-
- // calc min/max/avg impedance accross sensors
-
- // max impedance, if the values is larger we assume the lead is unconnected and ignore the electrode
- const double electrodeActiveDetectionThreshold = 200; // 200 kiloohms
-
- // note: impedance values are transported via the EEG sensors
- double minImpedance = DBL_MAX;
- double maxImpedance = -DBL_MAX;
- double avgImpedance = 0.0;
- uint32 avgCount = 0;
-
- bool testPassed = true;
-
- const uint32 numSensors = mDevice->GetNumNeuroSensors();
- for (uint32 i = 0; i < numSensors; ++i)
- {
- const double impedance = mDevice->GetImpedance(i);
-
- // skip invalid values (Eccept exactly 0.0), dont skip used electrodes
- if (activeClassifier == NULL || (activeClassifier != NULL && activeClassifier->IsSensorUsed(mDevice->GetSensor(i)) == false))
- if (impedance > electrodeActiveDetectionThreshold || impedance <= 0.0)
- continue;
-
- minImpedance = Min(minImpedance, impedance);
- maxImpedance = Max(maxImpedance, impedance);
- avgImpedance += impedance;
- avgCount++;
- }
-
-
- // no active electrode: test failed
- if (avgCount == 0)
- {
- mMinImpedanceLabel->setText("-");
- mMaxImpedanceLabel->setText("-");
- mAvgImpedanceLabel->setText("-");
-
- testPassed = false;
-
- // set sensor contact quality to no-signal
- for (uint32 i = 0; i < numSensors; ++i)
- mDevice->GetNeuroSensor(i)->SetContactQuality(Sensor::CONTACTQUALITY_NO_SIGNAL);
- }
- else
- {
- avgImpedance /= (double)avgCount;
-
- // update min/max/avg labels
- mTempString.Format("%.2f k", minImpedance);
- mMinImpedanceLabel->setText(mTempString.AsChar());
- mTempString.Format("%.2f k", maxImpedance);
- mMaxImpedanceLabel->setText(mTempString.AsChar());
- mTempString.Format("%.2f k", avgImpedance);
- mAvgImpedanceLabel->setText(mTempString.AsChar());
-
- // test passed?
- testPassed = (maxImpedance <= mImpedanceThreshold->yellow);
-
- // set sensor contact quality (reuse electrode widget) to indicate pass/fail of individual electrodes
- for (uint32 i = 0; i < numSensors; ++i)
- {
- const double impedance = mDevice->GetImpedance(i);
-
- if (impedance < 0.1) // == 0.0 case
- mDevice->GetNeuroSensor(i)->SetContactQuality(Sensor::CONTACTQUALITY_NO_SIGNAL);
-
- else if (impedance <= mImpedanceThreshold->green)
- mDevice->GetNeuroSensor(i)->SetContactQuality(Sensor::CONTACTQUALITY_GOOD);
-
- else if (impedance <= mImpedanceThreshold->yellow)
- mDevice->GetNeuroSensor(i)->SetContactQuality(Sensor::CONTACTQUALITY_FAIR);
-
- else if (impedance <= mImpedanceThreshold->orange)
- mDevice->GetNeuroSensor(i)->SetContactQuality(Sensor::CONTACTQUALITY_POOR);
-
- else if (impedance <= mImpedanceThreshold->red)
- mDevice->GetNeuroSensor(i)->SetContactQuality(Sensor::CONTACTQUALITY_VERY_BAD);
-
- else
- mDevice->GetNeuroSensor(i)->SetContactQuality(Sensor::CONTACTQUALITY_NO_SIGNAL);
- }
- }
-
- // update fail/passed icon
- mPassIconLabel->setVisible(testPassed);
- mFailIconLabel->setVisible(!testPassed);
+ constexpr double minthreshold = 0.1;
+
+ Classifier* classifier = GetEngine()->GetActiveClassifier();
+ const uint32 numSensors = mDevice->GetNumNeuroSensors();
+ const uint32 numUsedSensors = classifier ? classifier->GetNumUsedSensors() : 0U;
+
+ bool testok = true;
+
+ for (uint32 i = 0; i < numSensors; ++i)
+ {
+ Sensor* sensor = mDevice->GetNeuroSensor(i);
+ double impedance = mDevice->GetImpedance(i);
+ bool isused = classifier && classifier->IsSensorUsed(sensor);
+
+ // update contact quality on sensor based on selected thresholds
+ if (impedance < minthreshold) sensor->SetContactQuality(Sensor::CONTACTQUALITY_NO_SIGNAL);
+ else if (impedance <= mImpedanceThreshold->green) sensor->SetContactQuality(Sensor::CONTACTQUALITY_GOOD);
+ else if (impedance <= mImpedanceThreshold->yellow) sensor->SetContactQuality(Sensor::CONTACTQUALITY_FAIR);
+ else if (impedance <= mImpedanceThreshold->orange) sensor->SetContactQuality(Sensor::CONTACTQUALITY_POOR);
+ else if (impedance <= mImpedanceThreshold->red) sensor->SetContactQuality(Sensor::CONTACTQUALITY_VERY_BAD);
+ else sensor->SetContactQuality(Sensor::CONTACTQUALITY_NO_SIGNAL);
+
+ // test fails if either a used electrode has poor/bad/no signal
+ // or (if none is used), any electrode has poor/bad/no signal
+ if (isused || numUsedSensors == 0)
+ {
+ switch (sensor->GetContactQuality())
+ {
+ case Sensor::CONTACTQUALITY_POOR:
+ case Sensor::CONTACTQUALITY_VERY_BAD:
+ case Sensor::CONTACTQUALITY_NO_SIGNAL:
+ testok = false;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // update fail/passed icon
+ mTestLabel->setPixmap(testok ? mPixmapPass : mPixmapFail);
}
diff --git a/src/Studio/Widgets/ImpedanceTestWidget.h b/src/Studio/Widgets/ImpedanceTestWidget.h
index 71b576608..cf1d2821e 100644
--- a/src/Studio/Widgets/ImpedanceTestWidget.h
+++ b/src/Studio/Widgets/ImpedanceTestWidget.h
@@ -74,13 +74,10 @@ class ImpedanceTestWidget : public QWidget
Threshold* mImpedanceThreshold;
QComboBox* mThresholdComboBox;
- QLabel* mMinImpedanceLabel;
- QLabel* mMaxImpedanceLabel;
- QLabel* mAvgImpedanceLabel;
- QLabel* mPassIconLabel;
- QLabel* mFailIconLabel;
-
+ QLabel* mTestLabel;
+ QPixmap mPixmapPass;
+ QPixmap mPixmapFail;
};
diff --git a/src/Studio/Widgets/SignalQualityWidget.cpp b/src/Studio/Widgets/SignalQualityWidget.cpp
index 8deb142d2..a12e4e1e4 100644
--- a/src/Studio/Widgets/SignalQualityWidget.cpp
+++ b/src/Studio/Widgets/SignalQualityWidget.cpp
@@ -32,7 +32,7 @@ using namespace Core;
// constructor
SignalQualityWidget::SignalQualityWidget(QWidget* parent) : QWidget(parent)
{
- mPixmapHeight = 24;
+ mPixmapHeight = 18;
setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum );
@@ -57,7 +57,7 @@ SignalQualityWidget::SignalQualityWidget(QWidget* parent) : QWidget(parent)
mTextLabel = new QLabel();
mTextLabel->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
QFont timeFont = mTextLabel->font();
- timeFont.setPixelSize(20);
+ timeFont.setPixelSize(14);
mTextLabel->setFont(timeFont);
mainLayout->addWidget( mTextLabel, 0, Qt::AlignLeft | Qt::AlignHCenter );
@@ -103,7 +103,7 @@ void SignalQualityWidget::SetSignalQuality(double normalizedSignalQuality)
// update the signal quality
mSignalQuality = normalizedSignalQuality;
- mTempString.Format( "%.0f %%", normalizedSignalQuality * 100.0 );
+ mTempString.Format( "%.0f%%", normalizedSignalQuality * 100.0 );
mTextLabel->setText( mTempString.AsChar() );
const int32 pixmapIndex = normalizedSignalQuality * mPixmaps.Size() - Math::epsilon;
diff --git a/src/Studio/Windows/ReportWindow.cpp b/src/Studio/Windows/ReportWindow.cpp
index f2453fdb3..a9f900d28 100644
--- a/src/Studio/Windows/ReportWindow.cpp
+++ b/src/Studio/Windows/ReportWindow.cpp
@@ -44,6 +44,7 @@ ReportWindow::ReportWindow(const Core::String& dataChunkId, QWidget* parent) : Q
// create the back to selection button
QPushButton* backButton = new QPushButton("Back to selection");
+ backButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
mainLayout->addWidget(backButton);
connect( backButton, &QPushButton::clicked, this, [=]
{
@@ -58,6 +59,7 @@ ReportWindow::ReportWindow(const Core::String& dataChunkId, QWidget* parent) : Q
// create show report button
mShowReportButton = new QPushButton("");
mShowReportButton->setEnabled(false);
+ mShowReportButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
mShowReportButton->setIcon(GetQtBaseManager()->FindIcon("Images/Icons/Report.png"));
mainLayout->addWidget(mShowReportButton);
connect( mShowReportButton, &QPushButton::clicked, this, [dataChunkId]
@@ -74,7 +76,7 @@ ReportWindow::ReportWindow(const Core::String& dataChunkId, QWidget* parent) : Q
// avoid resizing
setSizeGripEnabled(false);
setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
- setFixedSize( QSize(500, sizeHint().height()) );
+ setFixedSize( QSize(512, 64) );
// position window in the screen center
GetQtBaseManager()->CenterToScreen(this);
diff --git a/src/Studio/Windows/SelectUserWindow.cpp b/src/Studio/Windows/SelectUserWindow.cpp
index a26f20302..7703a19f3 100644
--- a/src/Studio/Windows/SelectUserWindow.cpp
+++ b/src/Studio/Windows/SelectUserWindow.cpp
@@ -90,6 +90,7 @@ SelectUserWindow::SelectUserWindow(const User& user, QWidget* parent, bool showS
connect(mSearchEdit, SIGNAL(TextChanged(const QString&)), this, SLOT(OnSearchEdited(const QString&)));
connect(mSearchEdit, SIGNAL(TextCleared()), this, SLOT(OnSearchCleared()));
topHLayout->addWidget(mSearchEdit);
+ mSearchEdit->setFocus();
// search timer
mSearchTimer = new QTimer(this);