From a00a0997226e6eb851c51bc120d13ae09f45e238 Mon Sep 17 00:00:00 2001 From: Zach Glenwright Date: Tue, 18 Jan 2022 22:25:14 -0500 Subject: [PATCH 01/15] 1-18-22 - Start of Custom Preset logic Added set of 6 pre-defined settings to a global list. Start of working on #23 - Only the settings themselves are here at the moment, next the logic needs to be implemented. The pre-defined settings are: 1 - CCT mode, 5600K, 100% 2 - CCT mode, 3200K, 100% 3 - CCT mode, 5600K, 0% 4 - HSL mode, Red, 100% sat, 100% bri 5 - HSL mode, Blue 100% sat, 100% bri 6 - HSL mode, Green, 100% sat, 100% bri --- NeewerLite-Python.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/NeewerLite-Python.py b/NeewerLite-Python.py index 9ed21f7..f399d14 100644 --- a/NeewerLite-Python.py +++ b/NeewerLite-Python.py @@ -83,6 +83,21 @@ # 0 1 2 3 4 5 6 7 # [Bleak Scan Object, Bleak Connection, Custom Name, Last Params, Extend CCT Range, Send BRI/HUE independently, Light On/Off, Power/CH Data Returned] +# A list of preset mode settings, either pre-set (here) or set by custom file - element [0] is the sendValue, and element [1] is whether or +# not the preset in that position is custom or preset (True if custom) - this is for letting the GUI know if the button should be colored differently +# to mark it as "custom" - the default settings are: CCT mode - 0 is 5600K, 100% brightness, 1 is 3200K, 100% brightness, 2 is 5600K, 0% brightness +# 3 is RED at 100% saturation and brightness, 4 is BLUE at 100% saturation and brightness, and 5 is GREEN at 100% saturation and brightness +# RIGHT click the preset button to set a custom preset to the current settings, LEFT click it to recall the preset stored in that button. +# TODO: Possibly add a tool tip showing what that preset is stored as (HSI / 100° / 100% Saturation, 100% Brightness), etc. +# TODO: Find out the most optimal way to store custom presets, either via a sidecar light_prefs/.prefs file, or in the main prefs file +# TODO: Make GUI elements to set/recall these presets, and make either a custom function or use setUpGUI (or both) to use these values +customLightPresets = [[[120, 135, 2, 100, 56, 157], False], \ + [[120, 135, 2, 100, 32, 133], False], \ + [[120, 135, 2, 0, 56, 57], False], \ + [[120, 134, 4, 0, 0, 100, 100, 202], False], \ + [[120, 134, 4, 240, 0, 100, 100, 186], False], \ + [[120, 134, 4, 120, 0, 100, 100, 66], False]] + threadAction = "" # the current action to take from the thread setLightUUID = "69400002-B5A3-F393-E0A9-E50E24DCCA99" # the UUID to send information to the light From 011de42188c7ab5be72a31da7cc1ae5e991557de Mon Sep 17 00:00:00 2001 From: Zach Glenwright Date: Sat, 22 Jan 2022 13:31:33 -0500 Subject: [PATCH 02/15] 1-22-22 - Custom Presets / GUI elements Fully wrote the logic behind custom presets - not 100% happy with how the GUI is set up yet, but that's a small issue in the grander scheme of things :D I'd say presets are 90-95% sorted. There will be 2 types of presets - "global" presets just store the current light's mode/parameter settings, and can be re-called on individual/multiple lights (highlight all lights and click '3', and all will go to red, at least at the moment), and "snapshot" presets that store *all* of the settings of the lights you currently have selected, and will only recall those settings for those specific lights (for example, if you have 3 lights that are set for a backdrop, you can set those 3 as a snapshot preset, then clicking on that preset will set those 3 lights to the settings you set up in that preset.) - more testing, and more GUI tweaking to come, but the base structure is 90% there... --- NeewerLite-Python.py | 173 ++++++++++++++++++++++++++++++++++--------- ui_NeewerLightUI.py | 54 +++++++++++++- 2 files changed, 190 insertions(+), 37 deletions(-) diff --git a/NeewerLite-Python.py b/NeewerLite-Python.py index f399d14..b3a0293 100644 --- a/NeewerLite-Python.py +++ b/NeewerLite-Python.py @@ -83,20 +83,19 @@ # 0 1 2 3 4 5 6 7 # [Bleak Scan Object, Bleak Connection, Custom Name, Last Params, Extend CCT Range, Send BRI/HUE independently, Light On/Off, Power/CH Data Returned] -# A list of preset mode settings, either pre-set (here) or set by custom file - element [0] is the sendValue, and element [1] is whether or -# not the preset in that position is custom or preset (True if custom) - this is for letting the GUI know if the button should be colored differently -# to mark it as "custom" - the default settings are: CCT mode - 0 is 5600K, 100% brightness, 1 is 3200K, 100% brightness, 2 is 5600K, 0% brightness -# 3 is RED at 100% saturation and brightness, 4 is BLUE at 100% saturation and brightness, and 5 is GREEN at 100% saturation and brightness -# RIGHT click the preset button to set a custom preset to the current settings, LEFT click it to recall the preset stored in that button. -# TODO: Possibly add a tool tip showing what that preset is stored as (HSI / 100° / 100% Saturation, 100% Brightness), etc. -# TODO: Find out the most optimal way to store custom presets, either via a sidecar light_prefs/.prefs file, or in the main prefs file -# TODO: Make GUI elements to set/recall these presets, and make either a custom function or use setUpGUI (or both) to use these values -customLightPresets = [[[120, 135, 2, 100, 56, 157], False], \ - [[120, 135, 2, 100, 32, 133], False], \ - [[120, 135, 2, 0, 56, 57], False], \ - [[120, 134, 4, 0, 0, 100, 100, 202], False], \ - [[120, 134, 4, 240, 0, 100, 100, 186], False], \ - [[120, 134, 4, 120, 0, 100, 100, 66], False]] +# A list of preset mode settings - custom file will overwrite, but here are the default values +# (0 - CCT - 5600K / 100%) / (1 - CCT - 3200K / 100%) / (2 - CCT - 5600K / 0%) / (3 - HSI - Red / all 100%) +# (4 - HSI - Blue / all 100%) / (5 - HSI - Green / all 100%) / (6 - HSI - Purple / all 100%) / (7 - HSI - Cyan / all 100%) +customLightPresets = [ + [[-1, [5, 100, 56]]], + [[-1, [5, 100, 32]]], + [[-1, [5, 0, 56]]], + [[-1, [4, 100, 0, 100]]], + [[-1, [4, 100, 240, 100]]], + [[-1, [4, 100, 120, 100]]], + [[-1, [4, 100, 300, 100]]], + [[-1, [4, 100, 160, 100]]] + ] threadAction = "" # the current action to take from the thread @@ -181,6 +180,23 @@ def connectMe(self): self.ColorModeTabWidget.currentChanged.connect(self.tabChanged) self.lightTable.itemSelectionChanged.connect(self.selectionChanged) + self.customPreset_0_Button.clicked.connect(lambda: self.recallCustomPreset(0)) + self.customPreset_0_Button.rightclicked.connect(lambda: saveCustomPreset("global", 0)) + self.customPreset_1_Button.clicked.connect(lambda: self.recallCustomPreset(1)) + self.customPreset_1_Button.rightclicked.connect(lambda: saveCustomPreset("global", 1)) + self.customPreset_2_Button.clicked.connect(lambda: self.recallCustomPreset(2)) + self.customPreset_2_Button.rightclicked.connect(lambda: saveCustomPreset("global", 2)) + self.customPreset_3_Button.clicked.connect(lambda: self.recallCustomPreset(3)) + self.customPreset_3_Button.rightclicked.connect(lambda: saveCustomPreset("global", 3)) + self.customPreset_4_Button.clicked.connect(lambda: self.recallCustomPreset(4)) + self.customPreset_4_Button.rightclicked.connect(lambda: saveCustomPreset("global", 4)) + self.customPreset_5_Button.clicked.connect(lambda: self.recallCustomPreset(5)) + self.customPreset_5_Button.rightclicked.connect(lambda: saveCustomPreset("global", 5)) + self.customPreset_6_Button.clicked.connect(lambda: self.recallCustomPreset(6)) + self.customPreset_6_Button.rightclicked.connect(lambda: saveCustomPreset("global", 6)) + self.customPreset_7_Button.clicked.connect(lambda: self.recallCustomPreset(7)) + self.customPreset_7_Button.rightclicked.connect(lambda: saveCustomPreset("global", 7)) + self.Slider_CCT_Hue.valueChanged.connect(lambda: self.computeValueCCT(2)) self.Slider_CCT_Bright.valueChanged.connect(lambda: self.computeValueCCT(1)) @@ -294,7 +310,7 @@ def connectMe(self): self.SC_Num8.activated.connect(lambda: self.numberShortcuts(8)) self.SC_Num9 = QShortcut(QKeySequence("9"), self) self.SC_Num9.activated.connect(lambda: self.numberShortcuts(9)) - + def switchToTab(self, theTab): # SWITCH TO THE REQUESTED TAB **IF IT IS AVAILABLE** if self.ColorModeTabWidget.isTabEnabled(theTab) == True: self.ColorModeTabWidget.setCurrentIndex(theTab) @@ -1067,6 +1083,50 @@ def closeEvent(self, event): printDebugString("Closing the program NOW") + def recallCustomPreset(self, numOfPreset): + if len(customLightPresets[numOfPreset]) == 1: + if customLightPresets[numOfPreset][0][0] == -1: # we're looking at a global preset + if customLightPresets[numOfPreset][0][1][0] == 5: + self.setUpGUI(colorMode="CCT", + brightness=customLightPresets[numOfPreset][0][1][1], + temp=customLightPresets[numOfPreset][0][1][2]) + elif customLightPresets[numOfPreset][0][1][0] == 4: + self.setUpGUI(colorMode="HSI", + brightness=customLightPresets[numOfPreset][0][1][1], + hue=customLightPresets[numOfPreset][0][1][2], + sat=customLightPresets[numOfPreset][0][1][3]) + elif customLightPresets[numOfPreset][0][1][0] == 6: + self.setUpGUI(colorMode="ANM", + brightness=customLightPresets[numOfPreset][0][1][1], + scene=customLightPresets[numOfPreset][0][1][2]) + else: + global availableLights + + for a in range(len(customLightPresets[numOfPreset])): # check all the entries stored in this preset + currentLight = returnLightIndexesFromMacAddress(customLightPresets[numOfPreset][a][0]) + + if currentLight != []: # if we have a match + print("Before value:") + print(availableLights[currentLight[0]][3]) + + # always refer to the light it found as currentLight[0] + if customLightPresets[numOfPreset][a][1][0] == 5: # the preset is in CCT mode + availableLights[currentLight[0]][3] = calculateByteString(True, colorMode="CCT",\ + brightness=customLightPresets[numOfPreset][a][1][1],\ + temp=customLightPresets[numOfPreset][a][1][2]) + elif customLightPresets[numOfPreset][a][1][0] == 4: # the preset is in HSI mode + availableLights[currentLight[0]][3] = calculateByteString(True, colorMode="HSI",\ + HSI_I=customLightPresets[numOfPreset][a][1][1],\ + HSI_H=customLightPresets[numOfPreset][a][1][2],\ + HSI_S=customLightPresets[numOfPreset][a][1][3]) + elif customLightPresets[numOfPreset][a][1][0] == 6: # the preset is in ANM/SCENE mode + availableLights[currentLight[0]][3] = calculateByteString(True, colorMode="ANM",\ + brightness=customLightPresets[numOfPreset][a][1][1],\ + animation=customLightPresets[numOfPreset][a][1][2]) + + print("After value:") + print(availableLights[currentLight[0]][3]) + # SET UP THE GUI BASED ON COMMAND LINE ARGUMENTS def setUpGUI(self, **modeArgs): if modeArgs["colorMode"] == "CCT": @@ -1092,13 +1152,54 @@ def setUpGUI(self, **modeArgs): except NameError: pass # could not load the GUI, but we have already logged an error message +# WORKING WITH CUSTOM PRESETS +def saveCustomPreset(presetType, numOfPreset): + global customLightPresets + + if presetType == "global": + customLightPresets[numOfPreset] = [listBuilder(-1)] + elif presetType == "snapshot": + listConstructor = [] + + for a in range(len(availableLights)): + listConstructor.append(listBuilder(a)) + + customLightPresets[numOfPreset] = listConstructor + +def listBuilder(selectedLight): + paramsListBuilder = [] # the cut-down list of parameters to return to the main preset constructor + + if selectedLight == -1: # then we get the value from sendValue + lightMACAddress = -1 # this is a global preset + listToWorkWith = sendValue # we're using the last sent parameter on any light for this + else: # we're recalling the params for a specific light + lightMACAddress = availableLights[selectedLight][0].address # this is a snapshot preset + listToWorkWith = availableLights[selectedLight][3] # we're specificially using the last parameter for the specified light for this + + if listToWorkWith != []: # if we have elements in this list, then sort them out + paramsListBuilder.append(listToWorkWith[1] - 130) # the first value is the mode, but minus 130 to simplify it + + if listToWorkWith[1] == 135: # we're in CCT mode + paramsListBuilder.append(listToWorkWith[3]) # the brightness + paramsListBuilder.append(listToWorkWith[4]) # the color temperature + elif listToWorkWith[1] == 134: # we're in HSI mode + paramsListBuilder.append(listToWorkWith[6]) # the brightness + paramsListBuilder.append(listToWorkWith[3] + (256 * listToWorkWith[4])) # the hue + paramsListBuilder.append(listToWorkWith[5]) # the saturation + elif listToWorkWith[1] == 136: # we're in ANM/SCENE + paramsListBuilder.append(listToWorkWith[3]) # the brightness + paramsListBuilder.append(listToWorkWith[4]) # the scene + + return [lightMACAddress, paramsListBuilder] + +# RETURN THE CORRECT NAME FOR THE IDENTIFIER OF THE LIGHT (FOR DEBUG STRINGS) def returnMACname(): - # RETURN THE CORRECT NAME FOR THE IDENTIFIER OF THE LIGHT (FOR DEBUG STRINGS) if platform.system() == "Darwin": return "UUID:" else: return "MAC Address:" +# TEST TO MAKE SURE THE VALUE GIVEN TO THE FUNCTION IS VALID OR IN BOUNDS def testValid(theParam, theValue, defaultValue, startBounds, endBounds): if theParam == "temp": if len(theValue) > 1: # if the temp has at least 2 characters in it @@ -1132,34 +1233,38 @@ def printDebugString(theString): print("[" + currentTime + "] - " + theString) # CALCULATE THE BYTESTRING TO SEND TO THE LIGHT -def calculateByteString(**modeArgs): - global sendValue - +def calculateByteString(returnValue = False, **modeArgs): if modeArgs["colorMode"] == "CCT": # We're in CCT (color balance) mode - sendValue = [120, 135, 2, 0, 0, 0] + computedValue = [120, 135, 2, 0, 0, 0] - sendValue[3] = int(modeArgs["brightness"]) # the brightness value - sendValue[4] = int(modeArgs["temp"]) # the color temp value, ranging from 32(00K) to 85(00)K - some lights (like the SL-80) can go as high as 8500K - sendValue[5] = calculateChecksum(sendValue) # compute the checksum + computedValue[3] = int(modeArgs["brightness"]) # the brightness value + computedValue[4] = int(modeArgs["temp"]) # the color temp value, ranging from 32(00K) to 85(00)K - some lights (like the SL-80) can go as high as 8500K + computedValue[5] = calculateChecksum(computedValue) # compute the checksum elif modeArgs["colorMode"] == "HSI": # We're in HSI (any color of the spectrum) mode - sendValue = [120, 134, 4, 0, 0, 0, 0, 0] + computedValue = [120, 134, 4, 0, 0, 0, 0, 0] - sendValue[3] = int(modeArgs["HSI_H"]) & 255 # hue value, up to 255 - sendValue[4] = (int(modeArgs["HSI_H"]) & 65280) >> 8 # offset value, computed from above value - sendValue[5] = int(modeArgs["HSI_S"]) # saturation value - sendValue[6] = int(modeArgs["HSI_I"]) # intensity value - sendValue[7] = calculateChecksum(sendValue) # compute the checksum + computedValue[3] = int(modeArgs["HSI_H"]) & 255 # hue value, up to 255 + computedValue[4] = (int(modeArgs["HSI_H"]) & 65280) >> 8 # offset value, computed from above value + computedValue[5] = int(modeArgs["HSI_S"]) # saturation value + computedValue[6] = int(modeArgs["HSI_I"]) # intensity value + computedValue[7] = calculateChecksum(computedValue) # compute the checksum elif modeArgs["colorMode"] == "ANM": # We're in ANM (animation) mode - sendValue = [120, 136, 2, 0, 0, 0] + computedValue = [120, 136, 2, 0, 0, 0] + + computedValue[3] = int(modeArgs["brightness"]) # brightness value + computedValue[4] = int(modeArgs["animation"]) # the number of animation you're going to run (check comments above) + computedValue[5] = calculateChecksum(computedValue) # compute the checksum + else: + computedValue = [0] - sendValue[3] = int(modeArgs["brightness"]) # brightness value - sendValue[4] = int(modeArgs["animation"]) # the number of animation you're going to run (check comments above) - sendValue[5] = calculateChecksum(sendValue) # compute the checksum + if returnValue == False: # if we aren't supposed to return a value, then just set sendValue to the value returned from computedValue + global sendValue + sendValue = computedValue else: - sendValue = [0] + return computedValue # return the computed value # RECALCULATE THE BYTESTRING FOR CCT-ONLY NEEWER LIGHTS INTO HUE AND BRIGHTNESS SEPARATELY def calculateSeparateBytestrings(sendValue): diff --git a/ui_NeewerLightUI.py b/ui_NeewerLightUI.py index 216b592..52caaed 100644 --- a/ui_NeewerLightUI.py +++ b/ui_NeewerLightUI.py @@ -1,4 +1,4 @@ -from PySide2.QtCore import QRect +from PySide2.QtCore import QRect, Signal from PySide2.QtGui import QFont, QLinearGradient, QColor, Qt, QKeySequence from PySide2.QtWidgets import QFormLayout, QGridLayout, QKeySequenceEdit, QWidget, QPushButton, QTableWidget, QTableWidgetItem, QAbstractScrollArea, QAbstractItemView, \ QTabWidget, QGraphicsScene, QGraphicsView, QFrame, QSlider, QLabel, QLineEdit, QCheckBox, QStatusBar, QScrollArea, QTextEdit @@ -10,7 +10,7 @@ def setupUi(self, MainWindow): mainFont.setBold(True) mainFont.setWeight(75) - MainWindow.setFixedSize(590, 521) # the main window should be this size at launch, and no bigger + MainWindow.setFixedSize(590, 610) # the main window should be this size at launch, and no bigger MainWindow.setWindowTitle("NeewerLite-Python 0.8 by Zach Glenwright") self.centralwidget = QWidget(MainWindow) @@ -69,9 +69,33 @@ def setupUi(self, MainWindow): self.lightTable.setSelectionBehavior(QAbstractItemView.SelectRows) self.lightTable.verticalHeader().setStretchLastSection(False) + # ============ THE CUSTOM PRESET BUTTONS ============ + + self.customPresetButtonsCW = QWidget(self.centralwidget) + self.customPresetButtonsCW.setGeometry(QRect(10, 300, 571, 74)) + self.customPresetButtonsLay = QGridLayout(self.customPresetButtonsCW) + self.customPresetButtonsLay.setContentsMargins(0, 0, 0, 0) + + self.customPreset_0_Button = customPresetButton(self.centralwidget, text="0") + self.customPresetButtonsLay.addWidget(self.customPreset_0_Button, 1, 1) + self.customPreset_1_Button = customPresetButton(self.centralwidget, text="1") + self.customPresetButtonsLay.addWidget(self.customPreset_1_Button, 1, 2) + self.customPreset_2_Button = customPresetButton(self.centralwidget, text="2") + self.customPresetButtonsLay.addWidget(self.customPreset_2_Button, 1, 3) + self.customPreset_3_Button = customPresetButton(self.centralwidget, text="3") + self.customPresetButtonsLay.addWidget(self.customPreset_3_Button, 1, 4) + self.customPreset_4_Button = customPresetButton(self.centralwidget, text="4") + self.customPresetButtonsLay.addWidget(self.customPreset_4_Button, 1, 5) + self.customPreset_5_Button = customPresetButton(self.centralwidget, text="5") + self.customPresetButtonsLay.addWidget(self.customPreset_5_Button, 1, 6) + self.customPreset_6_Button = customPresetButton(self.centralwidget, text="6") + self.customPresetButtonsLay.addWidget(self.customPreset_6_Button, 1, 7) + self.customPreset_7_Button = customPresetButton(self.centralwidget, text="7") + self.customPresetButtonsLay.addWidget(self.customPreset_7_Button, 1, 8) + # ============ THE MODE TABS ============ self.ColorModeTabWidget = QTabWidget(self.centralwidget) - self.ColorModeTabWidget.setGeometry(QRect(10, 300, 571, 201)) + self.ColorModeTabWidget.setGeometry(QRect(10, 380, 571, 201)) # === >> THE CCT TAB << === self.CCT = QWidget() @@ -453,6 +477,30 @@ def setupUi(self, MainWindow): self.Slider_HSI_3_L.valueChanged.connect(self.TFV_HSI_3_L.setNum) self.Slider_ANM_Brightness.valueChanged.connect(self.TFV_ANM_Brightness.setNum) +class customPresetButton(QLabel): + clicked = Signal() + rightclicked = Signal() + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.setText(kwargs['text']) + self.setAlignment(Qt.AlignCenter) + self.setStyleSheet("customPresetButton" + "{" + "border: 1px solid black; background-color: #20B2AA;" + "}" + "customPresetButton::hover" + "{" + "background-color: #66CDAA;" + "}") + + def mousePressEvent(self, event): + if event.button() == Qt.RightButton: + self.rightclicked.emit() + elif event.button() == Qt.LeftButton: + self.clicked.emit() + class singleKeySequenceEditCancel(QWidget): def __init__(self, defaultValue): super(singleKeySequenceEditCancel, self).__init__() From 147c516d72228a8f48cf2ec20865850b4a86c569 Mon Sep 17 00:00:00 2001 From: Zach Glenwright Date: Sun, 23 Jan 2022 11:32:05 -0500 Subject: [PATCH 03/15] 1-23-22 - Saving custom presets, more GUI tweaking Still working on it... not 100% there, but more like... 96-98% or so --- NeewerLite-Python.py | 105 +++++++++++++++++++++++++++++++++---------- ui_NeewerLightUI.py | 14 +++++- 2 files changed, 94 insertions(+), 25 deletions(-) diff --git a/NeewerLite-Python.py b/NeewerLite-Python.py index b3a0293..e557beb 100644 --- a/NeewerLite-Python.py +++ b/NeewerLite-Python.py @@ -181,21 +181,21 @@ def connectMe(self): self.lightTable.itemSelectionChanged.connect(self.selectionChanged) self.customPreset_0_Button.clicked.connect(lambda: self.recallCustomPreset(0)) - self.customPreset_0_Button.rightclicked.connect(lambda: saveCustomPreset("global", 0)) + self.customPreset_0_Button.rightclicked.connect(lambda: self.saveCustomPresetDialog(0)) self.customPreset_1_Button.clicked.connect(lambda: self.recallCustomPreset(1)) - self.customPreset_1_Button.rightclicked.connect(lambda: saveCustomPreset("global", 1)) + self.customPreset_1_Button.rightclicked.connect(lambda: self.saveCustomPresetDialog(1)) self.customPreset_2_Button.clicked.connect(lambda: self.recallCustomPreset(2)) - self.customPreset_2_Button.rightclicked.connect(lambda: saveCustomPreset("global", 2)) + self.customPreset_2_Button.rightclicked.connect(lambda: self.saveCustomPresetDialog(2)) self.customPreset_3_Button.clicked.connect(lambda: self.recallCustomPreset(3)) - self.customPreset_3_Button.rightclicked.connect(lambda: saveCustomPreset("global", 3)) + self.customPreset_3_Button.rightclicked.connect(lambda: self.saveCustomPresetDialog(3)) self.customPreset_4_Button.clicked.connect(lambda: self.recallCustomPreset(4)) - self.customPreset_4_Button.rightclicked.connect(lambda: saveCustomPreset("global", 4)) + self.customPreset_4_Button.rightclicked.connect(lambda: self.saveCustomPresetDialog(4)) self.customPreset_5_Button.clicked.connect(lambda: self.recallCustomPreset(5)) - self.customPreset_5_Button.rightclicked.connect(lambda: saveCustomPreset("global", 5)) + self.customPreset_5_Button.rightclicked.connect(lambda: self.saveCustomPresetDialog(5)) self.customPreset_6_Button.clicked.connect(lambda: self.recallCustomPreset(6)) - self.customPreset_6_Button.rightclicked.connect(lambda: saveCustomPreset("global", 6)) + self.customPreset_6_Button.rightclicked.connect(lambda: self.saveCustomPresetDialog(6)) self.customPreset_7_Button.clicked.connect(lambda: self.recallCustomPreset(7)) - self.customPreset_7_Button.rightclicked.connect(lambda: saveCustomPreset("global", 7)) + self.customPreset_7_Button.rightclicked.connect(lambda: self.saveCustomPresetDialog(7)) self.Slider_CCT_Hue.valueChanged.connect(lambda: self.computeValueCCT(2)) self.Slider_CCT_Bright.valueChanged.connect(lambda: self.computeValueCCT(1)) @@ -1083,30 +1083,66 @@ def closeEvent(self, event): printDebugString("Closing the program NOW") + def saveCustomPresetDialog(self, numOfPreset): + saveDlg = QMessageBox(self) + saveDlg.setWindowTitle("Save a Custom Preset") + saveDlg.setTextFormat(Qt.TextFormat.RichText) + saveDlg.setText("Would you like to save a Global or Snapshot preset for preset " + str(numOfPreset + 1) + "?") + saveDlg.addButton(" Global Preset ", QMessageBox.ButtonRole.YesRole) + saveDlg.addButton(" Snapshot Preset ", QMessageBox.ButtonRole.NoRole) + saveDlg.addButton(" Cancel ", QMessageBox.ButtonRole.RejectRole) + saveDlg.setIcon(QMessageBox.Question) + + clickedButton = saveDlg.exec_() + + if clickedButton == 0: # save a "Global" preset + saveCustomPreset("global", numOfPreset) + elif clickedButton == 1: # save a "Snapshot" preset + saveCustomPreset("snapshot", numOfPreset) + # TODO: Apply these changes here once the settings have been switched + + if clickedButton != 2: # if we didn't cancel out, then mark that button as being "custom" + if numOfPreset == 0: + self.customPreset_0_Button.markCustom() + if numOfPreset == 1: + self.customPreset_1_Button.markCustom() + if numOfPreset == 2: + self.customPreset_2_Button.markCustom() + if numOfPreset == 3: + self.customPreset_3_Button.markCustom() + if numOfPreset == 4: + self.customPreset_4_Button.markCustom() + if numOfPreset == 5: + self.customPreset_5_Button.markCustom() + if numOfPreset == 6: + self.customPreset_6_Button.markCustom() + if numOfPreset == 7: + self.customPreset_7_Button.markCustom() + def recallCustomPreset(self, numOfPreset): - if len(customLightPresets[numOfPreset]) == 1: - if customLightPresets[numOfPreset][0][0] == -1: # we're looking at a global preset - if customLightPresets[numOfPreset][0][1][0] == 5: + global availableLights + changedLights = [] # if a snapshot preset exists in this setting, log the lights that are to be changed here + + for a in range(len(customLightPresets[numOfPreset])): # check all the entries stored in this preset + if customLightPresets[numOfPreset][0][0] == -1: # we're looking at a global preset, so set the light(s) up accordingly + if customLightPresets[numOfPreset][0][1][0] == 5: # the preset is in CCT mode self.setUpGUI(colorMode="CCT", brightness=customLightPresets[numOfPreset][0][1][1], temp=customLightPresets[numOfPreset][0][1][2]) - elif customLightPresets[numOfPreset][0][1][0] == 4: + elif customLightPresets[numOfPreset][0][1][0] == 4: # the preset is in HSI mode self.setUpGUI(colorMode="HSI", brightness=customLightPresets[numOfPreset][0][1][1], hue=customLightPresets[numOfPreset][0][1][2], sat=customLightPresets[numOfPreset][0][1][3]) - elif customLightPresets[numOfPreset][0][1][0] == 6: + elif customLightPresets[numOfPreset][0][1][0] == 6: # the preset is in ANM/SCENE mode self.setUpGUI(colorMode="ANM", brightness=customLightPresets[numOfPreset][0][1][1], scene=customLightPresets[numOfPreset][0][1][2]) - else: - global availableLights - - for a in range(len(customLightPresets[numOfPreset])): # check all the entries stored in this preset + else: # we're looking at a snapshot preset, so see if any of those lights are available to change currentLight = returnLightIndexesFromMacAddress(customLightPresets[numOfPreset][a][0]) if currentLight != []: # if we have a match - print("Before value:") + print("Before value for light " + str(currentLight[0]) + ":") print(availableLights[currentLight[0]][3]) # always refer to the light it found as currentLight[0] @@ -1124,9 +1160,17 @@ def recallCustomPreset(self, numOfPreset): brightness=customLightPresets[numOfPreset][a][1][1],\ animation=customLightPresets[numOfPreset][a][1][2]) - print("After value:") + print("After value for light " + str(currentLight[0]) + ":") print(availableLights[currentLight[0]][3]) + changedLights.append(currentLight[0]) + + print(changedLights) + + if changedLights != []: + global threadAction + threadAction = "send|" + "|".join(map(str, changedLights)) + # SET UP THE GUI BASED ON COMMAND LINE ARGUMENTS def setUpGUI(self, **modeArgs): if modeArgs["colorMode"] == "CCT": @@ -1161,7 +1205,7 @@ def saveCustomPreset(presetType, numOfPreset): elif presetType == "snapshot": listConstructor = [] - for a in range(len(availableLights)): + for a in range(len(availableLights)): # TODO: Figure out which lights we have selected, in the GUI thread - at the moment, it does all listConstructor.append(listBuilder(a)) customLightPresets[numOfPreset] = listConstructor @@ -1556,7 +1600,7 @@ async def disconnectFromLight(selectedLight, updateGUI=True): return returnValue # WRITE TO A LIGHT - optional arguments for the CLI version (GUI version doesn't use either of these) -async def writeToLight(selectedLights=0, updateGUI=True): +async def writeToLight(selectedLights=0, updateGUI=True, useGlobalValue=True): returnValue = "" # same as above, return value "" for GUI, or boolean for CLI startTimer = time.time() # the start of the triggering @@ -1564,7 +1608,8 @@ async def writeToLight(selectedLights=0, updateGUI=True): try: if updateGUI == True: - selectedLights = mainWindow.selectedLights() # get the list of currently selected lights from the GUI table + if selectedLights == 0: + selectedLights = mainWindow.selectedLights() # get the list of currently selected lights from the GUI table else: if type(selectedLights) is int: # if we specify an integer-based index selectedLights = [selectedLights] # convert asked-for light to list @@ -1577,6 +1622,9 @@ async def writeToLight(selectedLights=0, updateGUI=True): currentSendValue = sendValue # get this value before sending to multiple lights, to ensure the same value is sent to each one for a in range(len(selectedLights)): # try to write each light in turn, and show the current data being sent to them in the table + if useGlobalValue == False: # if we're forcing the lights to use their stored parameters, then load that in here + currentSendValue = availableLights[selectedLights[a]][3] + if availableLights[selectedLights[a]][1] != "": # if a Bleak connection is there try: if availableLights[(int(selectedLights[a]))][5] == True: # if we're using the old style of light @@ -1636,7 +1684,8 @@ async def writeToLight(selectedLights=0, updateGUI=True): else: returnValue = 0 # the light is not linked, even though it *should* be if it gets to this point, so this is an odd error - startTimer = time.time() # if we sent a value, then reset the timer + if useGlobalValue == True: + startTimer = time.time() # if we sent a value, then reset the timer await asyncio.sleep(0.05) # wait 1/20th of a second to give the Bluetooth bus a little time to recover @@ -1728,6 +1777,16 @@ def workerThread(_loop): threadAction = "" elif threadAction == "send": threadAction = _loop.run_until_complete(writeToLight()) # write a value to the light(s) - the selectedLights() section is in the write loop itself for responsiveness + elif threadAction != "": + currentThreadAction = threadAction.split("|") + + if currentThreadAction[0] == "send": # this will come from loading a custom snapshot preset + lightsToSendTo = [] # the current lights to affect + + for a in range (1, len(currentThreadAction)): + lightsToSendTo.append(int(currentThreadAction[a])) + + threadAction = _loop.run_until_complete(writeToLight(lightsToSendTo, True, False)) # write the value stored in the lights to the light(s) time.sleep(0.25) diff --git a/ui_NeewerLightUI.py b/ui_NeewerLightUI.py index 52caaed..671fb91 100644 --- a/ui_NeewerLightUI.py +++ b/ui_NeewerLightUI.py @@ -488,11 +488,11 @@ def __init__(self, *args, **kwargs): self.setAlignment(Qt.AlignCenter) self.setStyleSheet("customPresetButton" "{" - "border: 1px solid black; background-color: #20B2AA;" + "border: 1px solid black; background-color: #a5cbf7;" "}" "customPresetButton::hover" "{" - "background-color: #66CDAA;" + "background-color: #a5e3f7;" "}") def mousePressEvent(self, event): @@ -501,6 +501,16 @@ def mousePressEvent(self, event): elif event.button() == Qt.LeftButton: self.clicked.emit() + def markCustom(self): + self.setStyleSheet("customPresetButton" + "{" + "border: 1px solid black; background-color: #7188ff;" + "}" + "customPresetButton::hover" + "{" + "background-color: #70b0ff;" + "}") + class singleKeySequenceEditCancel(QWidget): def __init__(self, defaultValue): super(singleKeySequenceEditCancel, self).__init__() From 9c2742efcc54a6f948aa21465d8110544221edf7 Mon Sep 17 00:00:00 2001 From: Zach Glenwright Date: Sun, 23 Jan 2022 18:27:25 -0500 Subject: [PATCH 04/15] 1-23-22 - More custom preset refining - Added default bytestrings to both light addition and global sendValue - Changed #s on buttons from 0-7 to 1-8 - Added global/selected snapshot logic --- NeewerLite-Python.py | 50 +++++++++++++++++++++++++------------------- ui_NeewerLightUI.py | 29 ++++++++++++++----------- 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/NeewerLite-Python.py b/NeewerLite-Python.py index e557beb..7895341 100644 --- a/NeewerLite-Python.py +++ b/NeewerLite-Python.py @@ -76,7 +76,7 @@ pass # if there are any HTTP errors, don't do anything yet CCTSlider = -1 # the current slider moved in the CCT window - 1 - Brightness / 2 - Hue / -1 - Both Brightness and Hue -sendValue = [] # an array to hold the values to be sent to the light +sendValue = [120, 135, 2, 100, 56, 157] # an array to hold the values to be sent to the light - the default is CCT / 5600K / 100% lastAnimButtonPressed = 1 # which animation button you clicked last - if none, then it defaults to 1 (the police sirens) availableLights = [] # the list of Neewer lights currently available to control - format: @@ -1084,12 +1084,23 @@ def closeEvent(self, event): printDebugString("Closing the program NOW") def saveCustomPresetDialog(self, numOfPreset): + selectedLights = self.selectedLights() # get the currently selected lights + saveDlg = QMessageBox(self) saveDlg.setWindowTitle("Save a Custom Preset") saveDlg.setTextFormat(Qt.TextFormat.RichText) - saveDlg.setText("Would you like to save a Global or Snapshot preset for preset " + str(numOfPreset + 1) + "?") + saveDlg.setText("Would you like to save a Global or Snapshot preset for preset " + str(numOfPreset + 1) + "?" + "
" + "A Global Preset saves only the currently set global parameters (mode, hue, color temperature, brightness, etc.) and applies that global preset to all the lights that are currently selected.

" + "A Snapshot Preset saves the currently set parameters for each light individually, allowing you to recall more complex lighting setups. You can also either set a snapshot preset for a series of selected lights (you have to select 1 or more lights for this option), or all the currently available lights. If you save a snapshot preset of a series of selected lights, it will only apply the settings for those specific lights.") saveDlg.addButton(" Global Preset ", QMessageBox.ButtonRole.YesRole) - saveDlg.addButton(" Snapshot Preset ", QMessageBox.ButtonRole.NoRole) + saveDlg.addButton(" Snapshot Preset - All Lights ", QMessageBox.ButtonRole.YesRole) + + selectedLightsQuestion = 0 + + if selectedLights != []: + saveDlg.addButton(" Snapshot Preset - Selected Lights ", QMessageBox.ButtonRole.YesRole) + selectedLightsQuestion = 1 + saveDlg.addButton(" Cancel ", QMessageBox.ButtonRole.RejectRole) saveDlg.setIcon(QMessageBox.Question) @@ -1097,11 +1108,12 @@ def saveCustomPresetDialog(self, numOfPreset): if clickedButton == 0: # save a "Global" preset saveCustomPreset("global", numOfPreset) - elif clickedButton == 1: # save a "Snapshot" preset + elif clickedButton == 1: # save a "Snapshot" preset with all lights saveCustomPreset("snapshot", numOfPreset) - # TODO: Apply these changes here once the settings have been switched - - if clickedButton != 2: # if we didn't cancel out, then mark that button as being "custom" + elif clickedButton == 2: # save a "Snapshot" preset with only the selected lights + saveCustomPreset("snapshot", numOfPreset, selectedLights) + + if clickedButton != (2 + selectedLightsQuestion): # if we didn't cancel out, then mark that button as being "custom" if numOfPreset == 0: self.customPreset_0_Button.markCustom() if numOfPreset == 1: @@ -1142,9 +1154,6 @@ def recallCustomPreset(self, numOfPreset): currentLight = returnLightIndexesFromMacAddress(customLightPresets[numOfPreset][a][0]) if currentLight != []: # if we have a match - print("Before value for light " + str(currentLight[0]) + ":") - print(availableLights[currentLight[0]][3]) - # always refer to the light it found as currentLight[0] if customLightPresets[numOfPreset][a][1][0] == 5: # the preset is in CCT mode availableLights[currentLight[0]][3] = calculateByteString(True, colorMode="CCT",\ @@ -1160,13 +1169,8 @@ def recallCustomPreset(self, numOfPreset): brightness=customLightPresets[numOfPreset][a][1][1],\ animation=customLightPresets[numOfPreset][a][1][2]) - print("After value for light " + str(currentLight[0]) + ":") - print(availableLights[currentLight[0]][3]) - changedLights.append(currentLight[0]) - print(changedLights) - if changedLights != []: global threadAction threadAction = "send|" + "|".join(map(str, changedLights)) @@ -1197,7 +1201,7 @@ def setUpGUI(self, **modeArgs): pass # could not load the GUI, but we have already logged an error message # WORKING WITH CUSTOM PRESETS -def saveCustomPreset(presetType, numOfPreset): +def saveCustomPreset(presetType, numOfPreset, selectedLights = []): global customLightPresets if presetType == "global": @@ -1205,8 +1209,12 @@ def saveCustomPreset(presetType, numOfPreset): elif presetType == "snapshot": listConstructor = [] - for a in range(len(availableLights)): # TODO: Figure out which lights we have selected, in the GUI thread - at the moment, it does all - listConstructor.append(listBuilder(a)) + if selectedLights == []: # add all the lights to the snapshot preset + for a in range(len(availableLights)): + listConstructor.append(listBuilder(a)) + else: # add only the selected lights to the snapshot preset + for a in range(len(selectedLights)): + listConstructor.append(listBuilder(selectedLights[a])) customLightPresets[numOfPreset] = listConstructor @@ -1409,7 +1417,7 @@ async def findDevices(): customPrefs = getCustomLightPrefs(currentScan[a].address, currentScan[a].name) if len(customPrefs) == 3: # we need to rename the light and set up CCT and color temp range - availableLights.append([currentScan[a], "", customPrefs[0], [], customPrefs[1], customPrefs[2], True, ["---", "---"]]) # add it to the global list + availableLights.append([currentScan[a], "", customPrefs[0], [120, 135, 2, 100, 56, 157], customPrefs[1], customPrefs[2], True, ["---", "---"]]) # add it to the global list elif len(customPrefs) == 4: # same as above, but we have previously stored parameters, so add them in as well availableLights.append([currentScan[a], "", customPrefs[0], customPrefs[3], customPrefs[1], customPrefs[2], True, ["---", "---"]]) # add it to the global list @@ -2193,7 +2201,7 @@ def writeHTMLSections(self, theSection, errorMsg = ""): footerLinks = footerLinks + "List Currently Available Lights" self.wfile.write(bytes("
" + footerLinks + "
", "utf-8")) - self.wfile.write(bytes("NeewerLite-Python 0.8 by Zach Glenwright
", "utf-8")) + self.wfile.write(bytes("NeewerLite-Python 0.9 by Zach Glenwright
", "utf-8")) self.wfile.write(bytes("", "utf-8")) def formatStringForConsole(theString, maxLength): @@ -2358,7 +2366,7 @@ def loadPrefsFile(globalPrefsFile = ""): if cmdReturn[0] == "LIST": doAnotherInstanceCheck() # check to see if another instance is running, and if it is, then error out and quit - print("NeewerLite-Python 0.8 by Zach Glenwright") + print("NeewerLite-Python 0.9 by Zach Glenwright") print("Searching for nearby Neewer lights...") loop.run_until_complete(findDevices()) diff --git a/ui_NeewerLightUI.py b/ui_NeewerLightUI.py index 671fb91..ace4f26 100644 --- a/ui_NeewerLightUI.py +++ b/ui_NeewerLightUI.py @@ -10,8 +10,8 @@ def setupUi(self, MainWindow): mainFont.setBold(True) mainFont.setWeight(75) - MainWindow.setFixedSize(590, 610) # the main window should be this size at launch, and no bigger - MainWindow.setWindowTitle("NeewerLite-Python 0.8 by Zach Glenwright") + MainWindow.setFixedSize(590, 606) # the main window should be this size at launch, and no bigger + MainWindow.setWindowTitle("NeewerLite-Python 0.9 by Zach Glenwright") self.centralwidget = QWidget(MainWindow) self.centralwidget.setObjectName(u"centralwidget") @@ -72,30 +72,30 @@ def setupUi(self, MainWindow): # ============ THE CUSTOM PRESET BUTTONS ============ self.customPresetButtonsCW = QWidget(self.centralwidget) - self.customPresetButtonsCW.setGeometry(QRect(10, 300, 571, 74)) + self.customPresetButtonsCW.setGeometry(QRect(10, 300, 571, 68)) self.customPresetButtonsLay = QGridLayout(self.customPresetButtonsCW) self.customPresetButtonsLay.setContentsMargins(0, 0, 0, 0) - self.customPreset_0_Button = customPresetButton(self.centralwidget, text="0") + self.customPreset_0_Button = customPresetButton(self.centralwidget, text="1") self.customPresetButtonsLay.addWidget(self.customPreset_0_Button, 1, 1) - self.customPreset_1_Button = customPresetButton(self.centralwidget, text="1") + self.customPreset_1_Button = customPresetButton(self.centralwidget, text="2") self.customPresetButtonsLay.addWidget(self.customPreset_1_Button, 1, 2) - self.customPreset_2_Button = customPresetButton(self.centralwidget, text="2") + self.customPreset_2_Button = customPresetButton(self.centralwidget, text="3") self.customPresetButtonsLay.addWidget(self.customPreset_2_Button, 1, 3) - self.customPreset_3_Button = customPresetButton(self.centralwidget, text="3") + self.customPreset_3_Button = customPresetButton(self.centralwidget, text="4") self.customPresetButtonsLay.addWidget(self.customPreset_3_Button, 1, 4) - self.customPreset_4_Button = customPresetButton(self.centralwidget, text="4") + self.customPreset_4_Button = customPresetButton(self.centralwidget, text="5") self.customPresetButtonsLay.addWidget(self.customPreset_4_Button, 1, 5) - self.customPreset_5_Button = customPresetButton(self.centralwidget, text="5") + self.customPreset_5_Button = customPresetButton(self.centralwidget, text="6") self.customPresetButtonsLay.addWidget(self.customPreset_5_Button, 1, 6) - self.customPreset_6_Button = customPresetButton(self.centralwidget, text="6") + self.customPreset_6_Button = customPresetButton(self.centralwidget, text="7") self.customPresetButtonsLay.addWidget(self.customPreset_6_Button, 1, 7) - self.customPreset_7_Button = customPresetButton(self.centralwidget, text="7") + self.customPreset_7_Button = customPresetButton(self.centralwidget, text="8") self.customPresetButtonsLay.addWidget(self.customPreset_7_Button, 1, 8) # ============ THE MODE TABS ============ self.ColorModeTabWidget = QTabWidget(self.centralwidget) - self.ColorModeTabWidget.setGeometry(QRect(10, 380, 571, 201)) + self.ColorModeTabWidget.setGeometry(QRect(10, 376, 571, 201)) # === >> THE CCT TAB << === self.CCT = QWidget() @@ -483,9 +483,14 @@ class customPresetButton(QLabel): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + + customPresetFont = QFont() + customPresetFont.setPointSize(12) self.setText(kwargs['text']) + self.setFont(customPresetFont) self.setAlignment(Qt.AlignCenter) + self.setStyleSheet("customPresetButton" "{" "border: 1px solid black; background-color: #a5cbf7;" From 51cbb9a95cc7ff92075aecdf23b54cd325ef6672 Mon Sep 17 00:00:00 2001 From: Zach Glenwright Date: Mon, 24 Jan 2022 16:40:15 -0500 Subject: [PATCH 05/15] 1-24-22 - Saving the custom presets to a file Implemented saving presets to file under light_prefs - right-clicking saves a preset, ALT-right-clicking restores it to the default. --- .gitignore | 3 +- NeewerLite-Python.py | 289 ++++++++++++++++++++++++++++++++++++------- ui_NeewerLightUI.py | 59 ++++++--- 3 files changed, 291 insertions(+), 60 deletions(-) diff --git a/.gitignore b/.gitignore index 1054c76..1e7039f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ light_prefs/ __pycache__/ IGNORE ME!/ NeewerLightUI.ui -NeewerLite-Python.prefs \ No newline at end of file +NeewerLite-Python.prefs +.DS_Store diff --git a/NeewerLite-Python.py b/NeewerLite-Python.py index 7895341..8ccf314 100644 --- a/NeewerLite-Python.py +++ b/NeewerLite-Python.py @@ -83,6 +83,18 @@ # 0 1 2 3 4 5 6 7 # [Bleak Scan Object, Bleak Connection, Custom Name, Last Params, Extend CCT Range, Send BRI/HUE independently, Light On/Off, Power/CH Data Returned] +# The list of **default** light presets for restoring and checking against +defaultLightPresets = [ + [[-1, [5, 100, 56]]], + [[-1, [5, 100, 32]]], + [[-1, [5, 0, 56]]], + [[-1, [4, 100, 0, 100]]], + [[-1, [4, 100, 240, 100]]], + [[-1, [4, 100, 120, 100]]], + [[-1, [4, 100, 300, 100]]], + [[-1, [4, 100, 160, 100]]] + ] + # A list of preset mode settings - custom file will overwrite, but here are the default values # (0 - CCT - 5600K / 100%) / (1 - CCT - 3200K / 100%) / (2 - CCT - 5600K / 0%) / (3 - HSI - Red / all 100%) # (4 - HSI - Blue / all 100%) / (5 - HSI - Green / all 100%) / (6 - HSI - Purple / all 100%) / (7 - HSI - Cyan / all 100%) @@ -110,6 +122,7 @@ printDebug = True # show debug messages in the console for all of the program's events maxNumOfAttempts = 6 # the maximum attempts the program will attempt an action before erroring out rememberLightsOnExit = False # whether or not to save the currently set light settings (mode/hue/brightness/etc.) when quitting out +rememberPresetsOnExit = True # whether or not to save the custom preset list when quitting out acceptable_HTTP_IPs = [] # the acceptable IPs for the HTTP server, set on launch by prefs file customKeys = [] # custom keymappings for keyboard shortcuts, set on launch by the prefs file enableTabsOnLaunch = False # whether or not to enable tabs on startup (even with no lights connected) @@ -117,6 +130,7 @@ lockFile = tempfile.gettempdir() + os.sep + "NeewerLite-Python.lock" anotherInstance = False # whether or not we're using a new instance (for the Singleton check) globalPrefsFile = os.path.dirname(os.path.abspath(sys.argv[0])) + os.sep + "NeewerLite-Python.prefs" # the global preferences file for saving/loading +customLightPresetsFile = os.path.dirname(os.path.abspath(sys.argv[0])) + os.sep + "light_prefs" + os.sep + "customLights.prefs" # FILE LOCKING FOR SINGLE INSTANCE def singleInstanceLock(): @@ -168,6 +182,48 @@ def __init__(self): if platform.system() == "Darwin": # if we're on MacOS, then change the column text for the 2nd column in the light table self.lightTable.horizontalHeaderItem(1).setText("Light UUID") + # IF ANY OF THE CUSTOM PRESETS ARE ACTUALLY CUSTOM, THEN MARK THOSE BUTTONS AS CUSTOM + if customLightPresets[0] != defaultLightPresets[0]: + if customLightPresets[0][0][0] == -1: # if the current preset is custom, but a global, mark it that way + self.customPreset_0_Button.markCustom(0) + else: # the current preset is a snapshot preset + self.customPreset_0_Button.markCustom(0, 1) + if customLightPresets[1] != defaultLightPresets[1]: + if customLightPresets[1][0][0] == -1: + self.customPreset_1_Button.markCustom(1) + else: + self.customPreset_1_Button.markCustom(1, 1) + if customLightPresets[2] != defaultLightPresets[2]: + if customLightPresets[2][0][0] == -1: + self.customPreset_2_Button.markCustom(2) + else: + self.customPreset_2_Button.markCustom(2, 1) + if customLightPresets[3] != defaultLightPresets[3]: + if customLightPresets[3][0][0] == -1: + self.customPreset_3_Button.markCustom(3) + else: + self.customPreset_3_Button.markCustom(3, 1) + if customLightPresets[4] != defaultLightPresets[4]: + if customLightPresets[4][0][0] == -1: + self.customPreset_4_Button.markCustom(4) + else: + self.customPreset_4_Button.markCustom(4, 1) + if customLightPresets[5] != defaultLightPresets[5]: + if customLightPresets[5][0][0] == -1: + self.customPreset_5_Button.markCustom(5) + else: + self.customPreset_5_Button.markCustom(5, 1) + if customLightPresets[6] != defaultLightPresets[6]: + if customLightPresets[6][0][0] == -1: + self.customPreset_6_Button.markCustom(6) + else: + self.customPreset_6_Button.markCustom(6, 1) + if customLightPresets[7] != defaultLightPresets[7]: + if customLightPresets[7][0][0] == -1: + self.customPreset_7_Button.markCustom(7) + else: + self.customPreset_7_Button.markCustom(7, 1) + self.show def connectMe(self): @@ -810,11 +866,7 @@ def checkLightPrefs(self): # check the new settings and save the custom file self.saveLightPrefs(selectedRows[0]) # save the light settings to a special file def saveLightPrefs(self, lightID): # save a sidecar file with the preferences for a specific light - #CREATE THE light_prefs FOLDER IF IT DOESN'T EXIST - try: - os.mkdir(os.path.dirname(os.path.abspath(sys.argv[0])) + os.sep + "light_prefs") - except FileExistsError: - pass # the folder already exists, so we don't need to create it + createLightPrefsFolder() # create the light_prefs folder if it doesn't exist # GET THE CUSTOM FILENAME FOR THIS FILE, NOTED FROM THE MAC ADDRESS OF THE CURRENT LIGHT exportFileName = availableLights[lightID][0].address.split(":") # take the colons out of the MAC address @@ -1065,6 +1117,39 @@ def closeEvent(self, event): threadAction = "quit" # make sure to tell the thread to quit again (if it missed it the first time) time.sleep(2) + if rememberPresetsOnExit == True: + printDebugString("You asked NeewerLite-Python to save the custom parameters on exit, so we will do that now...") + customPresetsToWrite = [] # the list of custom presets to write to file + + # CHECK EVERY SINGLE CUSTOM PRESET AGAINST THE "DEFAULT" LIST, AND IF IT'S DIFFERENT, THEN LOG THAT ONE + if customLightPresets[0] != defaultLightPresets[0]: + customPresetsToWrite.append(customPresetToString(0)) + if customLightPresets[1] != defaultLightPresets[1]: + customPresetsToWrite.append(customPresetToString(1)) + if customLightPresets[2] != defaultLightPresets[2]: + customPresetsToWrite.append(customPresetToString(2)) + if customLightPresets[3] != defaultLightPresets[3]: + customPresetsToWrite.append(customPresetToString(3)) + if customLightPresets[4] != defaultLightPresets[4]: + customPresetsToWrite.append(customPresetToString(4)) + if customLightPresets[5] != defaultLightPresets[5]: + customPresetsToWrite.append(customPresetToString(5)) + if customLightPresets[6] != defaultLightPresets[6]: + customPresetsToWrite.append(customPresetToString(6)) + if customLightPresets[7] != defaultLightPresets[7]: + customPresetsToWrite.append(customPresetToString(7)) + + if customPresetsToWrite != []: # if there are any altered presets, then write them to the custom presets file + createLightPrefsFolder() # create the light_prefs folder if it doesn't exist + + # WRITE THE PREFERENCES FILE + with open(customLightPresetsFile, "w") as prefsFileToWrite: + prefsFileToWrite.write("\n".join(customPresetsToWrite)) + + printDebugString("Exported custom presets to " + customLightPresetsFile) + else: + printDebugString("There were no changed custom presets, so not saving a custom presets file!") + # Keep in mind, this is broken into 2 separate "for" loops, so we save all the light params FIRST, then try to unlink from them if rememberLightsOnExit == True: printDebugString("You asked NeewerLite-Python to save the last used light parameters on exit, so we will do that now...") @@ -1084,52 +1169,72 @@ def closeEvent(self, event): printDebugString("Closing the program NOW") def saveCustomPresetDialog(self, numOfPreset): - selectedLights = self.selectedLights() # get the currently selected lights - - saveDlg = QMessageBox(self) - saveDlg.setWindowTitle("Save a Custom Preset") - saveDlg.setTextFormat(Qt.TextFormat.RichText) - saveDlg.setText("Would you like to save a Global or Snapshot preset for preset " + str(numOfPreset + 1) + "?" + "
" - "A Global Preset saves only the currently set global parameters (mode, hue, color temperature, brightness, etc.) and applies that global preset to all the lights that are currently selected.

" - "A Snapshot Preset saves the currently set parameters for each light individually, allowing you to recall more complex lighting setups. You can also either set a snapshot preset for a series of selected lights (you have to select 1 or more lights for this option), or all the currently available lights. If you save a snapshot preset of a series of selected lights, it will only apply the settings for those specific lights.") - saveDlg.addButton(" Global Preset ", QMessageBox.ButtonRole.YesRole) - saveDlg.addButton(" Snapshot Preset - All Lights ", QMessageBox.ButtonRole.YesRole) - - selectedLightsQuestion = 0 - - if selectedLights != []: - saveDlg.addButton(" Snapshot Preset - Selected Lights ", QMessageBox.ButtonRole.YesRole) - selectedLightsQuestion = 1 - - saveDlg.addButton(" Cancel ", QMessageBox.ButtonRole.RejectRole) - saveDlg.setIcon(QMessageBox.Question) + if (QApplication.keyboardModifiers() & Qt.AltModifier) == Qt.AltModifier: # if you have the ALT key held down, then delete the current preset + customLightPresets[numOfPreset] = defaultLightPresets[numOfPreset] - clickedButton = saveDlg.exec_() - - if clickedButton == 0: # save a "Global" preset - saveCustomPreset("global", numOfPreset) - elif clickedButton == 1: # save a "Snapshot" preset with all lights - saveCustomPreset("snapshot", numOfPreset) - elif clickedButton == 2: # save a "Snapshot" preset with only the selected lights - saveCustomPreset("snapshot", numOfPreset, selectedLights) - - if clickedButton != (2 + selectedLightsQuestion): # if we didn't cancel out, then mark that button as being "custom" if numOfPreset == 0: - self.customPreset_0_Button.markCustom() + self.customPreset_0_Button.markCustom(0, -1) if numOfPreset == 1: - self.customPreset_1_Button.markCustom() + self.customPreset_1_Button.markCustom(1, -1) if numOfPreset == 2: - self.customPreset_2_Button.markCustom() + self.customPreset_2_Button.markCustom(2, -1) if numOfPreset == 3: - self.customPreset_3_Button.markCustom() + self.customPreset_3_Button.markCustom(3, -1) if numOfPreset == 4: - self.customPreset_4_Button.markCustom() + self.customPreset_4_Button.markCustom(4, -1) if numOfPreset == 5: - self.customPreset_5_Button.markCustom() + self.customPreset_5_Button.markCustom(5, -1) if numOfPreset == 6: - self.customPreset_6_Button.markCustom() + self.customPreset_6_Button.markCustom(6, -1) if numOfPreset == 7: - self.customPreset_7_Button.markCustom() + self.customPreset_7_Button.markCustom(7, -1) + else: + selectedLights = self.selectedLights() # get the currently selected lights + + saveDlg = QMessageBox(self) + saveDlg.setWindowTitle("Save a Custom Preset") + saveDlg.setTextFormat(Qt.TextFormat.RichText) + saveDlg.setText("Would you like to save a Global or Snapshot preset for preset " + str(numOfPreset + 1) + "?" + "
" + "A Global Preset saves only the currently set global parameters (mode, hue, color temperature, brightness, etc.) and applies that global preset to all the lights that are currently selected.

" + "A Snapshot Preset saves the currently set parameters for each light individually, allowing you to recall more complex lighting setups. You can also either set a snapshot preset for a series of selected lights (you have to select 1 or more lights for this option), or all the currently available lights. If you save a snapshot preset of a series of selected lights, it will only apply the settings for those specific lights.") + saveDlg.addButton(" Global Preset ", QMessageBox.ButtonRole.YesRole) + saveDlg.addButton(" Snapshot Preset - All Lights ", QMessageBox.ButtonRole.YesRole) + + selectedLightsQuestion = 0 + + if selectedLights != []: + saveDlg.addButton(" Snapshot Preset - Selected Lights ", QMessageBox.ButtonRole.YesRole) + selectedLightsQuestion = 1 + + saveDlg.addButton(" Cancel ", QMessageBox.ButtonRole.RejectRole) + saveDlg.setIcon(QMessageBox.Question) + + clickedButton = saveDlg.exec_() + + if clickedButton == 0: # save a "Global" preset + saveCustomPreset("global", numOfPreset) + elif clickedButton == 1: # save a "Snapshot" preset with all lights + saveCustomPreset("snapshot", numOfPreset) + elif clickedButton == 2: # save a "Snapshot" preset with only the selected lights + saveCustomPreset("snapshot", numOfPreset, selectedLights) + + if clickedButton != (2 + selectedLightsQuestion): # if we didn't cancel out, then mark that button as being "custom" + if numOfPreset == 0: + self.customPreset_0_Button.markCustom(0, clickedButton) + if numOfPreset == 1: + self.customPreset_1_Button.markCustom(1, clickedButton) + if numOfPreset == 2: + self.customPreset_2_Button.markCustom(2, clickedButton) + if numOfPreset == 3: + self.customPreset_3_Button.markCustom(3, clickedButton) + if numOfPreset == 4: + self.customPreset_4_Button.markCustom(4, clickedButton) + if numOfPreset == 5: + self.customPreset_5_Button.markCustom(5, clickedButton) + if numOfPreset == 6: + self.customPreset_6_Button.markCustom(6, clickedButton) + if numOfPreset == 7: + self.customPreset_7_Button.markCustom(7, clickedButton) def recallCustomPreset(self, numOfPreset): global availableLights @@ -1137,6 +1242,12 @@ def recallCustomPreset(self, numOfPreset): for a in range(len(customLightPresets[numOfPreset])): # check all the entries stored in this preset if customLightPresets[numOfPreset][0][0] == -1: # we're looking at a global preset, so set the light(s) up accordingly + + # If no lights are selected for a Global preset, then automatically select all of the lights + if self.selectedLights() == []: + self.lightTable.selectAll() + time.sleep(0.2) + if customLightPresets[numOfPreset][0][1][0] == 5: # the preset is in CCT mode self.setUpGUI(colorMode="CCT", brightness=customLightPresets[numOfPreset][0][1][1], @@ -1244,6 +1355,86 @@ def listBuilder(selectedLight): return [lightMACAddress, paramsListBuilder] +def customPresetToString(numOfPreset): + returnedString = "customPreset" + str(numOfPreset) + "=" # the string to return back to the saving mechanism + numOfLights = len(customLightPresets[numOfPreset]) # how many lights this custom preset holds values for + + for a in range(numOfLights): # get all of the lights stored in this preset (or 1 if it's a global) + returnedString += str(customLightPresets[numOfPreset][a][0]) # get the MAC address/UUID of the nth light + returnedString += "|" + "|".join(map(str,customLightPresets[numOfPreset][a][1])) # get a string for the rest of this current array + + if numOfLights > 1 and a < (numOfLights - 1): # if there are more lights left, then add a semicolon to differentiate that + returnedString += ";" + + return returnedString + +def stringToCustomPreset(presetString): + lightsToWorkWith = presetString.split(";") # split the current string into individual lights + presetToReturn = [] # a list containing all of the preset information + + for a in range(len(lightsToWorkWith)): + presetList = lightsToWorkWith[a].split("|") # split the current light list into its individual items + presetPayload = [] # the actual preset list + + for b in range(1, len(presetList)): + presetPayload.append(int(presetList[b])) + + if presetList[0] == "-1": + presetToReturn.append([-1, presetPayload]) # if the light ID is -1, keep that value as an integer + else: + presetToReturn.append([presetList[0], presetPayload]) # if it isn't, then the MAC address is a string, so keep it that way + + return presetToReturn + +def loadCustomPresets(): + global customLightPresets + + # READ THE PREFERENCES FILE INTO A LIST + fileToOpen = open(customLightPresetsFile) + customPresets = fileToOpen.read().split("\n") + fileToOpen.close() + + acceptable_arguments = ["customPreset0", "customPreset1", "customPreset2", "customPreset3", \ + "customPreset4", "customPreset5", "customPreset6", "customPreset7"] + + for a in range(len(customPresets) - 1, -1, -1): + if not any(x in customPresets[a] for x in acceptable_arguments): # if the current argument is invalid + customPresets.pop(a) # delete the invalid argument from the list + + # NOW THAT ANY STRAGGLERS ARE OUT, ADD DASHES TO WHAT REMAINS TO PROPERLY PARSE IN THE PARSER + for a in range(len(customPresets)): + customPresets[a] = "--" + customPresets[a] + + customPresetParser = argparse.ArgumentParser() + + customPresetParser.add_argument("--customPreset0", default=-1) + customPresetParser.add_argument("--customPreset1", default=-1) + customPresetParser.add_argument("--customPreset2", default=-1) + customPresetParser.add_argument("--customPreset3", default=-1) + customPresetParser.add_argument("--customPreset4", default=-1) + customPresetParser.add_argument("--customPreset5", default=-1) + customPresetParser.add_argument("--customPreset6", default=-1) + customPresetParser.add_argument("--customPreset7", default=-1) + + customPresets = customPresetParser.parse_args(customPresets) + + if customPresets.customPreset0 != -1: + customLightPresets[0] = stringToCustomPreset(customPresets.customPreset0) + if customPresets.customPreset1 != -1: + customLightPresets[1] = stringToCustomPreset(customPresets.customPreset1) + if customPresets.customPreset2 != -1: + customLightPresets[2] = stringToCustomPreset(customPresets.customPreset2) + if customPresets.customPreset3 != -1: + customLightPresets[3] = stringToCustomPreset(customPresets.customPreset3) + if customPresets.customPreset4 != -1: + customLightPresets[4] = stringToCustomPreset(customPresets.customPreset4) + if customPresets.customPreset5 != -1: + customLightPresets[5] = stringToCustomPreset(customPresets.customPreset5) + if customPresets.customPreset6 != -1: + customLightPresets[6] = stringToCustomPreset(customPresets.customPreset6) + if customPresets.customPreset7 != -1: + customLightPresets[7] = stringToCustomPreset(customPresets.customPreset7) + # RETURN THE CORRECT NAME FOR THE IDENTIFIER OF THE LIGHT (FOR DEBUG STRINGS) def returnMACname(): if platform.system() == "Darwin": @@ -1791,11 +1982,13 @@ def workerThread(_loop): if currentThreadAction[0] == "send": # this will come from loading a custom snapshot preset lightsToSendTo = [] # the current lights to affect - for a in range (1, len(currentThreadAction)): + for a in range (1, len(currentThreadAction)): # find the lights that need to be refreshed lightsToSendTo.append(int(currentThreadAction[a])) threadAction = _loop.run_until_complete(writeToLight(lightsToSendTo, True, False)) # write the value stored in the lights to the light(s) + mainWindow.lightTable.clearSelection() # clear the selection first before re-selecting lights + time.sleep(0.25) async def parallelAction(theAction, theLights, updateGUI = True): @@ -2215,6 +2408,13 @@ def formatStringForConsole(theString, maxLength): else: # truncate the string, it's too long return theString[0:maxLength - 4] + " ..." +def createLightPrefsFolder(): + #CREATE THE light_prefs FOLDER IF IT DOESN'T EXIST + try: + os.mkdir(os.path.dirname(os.path.abspath(sys.argv[0])) + os.sep + "light_prefs") + except FileExistsError: + pass # the folder already exists, so we don't need to create it + def loadPrefsFile(globalPrefsFile = ""): global findLightsOnStartup, autoConnectToLights, printDebug, maxNumOfAttempts, \ rememberLightsOnExit, acceptable_HTTP_IPs, customKeys, enableTabsOnLaunch @@ -2328,6 +2528,9 @@ def loadPrefsFile(globalPrefsFile = ""): else: loadPrefsFile() # if it doesn't, then just load the defaults + if os.path.exists(customLightPresetsFile): + loadCustomPresets() # if there's a custom mapping for presets, then load that into memory + loop = asyncio.get_event_loop() # get the current asyncio loop cmdReturn = [True] # initially set to show the GUI interface over the CLI interface diff --git a/ui_NeewerLightUI.py b/ui_NeewerLightUI.py index ace4f26..82222de 100644 --- a/ui_NeewerLightUI.py +++ b/ui_NeewerLightUI.py @@ -1,5 +1,5 @@ from PySide2.QtCore import QRect, Signal -from PySide2.QtGui import QFont, QLinearGradient, QColor, Qt, QKeySequence +from PySide2.QtGui import QFont, QLinearGradient, QColor, Qt, QKeySequence, QPalette from PySide2.QtWidgets import QFormLayout, QGridLayout, QKeySequenceEdit, QWidget, QPushButton, QTableWidget, QTableWidgetItem, QAbstractScrollArea, QAbstractItemView, \ QTabWidget, QGraphicsScene, QGraphicsView, QFrame, QSlider, QLabel, QLineEdit, QCheckBox, QStatusBar, QScrollArea, QTextEdit @@ -76,21 +76,21 @@ def setupUi(self, MainWindow): self.customPresetButtonsLay = QGridLayout(self.customPresetButtonsCW) self.customPresetButtonsLay.setContentsMargins(0, 0, 0, 0) - self.customPreset_0_Button = customPresetButton(self.centralwidget, text="1") + self.customPreset_0_Button = customPresetButton(self.centralwidget, text="1
PRESET
GLOBAL") self.customPresetButtonsLay.addWidget(self.customPreset_0_Button, 1, 1) - self.customPreset_1_Button = customPresetButton(self.centralwidget, text="2") + self.customPreset_1_Button = customPresetButton(self.centralwidget, text="2
PRESET
GLOBAL") self.customPresetButtonsLay.addWidget(self.customPreset_1_Button, 1, 2) - self.customPreset_2_Button = customPresetButton(self.centralwidget, text="3") + self.customPreset_2_Button = customPresetButton(self.centralwidget, text="3
PRESET
GLOBAL") self.customPresetButtonsLay.addWidget(self.customPreset_2_Button, 1, 3) - self.customPreset_3_Button = customPresetButton(self.centralwidget, text="4") + self.customPreset_3_Button = customPresetButton(self.centralwidget, text="4
PRESET
GLOBAL") self.customPresetButtonsLay.addWidget(self.customPreset_3_Button, 1, 4) - self.customPreset_4_Button = customPresetButton(self.centralwidget, text="5") + self.customPreset_4_Button = customPresetButton(self.centralwidget, text="5
PRESET
GLOBAL") self.customPresetButtonsLay.addWidget(self.customPreset_4_Button, 1, 5) - self.customPreset_5_Button = customPresetButton(self.centralwidget, text="6") + self.customPreset_5_Button = customPresetButton(self.centralwidget, text="6
PRESET
GLOBAL") self.customPresetButtonsLay.addWidget(self.customPreset_5_Button, 1, 6) - self.customPreset_6_Button = customPresetButton(self.centralwidget, text="7") + self.customPreset_6_Button = customPresetButton(self.centralwidget, text="7
PRESET
GLOBAL") self.customPresetButtonsLay.addWidget(self.customPreset_6_Button, 1, 7) - self.customPreset_7_Button = customPresetButton(self.centralwidget, text="8") + self.customPreset_7_Button = customPresetButton(self.centralwidget, text="8
PRESET
GLOBAL") self.customPresetButtonsLay.addWidget(self.customPreset_7_Button, 1, 8) # ============ THE MODE TABS ============ @@ -486,14 +486,16 @@ def __init__(self, *args, **kwargs): customPresetFont = QFont() customPresetFont.setPointSize(12) - + + self.setTextFormat(Qt.TextFormat.RichText) + self.setText(kwargs['text']) self.setFont(customPresetFont) self.setAlignment(Qt.AlignCenter) self.setStyleSheet("customPresetButton" "{" - "border: 1px solid black; background-color: #a5cbf7;" + "border: 1px solid grey; background-color: #a5cbf7;" "}" "customPresetButton::hover" "{" @@ -501,13 +503,16 @@ def __init__(self, *args, **kwargs): "}") def mousePressEvent(self, event): - if event.button() == Qt.RightButton: - self.rightclicked.emit() - elif event.button() == Qt.LeftButton: + if event.button() == Qt.LeftButton: self.clicked.emit() + elif event.button() == Qt.RightButton: + self.rightclicked.emit() - def markCustom(self): - self.setStyleSheet("customPresetButton" + def markCustom(self, presetNum, isSnap = 0): + if isSnap == 0: # we're using a global preset + self.setText("" + str(presetNum + 1) + "
CUSTOM
GLOBAL") + + self.setStyleSheet("customPresetButton" "{" "border: 1px solid black; background-color: #7188ff;" "}" @@ -515,6 +520,28 @@ def markCustom(self): "{" "background-color: #70b0ff;" "}") + elif isSnap >= 1: # we're using a snapshot preset + self.setText("" + str(presetNum + 1) + "
CUSTOM
SNAP") + + self.setStyleSheet("customPresetButton" + "{" + "border: 1px solid black; background-color: #71e993;" + "}" + "customPresetButton::hover" + "{" + "background-color: #abe9ab;" + "}") + else: # we're resetting back to a default preset + self.setText("" + str(presetNum + 1) + "
PRESET
GLOBAL") + + self.setStyleSheet("customPresetButton" + "{" + "border: 1px solid grey; background-color: #a5cbf7;" + "}" + "customPresetButton::hover" + "{" + "background-color: #a5e3f7;" + "}") class singleKeySequenceEditCancel(QWidget): def __init__(self, defaultValue): From 905a98928cd748a36f3860fdc0bb7ae2642816fd Mon Sep 17 00:00:00 2001 From: Zach Glenwright Date: Tue, 25 Jan 2022 04:37:53 -0500 Subject: [PATCH 06/15] 1-25-22 - More work with customizing, selection display - Selecting a snapshot preset now (correctly!) displays which lights are affected by it - A little more look-and-feel customization, trying to get the button fonts on Windows and Mac to look similar --- NeewerLite-Python.py | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/NeewerLite-Python.py b/NeewerLite-Python.py index 8ccf314..77a79f5 100644 --- a/NeewerLite-Python.py +++ b/NeewerLite-Python.py @@ -55,8 +55,8 @@ # IMPORT PYSIDE2 (the GUI libraries) try: - from PySide2.QtCore import Qt - from PySide2.QtGui import QLinearGradient, QColor, QKeySequence + from PySide2.QtCore import Qt, QItemSelectionModel + from PySide2.QtGui import QLinearGradient, QColor, QKeySequence, QFont from PySide2.QtWidgets import QApplication, QMainWindow, QTableWidgetItem, QShortcut, QMessageBox except Exception as e: @@ -181,6 +181,18 @@ def __init__(self): if platform.system() == "Darwin": # if we're on MacOS, then change the column text for the 2nd column in the light table self.lightTable.horizontalHeaderItem(1).setText("Light UUID") + elif platform.system() == "Windows": # if we're on Windows, then change the font for the custom preset buttons + windowsCustomFont = QFont("Gadugi") + windowsCustomFont.setPointSize(10.5) + + self.customPreset_0_Button.setFont(windowsCustomFont) + self.customPreset_1_Button.setFont(windowsCustomFont) + self.customPreset_2_Button.setFont(windowsCustomFont) + self.customPreset_3_Button.setFont(windowsCustomFont) + self.customPreset_4_Button.setFont(windowsCustomFont) + self.customPreset_5_Button.setFont(windowsCustomFont) + self.customPreset_6_Button.setFont(windowsCustomFont) + self.customPreset_7_Button.setFont(windowsCustomFont) # IF ANY OF THE CUSTOM PRESETS ARE ACTUALLY CUSTOM, THEN MARK THOSE BUTTONS AS CUSTOM if customLightPresets[0] != defaultLightPresets[0]: @@ -928,6 +940,11 @@ def clearTheTable(self): self.lightTable.clearContents() self.lightTable.setRowCount(0) + def selectRows(self, rowsToSelect): + self.lightTable.clearSelection() + indexes = [self.lightTable.model().index(r, 0) for r in rowsToSelect] + [self.lightTable.selectionModel().select(i, QItemSelectionModel.Select | QItemSelectionModel.Rows) for i in indexes] + # TELL THE BACKGROUND THREAD TO START LOOKING FOR LIGHTS def startSelfSearch(self): global threadAction @@ -1283,6 +1300,9 @@ def recallCustomPreset(self, numOfPreset): changedLights.append(currentLight[0]) if changedLights != []: + self.lightTable.setFocus() + self.selectRows(changedLights) + global threadAction threadAction = "send|" + "|".join(map(str, changedLights)) @@ -1987,8 +2007,6 @@ def workerThread(_loop): threadAction = _loop.run_until_complete(writeToLight(lightsToSendTo, True, False)) # write the value stored in the lights to the light(s) - mainWindow.lightTable.clearSelection() # clear the selection first before re-selecting lights - time.sleep(0.25) async def parallelAction(theAction, theLights, updateGUI = True): From be4399db359000546bf8f7e0b67465bd49b4ad0c Mon Sep 17 00:00:00 2001 From: Zach Glenwright Date: Tue, 25 Jan 2022 07:28:19 -0500 Subject: [PATCH 07/15] 1-25-22 B - Font maneuvering Moved the font constructor to the custom customPresetButton class - if it's MacOS or Linux, it uses the default font (for now) - if it's Windows, it uses a slightly smaller one. --- NeewerLite-Python.py | 12 ------------ ui_NeewerLightUI.py | 16 +++++++++++----- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/NeewerLite-Python.py b/NeewerLite-Python.py index 77a79f5..1bffee4 100644 --- a/NeewerLite-Python.py +++ b/NeewerLite-Python.py @@ -181,18 +181,6 @@ def __init__(self): if platform.system() == "Darwin": # if we're on MacOS, then change the column text for the 2nd column in the light table self.lightTable.horizontalHeaderItem(1).setText("Light UUID") - elif platform.system() == "Windows": # if we're on Windows, then change the font for the custom preset buttons - windowsCustomFont = QFont("Gadugi") - windowsCustomFont.setPointSize(10.5) - - self.customPreset_0_Button.setFont(windowsCustomFont) - self.customPreset_1_Button.setFont(windowsCustomFont) - self.customPreset_2_Button.setFont(windowsCustomFont) - self.customPreset_3_Button.setFont(windowsCustomFont) - self.customPreset_4_Button.setFont(windowsCustomFont) - self.customPreset_5_Button.setFont(windowsCustomFont) - self.customPreset_6_Button.setFont(windowsCustomFont) - self.customPreset_7_Button.setFont(windowsCustomFont) # IF ANY OF THE CUSTOM PRESETS ARE ACTUALLY CUSTOM, THEN MARK THOSE BUTTONS AS CUSTOM if customLightPresets[0] != defaultLightPresets[0]: diff --git a/ui_NeewerLightUI.py b/ui_NeewerLightUI.py index 82222de..60d4643 100644 --- a/ui_NeewerLightUI.py +++ b/ui_NeewerLightUI.py @@ -3,6 +3,8 @@ from PySide2.QtWidgets import QFormLayout, QGridLayout, QKeySequenceEdit, QWidget, QPushButton, QTableWidget, QTableWidgetItem, QAbstractScrollArea, QAbstractItemView, \ QTabWidget, QGraphicsScene, QGraphicsView, QFrame, QSlider, QLabel, QLineEdit, QCheckBox, QStatusBar, QScrollArea, QTextEdit +import platform # for selecting specific fonts for specific systems + class Ui_MainWindow(object): def setupUi(self, MainWindow): # ============ FONTS AND OTHER WINDOW SPECIFICS ============ @@ -484,14 +486,18 @@ class customPresetButton(QLabel): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - customPresetFont = QFont() - customPresetFont.setPointSize(12) + if platform.system() != "Windows": # if we're not on Windows, use the default font + customPresetFont = QFont() + customPresetFont.setPointSize(12) + self.setFont(customPresetFont) + else: # if we are on Windows, use a slightly less garish font than the default one + windowsCustomPresetFont = QFont("Segoe UI") + windowsCustomPresetFont.setPointSize(10.5) + self.setFont(windowsCustomPresetFont) + self.setAlignment(Qt.AlignCenter) self.setTextFormat(Qt.TextFormat.RichText) - self.setText(kwargs['text']) - self.setFont(customPresetFont) - self.setAlignment(Qt.AlignCenter) self.setStyleSheet("customPresetButton" "{" From d41e9c3cb1b0886f92c8ef1f1e395e12566f2de3 Mon Sep 17 00:00:00 2001 From: Zach Glenwright Date: Tue, 25 Jan 2022 12:35:48 -0500 Subject: [PATCH 08/15] 1-25-22 - Added enter/leave widget signals Added enter/leave widget signals for each custom preset button, so every one of them can show the affected lights on a snapshot preset (if you hover over the preset, the affected lights show up in light green, the same color as the snapshot button) --- NeewerLite-Python.py | 49 ++++++++++++++++++++++++++++++++++++++++++-- ui_NeewerLightUI.py | 8 ++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/NeewerLite-Python.py b/NeewerLite-Python.py index 1bffee4..f050b94 100644 --- a/NeewerLite-Python.py +++ b/NeewerLite-Python.py @@ -236,22 +236,39 @@ def connectMe(self): self.ColorModeTabWidget.currentChanged.connect(self.tabChanged) self.lightTable.itemSelectionChanged.connect(self.selectionChanged) - self.customPreset_0_Button.clicked.connect(lambda: self.recallCustomPreset(0)) - self.customPreset_0_Button.rightclicked.connect(lambda: self.saveCustomPresetDialog(0)) + # COMMENTS ARE THE SAME THE ENTIRE WAY DOWN THIS CHAIN + self.customPreset_0_Button.clicked.connect(lambda: self.recallCustomPreset(0)) # when you click a preset + self.customPreset_0_Button.rightclicked.connect(lambda: self.saveCustomPresetDialog(0)) # when you right-click a preset + self.customPreset_0_Button.enteredWidget.connect(lambda: self.highlightLightsForSnapshotPreset(0)) # when the mouse enters the widget + self.customPreset_0_Button.leftWidget.connect(lambda: self.highlightLightsForSnapshotPreset(0, True)) # when the mouse leaves the widget self.customPreset_1_Button.clicked.connect(lambda: self.recallCustomPreset(1)) self.customPreset_1_Button.rightclicked.connect(lambda: self.saveCustomPresetDialog(1)) + self.customPreset_1_Button.enteredWidget.connect(lambda: self.highlightLightsForSnapshotPreset(1)) + self.customPreset_1_Button.leftWidget.connect(lambda: self.highlightLightsForSnapshotPreset(1, True)) self.customPreset_2_Button.clicked.connect(lambda: self.recallCustomPreset(2)) self.customPreset_2_Button.rightclicked.connect(lambda: self.saveCustomPresetDialog(2)) + self.customPreset_2_Button.enteredWidget.connect(lambda: self.highlightLightsForSnapshotPreset(2)) + self.customPreset_2_Button.leftWidget.connect(lambda: self.highlightLightsForSnapshotPreset(2, True)) self.customPreset_3_Button.clicked.connect(lambda: self.recallCustomPreset(3)) self.customPreset_3_Button.rightclicked.connect(lambda: self.saveCustomPresetDialog(3)) + self.customPreset_3_Button.enteredWidget.connect(lambda: self.highlightLightsForSnapshotPreset(3)) + self.customPreset_3_Button.leftWidget.connect(lambda: self.highlightLightsForSnapshotPreset(3, True)) self.customPreset_4_Button.clicked.connect(lambda: self.recallCustomPreset(4)) self.customPreset_4_Button.rightclicked.connect(lambda: self.saveCustomPresetDialog(4)) + self.customPreset_4_Button.enteredWidget.connect(lambda: self.highlightLightsForSnapshotPreset(4)) + self.customPreset_4_Button.leftWidget.connect(lambda: self.highlightLightsForSnapshotPreset(4, True)) self.customPreset_5_Button.clicked.connect(lambda: self.recallCustomPreset(5)) self.customPreset_5_Button.rightclicked.connect(lambda: self.saveCustomPresetDialog(5)) + self.customPreset_5_Button.enteredWidget.connect(lambda: self.highlightLightsForSnapshotPreset(5)) + self.customPreset_5_Button.leftWidget.connect(lambda: self.highlightLightsForSnapshotPreset(5, True)) self.customPreset_6_Button.clicked.connect(lambda: self.recallCustomPreset(6)) self.customPreset_6_Button.rightclicked.connect(lambda: self.saveCustomPresetDialog(6)) + self.customPreset_6_Button.enteredWidget.connect(lambda: self.highlightLightsForSnapshotPreset(6)) + self.customPreset_6_Button.leftWidget.connect(lambda: self.highlightLightsForSnapshotPreset(6, True)) self.customPreset_7_Button.clicked.connect(lambda: self.recallCustomPreset(7)) self.customPreset_7_Button.rightclicked.connect(lambda: self.saveCustomPresetDialog(7)) + self.customPreset_7_Button.enteredWidget.connect(lambda: self.highlightLightsForSnapshotPreset(7)) + self.customPreset_7_Button.leftWidget.connect(lambda: self.highlightLightsForSnapshotPreset(7, True)) self.Slider_CCT_Hue.valueChanged.connect(lambda: self.computeValueCCT(2)) self.Slider_CCT_Bright.valueChanged.connect(lambda: self.computeValueCCT(1)) @@ -1241,6 +1258,34 @@ def saveCustomPresetDialog(self, numOfPreset): if numOfPreset == 7: self.customPreset_7_Button.markCustom(7, clickedButton) + def highlightLightsForSnapshotPreset(self, numOfPreset, exited = False): + if exited == False: + lightsToHighlight = self.checkForSnapshotPreset(numOfPreset) + + for a in range(len(lightsToHighlight)): + self.lightTable.item(lightsToHighlight[a], 3).font().setBold(True) + self.lightTable.item(lightsToHighlight[a], 3).setBackground(QColor(113, 233, 147)) # set the affected rows the same color as the snapshot button + else: + lightsToHighlight = self.checkForSnapshotPreset(numOfPreset) + + for a in range(len(lightsToHighlight)): + self.lightTable.item(lightsToHighlight[a], 3).font().setBold(False) + self.lightTable.item(lightsToHighlight[a], 3).setBackground(Qt.white) # clear formatting on the previously selected rows + + def checkForSnapshotPreset(self, numOfPreset): + if customLightPresets[numOfPreset][0][0] != -1: # if the value is not -1, then we most likely have a snapshot preset + lightsToHighlight = [] + + for a in range(len(customLightPresets[numOfPreset])): # check each entry in the preset for matching lights + currentLight = returnLightIndexesFromMacAddress(customLightPresets[numOfPreset][a][0]) + + if currentLight != []: # if we have a match, add it to the list of lights to highlight + lightsToHighlight.append(currentLight[0]) + + return lightsToHighlight + else: + return [] # if we don't have a snapshot preset, then just return an empty list (no lights directly affected) + def recallCustomPreset(self, numOfPreset): global availableLights changedLights = [] # if a snapshot preset exists in this setting, log the lights that are to be changed here diff --git a/ui_NeewerLightUI.py b/ui_NeewerLightUI.py index 60d4643..a32e3cd 100644 --- a/ui_NeewerLightUI.py +++ b/ui_NeewerLightUI.py @@ -482,6 +482,8 @@ def setupUi(self, MainWindow): class customPresetButton(QLabel): clicked = Signal() rightclicked = Signal() + enteredWidget = Signal() + leftWidget = Signal() def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -514,6 +516,12 @@ def mousePressEvent(self, event): elif event.button() == Qt.RightButton: self.rightclicked.emit() + def enterEvent(self, event): + self.enteredWidget.emit() + + def leaveEvent(self, event): + self.leftWidget.emit() + def markCustom(self, presetNum, isSnap = 0): if isSnap == 0: # we're using a global preset self.setText("" + str(presetNum + 1) + "
CUSTOM
GLOBAL") From 2156e94e3c4df920b05896070c4ae6576cc2e929 Mon Sep 17 00:00:00 2001 From: Zach Glenwright Date: Tue, 25 Jan 2022 13:13:01 -0500 Subject: [PATCH 09/15] 1-25-22 - Refined setTheTable and tweaked selections Made setTheTable less resource-heavy (I think) by only creating QTableWidgetItems on initialization, and then directly setting the text for those rows. Also refined the snapshot selection/highlight system --- NeewerLite-Python.py | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/NeewerLite-Python.py b/NeewerLite-Python.py index f050b94..fc6d1e7 100644 --- a/NeewerLite-Python.py +++ b/NeewerLite-Python.py @@ -78,6 +78,7 @@ CCTSlider = -1 # the current slider moved in the CCT window - 1 - Brightness / 2 - Hue / -1 - Both Brightness and Hue sendValue = [120, 135, 2, 100, 56, 157] # an array to hold the values to be sent to the light - the default is CCT / 5600K / 100% lastAnimButtonPressed = 1 # which animation button you clicked last - if none, then it defaults to 1 (the police sirens) +lastSelection = [] # the current light selection (this is for snapshot preset entering/leaving buttons) availableLights = [] # the list of Neewer lights currently available to control - format: # 0 1 2 3 4 5 6 7 @@ -916,23 +917,27 @@ def setTheTable(self, infoArray, rowToChange = -1): if rowToChange == -1: currentRow = self.lightTable.rowCount() self.lightTable.insertRow(currentRow) # if rowToChange is not specified, then we'll make a new row at the end + self.lightTable.setItem(currentRow, 0, QTableWidgetItem()) + self.lightTable.setItem(currentRow, 1, QTableWidgetItem()) + self.lightTable.setItem(currentRow, 2, QTableWidgetItem()) + self.lightTable.setItem(currentRow, 3, QTableWidgetItem()) else: currentRow = rowToChange # change data for the specified row # THIS SECTION BELOW LIMITS UPDATING THE TABLE **ONLY** IF THE DATA SUPPLIED IS DIFFERENT THAN IT WAS ORIGINALLY if infoArray[0] != "": # the name of the light if rowToChange == -1 or (rowToChange != -1 and infoArray[0] != self.returnTableInfo(rowToChange, 0)): - self.lightTable.setItem(currentRow, 0, QTableWidgetItem(infoArray[0])) + self.lightTable.item(currentRow, 0).setText(infoArray[0]) if infoArray[1] != "": # the MAC address of the light if rowToChange == -1 or (rowToChange != -1 and infoArray[1] != self.returnTableInfo(rowToChange, 1)): - self.lightTable.setItem(currentRow, 1, QTableWidgetItem(infoArray[1])) + self.lightTable.item(currentRow, 1).setText(infoArray[1]) if infoArray[2] != "": # the Linked status of the light if rowToChange == -1 or (rowToChange != -1 and infoArray[2] != self.returnTableInfo(rowToChange, 2)): - self.lightTable.setItem(currentRow, 2, QTableWidgetItem(infoArray[2])) + self.lightTable.item(currentRow, 2).setText(infoArray[2]) self.lightTable.item(currentRow, 2).setTextAlignment(Qt.AlignCenter) # align the light status info to be center-justified if infoArray[3] != "": # the current status message of the light if rowToChange == -1 or (rowToChange != -1 and infoArray[2] != self.returnTableInfo(rowToChange, 3)): - self.lightTable.setItem(currentRow, 3, QTableWidgetItem(infoArray[3])) + self.lightTable.item(currentRow, 3).setText(infoArray[3]) self.lightTable.resizeRowsToContents() @@ -1259,18 +1264,27 @@ def saveCustomPresetDialog(self, numOfPreset): self.customPreset_7_Button.markCustom(7, clickedButton) def highlightLightsForSnapshotPreset(self, numOfPreset, exited = False): + global lastSelection + if exited == False: lightsToHighlight = self.checkForSnapshotPreset(numOfPreset) - for a in range(len(lightsToHighlight)): - self.lightTable.item(lightsToHighlight[a], 3).font().setBold(True) - self.lightTable.item(lightsToHighlight[a], 3).setBackground(QColor(113, 233, 147)) # set the affected rows the same color as the snapshot button + if lightsToHighlight != []: + lastSelection = self.selectedLights() # store the current selection to restore it when leaving the control + self.lightTable.clearSelection() # clear the current selection to allow the preset to shine + + for a in range(len(lightsToHighlight)): + for b in range(4): + self.lightTable.item(lightsToHighlight[a], b).setBackground(QColor(113, 233, 147)) # set the affected rows the same color as the snapshot button else: lightsToHighlight = self.checkForSnapshotPreset(numOfPreset) - for a in range(len(lightsToHighlight)): - self.lightTable.item(lightsToHighlight[a], 3).font().setBold(False) - self.lightTable.item(lightsToHighlight[a], 3).setBackground(Qt.white) # clear formatting on the previously selected rows + if lightsToHighlight != []: + self.selectRows(lastSelection) # re-highlight the last selected lights on exit + + for a in range(len(lightsToHighlight)): + for b in range(4): + self.lightTable.item(lightsToHighlight[a], b).setBackground(Qt.white) # clear formatting on the previously selected rows def checkForSnapshotPreset(self, numOfPreset): if customLightPresets[numOfPreset][0][0] != -1: # if the value is not -1, then we most likely have a snapshot preset @@ -1288,6 +1302,7 @@ def checkForSnapshotPreset(self, numOfPreset): def recallCustomPreset(self, numOfPreset): global availableLights + changedLights = [] # if a snapshot preset exists in this setting, log the lights that are to be changed here for a in range(len(customLightPresets[numOfPreset])): # check all the entries stored in this preset @@ -1333,6 +1348,9 @@ def recallCustomPreset(self, numOfPreset): changedLights.append(currentLight[0]) if changedLights != []: + global lastSelection + lastSelection = changedLights + self.lightTable.setFocus() self.selectRows(changedLights) From 6ca00e24cfad79a77fe5de09ef5d0f93dad85422 Mon Sep 17 00:00:00 2001 From: Zach Glenwright Date: Tue, 25 Jan 2022 16:35:01 -0500 Subject: [PATCH 10/15] 1-25-22 - Clear last selection when changing snapshot preset When launching a snapshot preset, we now clear lastSelection, as the last selection is no longer valid. Also changed setTheTable to take the value from currentSendValue instead of just accepting sendValue is going to be correct (maunly for updating the GUI of the last set params for a series in snapshot mode) --- NeewerLite-Python.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/NeewerLite-Python.py b/NeewerLite-Python.py index fc6d1e7..18791d4 100644 --- a/NeewerLite-Python.py +++ b/NeewerLite-Python.py @@ -1302,6 +1302,7 @@ def checkForSnapshotPreset(self, numOfPreset): def recallCustomPreset(self, numOfPreset): global availableLights + global lastSelection changedLights = [] # if a snapshot preset exists in this setting, log the lights that are to be changed here @@ -1348,9 +1349,8 @@ def recallCustomPreset(self, numOfPreset): changedLights.append(currentLight[0]) if changedLights != []: - global lastSelection - lastSelection = changedLights - + lastSelection = [] # clear the last selection if you've clicked on a snapshot preset (which, if we're here, you did) + self.lightTable.setFocus() self.selectRows(changedLights) @@ -1927,7 +1927,7 @@ async def writeToLight(selectedLights=0, updateGUI=True, useGlobalValue=True): # if we're not looking at an old light, or if we are, we're not in either HSI or ANM modes, then update the status of that light if not (availableLights[(int(selectedLights[a]))][5] == True and (currentSendValue[1] == 134 or currentSendValue[1] == 136)): if currentSendValue[1] != 129: # if we're not turning the light on or off - mainWindow.setTheTable(["", "", "", updateStatus(True)], int(selectedLights[a])) + mainWindow.setTheTable(["", "", "", updateStatus(True, currentSendValue)], int(selectedLights[a])) else: # we ARE turning the light on or off if currentSendValue[3] == 1: # we turned the light on availableLights[int(selectedLights[a])][6] = True # toggle the "light on" parameter of this light to ON From 053ca6e97de61be26cc5dea3b8f4feb43c92e6d0 Mon Sep 17 00:00:00 2001 From: Zach Glenwright Date: Thu, 27 Jan 2022 06:36:03 -0500 Subject: [PATCH 11/15] 1-27-22 - Added preferences for whitelisting and custom presets Added preferences, both GUI and logic, for both the whitelisting function (last main commit) and saving custom presets on quitting out. Also going through everything just to make sure it's all good! --- NeewerLite-Python.py | 68 +++++++++++++++++++++++++++++--------------- ui_NeewerLightUI.py | 18 ++++++++---- 2 files changed, 57 insertions(+), 29 deletions(-) diff --git a/NeewerLite-Python.py b/NeewerLite-Python.py index afaee18..93c04e2 100644 --- a/NeewerLite-Python.py +++ b/NeewerLite-Python.py @@ -56,7 +56,7 @@ # IMPORT PYSIDE2 (the GUI libraries) try: from PySide2.QtCore import Qt, QItemSelectionModel - from PySide2.QtGui import QLinearGradient, QColor, QKeySequence, QFont + from PySide2.QtGui import QLinearGradient, QColor, QKeySequence from PySide2.QtWidgets import QApplication, QMainWindow, QTableWidgetItem, QShortcut, QMessageBox except Exception as e: @@ -76,7 +76,7 @@ pass # if there are any HTTP errors, don't do anything yet CCTSlider = -1 # the current slider moved in the CCT window - 1 - Brightness / 2 - Hue / -1 - Both Brightness and Hue -sendValue = [120, 135, 2, 100, 56, 157] # an array to hold the values to be sent to the light - the default is CCT / 5600K / 100% +sendValue = [120, 135, 2, 20, 56, 157] # an array to hold the values to be sent to the light - the default is CCT / 5600K / 100% lastAnimButtonPressed = 1 # which animation button you clicked last - if none, then it defaults to 1 (the police sirens) lastSelection = [] # the current light selection (this is for snapshot preset entering/leaving buttons) @@ -86,28 +86,28 @@ # The list of **default** light presets for restoring and checking against defaultLightPresets = [ - [[-1, [5, 100, 56]]], - [[-1, [5, 100, 32]]], + [[-1, [5, 20, 56]]], + [[-1, [5, 20, 32]]], [[-1, [5, 0, 56]]], - [[-1, [4, 100, 0, 100]]], - [[-1, [4, 100, 240, 100]]], - [[-1, [4, 100, 120, 100]]], - [[-1, [4, 100, 300, 100]]], - [[-1, [4, 100, 160, 100]]] + [[-1, [4, 20, 0, 100]]], + [[-1, [4, 20, 240, 100]]], + [[-1, [4, 20, 120, 100]]], + [[-1, [4, 20, 300, 100]]], + [[-1, [4, 20, 160, 100]]] ] # A list of preset mode settings - custom file will overwrite, but here are the default values # (0 - CCT - 5600K / 100%) / (1 - CCT - 3200K / 100%) / (2 - CCT - 5600K / 0%) / (3 - HSI - Red / all 100%) # (4 - HSI - Blue / all 100%) / (5 - HSI - Green / all 100%) / (6 - HSI - Purple / all 100%) / (7 - HSI - Cyan / all 100%) customLightPresets = [ - [[-1, [5, 100, 56]]], - [[-1, [5, 100, 32]]], + [[-1, [5, 20, 56]]], + [[-1, [5, 20, 32]]], [[-1, [5, 0, 56]]], - [[-1, [4, 100, 0, 100]]], - [[-1, [4, 100, 240, 100]]], - [[-1, [4, 100, 120, 100]]], - [[-1, [4, 100, 300, 100]]], - [[-1, [4, 100, 160, 100]]] + [[-1, [4, 20, 0, 100]]], + [[-1, [4, 20, 240, 100]]], + [[-1, [4, 20, 120, 100]]], + [[-1, [4, 20, 300, 100]]], + [[-1, [4, 20, 160, 100]]] ] threadAction = "" # the current action to take from the thread @@ -516,8 +516,10 @@ def setupGlobalLightPrefsTab(self, setDefault=False): self.autoConnectToLights_check.setChecked(autoConnectToLights) self.printDebug_check.setChecked(printDebug) self.rememberLightsOnExit_check.setChecked(rememberLightsOnExit) + self.rememberPresetsOnExit_check.setChecked(rememberPresetsOnExit) self.maxNumOfAttempts_field.setText(str(maxNumOfAttempts)) self.acceptable_HTTP_IPs_field.setText("\n".join(acceptable_HTTP_IPs)) + self.whiteListedMACs_field.setText("\n".join(whiteListedMACs)) self.SC_turnOffButton_field.setKeySequence(customKeys[0]) self.SC_turnOnButton_field.setKeySequence(customKeys[1]) self.SC_scanCommandButton_field.setKeySequence(customKeys[2]) @@ -547,8 +549,10 @@ def setupGlobalLightPrefsTab(self, setDefault=False): self.autoConnectToLights_check.setChecked(True) self.printDebug_check.setChecked(True) self.rememberLightsOnExit_check.setChecked(False) + self.rememberPresetsOnExit_check.setChecked(True) self.maxNumOfAttempts_field.setText("6") self.acceptable_HTTP_IPs_field.setText("\n".join(["127.0.0.1", "192.168", "10.0.0"])) + self.whiteListedMACs_field.setText("") self.SC_turnOffButton_field.setKeySequence("Ctrl+PgDown") self.SC_turnOnButton_field.setKeySequence("Ctrl+PgUp") self.SC_scanCommandButton_field.setKeySequence("Ctrl+Shift+S") @@ -576,7 +580,7 @@ def setupGlobalLightPrefsTab(self, setDefault=False): def saveGlobalPrefs(self): # change these global values to the new values in Prefs - global customKeys, autoConnectToLights, printDebug, rememberLightsOnExit, maxNumOfAttempts, acceptable_HTTP_IPs + global customKeys, autoConnectToLights, printDebug, rememberLightsOnExit, rememberPresetsOnExit, maxNumOfAttempts, acceptable_HTTP_IPs, whiteListedMACs finalPrefs = [] # list of final prefs to merge together at the end @@ -600,6 +604,12 @@ def saveGlobalPrefs(self): finalPrefs.append("rememberLightsOnExit=1") else: rememberLightsOnExit = False + + if not self.rememberPresetsOnExit_check.isChecked(): # this option is usually on, so only add if false + rememberPresetsOnExit = False + finalPrefs.append("rememberPresetsOnExit=0") + else: + rememberPresetsOnExit = True if self.maxNumOfAttempts_field.text() != "6": # the default for this option is 6 attempts maxNumOfAttempts = int(self.maxNumOfAttempts_field.text()) @@ -615,6 +625,15 @@ def saveGlobalPrefs(self): finalPrefs.append("acceptable_HTTP_IPs=" + ";".join(acceptable_HTTP_IPs)) # add the new ones to the preferences else: acceptable_HTTP_IPs = ["127.0.0.1", "192.168", "10.0.0"] # if we reset the IPs, then re-reset the parameter + + # ADD WHITELISTED LIGHTS TO PREFERENCES IF THEY EXIST + returnedList_whiteListedMACs = self.whiteListedMACs_field.toPlainText().replace(" ", "").split("\n") # remove spaces and split on newlines + + if returnedList_whiteListedMACs[0] != "": # if we have any MAC addresses specified + whiteListedMACs = returnedList_whiteListedMACs # then set the list to the addresses specified + finalPrefs.append("whiteListedMACs=" + ";".join(whiteListedMACs)) # add the new addresses to the preferences + else: + whiteListedMACs = [] # or clear the list # SET THE NEW KEYBOARD SHORTCUTS TO THE VALUES IN PREFERENCES customKeys[0] = self.SC_turnOffButton_field.keySequence().toString() @@ -1197,9 +1216,10 @@ def closeEvent(self, event): printDebugString("Closing the program NOW") def saveCustomPresetDialog(self, numOfPreset): - if (QApplication.keyboardModifiers() & Qt.AltModifier) == Qt.AltModifier: # if you have the ALT key held down, then delete the current preset - customLightPresets[numOfPreset] = defaultLightPresets[numOfPreset] + if (QApplication.keyboardModifiers() & Qt.AltModifier) == Qt.AltModifier: # if you have the ALT key held down + customLightPresets[numOfPreset] = defaultLightPresets[numOfPreset] # then restore the default for this preset + # And change the button display back to "PRESET GLOBAL" if numOfPreset == 0: self.customPreset_0_Button.markCustom(0, -1) if numOfPreset == 1: @@ -1682,7 +1702,7 @@ async def findDevices(): customPrefs = getCustomLightPrefs(currentScan[a].address, currentScan[a].name) if len(customPrefs) == 3: # we need to rename the light and set up CCT and color temp range - availableLights.append([currentScan[a], "", customPrefs[0], [120, 135, 2, 100, 56, 157], customPrefs[1], customPrefs[2], True, ["---", "---"]]) # add it to the global list + availableLights.append([currentScan[a], "", customPrefs[0], [120, 135, 2, 20, 56, 157], customPrefs[1], customPrefs[2], True, ["---", "---"]]) # add it to the global list elif len(customPrefs) == 4: # same as above, but we have previously stored parameters, so add them in as well availableLights.append([currentScan[a], "", customPrefs[0], customPrefs[3], customPrefs[1], customPrefs[2], True, ["---", "---"]]) # add it to the global list @@ -2490,7 +2510,7 @@ def createLightPrefsFolder(): def loadPrefsFile(globalPrefsFile = ""): global findLightsOnStartup, autoConnectToLights, printDebug, maxNumOfAttempts, \ rememberLightsOnExit, acceptable_HTTP_IPs, customKeys, enableTabsOnLaunch, \ - whiteListedMACs + whiteListedMACs, rememberPresetsOnExit if globalPrefsFile != "": printDebugString("Loading global preferences from file...") @@ -2504,7 +2524,7 @@ def loadPrefsFile(globalPrefsFile = ""): "SC_Dec_Bri_Small", "SC_Inc_Bri_Small", "SC_Dec_Bri_Large", "SC_Inc_Bri_Large", \ "SC_Dec_1_Small", "SC_Inc_1_Small", "SC_Dec_2_Small", "SC_Inc_2_Small", "SC_Dec_3_Small", "SC_Inc_3_Small", \ "SC_Dec_1_Large", "SC_Inc_1_Large", "SC_Dec_2_Large", "SC_Inc_2_Large", "SC_Dec_3_Large", "SC_Inc_3_Large", \ - "enableTabsOnLaunch", "whiteListedMACs"] + "enableTabsOnLaunch", "whiteListedMACs", "rememberPresetsOnExit"] # KICK OUT ANY PARAMETERS THAT AREN'T IN THE "ACCEPTABLE ARGUMENTS" LIST ABOVE # THIS SECTION OF CODE IS *SLIGHTLY* DIFFERENT THAN THE CLI KICK OUT CODE @@ -2528,6 +2548,8 @@ def loadPrefsFile(globalPrefsFile = ""): prefsParser.add_argument("--maxNumOfAttempts", default=6) prefsParser.add_argument("--rememberLightsOnExit", default=0) prefsParser.add_argument("--acceptableIPs", default=["127.0.0.1", "192.168", "10.0.0"]) + prefsParser.add_argument("--whiteListedMACs" , default=[]) + prefsParser.add_argument("--rememberPresetsOnExit", default=1) # SHORTCUT KEY CUSTOMIZATIONS prefsParser.add_argument("--SC_turnOffButton", default="Ctrl+PgDown") # 0 @@ -2559,7 +2581,6 @@ def loadPrefsFile(globalPrefsFile = ""): # THESE ARE OPTIONS THAT HELP DEBUG THINGS, BUT AREN'T REALLY USEFUL FOR NORMAL OPERATION # enableTabsOnLaunch SHOWS ALL TABS ACTIVE (INSTEAD OF DISABLING THEM) ON LAUNCH SO EVEN WITHOUT A LIGHT, A BYTESTRING CAN BE CALCULATED prefsParser.add_argument("--enableTabsOnLaunch", default=0) - prefsParser.add_argument("--whiteListedMACs" , default=[]) mainPrefs = prefsParser.parse_args(mainPrefs) @@ -2569,6 +2590,7 @@ def loadPrefsFile(globalPrefsFile = ""): printDebug = bool(int(mainPrefs.printDebug)) # whether or not to display debug messages in the console maxNumOfAttempts = int(mainPrefs.maxNumOfAttempts) # maximum number of attempts before failing out rememberLightsOnExit = bool(int(mainPrefs.rememberLightsOnExit)) # whether or not to remember light mode/settings when quitting out + rememberPresetsOnExit = bool(int(mainPrefs.rememberPresetsOnExit)) # whether or not to remember the custom presets when quitting out if type(mainPrefs.acceptableIPs) is not list: # we have a string in the return, so we need to post-process it acceptable_HTTP_IPs = mainPrefs.acceptableIPs.replace(" ", "").split(";") # split the IP addresses into a list for acceptable IPs diff --git a/ui_NeewerLightUI.py b/ui_NeewerLightUI.py index a32e3cd..291bd25 100644 --- a/ui_NeewerLightUI.py +++ b/ui_NeewerLightUI.py @@ -76,7 +76,7 @@ def setupUi(self, MainWindow): self.customPresetButtonsCW = QWidget(self.centralwidget) self.customPresetButtonsCW.setGeometry(QRect(10, 300, 571, 68)) self.customPresetButtonsLay = QGridLayout(self.customPresetButtonsCW) - self.customPresetButtonsLay.setContentsMargins(0, 0, 0, 0) + self.customPresetButtonsLay.setContentsMargins(0, 0, 0, 0) # ensure this widget spans from the left to the right edge of the light table self.customPreset_0_Button = customPresetButton(self.centralwidget, text="1
PRESET
GLOBAL") self.customPresetButtonsLay.addWidget(self.customPreset_0_Button, 1, 1) @@ -328,11 +328,14 @@ def setupUi(self, MainWindow): self.findLightsOnStartup_check = QCheckBox("Scan for Neewer lights on program launch") self.autoConnectToLights_check = QCheckBox("Automatically try to link to newly found lights") self.printDebug_check = QCheckBox("Print debug information to the console") - self.rememberLightsOnExit_check = QCheckBox("Remember the last parameters set for lights on exit") + self.rememberLightsOnExit_check = QCheckBox("Remember the last mode parameters set for lights on exit") + self.rememberPresetsOnExit_check = QCheckBox("Save configuration of custom presets on exit") self.maxNumOfAttempts_field = QLineEdit() self.maxNumOfAttempts_field.setFixedWidth(35) self.acceptable_HTTP_IPs_field = QTextEdit() self.acceptable_HTTP_IPs_field.setFixedHeight(70) + self.whiteListedMACs_field = QTextEdit() + self.whiteListedMACs_field.setFixedHeight(70) self.resetGlobalPrefsButton = QPushButton("Reset Preferences to Defaults") self.saveGlobalPrefsButton = QPushButton("Save Global Preferences") @@ -444,9 +447,12 @@ def setupUi(self, MainWindow): self.globalPrefsLay.addRow(self.autoConnectToLights_check) self.globalPrefsLay.addRow(self.printDebug_check) self.globalPrefsLay.addRow(self.rememberLightsOnExit_check) + self.globalPrefsLay.addRow(self.rememberPresetsOnExit_check) self.globalPrefsLay.addRow("Maximum Number of retries:", self.maxNumOfAttempts_field) self.globalPrefsLay.addRow(QLabel("
Acceptable IPs to use for the HTTP Server:
Each line below is an IP allows access to NeewerLite-Python's HTTP server.
Wildcards for IP addresses can be entered by just leaving that section blank.
For example:
192.168.*.* would be entered as just 192.168
10.0.1.* is 10.0.1", alignment=Qt.AlignCenter)) self.globalPrefsLay.addRow(self.acceptable_HTTP_IPs_field) + self.globalPrefsLay.addRow(QLabel("
Whitelisted MAC Addresses/GUIDs
Whitelisted MAC Addresses/GUIDs are added to the light list
even if their name doesn't contain Neewer in it.

This preference is really only useful if you have compatible lights
that don't show up properly due to name mismatches.
", alignment=Qt.AlignCenter)) + self.globalPrefsLay.addRow(self.whiteListedMACs_field) self.globalPrefsLay.addRow(QLabel("
Custom GUI Keyboard Shortcut Mapping - GUI Buttons
To switch a keyboard shortcut, click on the old shortcut and type a new one in.
To reset a shortcut to default, click the X button next to it.


These 4 keyboard shortcuts control the buttons on the top of the window.", alignment=Qt.AlignCenter)) self.globalPrefsLay.addRow(self.windowButtonsCW) self.globalPrefsLay.addRow(QLabel("
Custom GUI Keyboard Shortcut Mapping - Switching Mode Tabs
To switch a keyboard shortcut, click on the old shortcut and type a new one in.
To reset a shortcut to default, click the X button next to it.


These 4 keyboard shortcuts switch between
the CCT, HSI, SCENE and LIGHT PREFS tabs.", alignment=Qt.AlignCenter)) @@ -480,10 +486,10 @@ def setupUi(self, MainWindow): self.Slider_ANM_Brightness.valueChanged.connect(self.TFV_ANM_Brightness.setNum) class customPresetButton(QLabel): - clicked = Signal() - rightclicked = Signal() - enteredWidget = Signal() - leftWidget = Signal() + clicked = Signal() # signal sent when you click on the button + rightclicked = Signal() # signal sent when you right-click on the button + enteredWidget = Signal() # signal sent when the mouse enters the button + leftWidget = Signal() # signal sent when the mouse leaves the button def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) From 22768403d37846dfb6b0aae5b0acc9b81c605056 Mon Sep 17 00:00:00 2001 From: Zach Glenwright Date: Thu, 27 Jan 2022 06:46:15 -0500 Subject: [PATCH 12/15] 1-27-22 B - Slight fix, HTTP server Updated disconnectFromLight() a while back to show a disconnect on the GUI exiting (for the parallel disconnect function), but that line accidentally also ran in the HTTP server, so fixed this function so we must be in GUI mode to update the table. --- NeewerLite-Python.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/NeewerLite-Python.py b/NeewerLite-Python.py index 93c04e2..d57b302 100644 --- a/NeewerLite-Python.py +++ b/NeewerLite-Python.py @@ -1882,9 +1882,10 @@ async def disconnectFromLight(selectedLight, updateGUI=True): try: if not availableLights[selectedLight][1].is_connected: # if the current light is NOT connected, then we're good - if updateGUI == False: - returnValue = True # if we're in CLI mode, then return False if there is an error disconnecting + if updateGUI == True: # if we're using the GUI, update the display (if we're waiting) mainWindow.setTheTable(["", "", "NOT\nLINKED", "Light disconnected!"], selectedLight) # show the new status in the table + else: # if we're not, then indicate that we're good + returnValue = True # if we're in CLI mode, then return False if there is an error disconnecting printDebugString("Successfully unlinked from light " + str(selectedLight + 1) + " [" + availableLights[selectedLight][0].name + "] " + returnMACname() + " " + availableLights[selectedLight][0].address) except AttributeError: From 9bbdb9ebfac31014dfd78ff76d166e4b242e4ac5 Mon Sep 17 00:00:00 2001 From: Zach Glenwright Date: Thu, 27 Jan 2022 15:36:01 -0500 Subject: [PATCH 13/15] 1-27-22 C - Disabled adding presets when no lights When you have no lights, NeewerLite-Python will not allow you to save presets. --- NeewerLite-Python.py | 96 ++++++++++++++++++++++++-------------------- ui_NeewerLightUI.py | 2 +- 2 files changed, 53 insertions(+), 45 deletions(-) diff --git a/NeewerLite-Python.py b/NeewerLite-Python.py index d57b302..9573219 100644 --- a/NeewerLite-Python.py +++ b/NeewerLite-Python.py @@ -1237,52 +1237,60 @@ def saveCustomPresetDialog(self, numOfPreset): if numOfPreset == 7: self.customPreset_7_Button.markCustom(7, -1) else: - selectedLights = self.selectedLights() # get the currently selected lights - - saveDlg = QMessageBox(self) - saveDlg.setWindowTitle("Save a Custom Preset") - saveDlg.setTextFormat(Qt.TextFormat.RichText) - saveDlg.setText("Would you like to save a Global or Snapshot preset for preset " + str(numOfPreset + 1) + "?" + "
" - "A Global Preset saves only the currently set global parameters (mode, hue, color temperature, brightness, etc.) and applies that global preset to all the lights that are currently selected.

" - "A Snapshot Preset saves the currently set parameters for each light individually, allowing you to recall more complex lighting setups. You can also either set a snapshot preset for a series of selected lights (you have to select 1 or more lights for this option), or all the currently available lights. If you save a snapshot preset of a series of selected lights, it will only apply the settings for those specific lights.") - saveDlg.addButton(" Global Preset ", QMessageBox.ButtonRole.YesRole) - saveDlg.addButton(" Snapshot Preset - All Lights ", QMessageBox.ButtonRole.YesRole) - - selectedLightsQuestion = 0 - - if selectedLights != []: - saveDlg.addButton(" Snapshot Preset - Selected Lights ", QMessageBox.ButtonRole.YesRole) - selectedLightsQuestion = 1 - - saveDlg.addButton(" Cancel ", QMessageBox.ButtonRole.RejectRole) - saveDlg.setIcon(QMessageBox.Question) + if len(availableLights) == 0: # if we don't have lights, then we can't save a preset! + errDlg = QMessageBox(self) + errDlg.setWindowTitle("Can't Save Preset!") + errDlg.setText("You can't save a custom preset at the moment because you don't have any lights set up yet. To save a custom preset, connect a light to NeewerLite-Python first.") + errDlg.addButton("OK", QMessageBox.ButtonRole.AcceptRole) + errDlg.setIcon(QMessageBox.Warning) + errDlg.exec_() + else: # we have lights, we can do it! + selectedLights = self.selectedLights() # get the currently selected lights + + saveDlg = QMessageBox(self) + saveDlg.setWindowTitle("Save a Custom Preset") + saveDlg.setTextFormat(Qt.TextFormat.RichText) + saveDlg.setText("Would you like to save a Global or Snapshot preset for preset " + str(numOfPreset + 1) + "?" + "
" + "A Global Preset saves only the currently set global parameters (mode, hue, color temperature, brightness, etc.) and applies that global preset to all the lights that are currently selected.

" + "A Snapshot Preset saves the currently set parameters for each light individually, allowing you to recall more complex lighting setups. You can also either set a snapshot preset for a series of selected lights (you have to select 1 or more lights for this option), or all the currently available lights. If you save a snapshot preset of a series of selected lights, it will only apply the settings for those specific lights.") + saveDlg.addButton(" Global Preset ", QMessageBox.ButtonRole.YesRole) + saveDlg.addButton(" Snapshot Preset - All Lights ", QMessageBox.ButtonRole.YesRole) + + selectedLightsQuestion = 0 + + if selectedLights != []: + saveDlg.addButton(" Snapshot Preset - Selected Lights ", QMessageBox.ButtonRole.YesRole) + selectedLightsQuestion = 1 + + saveDlg.addButton(" Cancel ", QMessageBox.ButtonRole.RejectRole) + saveDlg.setIcon(QMessageBox.Question) - clickedButton = saveDlg.exec_() - - if clickedButton == 0: # save a "Global" preset - saveCustomPreset("global", numOfPreset) - elif clickedButton == 1: # save a "Snapshot" preset with all lights - saveCustomPreset("snapshot", numOfPreset) - elif clickedButton == 2: # save a "Snapshot" preset with only the selected lights - saveCustomPreset("snapshot", numOfPreset, selectedLights) + clickedButton = saveDlg.exec_() - if clickedButton != (2 + selectedLightsQuestion): # if we didn't cancel out, then mark that button as being "custom" - if numOfPreset == 0: - self.customPreset_0_Button.markCustom(0, clickedButton) - if numOfPreset == 1: - self.customPreset_1_Button.markCustom(1, clickedButton) - if numOfPreset == 2: - self.customPreset_2_Button.markCustom(2, clickedButton) - if numOfPreset == 3: - self.customPreset_3_Button.markCustom(3, clickedButton) - if numOfPreset == 4: - self.customPreset_4_Button.markCustom(4, clickedButton) - if numOfPreset == 5: - self.customPreset_5_Button.markCustom(5, clickedButton) - if numOfPreset == 6: - self.customPreset_6_Button.markCustom(6, clickedButton) - if numOfPreset == 7: - self.customPreset_7_Button.markCustom(7, clickedButton) + if clickedButton == 0: # save a "Global" preset + saveCustomPreset("global", numOfPreset) + elif clickedButton == 1: # save a "Snapshot" preset with all lights + saveCustomPreset("snapshot", numOfPreset) + elif clickedButton == 2: # save a "Snapshot" preset with only the selected lights + saveCustomPreset("snapshot", numOfPreset, selectedLights) + + if clickedButton != (2 + selectedLightsQuestion): # if we didn't cancel out, then mark that button as being "custom" + if numOfPreset == 0: + self.customPreset_0_Button.markCustom(0, clickedButton) + if numOfPreset == 1: + self.customPreset_1_Button.markCustom(1, clickedButton) + if numOfPreset == 2: + self.customPreset_2_Button.markCustom(2, clickedButton) + if numOfPreset == 3: + self.customPreset_3_Button.markCustom(3, clickedButton) + if numOfPreset == 4: + self.customPreset_4_Button.markCustom(4, clickedButton) + if numOfPreset == 5: + self.customPreset_5_Button.markCustom(5, clickedButton) + if numOfPreset == 6: + self.customPreset_6_Button.markCustom(6, clickedButton) + if numOfPreset == 7: + self.customPreset_7_Button.markCustom(7, clickedButton) def highlightLightsForSnapshotPreset(self, numOfPreset, exited = False): global lastSelection diff --git a/ui_NeewerLightUI.py b/ui_NeewerLightUI.py index 291bd25..236adc5 100644 --- a/ui_NeewerLightUI.py +++ b/ui_NeewerLightUI.py @@ -451,7 +451,7 @@ def setupUi(self, MainWindow): self.globalPrefsLay.addRow("Maximum Number of retries:", self.maxNumOfAttempts_field) self.globalPrefsLay.addRow(QLabel("
Acceptable IPs to use for the HTTP Server:
Each line below is an IP allows access to NeewerLite-Python's HTTP server.
Wildcards for IP addresses can be entered by just leaving that section blank.
For example:
192.168.*.* would be entered as just 192.168
10.0.1.* is 10.0.1", alignment=Qt.AlignCenter)) self.globalPrefsLay.addRow(self.acceptable_HTTP_IPs_field) - self.globalPrefsLay.addRow(QLabel("
Whitelisted MAC Addresses/GUIDs
Whitelisted MAC Addresses/GUIDs are added to the light list
even if their name doesn't contain Neewer in it.

This preference is really only useful if you have compatible lights
that don't show up properly due to name mismatches.
", alignment=Qt.AlignCenter)) + self.globalPrefsLay.addRow(QLabel("
Whitelisted MAC Addresses/GUIDs
Devices with whitelisted MAC Addresses/GUIDs are added to the
list of lights even if their name doesn't contain Neewer in it.

This preference is really only useful if you have compatible lights
that don't show up properly due to name mismatches.
", alignment=Qt.AlignCenter)) self.globalPrefsLay.addRow(self.whiteListedMACs_field) self.globalPrefsLay.addRow(QLabel("
Custom GUI Keyboard Shortcut Mapping - GUI Buttons
To switch a keyboard shortcut, click on the old shortcut and type a new one in.
To reset a shortcut to default, click the X button next to it.


These 4 keyboard shortcuts control the buttons on the top of the window.", alignment=Qt.AlignCenter)) self.globalPrefsLay.addRow(self.windowButtonsCW) From 3c24e2053e6b816d63e33d21afd6864a0b17c1db Mon Sep 17 00:00:00 2001 From: Zach Glenwright Date: Thu, 27 Jan 2022 21:40:21 -0500 Subject: [PATCH 14/15] 1-27-22 D - Error handling, on the loading of presets With this commit, if the preset stored in the customLights.prefs comes up as invalid, the returned preset becomes a default preset instead. Also sprinkled some commenting around the script, going through the entire thing again now! --- NeewerLite-Python.py | 57 +++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/NeewerLite-Python.py b/NeewerLite-Python.py index 9573219..ee0b04a 100644 --- a/NeewerLite-Python.py +++ b/NeewerLite-Python.py @@ -1295,7 +1295,7 @@ def saveCustomPresetDialog(self, numOfPreset): def highlightLightsForSnapshotPreset(self, numOfPreset, exited = False): global lastSelection - if exited == False: + if exited == False: # if we're entering a snapshot preset, then highlight the affected lights in green lightsToHighlight = self.checkForSnapshotPreset(numOfPreset) if lightsToHighlight != []: @@ -1305,7 +1305,7 @@ def highlightLightsForSnapshotPreset(self, numOfPreset, exited = False): for a in range(len(lightsToHighlight)): for b in range(4): self.lightTable.item(lightsToHighlight[a], b).setBackground(QColor(113, 233, 147)) # set the affected rows the same color as the snapshot button - else: + else: # if we're exiting a snapshot preset, then reset the color of the affected lights back to white lightsToHighlight = self.checkForSnapshotPreset(numOfPreset) if lightsToHighlight != []: @@ -1380,11 +1380,11 @@ def recallCustomPreset(self, numOfPreset): if changedLights != []: lastSelection = [] # clear the last selection if you've clicked on a snapshot preset (which, if we're here, you did) - self.lightTable.setFocus() - self.selectRows(changedLights) + self.lightTable.setFocus() # set the focus to the light table, in order to show which rows are selected + self.selectRows(changedLights) # select those rows affected by the lights above global threadAction - threadAction = "send|" + "|".join(map(str, changedLights)) + threadAction = "send|" + "|".join(map(str, changedLights)) # set the thread to write to all of the affected lights # SET UP THE GUI BASED ON COMMAND LINE ARGUMENTS def setUpGUI(self, **modeArgs): @@ -1468,23 +1468,26 @@ def customPresetToString(numOfPreset): return returnedString -def stringToCustomPreset(presetString): - lightsToWorkWith = presetString.split(";") # split the current string into individual lights - presetToReturn = [] # a list containing all of the preset information +def stringToCustomPreset(presetString, numOfPreset): + if presetString != "|": # if the string is a valid string, then process it + lightsToWorkWith = presetString.split(";") # split the current string into individual lights + presetToReturn = [] # a list containing all of the preset information - for a in range(len(lightsToWorkWith)): - presetList = lightsToWorkWith[a].split("|") # split the current light list into its individual items - presetPayload = [] # the actual preset list - - for b in range(1, len(presetList)): - presetPayload.append(int(presetList[b])) + for a in range(len(lightsToWorkWith)): + presetList = lightsToWorkWith[a].split("|") # split the current light list into its individual items + presetPayload = [] # the actual preset list + + for b in range(1, len(presetList)): + presetPayload.append(int(presetList[b])) - if presetList[0] == "-1": - presetToReturn.append([-1, presetPayload]) # if the light ID is -1, keep that value as an integer - else: - presetToReturn.append([presetList[0], presetPayload]) # if it isn't, then the MAC address is a string, so keep it that way + if presetList[0] == "-1": + presetToReturn.append([-1, presetPayload]) # if the light ID is -1, keep that value as an integer + else: + presetToReturn.append([presetList[0], presetPayload]) # if it isn't, then the MAC address is a string, so keep it that way - return presetToReturn + return presetToReturn + else: # if it isn't, then just return the default parameters for this preset + return defaultLightPresets[numOfPreset] def loadCustomPresets(): global customLightPresets @@ -1519,21 +1522,21 @@ def loadCustomPresets(): customPresets = customPresetParser.parse_args(customPresets) if customPresets.customPreset0 != -1: - customLightPresets[0] = stringToCustomPreset(customPresets.customPreset0) + customLightPresets[0] = stringToCustomPreset(customPresets.customPreset0, 0) if customPresets.customPreset1 != -1: - customLightPresets[1] = stringToCustomPreset(customPresets.customPreset1) + customLightPresets[1] = stringToCustomPreset(customPresets.customPreset1, 1) if customPresets.customPreset2 != -1: - customLightPresets[2] = stringToCustomPreset(customPresets.customPreset2) + customLightPresets[2] = stringToCustomPreset(customPresets.customPreset2, 2) if customPresets.customPreset3 != -1: - customLightPresets[3] = stringToCustomPreset(customPresets.customPreset3) + customLightPresets[3] = stringToCustomPreset(customPresets.customPreset3, 3) if customPresets.customPreset4 != -1: - customLightPresets[4] = stringToCustomPreset(customPresets.customPreset4) + customLightPresets[4] = stringToCustomPreset(customPresets.customPreset4, 4) if customPresets.customPreset5 != -1: - customLightPresets[5] = stringToCustomPreset(customPresets.customPreset5) + customLightPresets[5] = stringToCustomPreset(customPresets.customPreset5, 5) if customPresets.customPreset6 != -1: - customLightPresets[6] = stringToCustomPreset(customPresets.customPreset6) + customLightPresets[6] = stringToCustomPreset(customPresets.customPreset6, 6) if customPresets.customPreset7 != -1: - customLightPresets[7] = stringToCustomPreset(customPresets.customPreset7) + customLightPresets[7] = stringToCustomPreset(customPresets.customPreset7, 7) # RETURN THE CORRECT NAME FOR THE IDENTIFIER OF THE LIGHT (FOR DEBUG STRINGS) def returnMACname(): From 5c4cafaa4c51d172b2abb645dd6899aac47037f8 Mon Sep 17 00:00:00 2001 From: Zach Glenwright Date: Thu, 27 Jan 2022 21:45:38 -0500 Subject: [PATCH 15/15] 1-27-22 E - Small change Removed QtGui.Palette --- ui_NeewerLightUI.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui_NeewerLightUI.py b/ui_NeewerLightUI.py index 236adc5..8a44628 100644 --- a/ui_NeewerLightUI.py +++ b/ui_NeewerLightUI.py @@ -1,5 +1,5 @@ from PySide2.QtCore import QRect, Signal -from PySide2.QtGui import QFont, QLinearGradient, QColor, Qt, QKeySequence, QPalette +from PySide2.QtGui import QFont, QLinearGradient, QColor, Qt, QKeySequence from PySide2.QtWidgets import QFormLayout, QGridLayout, QKeySequenceEdit, QWidget, QPushButton, QTableWidget, QTableWidgetItem, QAbstractScrollArea, QAbstractItemView, \ QTabWidget, QGraphicsScene, QGraphicsView, QFrame, QSlider, QLabel, QLineEdit, QCheckBox, QStatusBar, QScrollArea, QTextEdit