Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release v1.15.2 #90

Merged
merged 10 commits into from
Nov 15, 2024
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- uses: actions/checkout@v3

# Download artifacts for release
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4.1.7
with:
path: etc/usr/artifacts/

Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
### Changes this version:
- Added TMC debug openloop test mode
- Improved stability
- Improved language selector
- Added BISS-C direction selection
- Fixed local ABN encoder index checkbox

### Changes in 1.15.x:
- Added permanent inertia and friction effect sliders
- Added position save toggle for ODrive
1 change: 1 addition & 0 deletions base_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def __init__(self, parent: PyQt6.QtWidgets.QWidget = None, ui_form: str = ""):
self.tech_log = logging.getLogger(ui_form)
if ui_form:
PyQt6.uic.loadUi(helper.res_path(ui_form), self)
# print(f"UI file loaded : {ui_form}")

def init_ui(self):
"""Prototype of init_ui to manage this status in subclass."""
Expand Down
8 changes: 5 additions & 3 deletions encoderconf_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def onshown(self):
def apply(self):
val = self.spinBox_cpr.value()
self.send_value("localenc","cpr",val=val)
self.send_value("localenc","index",val = 1 if self.checkBox_index.checkState() else 0)
self.send_value("localenc","index",val = 1 if self.checkBox_index.isChecked() else 0)


class MtEncoderConf(EncoderOption,CommunicationHandler):
Expand Down Expand Up @@ -144,20 +144,22 @@ def initUI(self):
layout = QFormLayout()
layout.setContentsMargins(0,0,0,0)

self.spinBox_cs = QSpinBox()
self.spinBox_cs.setRange(1,3)
self.checkBox_direction = QCheckBox("Reverse direction (default)")
self.spinBox_bits = QSpinBox()
self.spinBox_bits.setRange(1,32)
layout.addWidget(QLabel("SPI3 extension port"))
layout.addRow(QLabel("Bits"),self.spinBox_bits)
layout.addWidget(self.checkBox_direction)
layout.addWidget(QLabel("Port is used exclusively!"))
self.setLayout(layout)

def onshown(self):
self.get_value_async("bissenc","bits",self.spinBox_bits.setValue,0,int)
self.get_value_async("bissenc","dir",self.checkBox_direction.setChecked,0,int)

def apply(self):
self.send_value("bissenc","bits",val=self.spinBox_bits.value())
self.send_value("bissenc","dir",val= 1 if self.checkBox_direction.isChecked() else 0)

class SsiEncoderConf(EncoderOption,CommunicationHandler):
def __init__(self,parent,main):
Expand Down
104 changes: 47 additions & 57 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@
import activetasks

# This GUIs version
VERSION = "1.15.0"
VERSION = "1.15.2"

# Minimal supported firmware version.
# Major version of firmware must match firmware. Minor versions must be higher or equal
MIN_FW = "1.15.0"
MIN_FW = "1.15.1"

DEFAULTLANG = "en_US"

Expand All @@ -73,6 +73,10 @@ def __init__(self):
"""Init the mainUI : init the UI, all the dlg element, and the main timer."""
PyQt6.QtWidgets.QMainWindow.__init__(self)
base_ui.CommunicationHandler.__init__(self)

self.profile_ui = profile_ui.ProfileUI(main=self) # load profile without UI
self.load_language_id(self.profile_ui.get_global_setting("language",DEFAULTLANG)) # load language file

base_ui.WidgetUI.__init__(self, None, "MainWindow.ui")

self.restart_app_flag = False
Expand All @@ -88,18 +92,16 @@ def __init__(self):
self.systray : SystrayWrapper = None

self.lang_actions = {}
self.translator = PyQt6.QtCore.QTranslator(self)
self.language_action_group = QActionGroup(self)
self.language_action_group.setExclusive(True)
self.language_action_group.setExclusive(True)

self.tab_connections = [] # Signals to disconnect on reset

# Systray
self.systray = SystrayWrapper(self)
# Profile
self.profile_ui = profile_ui.ProfileUI(main=self)

self.make_lang_selector() # Languages must be created as early as possible
self.profile_ui.initialize_ui() # Profile UI
self.make_lang_selector()

self.timer = PyQt6.QtCore.QTimer(self)
self.timer.timeout.connect(self.update_timer) # pylint: disable=no-value-for-parameter
Expand All @@ -114,13 +116,16 @@ def __init__(self):
self.active_classes = {}
self.fw_version_str = None

self.setup()

self.process_events_timer = PyQt6.QtCore.QTimer()
self.process_events_timer.timeout.connect(process_events) # Kick eventloop when timeouting
self.axes = 0

self.setup()
self.languagechanged.connect(self.restart_app)

# start the auto disconnect timer (call the board)
self.timer.start(5000)

def setup(self):
"""Init the systray, the serial, the toolbar, the status bar and the connection status."""
Expand All @@ -147,9 +152,6 @@ def setup(self):

self.actionDebug_mode.triggered.connect(self.toggle_debug)

self.timer.start(5000)


#self.serialchooser.connected.connect(self.effects_monitor_dlg.setEnabled) # Gets enabled in class management
self.effects_monitor_dlg.setEnabled(False)

Expand Down Expand Up @@ -192,36 +194,31 @@ def setup(self):
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.profile_ui)
self.groupBox_main.setLayout(layout)



def autoconnect(self) :
# after UI load get serial port and if only one : autoconnect
nb_device_compat = self.serialchooser.get_ports()
self.serialchooser.auto_connect(nb_device_compat)

# def refresh_widgets(self):
# for w in app.allWidgets(): # Does not actually update the translation
# w.update()
# w.repaint()
# # print(w)

def change_language(self,enabled):
if(not enabled):
return
langfile,user_language = self.language_action_group.checkedAction().data()
def load_language_id(self, langid:str):
"""load language file"""
if langid != DEFAULTLANG:
langfile = helper.res_path(f"{langid}.qm","translations")
if translator.load(langfile):
app.installTranslator(translator)

# try to load the user's language file
curlang = self.translator.language()
if self.translator.isEmpty():
curlang = DEFAULTLANG
if(curlang == user_language):
def change_lang_callback(self, enabled:bool):
"""Change language of the UI, this will run too when initializing the UI"""
if(not enabled): # Language not selected
return
app.removeTranslator(self.translator)
if self.translator.load(langfile):
app.installTranslator(self.translator)
self.profile_ui.set_global_setting("language",user_language) #store language

app.removeTranslator(translator)

user_lang_id = self.language_action_group.checkedAction().data()
self.profile_ui.set_global_setting("language",user_lang_id) # store language setting

#self.refresh_widgets()
self.languagechanged.emit()
self.languagechanged.emit() # loading in next start

def restart_app(self):
self.restart_app_flag = True
Expand All @@ -230,30 +227,19 @@ def restart_app(self):
app.quit()

def make_lang_selector(self):
'''Create the language selector menu, and connect the callback to change language.'''
languages = [DEFAULTLANG]
languages.extend([os.path.splitext(os.path.basename(f))[0] for f in glob.glob(helper.res_path("*.qm","translations"))])

languages = [("",DEFAULTLANG)]
languages.extend([(f,os.path.splitext(os.path.basename(f))[0]) for f in glob.glob(helper.res_path("*.qm","translations"))])

for langfile,langid in languages:
for langid in languages:
action = QAction(langid)
action.setData([langfile,langid])
action.setData(langid)
action.setCheckable(True)
self.language_action_group.addAction(action)
self.lang_actions[langid] = action
self.menuLanguage.addAction(action)
action.toggled.connect(self.change_language)
action.toggled.connect(self.change_lang_callback)

user_language = self.profile_ui.get_global_setting("language",None)

if not user_language:
user_language = PyQt6.QtCore.QLocale.system().name()

if user_language in self.lang_actions:
self.lang_actions.get(user_language).setChecked(True)
else:
self.lang_actions.get(DEFAULTLANG).setChecked(True)



def reboot(self):
"""Send the reboot message to the board."""
Expand Down Expand Up @@ -628,25 +614,27 @@ def version_check(self, ver):

def serial_connected(self, connected):
"""Check the release when a board is connected."""
self.serial_timer = PyQt6.QtCore.QTimer()


def timer_cb():
if not self.connected:
self.log("Can't detect board")
self.reset_port()

def id_cb(identifier):
if identifier:
self.connected = True
self.serial_timer.stop()
self.connected = True

if connected:
self.serial_timer.singleShot(500, timer_cb)
self.get_value_async("main", "id", id_cb, 0)
self.errors_dlg.registerCallbacks()
self.get_value_async("sys", "swver", self.version_check)
self.get_value_async("sys", "hwtype", self.wrapper_status_bar.set_board_text)
self.get_value_async("sys", "debug", self.actionDebug_mode.setChecked,0,int)

if (self.serial_timer is None) :
self.serial_timer = PyQt6.QtCore.QTimer(singleShot=True, timeout=timer_cb)
self.serial_timer.start(500)

else:
self.connected = False
Expand Down Expand Up @@ -902,6 +890,7 @@ def process_events():
restart = True
exit_code = -1
app = PyQt6.QtWidgets.QApplication(sys.argv)
translator = PyQt6.QtCore.QTranslator(app) # Translator must be created before UI loaded
while(restart):
restart = False
window = MainUi()
Expand All @@ -914,8 +903,8 @@ def process_events():
QueryValueEx as getSubkeyValue,
OpenKey as getKey,
)

if windows_theme_is_light() == 0:
# Check if is not using windows 11 style(windows 11 style is dark mode compatible)
if windows_theme_is_light() == 0 and app.style().objectName() != "windows11":
app.setStyle("Fusion")
app.setPalette(dark_palette.PALETTE_DARK)
window.menubar.setStyleSheet("QMenu::item {color: white; }") # Menu item text ignores palette setting and stays black. Force to white.
Expand All @@ -924,6 +913,7 @@ def process_events():
window.setWindowIcon(PyQt6.QtGui.QIcon(helper.res_path('app.ico')))
window.show()
window.check_configurator_update() # Check for updates after window is shown
window.autoconnect()

exit_code = app.exec()
# Check if we need to restart
Expand Down
39 changes: 24 additions & 15 deletions profile_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,29 @@ class ProfileUI(base_ui.WidgetUI, base_ui.CommunicationHandler):
profile_selected_event = PyQt6.QtCore.pyqtSignal(str)

def __init__(self, main=None):
"""Initialize profile without UI."""
self.main = main
self.profiles_dlg = None # ProfilesDialog is not initialized yet
self.profile_setup = {}
self.profiles = {}

self._current_class = -1
self._current_command = -1
self._current_instance = -1
self._map_class_running = []
self._running_profile = []
self._profilename_tosave: str = None

self.ui_initialized = False

self.load_profile_settings()
self.load_profiles()

def initialize_ui(self):
"""Init the UI and link the event."""
base_ui.WidgetUI.__init__(self, main, "profile.ui")
base_ui.WidgetUI.__init__(self, self.main, "profile.ui")
base_ui.CommunicationHandler.__init__(self)
self.main = main

self.profiles_dlg = ProfilesDialog(self)
self.profiles_dlg.closeSignal.connect(self.close_profile_manager)

Expand All @@ -63,19 +82,7 @@ def __init__(self, main=None):
self.main.systray.refresh_profile_action_status
)

self.profile_setup = {}
self.profiles = {}

self._current_class = -1
self._current_command = -1
self._current_instance = -1
self._map_class_running = []
self._running_profile = []
self._profilename_tosave: str = None

self.setEnabled(False)
self.load_profile_settings()
self.load_profiles()

def save_clicked(self):
"""Save current seeting in Flash and replace the 'Flash profile' settings by the new one."""
Expand Down Expand Up @@ -141,7 +148,9 @@ def load_profiles_from_file(self):
self.log("Profile: profiles are not compatible, need to redo them")
else:
self.log("Profile: profiles loaded")
self.refresh_combox_list()

if self.ui_initialized:
self.refresh_combox_list()

def create_or_update_profile_file(self, create: bool = False):
"""Create a profile file if not exist, else update the existing one."""
Expand Down
2 changes: 1 addition & 1 deletion res/effects_graph.ui
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@
</property>
<property name="minimumSize">
<size>
<width>30</width>
<width>100</width>
<height>0</height>
</size>
</property>
Expand Down
2 changes: 1 addition & 1 deletion res/effects_stats.ui
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@
</property>
<property name="minimumSize">
<size>
<width>30</width>
<width>100</width>
<height>0</height>
</size>
</property>
Expand Down
2 changes: 1 addition & 1 deletion res/effects_tuning.ui
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@
<widget class="QSpinBox" name="spinBox_axis">
<property name="minimumSize">
<size>
<width>40</width>
<width>100</width>
<height>0</height>
</size>
</property>
Expand Down
Loading
Loading