diff --git a/src/Info.plist b/src/Info.plist index 37a6f6ac5..1d3959ca8 100644 --- a/src/Info.plist +++ b/src/Info.plist @@ -142,7 +142,7 @@ LSHasLocalizedDisplayName LSMinimumSystemVersion - 11.0 + 12.0 LSMultipleInstancesProhibited false NSBluetoothAlwaysUsageDescription diff --git a/src/artisanlib/canvas.py b/src/artisanlib/canvas.py index f9530433b..23990ed5f 100644 --- a/src/artisanlib/canvas.py +++ b/src/artisanlib/canvas.py @@ -4577,7 +4577,7 @@ def updategraphics(self) -> None: # keep the RoR axis constant zlim = self.delta_ax.get_ylim() zlim_offset = (zlim[1] - zlim[0]) / 2. - tempd = (self.delta_ax.transData.inverted().transform((0,self.ax.transData.transform((0,temp))[1]))[1]) + tempd = float(self.delta_ax.transData.inverted().transform((0,self.ax.transData.transform((0,temp))[1]))[1]) zlim_new = (tempd - zlim_offset, tempd + zlim_offset) self.delta_ax.set_ylim(zlim_new) self.ax_background = None @@ -4606,7 +4606,6 @@ def updategraphics(self) -> None: zlim = self.delta_ax.get_ylim() zlim_offset = (zlim[1] - zlim[0]) / 2. zlim_new = (ror - zlim_offset, ror + zlim_offset) - self.delta_ax.set_ylim(zlim_new) self.ax_background = None ##### updated canvas @@ -10524,21 +10523,23 @@ def placelogoimage(self) -> None: coord_axes_middle_Display = self.ax.transAxes.transform((.5,.5)) coord_axes_upperright_Display = self.ax.transAxes.transform((1.,1.)) coord_axes_lowerleft_Display = self.ax.transAxes.transform((0.,0.)) - coord_axes_height_pixels = coord_axes_upperright_Display[1] - coord_axes_lowerleft_Display[1] - coord_axes_width_pixels = coord_axes_upperright_Display[0] - coord_axes_lowerleft_Display[0] + coord_axes_height_pixels = float(coord_axes_upperright_Display[1]) - float(coord_axes_lowerleft_Display[1]) + coord_axes_width_pixels = float(coord_axes_upperright_Display[0]) - float(coord_axes_lowerleft_Display[0]) coord_axes_aspect = coord_axes_height_pixels / coord_axes_width_pixels if img_aspect >= coord_axes_aspect: scale = min(1., coord_axes_height_pixels / img_height_pixels) else: scale = min(1., coord_axes_width_pixels / img_width_pixels) - corner_pixels = [0.,0.,0.,0.] + corner_pixels:List[float] = [0.,0.,0.,0.] corner_pixels[0] = coord_axes_middle_Display[0] - (scale * img_width_pixels / 2) corner_pixels[1] = coord_axes_middle_Display[1] - (scale * img_height_pixels / 2) corner_pixels[2] = corner_pixels[0] + scale * img_width_pixels corner_pixels[3] = corner_pixels[1] + scale * img_height_pixels - ll_corner_axes:List[float] = self.ax.transData.inverted().transform_point((corner_pixels[0],corner_pixels[1])).tolist() - ur_corner_axes:List[float] = self.ax.transData.inverted().transform_point((corner_pixels[2],corner_pixels[3])).tolist() + transformed_point1 = self.ax.transData.inverted().transform_point((corner_pixels[0],corner_pixels[1])) + transformed_point2 = self.ax.transData.inverted().transform_point((corner_pixels[2],corner_pixels[3])) + ll_corner_axes:List[float] = [float(transformed_point1[0]), float(transformed_point1[1])] + ur_corner_axes:List[float] = [float(transformed_point2[0]), float(transformed_point2[1])] extent = (ll_corner_axes[0], ur_corner_axes[0], ll_corner_axes[1], ur_corner_axes[1]) if self.ai is not None: try: @@ -11546,7 +11547,7 @@ def flavorchart(self) -> None: self.updateFlavorChartData() if self.ax1 is not None and self.flavorchart_angles is not None: try: - ticks_loc = self.ax1.get_yticks().tolist() + ticks_loc = [float(tick) for tick in self.ax1.get_yticks()] self.ax1.yaxis.set_major_locator(ticker.FixedLocator(ticks_loc)) except Exception: # pylint: disable=broad-except pass @@ -17381,7 +17382,7 @@ def drawWheel(self) -> None: # fixing yticks with matplotlib.ticker "FixedLocator" try: - ticks_loc = self.ax2.get_yticks().tolist() + ticks_loc = [float(tick) for tick in self.ax2.get_yticks()] self.ax2.yaxis.set_major_locator(ticker.FixedLocator(ticks_loc)) except Exception: # pylint: disable=broad-except pass @@ -17599,8 +17600,8 @@ def drawcross(self, event:'MouseEvent') -> None: deltaX = stringfromseconds(x - self.baseX) deltaY = str(float2float(y - self.baseY,1)) RoR = str(float2float(60 * (y - self.baseY) / (x - self.baseX),1)) - deltaRoR = (self.delta_ax.transData.inverted().transform((0,self.ax.transData.transform((0,y))[1]))[1] - - self.delta_ax.transData.inverted().transform((0,self.ax.transData.transform((0,self.baseY))[1]))[1]) + deltaRoR:float = (float(self.delta_ax.transData.inverted().transform((0,self.ax.transData.transform((0,y))[1]))[1]) + - float(self.delta_ax.transData.inverted().transform((0,self.ax.transData.transform((0,self.baseY))[1]))[1])) #RoRoR is always in C/min/min if self.mode == 'F': deltaRoR = RoRfromFtoCstrict(deltaRoR) diff --git a/src/artisanlib/main.py b/src/artisanlib/main.py index 76481e0bd..952a736d0 100644 --- a/src/artisanlib/main.py +++ b/src/artisanlib/main.py @@ -12247,7 +12247,7 @@ def changeEventNumber(self, _:int = 0) -> None: @pyqtSlot(bool) def miniEventRecord(self, _:bool) -> None: lenevents = self.eNumberSpinBox.value() - if lenevents: + if lenevents and len(self.qmc.specialevents) < lenevents-1: if self.qmc.timeindex[0] > -1: newtime = self.qmc.time2index(self.qmc.timex[self.qmc.timeindex[0]]+ stringtoseconds(str(self.etimeline.text()))) else: diff --git a/src/artisanlib/modbusport.py b/src/artisanlib/modbusport.py index 4d3cae804..8c065ca9c 100644 --- a/src/artisanlib/modbusport.py +++ b/src/artisanlib/modbusport.py @@ -449,9 +449,9 @@ async def read_active_registers_async(self) -> None: try: # we cache only MODBUS function 3 and 4 (not 1 and 2!) if code == 3: - res = await self._client.read_holding_registers(register,count,slave=slave) + res = await self._client.read_holding_registers(address=register,count=count,slave=slave) elif code == 4: - res = await self._client.read_input_registers(register,count,slave=slave) + res = await self._client.read_input_registers(address=register,count=count,slave=slave) except Exception as e: # pylint: disable=broad-except _log.info('readActive(%d,%d,%d,%d)', slave, code, register, count) _log.debug(e) @@ -594,7 +594,11 @@ def maskWriteRegister(self, slave:int, register:int, and_mask:int, or_mask:int) self.connect() if self._asyncLoopThread is not None and self.isConnected(): assert self._client is not None - asyncio.run_coroutine_threadsafe(self._client.mask_write_register(int(register),int(and_mask),int(or_mask),slave=int(slave)), self._asyncLoopThread.loop).result() + asyncio.run_coroutine_threadsafe(self._client.mask_write_register( + address=int(register), + and_mask=int(and_mask), + or_mask=int(or_mask), + slave=int(slave)), self._asyncLoopThread.loop).result() # time.sleep(.03) except Exception as ex: # pylint: disable=broad-except _log.info('maskWriteRegister(%d,%d,%s,%s) failed', slave, register, and_mask, or_mask) @@ -633,7 +637,7 @@ def writeRegisters(self, slave:int, register:int, values:Union[List[int], int]) # byte_values:List[bytes] = ([values.to_bytes(2, 'big')] if isinstance(values, int) else [v.to_bytes(2, 'big') for v in values]) int_values:List[int] = ([values] if isinstance(values, int) else values) # asyncio.run_coroutine_threadsafe(self._client.write_registers(int(register),byte_values,slave=int(slave)), self._asyncLoopThread.loop).result() - asyncio.run_coroutine_threadsafe(self._client.write_registers(int(register),int_values,slave=int(slave)), self._asyncLoopThread.loop).result() # type:ignore[arg-type] # type annotation wrong in pymodbus 3.7.3 + asyncio.run_coroutine_threadsafe(self._client.write_registers(int(register),int_values,slave=int(slave)), self._asyncLoopThread.loop).result() # time.sleep(.03) except Exception as ex: # pylint: disable=broad-except _log.info('writeRegisters(%d,%d,%s) failed', slave, register, values) @@ -663,7 +667,7 @@ def writeWord(self, slave:int, register:int, value:float) -> None: builder.add_32bit_float(float(value)) payload = builder.build() #payload:List[int] = [int.from_bytes(b,("little" if self.byteorderLittle else "big")) for b in builder.build()] - asyncio.run_coroutine_threadsafe(self._client.write_registers(int(register),payload,slave=int(slave),skip_encode=True), self._asyncLoopThread.loop).result() # type: ignore [reportGeneralTypeIssues, arg-type, unused-ignore] # Argument of type "list[bytes]" cannot be assigned to parameter "values" of type "List[int] | int" in function "write_registers"; in pymodbus 3.7.4 the error is now "List[bytes]"; expected "List[Union[bytes, int] + asyncio.run_coroutine_threadsafe(self._client.write_registers(int(register),payload,slave=int(slave)), self._asyncLoopThread.loop).result() # type: ignore [reportGeneralTypeIssues, arg-type, unused-ignore] # Argument of type "list[bytes]" cannot be assigned to parameter "values" of type "List[int] | int" in function "write_registers"; in pymodbus 3.7.4 the error is now "List[bytes]"; expected "List[Union[bytes, int] # time.sleep(.03) except Exception as ex: # pylint: disable=broad-except _log.info('writeWord(%d,%d,%s) failed', slave, register, value) @@ -692,7 +696,7 @@ def writeBCD(self, slave:int, register:int, value:int) -> None: builder.add_16bit_uint(r) payload = builder.build() #payload:List[int] = [int.from_bytes(b,("little" if self.byteorderLittle else "big")) for b in builder.build()] - asyncio.run_coroutine_threadsafe(self._client.write_registers(int(register),payload,slave=int(slave),skip_encode=True), self._asyncLoopThread.loop).result() # type: ignore [reportGeneralTypeIssues, arg-type, unused-ignore] # Argument of type "list[bytes]" cannot be assigned to parameter "values" of type "List[int] | int" in function "write_registers"; in pymodbus 3.7.4 the error is now "List[bytes]"; expected "List[Union[bytes, int] + asyncio.run_coroutine_threadsafe(self._client.write_registers(int(register),payload,slave=int(slave)), self._asyncLoopThread.loop).result() # type: ignore [reportGeneralTypeIssues, arg-type, unused-ignore] # Argument of type "list[bytes]" cannot be assigned to parameter "values" of type "List[int] | int" in function "write_registers"; in pymodbus 3.7.4 the error is now "List[bytes]"; expected "List[Union[bytes, int] # time.sleep(.03) except Exception as ex: # pylint: disable=broad-except _log.info('writeBCD(%d,%d,%s) failed', slave, register, value) @@ -722,7 +726,7 @@ def writeLong(self, slave:int, register:int, value:int) -> None: builder.add_32bit_int(int(value)) payload = builder.build() #payload:List[int] = [int.from_bytes(b,("little" if self.byteorderLittle else "big")) for b in builder.build()] - asyncio.run_coroutine_threadsafe(self._client.write_registers(int(register),payload,slave=int(slave),skip_encode=True), self._asyncLoopThread.loop).result() # type: ignore [reportGeneralTypeIssues, arg-type, unused-ignore] # Argument of type "list[bytes]" cannot be assigned to parameter "values" of type "List[int] | int" in function "write_registers"; in pymodbus 3.7.4 the error is now "List[bytes]"; expected "List[Union[bytes, int] + asyncio.run_coroutine_threadsafe(self._client.write_registers(int(register),payload,slave=int(slave)), self._asyncLoopThread.loop).result() # type: ignore [reportGeneralTypeIssues, arg-type, unused-ignore] # Argument of type "list[bytes]" cannot be assigned to parameter "values" of type "List[int] | int" in function "write_registers"; in pymodbus 3.7.4 the error is now "List[bytes]"; expected "List[Union[bytes, int] # await asyncio.sleep(.03) except Exception as ex: # pylint: disable=broad-except _log.info('writeLong(%d,%d,%s) failed', slave, register, value) @@ -746,13 +750,13 @@ async def read_async(self, slave:int, register:int, count:int, code:int) -> 'Tup while True: try: if code==1: - res = await self._client.read_coils(int(register),count,slave=int(slave)) + res = await self._client.read_coils(address=int(register),count=count,slave=int(slave)) elif code==2: - res = await self._client.read_discrete_inputs(int(register),count,slave=int(slave)) + res = await self._client.read_discrete_inputs(address=int(register),count=count,slave=int(slave)) elif code==4: - res = await self._client.read_input_registers(int(register),count,slave=int(slave)) + res = await self._client.read_input_registers(address=int(register),count=count,slave=int(slave)) else: # code==3 - res = await self._client.read_holding_registers(int(register),count,slave=int(slave)) + res = await self._client.read_holding_registers(address=int(register),count=count,slave=int(slave)) except Exception as ex: # pylint: disable=broad-except _log.debug(ex) res = None diff --git a/src/requirements-dev.txt b/src/requirements-dev.txt index e099050f4..9116dcb3f 100644 --- a/src/requirements-dev.txt +++ b/src/requirements-dev.txt @@ -1,9 +1,9 @@ types-openpyxl>=3.1.5.20241126 types-Pillow>=10.2.0.20240822 -types-protobuf>=5.28.3.20241030 +types-protobuf>=5.29.1.20241207 types-psutil>=6.1.0.20241102 types-pyserial>=3.5.0.20240826 -types-python-dateutil==2.9.0.20241003 +types-python-dateutil==2.9.0.20241206 types-pytz>=2024.2.0.20241003 types-pyyaml>=6.0.12.20240917 types-requests>=2.32.0.20241016 @@ -12,8 +12,8 @@ types-urllib3>=1.26.25.14 types-docutils>=0.21.0.20241128 lxml-stubs>=0.5.1 mypy==1.13.0 -pyright==1.1.389 -ruff>=0.8.1 +pyright==1.1.390 +ruff>=0.8.3 pylint==3.3.2 pre-commit>=4.0.1 pytest>=8.3.4 @@ -25,8 +25,8 @@ pytest-cov==5.0.0 #pytest-bdd==6.1.1 #pytest-benchmark==4.0.0 #pytest-mock==3.11.1 -hypothesis>=6.122.1 -coverage>=7.6.8 +hypothesis>=6.122.3 +coverage>=7.6.9 coverage-badge==1.1.2 codespell==2.3.0 # the following 2 packages are not installed along aiohttp on Python3.12 and make mypy complain diff --git a/src/requirements.txt b/src/requirements.txt index d41a6dd30..90b8a2c03 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -30,10 +30,10 @@ setuptools==70.3.0 # py2app fails on 71.0.3 and 71.0.4; pyinstaller windows/linu wheel==0.45.1 pyserial==3.5 pymodbus==3.6.9; python_version < '3.9' # last Python 3.8 release -pymodbus==3.7.4; python_version >= '3.9' +pymodbus==3.8.0; python_version >= '3.9' python-snap7==1.3; python_version < '3.10' # last Python 3.9 release python-snap7==2.0.2; python_version >= '3.10' -Phidget22==1.21.20241125 +Phidget22==1.22.20241209 Unidecode==1.3.8 qrcode==7.4.2; python_version < '3.9' # last Python 3.8 release qrcode==8.0; python_version >= '3.9' @@ -51,7 +51,7 @@ psutil==6.1.0 typing-extensions==4.10.0; python_version < '3.8' # required for supporting Final and TypeDict on Python <3.8 protobuf==5.29.1 numpy==1.24.3; python_version < '3.9' # last Python 3.8 release -numpy==2.1.3; python_version >= '3.9' +numpy==2.2.0; python_version >= '3.9' scipy==1.10.1; python_version < '3.9' # last Python 3.8 release scipy==1.14.1; python_version >= '3.9' wquantiles==0.6 @@ -62,17 +62,17 @@ prettytable==3.11.0; python_version < '3.9' # last Python 3.8 release prettytable==3.12.0; python_version >= '3.9' lxml==5.3.0 matplotlib==3.7.3; python_version < '3.9' # last Python 3.8 release -matplotlib==3.9.3; python_version >= '3.9' +matplotlib==3.9.4; python_version >= '3.9' jinja2==3.1.4 aiohttp==3.10.11; python_version < '3.9' # last Python 3.8 release -aiohttp==3.11.9; python_version >= '3.9' +aiohttp==3.11.10; python_version >= '3.9' aiohttp_jinja2==1.6 python-bidi==0.4.2; python_version < '3.9' # last Python 3.8 release python-bidi==0.6.3; python_version >= '3.9' arabic-reshaper==3.0.0 pillow==10.4.0; python_version < '3.9' # last Python 3.8 release pillow>=11.0.0; python_version >= '3.9' -pydantic==2.10.2; (platform_system=='Windows' and python_version>'3.10') or sys_platform=='darwin' or platform_system=='Linux' +pydantic==2.10.3; (platform_system=='Windows' and python_version>'3.10') or sys_platform=='darwin' or platform_system=='Linux' pydantic==2.7.1; (platform_system=='Windows' and python_version<'3.9') # last version working with Windows 7/8 pending resolution of pydantic Issue #9920 babel==2.16.0 bleak==0.22.3 @@ -83,7 +83,7 @@ bleak==0.22.3 # ### yoctopuce 1.10.42060 on macOS 10.13 yoctopuce==1.10.42060; sys_platform=='darwin' and platform_release<'20.0' # last version supporting macOS 10.13 -yoctopuce==2.0.62875; sys_platform!='darwin' or (sys_platform=='darwin' and platform_release>='20.0') +yoctopuce==2.0.63620; sys_platform!='darwin' or (sys_platform=='darwin' and platform_release>='20.0') # last 1.x yoctopuce lib: 1.10.57762 # 2.0.59414 is no longer universal2 lacking arm64 support; 2.0.59503 should fix this ## @@ -96,8 +96,8 @@ PyQt5-sip==12.15.0; (sys_platform=='darwin' and platform_release<'20.0') or (pla PyQt5==5.15.10; (sys_platform=='darwin' and platform_release<'20.0') or (platform_system=='Windows' and python_version<'3.9') PyQtWebEngine==5.15.6; (sys_platform=='darwin' and platform_release<'20.0') or (platform_system=='Windows' and python_version<'3.9') # Qt6 on macOS 11+, Windows 10/11 and Linux -PyQt6==6.7.1; (sys_platform=='darwin' and platform_release>='20.0') or (platform_system=='Windows' and python_version>'3.10') or (platform_system=='Linux' and platform_machine!='aarch64') -PyQt6-WebEngine==6.7.0; (sys_platform=='darwin' and platform_release>='20.0') or (platform_system=='Windows' and python_version>'3.10') or (platform_system=='Linux' and platform_machine!='aarch64') +PyQt6==6.8.0; (sys_platform=='darwin' and platform_release>='20.0') or (platform_system=='Windows' and python_version>'3.10') or (platform_system=='Linux' and platform_machine!='aarch64') +PyQt6-WebEngine==6.8.0; (sys_platform=='darwin' and platform_release>='20.0') or (platform_system=='Windows' and python_version>'3.10') or (platform_system=='Linux' and platform_machine!='aarch64') ### pyinstaller==6.11.1; platform_system=='Linux' # on Windows pyinstaller is separately installed (see above) ### diff --git a/wiki/ReleaseHistory.md b/wiki/ReleaseHistory.md index 666cf144e..70f818b8c 100644 --- a/wiki/ReleaseHistory.md +++ b/wiki/ReleaseHistory.md @@ -15,7 +15,7 @@ v3.1.1 * FIXES - ensure complete reset to defaults in energy tab loads tab - makes loading of (broken) profiles with inconsistent data length more robust - - prevents exceptions caused by empty event type names ([Discussion #1690](../../../discussions/1745)) + - prevents exceptions caused by empty event type names ([Discussion #1745](../../../discussions/1745)) ----