Compare commits

..

11 Commits

Author SHA1 Message Date
Wiktor Zykubek
45b5643cc7
Update screenshot_linux.png 2023-06-19 01:13:55 +02:00
Wiktor Zykubek
eeadc0a8c2
Create separate pause and stop button 2023-06-18 22:27:55 +02:00
Wiktor Zykubek
5358deab51
Change pagination for eposiodes list 2023-06-18 18:01:06 +02:00
Wiktor Zykubek
f664548d71
Allow to play podcasts 2023-06-18 17:57:27 +02:00
Wiktor Zykubek
c26d8833b3
Clear stationsListWidget each time listWidget is changed 2023-06-18 17:46:16 +02:00
Wiktor Zykubek
32ed78e7bc
Increase MainWindow width 2023-06-18 17:42:55 +02:00
Wiktor Zykubek
edc498c510
Print podcasts and episodes 2023-06-18 17:37:10 +02:00
Wiktor Zykubek
d02f7302a2
Add printPodcastGroups method 2023-06-18 16:37:12 +02:00
Wiktor Zykubek
e01edba7f3
Rename methods 2023-06-18 16:34:19 +02:00
Wiktor Zykubek
0b63621532
Adjust and rename getStationsData method to handle other URLs 2023-06-18 16:32:57 +02:00
Wiktor Zykubek
89cedd4f7c
Add tabWidget and podcasts category to UI 2023-06-18 16:14:26 +02:00
18 changed files with 319 additions and 573 deletions

View File

@ -1,23 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
## Description
...
## Additional information
...
## Steps to reproduce
1. ...
2. ...
...
## Setup information
+ Version: ...
+ Installation method: ...

View File

@ -1,14 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.

10
.gitignore vendored
View File

@ -35,9 +35,6 @@ Thumbs.db
/.qmake.cache /.qmake.cache
/.qmake.stash /.qmake.stash
ui_form.py
*.pyproject*
# qtcreator generated files # qtcreator generated files
*.pro.user* *.pro.user*
CMakeLists.txt.user* CMakeLists.txt.user*
@ -67,13 +64,8 @@ CMakeLists.txt.user*
*.Debug *.Debug
*.Release *.Release
# Python # Python byte code
__pycache__/
*.pyc *.pyc
dist/*
build/*
ui_*.py
.ropeproject/
# Binaries # Binaries
# -------- # --------

View File

@ -1,7 +0,0 @@
ISC License
Copyright 2023 Wiktor Zykubek
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

51
Open-FM.spec Normal file
View File

@ -0,0 +1,51 @@
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['main.py'],
pathex=[],
binaries=[('icon.ico', '.')],
datas=[],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name='Open-FM',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=['icon.ico'],
)
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='Open-FM',
)

View File

@ -2,41 +2,19 @@
Qt client for Polish internet radio Open FM. Qt client for Polish internet radio Open FM.
## GNU/Linux packages
### Arch Linux
Official package is available in AUR as [openfm-qt](https://aur.archlinux.org/packages/openfm-qt).
Install it using `yay` or other AUR helper.
```bash
$ yay -S openfm-qt
```
## Releases ## Releases
To download latest version go to [releases](https://github.com/wzykubek/openfm-qt/releases). To download latest version go to [releases](https://github.com/wzykubek/openfm-qt/releases).
> **_NOTE:_** Releases are currently suspended. You can download working one portable version for Windows or any Linux distro, but not the latest. Currently my main goal is to maintain AUR package and soon `.deb` and `.rpm` packages.
## Python package ## Build binary (Linux/Windows)
You can manually install app as Python package using `pip`. Go to root project directory and use following command. ```shell
```bash $ python3 build.py
$ python -m pip install .
``` ```
Alternatively use `pipx` to install app in virtual environment. Executable will be available in `dist/Open-FM` directory.
```bash
$ pipx install .
```
> **_NOTE:_** This option won't create desktop entry. ## Run (Linux/Windows)
```shell
## Development $ python3 main.py
Use `poetry` tool to manage project dependencies and virtual environments.
```bash
$ poetry install # in root project directory
```
Run application.
```bash
$ poetry run openfm-qt
``` ```
## Screenshots ## Screenshots

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

7
build.py Normal file
View File

@ -0,0 +1,7 @@
# This Python file uses the following encoding: utf-8
import PyInstaller.__main__
import os
os.system("pyside6-uic form.ui -o ui_form.py")
PyInstaller.__main__.run(['Open-FM.spec'])

View File

@ -6,19 +6,19 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>350</width> <width>650</width>
<height>500</height> <height>500</height>
</rect> </rect>
</property> </property>
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>350</width> <width>650</width>
<height>500</height> <height>500</height>
</size> </size>
</property> </property>
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
<width>350</width> <width>650</width>
<height>500</height> <height>500</height>
</size> </size>
</property> </property>
@ -33,7 +33,7 @@
</property> </property>
<property name="windowIcon"> <property name="windowIcon">
<iconset> <iconset>
<normaloff>../assets/icons/icon.png</normaloff>../assets/icons/icon.png</iconset> <normaloff>icon.ico</normaloff>icon.ico</iconset>
</property> </property>
<property name="locale"> <property name="locale">
<locale language="Polish" country="Poland"/> <locale language="Polish" country="Poland"/>
@ -44,7 +44,7 @@
<rect> <rect>
<x>9</x> <x>9</x>
<y>9</y> <y>9</y>
<width>332</width> <width>631</width>
<height>461</height> <height>461</height>
</rect> </rect>
</property> </property>
@ -52,19 +52,85 @@
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<widget class="QListWidget" name="groupsListWidget"> <widget class="QTabWidget" name="tabWidget">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>130</width> <width>200</width>
<height>0</height> <height>0</height>
</size> </size>
</property> </property>
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
<width>130</width> <width>200</width>
<height>16777215</height> <height>16777215</height>
</size> </size>
</property> </property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="radioTab">
<attribute name="title">
<string>Radio</string>
</attribute>
<widget class="QListWidget" name="radioGroupsListWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>200</width>
<height>400</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>400</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>400</height>
</size>
</property>
</widget>
</widget>
<widget class="QWidget" name="podcastTab">
<attribute name="title">
<string>Podcasty</string>
</attribute>
<widget class="QWidget" name="verticalLayoutWidget_2">
<property name="geometry">
<rect>
<x>-1</x>
<y>-1</y>
<width>201</width>
<height>401</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QListWidget" name="podcastGroupsListWidget"/>
</item>
<item>
<widget class="QListWidget" name="podcastListWidget">
<property name="minimumSize">
<size>
<width>0</width>
<height>195</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>195</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</widget> </widget>
</item> </item>
<item> <item>
@ -81,6 +147,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QToolButton" name="stopToolButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item> <item>
<widget class="Line" name="line"> <widget class="Line" name="line">
<property name="lineWidth"> <property name="lineWidth">
@ -99,6 +172,18 @@
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="value"> <property name="value">
<number>70</number> <number>70</number>
</property> </property>
@ -124,8 +209,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>350</width> <width>650</width>
<height>20</height> <height>22</height>
</rect> </rect>
</property> </property>
</widget> </widget>

View File

Before

Width:  |  Height:  |  Size: 252 KiB

After

Width:  |  Height:  |  Size: 252 KiB

View File

@ -1,18 +1,11 @@
# coding=utf-8 # This Python file uses the following encoding: utf-8
import sys
from PySide6.QtWidgets import QApplication from PySide6.QtWidgets import QApplication
from openfm_qt import MainWindow
import sys
from . import MainWindow if __name__ == "__main__":
def main():
app = QApplication(sys.argv) app = QApplication(sys.argv)
widget = MainWindow() widget = MainWindow()
widget.show() widget.show()
sys.exit(app.exec()) sys.exit(app.exec())
if __name__ == "__main__":
main()

3
openfm-qt.pyproject Normal file
View File

@ -0,0 +1,3 @@
{
"files": ["build.py","openfm_qt/__init__.py","form.ui","main.py","Open-FM.spec","pyproject.toml","openfm_qt/mainwindow.py","icon.ico","README.md","screenshot_linux.png"]
}

View File

@ -1,9 +1,5 @@
# coding=utf-8 # This Python file uses the following encoding: utf-8
API_URL = "https://open.fm/radio/api/v2/ofm/stations_slug.json"
DEFAULT_VOLUME = 70 DEFAULT_VOLUME = 70
from .ui_main_window import Ui_MainWindow # nopep8: E402 isort: skip from .mainwindow import MainWindow
from .mainwindow import MainWindow # nopep8: E402 isort: skip
__all__ = [Ui_MainWindow, MainWindow]

View File

@ -1,135 +1,198 @@
# coding=utf-8 # This Python file uses the following encoding: utf-8
import json
import requests
from PySide6.QtCore import QUrl from PySide6.QtCore import QUrl
from PySide6.QtMultimedia import QAudioOutput, QMediaPlayer
from PySide6.QtWidgets import QMainWindow, QMessageBox, QStyle from PySide6.QtWidgets import QMainWindow, QMessageBox, QStyle
from PySide6.QtMultimedia import QMediaPlayer, QAudioOutput
from . import API_URL, DEFAULT_VOLUME, Ui_MainWindow # Important:
# You need to run the following command to generate the ui_form.py file
from __feature__ import snake_case, true_property # isort: skip # pyside6-uic form.ui -o ui_form.py, or
# pyside2-uic form.ui -o ui_form.py
from ui_form import Ui_MainWindow
import json
import requests
from . import DEFAULT_VOLUME
class MainWindow(QMainWindow): class MainWindow(QMainWindow):
"""MainWindow Class.""" """MainWindow Class."""
def __init__(self): def __init__(self, parent=None):
"""Initialize UI, audio player and handlers.""" """Initialize UI, audio player and handlers."""
super().__init__() super().__init__(parent)
self.ui = Ui_MainWindow() self.ui = Ui_MainWindow()
self.ui.setupUi(self) self.ui.setupUi(self)
volume_icon = self.style().standardIcon(QStyle.SP_MediaVolume)
playback_icon = self.style().standardIcon(QStyle.SP_MediaPlay)
stop_icon = self.style().standardIcon(QStyle.SP_MediaStop)
self.ui.volumeToolButton.setIcon(volume_icon)
self.ui.playbackToolButton.setIcon(playback_icon)
self.ui.stopToolButton.setIcon(stop_icon)
class __icons(object): self.__stations = self.getData(
volume = self.style().standard_icon(QStyle.SP_MediaVolume) "https://open.fm/radio/api/v2/ofm/stations_slug.json"
muted = self.style().standard_icon(QStyle.SP_MediaVolumeMuted) )
play = self.style().standard_icon(QStyle.SP_MediaPlay) self.__podcasts_groups = self.getData(
stop = self.style().standard_icon(QStyle.SP_MediaStop) "https://open.fm/api/podcasts/categories"
)
self.__icons = __icons
self.ui.volumeToolButton.icon = self.__icons.volume
self.ui.playbackToolButton.icon = self.__icons.play
self.__stations_data = self.get_stations_data()
self.__player = QMediaPlayer() self.__player = QMediaPlayer()
self.__audio = QAudioOutput() self.__audio = QAudioOutput()
self.__player.audio_output = self.__audio self.__player.setAudioOutput(self.__audio)
self.print_groups() self.setVolume(DEFAULT_VOLUME)
self.printRadioGroups()
self.printPodcastGroups()
self.set_volume(DEFAULT_VOLUME) self.ui.radioGroupsListWidget.itemClicked.connect(self.printRadioStations)
self.ui.volumeHorizontalSlider.value = DEFAULT_VOLUME self.ui.podcastGroupsListWidget.itemClicked.connect(self.printPodcasts)
self.ui.podcastListWidget.itemClicked.connect(self.printPodcastEpisodes)
self.ui.stationsListWidget.itemClicked.connect(self.playRadio)
self.ui.playbackToolButton.clicked.connect(self.togglePlayer)
self.ui.stopToolButton.clicked.connect(self.stopPlayer)
self.ui.volumeHorizontalSlider.valueChanged.connect(self.setVolume)
self.ui.volumeToolButton.clicked.connect(self.toggleMute)
self.ui.groupsListWidget.itemClicked.connect(self.print_stations) def getData(self, url) -> dict:
self.ui.stationsListWidget.itemClicked.connect(self.play_radio)
self.ui.playbackToolButton.clicked.connect(self.toggle_player)
self.ui.volumeHorizontalSlider.valueChanged.connect(self.set_volume)
self.ui.volumeToolButton.clicked.connect(self.toggle_mute)
def get_stations_data(self) -> dict:
"""Get JSON data from API and convert it to dict.""" """Get JSON data from API and convert it to dict."""
try: resp = requests.get(url)
resp = requests.get(API_URL)
if resp.status_code not in range(200, 299 + 1): if resp.status_code not in range(200, 299 + 1):
raise requests.exceptions.ConnectionError()
else:
return json.loads(resp.text)
except requests.exceptions.ConnectionError:
error_box = QMessageBox.critical( error_box = QMessageBox.critical(
self, self,
"Błąd", "Błąd",
"Brak połączenia z serwerem.", f"Błąd połączenia o kodzie: {resp.status_code}",
QMessageBox.Cancel, QMessageBox.Cancel,
) )
error_box.exec() error_box.exec()
else:
return json.loads(resp.text)
def print_groups(self) -> None: def printRadioGroups(self) -> None:
"""Print groups (categories) in groupsListWidget.""" """Print groups (categories) in radioGroupsListWidget."""
self.ui.groupsListWidget.add_items( self.ui.stationsListWidget.clear()
[e["name"] for e in self.__stations_data["groups"]]) self.ui.radioGroupsListWidget.addItems(
[e["name"] for e in self.__stations["groups"]]
)
def print_stations(self) -> None: def printPodcastGroups(self) -> None:
"""Print groups (categories) in podcastGroupsListWidget."""
self.ui.stationsListWidget.clear()
self.ui.podcastGroupsListWidget.addItems(
[e["name"] for e in self.__podcasts_groups]
)
def printRadioStations(self) -> None:
"""Print stations (channels) in stationsListWidget.""" """Print stations (channels) in stationsListWidget."""
group = self.ui.groupsListWidget.selected_items()[0].text() group = self.ui.radioGroupsListWidget.selectedItems()[0].text()
group_id = None group_id = None
for e in self.__stations_data["groups"]: for e in self.__stations["groups"]:
if e["name"] == group: if e["name"] == group:
group_id = e["id"] group_id = e["id"]
self.ui.stationsListWidget.clear() self.ui.stationsListWidget.clear()
self.ui.stationsListWidget.add_items([ self.ui.stationsListWidget.addItems(
e["name"] for e in self.__stations_data["channels"] [
e["name"]
for e in self.__stations["channels"]
if e["group_id"] == group_id if e["group_id"] == group_id
]) ]
)
def set_volume(self, volume: int = None) -> None: def printPodcasts(self) -> None:
"""Print podcasts in podcastListWidget."""
self.ui.stationsListWidget.clear()
group = self.ui.podcastGroupsListWidget.selectedItems()[0].text()
group_name = None
for e in self.__podcasts_groups:
if e["name"] == group:
group_name = e["slug"]
self.__podcasts = self.getData(
f"https://open.fm/api/podcasts/category/{group_name}"
)["podcasts"]["content"]
self.ui.podcastListWidget.clear()
self.ui.podcastListWidget.addItems(
[e["title"] for e in self.__podcasts]
)
def printPodcastEpisodes(self) -> None:
"""Print podcast episodes in stationsListWidget."""
podcast_name = self.ui.podcastListWidget.selectedItems()[0].text()
podcast_id = None
for e in self.__podcasts:
if e["title"] == podcast_name:
podcast_id = e["id"]
self.__podcast_episodes = self.getData(
f"https://open.fm/api/podcast/{podcast_id}/episodes?size=999"
)
self.ui.stationsListWidget.clear()
self.ui.stationsListWidget.addItems(
[e["title"] for e in self.__podcast_episodes["content"]]
)
def setVolume(self, volume: int = None) -> None:
"""Set playback volume to given number or slider value.""" """Set playback volume to given number or slider value."""
if not volume: if not volume:
volume = self.ui.volumeHorizontalSlider.value volume = self.ui.volumeHorizontalSlider.value()
self.__audio.volume = volume / 100 self.__audio.setVolume(volume / 100)
def toggle_mute(self) -> None: def toggleMute(self) -> None:
"""Toggle playback volume between 0 and DEFAULT_VOLUME.""" """Toggle playback volume between 0 and DEFAULT_VOLUME."""
if self.ui.volumeHorizontalSlider.value == 0: if self.ui.volumeHorizontalSlider.value() == 0:
self.ui.volumeHorizontalSlider.value = self.previous_volume self.ui.volumeHorizontalSlider.setValue(DEFAULT_VOLUME)
icon = self.__icons.volume icon = self.style().standardIcon(QStyle.SP_MediaVolume)
self.set_volume(self.previous_volume) self.setVolume(DEFAULT_VOLUME)
else: else:
self.previous_volume = self.__audio.volume * 100 self.ui.volumeHorizontalSlider.setValue(0)
self.ui.volumeHorizontalSlider.value = 0 icon = self.style().standardIcon(QStyle.SP_MediaVolumeMuted)
icon = self.__icons.muted self.setVolume(0)
self.set_volume(0)
self.ui.volumeToolButton.icon = icon self.ui.volumeToolButton.setIcon(icon)
def play_radio(self) -> None: def playRadio(self) -> None:
"""Play station selected by user.""" """Play station selected by user."""
station = self.ui.stationsListWidget.selected_items()[0].text() selection = self.ui.stationsListWidget.selectedItems()[0].text()
stream_url = None stream_url = None
for e in self.__stations_data["channels"]: for e in self.__stations["channels"]:
if e["name"] == station: if e["name"] == selection:
stream_url = f"http://stream.open.fm/{e['id']}" stream_url = f"http://stream.open.fm/{e['id']}"
self.__player.source = QUrl(stream_url) if not stream_url:
self.window_title = f"Open FM - {station}" for e in self.__podcast_episodes["content"]:
if e["title"] == selection:
stream_url = e["file"].replace("https", "http")
# Required to avoid crashing. For some reason if you want to change self.__player.setSource(QUrl(stream_url))
# the station for the first time, you need to stop and resume playback. self.__fix_playback()
def __fix_playback(self) -> None:
"""Fix playack."""
# For some reason if you want to change the station for the first time,
# you need to stop and resume playback.
# If you won't, application would crash. # If you won't, application would crash.
for _ in range(3):
self.toggle_player()
def toggle_player(self) -> None:
"""Toggle playback (play/stop)."""
pb_state = QMediaPlayer.PlaybackState
if self.__player.playback_state == pb_state.PlayingState:
self.__player.stop()
icon = self.__icons.play
elif self.__player.playback_state == pb_state.StoppedState:
self.__player.play() self.__player.play()
icon = self.__icons.stop self.__player.stop()
self.__player.play()
icon = self.style().standardIcon(QStyle.SP_MediaPause)
self.ui.playbackToolButton.setIcon(icon)
def togglePlayer(self) -> None:
"""Toggle playback (play/pause)."""
pb_state = QMediaPlayer.PlaybackState
if self.__player.playbackState() == pb_state.PlayingState:
self.__player.pause()
icon = self.style().standardIcon(QStyle.SP_MediaPlay)
elif self.__player.playbackState() in [
pb_state.PausedState,
pb_state.StoppedState
]:
self.__player.play()
icon = self.style().standardIcon(QStyle.SP_MediaPause)
else: else:
pass pass
self.ui.playbackToolButton.icon = icon self.ui.playbackToolButton.setIcon(icon)
def stopPlayer(self) -> None:
self.__player.stop()
icon = self.style().standardIcon(QStyle.SP_MediaPlay)
self.ui.playbackToolButton.setIcon(icon)

315
poetry.lock generated
View File

@ -1,315 +0,0 @@
# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
[[package]]
name = "certifi"
version = "2023.5.7"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
python-versions = ">=3.6"
files = [
{file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"},
{file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"},
]
[[package]]
name = "charset-normalizer"
version = "3.2.0"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
optional = false
python-versions = ">=3.7.0"
files = [
{file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"},
{file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"},
{file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"},
{file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"},
{file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"},
{file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"},
{file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"},
{file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"},
{file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"},
{file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"},
{file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"},
{file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"},
{file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"},
{file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"},
{file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"},
{file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"},
{file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"},
{file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"},
{file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"},
{file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"},
{file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"},
{file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"},
{file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"},
{file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"},
{file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"},
{file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"},
{file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"},
{file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"},
{file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"},
{file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"},
{file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"},
{file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"},
{file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"},
{file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"},
{file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"},
{file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"},
{file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"},
{file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"},
{file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"},
{file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"},
{file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"},
{file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"},
{file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"},
{file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"},
{file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"},
{file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"},
{file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"},
{file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"},
{file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"},
{file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"},
{file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"},
{file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"},
{file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"},
{file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"},
{file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"},
{file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"},
{file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"},
{file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"},
{file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"},
{file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"},
{file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"},
{file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"},
]
[[package]]
name = "idna"
version = "3.4"
description = "Internationalized Domain Names in Applications (IDNA)"
optional = false
python-versions = ">=3.5"
files = [
{file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
{file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
]
[[package]]
name = "importlib-metadata"
version = "6.8.0"
description = "Read metadata from Python packages"
optional = false
python-versions = ">=3.8"
files = [
{file = "importlib_metadata-6.8.0-py3-none-any.whl", hash = "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb"},
{file = "importlib_metadata-6.8.0.tar.gz", hash = "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743"},
]
[package.dependencies]
zipp = ">=0.5"
[package.extras]
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
perf = ["ipython"]
testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"]
[[package]]
name = "isort"
version = "5.12.0"
description = "A Python utility / library to sort Python imports."
optional = false
python-versions = ">=3.8.0"
files = [
{file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"},
{file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"},
]
[package.extras]
colors = ["colorama (>=0.4.3)"]
pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"]
plugins = ["setuptools"]
requirements-deprecated-finder = ["pip-api", "pipreqs"]
[[package]]
name = "platformdirs"
version = "3.8.1"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
optional = false
python-versions = ">=3.7"
files = [
{file = "platformdirs-3.8.1-py3-none-any.whl", hash = "sha256:cec7b889196b9144d088e4c57d9ceef7374f6c39694ad1577a0aab50d27ea28c"},
{file = "platformdirs-3.8.1.tar.gz", hash = "sha256:f87ca4fcff7d2b0f81c6a748a77973d7af0f4d526f98f308477c3c436c74d528"},
]
[package.extras]
docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"]
[[package]]
name = "pyside6"
version = "6.5.1.1"
description = "Python bindings for the Qt cross-platform application and UI framework"
optional = false
python-versions = "<3.12,>=3.7"
files = [
{file = "PySide6-6.5.1.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:dc2d249ea2486526d1bb74e6cf96e2b49f2089cf069ab289a168aa48b5c251d5"},
{file = "PySide6-6.5.1.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b7b6588527eb1ca45afb8918f109d827f7d248beaec14e7acdaea18883680aee"},
{file = "PySide6-6.5.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:1517dd56f7235a98f3e1fcc983d3c8cf3d539b33164815c6e06442a297d2ab0d"},
{file = "PySide6-6.5.1.1-pp39-pypy39_pp73-macosx_10_9_universal2.whl", hash = "sha256:a55403254ff7421bea5b8e2196fded24e4a0357d3b1f10d5f01ffc3ae937c71a"},
{file = "PySide6-6.5.1.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:25c83843de0aa562631620721e4599eed55bc96747ed869caa631ecf92070951"},
{file = "PySide6-6.5.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:645c1a99c37a0ce045b23fb35faacf54d98442cc16f621fe1dad2a1d2880e5e3"},
]
[package.dependencies]
PySide6-Addons = "6.5.1.1"
PySide6-Essentials = "6.5.1.1"
shiboken6 = "6.5.1.1"
[[package]]
name = "pyside6-addons"
version = "6.5.1.1"
description = "Python bindings for the Qt cross-platform application and UI framework (Addons)"
optional = false
python-versions = "<3.12,>=3.7"
files = [
{file = "PySide6_Addons-6.5.1.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:aadda3376a81dbead867380f7ae81d2fe0fb1ffd482bfc10212be8b0d06c2c4c"},
{file = "PySide6_Addons-6.5.1.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cf18c88274d6c9c29cccbecb11c72b1f0b0bb077dc044f9b40354f13d0c1c52e"},
{file = "PySide6_Addons-6.5.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:d6370dfd03329cdcc91288d64bd1e26a5fc1908958b8a81fea5010a5849db441"},
{file = "PySide6_Addons-6.5.1.1-pp39-pypy39_pp73-macosx_10_9_universal2.whl", hash = "sha256:e3ab22776e620d9467ffb2d76be06d5a3b8b3859d77e2e73f65f2b29018645e7"},
{file = "PySide6_Addons-6.5.1.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4e1bc141a7aa9488dc77c9b29ad412e417e601cc8e23050517f7c6c8634feb46"},
{file = "PySide6_Addons-6.5.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:441f8797d6b01c9dd9b848d0e156b2b5042d71d9266d0a6ded48c9b1cd337821"},
]
[package.dependencies]
PySide6-Essentials = "6.5.1.1"
shiboken6 = "6.5.1.1"
[[package]]
name = "pyside6-essentials"
version = "6.5.1.1"
description = "Python bindings for the Qt cross-platform application and UI framework (Essentials)"
optional = false
python-versions = "<3.12,>=3.7"
files = [
{file = "PySide6_Essentials-6.5.1.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:c0cc3b1e7ca0e6b7a0cb8190a8538242972df43c1f9014cc0cd066d4ed7fc83a"},
{file = "PySide6_Essentials-6.5.1.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:bfd8900b1bf6a595b7d63539817bc825941f737486a093d519514c5b67af789f"},
{file = "PySide6_Essentials-6.5.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:1346e6a4e97d97f54e6c0805150c6dead4cc0e7914d77167f836eb36b83acb5c"},
{file = "PySide6_Essentials-6.5.1.1-pp39-pypy39_pp73-macosx_10_9_universal2.whl", hash = "sha256:0111386918064fa84a498ed7a6e93ec4155fe96e8d4f9c9e3ac54a5b65ddfadf"},
{file = "PySide6_Essentials-6.5.1.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6264b00099a9bed0020460033e2a4369513e0e69f7abe3f436c3c6390164bcea"},
{file = "PySide6_Essentials-6.5.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:620cefb6c954d59f941f06acecfe5cdf42bf470f2f3e6b9d023f49f93d740080"},
]
[package.dependencies]
shiboken6 = "6.5.1.1"
[[package]]
name = "requests"
version = "2.31.0"
description = "Python HTTP for Humans."
optional = false
python-versions = ">=3.7"
files = [
{file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"},
{file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"},
]
[package.dependencies]
certifi = ">=2017.4.17"
charset-normalizer = ">=2,<4"
idna = ">=2.5,<4"
urllib3 = ">=1.21.1,<3"
[package.extras]
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "shiboken6"
version = "6.5.1.1"
description = "Python/C++ bindings helper module"
optional = false
python-versions = "<3.12,>=3.7"
files = [
{file = "shiboken6-6.5.1.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:59fc1797df38c9e8c89cc43d5996a4c0331d0258087fa9d6466b03822aba6ff2"},
{file = "shiboken6-6.5.1.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4338a8eb00e8d6f5b27edf85ab689ada905a4490c73ed3f23ff3b3c5f72a563e"},
{file = "shiboken6-6.5.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:aa6289cdbaa12f364f1c45d60d9d483c3842ee9cf6e9acdc3efd21cd460c2eb5"},
{file = "shiboken6-6.5.1.1-pp39-pypy39_pp73-macosx_10_9_universal2.whl", hash = "sha256:db029b9d895052c1f42b1f929af3a5f6e688a88c5862aed24d5094086b034ab2"},
{file = "shiboken6-6.5.1.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1c7ba82093d66ace5c7a07ed9c7b4ea57a311ddbfecbe44aff7ed023efc8a380"},
{file = "shiboken6-6.5.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:34cacba2954bff632476fbead190868c6f068a6dd9e5c8fd7d2ed4f8f5cfade4"},
]
[[package]]
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
optional = false
python-versions = ">=3.7"
files = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
]
[[package]]
name = "urllib3"
version = "2.0.3"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
python-versions = ">=3.7"
files = [
{file = "urllib3-2.0.3-py3-none-any.whl", hash = "sha256:48e7fafa40319d358848e1bc6809b208340fafe2096f1725d05d67443d0483d1"},
{file = "urllib3-2.0.3.tar.gz", hash = "sha256:bee28b5e56addb8226c96f7f13ac28cb4c301dd5ea8a6ca179c0b9835e032825"},
]
[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "yapf"
version = "0.40.1"
description = "A formatter for Python code."
optional = false
python-versions = ">=3.7"
files = [
{file = "yapf-0.40.1-py3-none-any.whl", hash = "sha256:b8bfc1f280949153e795181768ca14ef43d7312629a06c43e7abd279323af313"},
{file = "yapf-0.40.1.tar.gz", hash = "sha256:958587eb5c8ec6c860119a9c25d02addf30a44f75aa152a4220d30e56a98037c"},
]
[package.dependencies]
importlib-metadata = ">=6.6.0"
platformdirs = ">=3.5.1"
tomli = ">=2.0.1"
[[package]]
name = "zipp"
version = "3.16.0"
description = "Backport of pathlib-compatible object wrapper for zip files"
optional = false
python-versions = ">=3.8"
files = [
{file = "zipp-3.16.0-py3-none-any.whl", hash = "sha256:5dadc3ad0a1f825fe42ce1bce0f2fc5a13af2e6b2d386af5b0ff295bc0a287d3"},
{file = "zipp-3.16.0.tar.gz", hash = "sha256:1876cb065531855bbe83b6c489dcf69ecc28f1068d8e95959fe8bbc77774c941"},
]
[package.extras]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff"]
[metadata]
lock-version = "2.0"
python-versions = "~3.11"
content-hash = "8a46b91bea175b77beffbe87a22e0fe9c21ea080cd3c8b0952d44233a24c3324"

View File

@ -1,40 +0,0 @@
# coding=utf-8
import logging
import os
import sys
from shutil import which
UI_DIR = "assets/ui"
logging.basicConfig(level=logging.INFO)
class UICNotFound(Exception):
pass
if sys.platform == "linux":
exe = "pyside6-uic"
exe_alt = "/usr/lib/qt6/uic"
elif sys.platform == "win32":
exe = "pyside6-uic.exe"
exe_alt = None
UI_DIR = UI_DIR.replace("/", "\\")
if not which(exe) and not which(exe_alt):
raise UICNotFound("User Interface Compiler binary not found.")
elif not which(exe) and which(exe_alt):
exe = exe_alt
logging.info("Building UI files...")
for f in os.listdir(UI_DIR):
if exe == "/usr/lib/qt6/uic":
__flags = "--generator python"
else:
__flags = ""
cmd = f"{exe} {UI_DIR}/{f} -o openfm_qt/ui_{f[:-3]}.py {__flags}"
if sys.platform == "win32":
cmd = cmd.replace("/", "\\")
os.system(cmd)
logging.info(f"Successfully builded {f} as ui_{f[:-3]}.py")

View File

@ -1,42 +1,19 @@
[tool.poetry] [tool.poetry]
name = "openfm-qt" name = "openfm-qt"
version = "0.1.3" version = "0.2.0"
description = "Qt client for Polish internet radio Open FM." description = "Qt client for Polish internet radio Open FM."
authors = ["Wiktor Zykubek <wz-git@mailbox.org>"] authors = ["Wiktor Zykubek <wz-git@mailbox.org>"]
license = "ISC" license = "ISC"
readme = "README.md" readme = "README.md"
repository = "https://github.com/wzykubek/openfm-qt"
keywords = ["music", "player", "radio", "desktop", "gui", "qt6"]
classifiers = [
"Environment :: X11 Applications :: Qt",
"Intended Audience :: End Users/Desktop",
"License :: OSI Approved :: ISC License (ISCL)",
"Natural Language :: Polish",
"Operating System :: Unix",
"Operating System :: Microsoft :: Windows",
"Programming Language :: Python :: 3.11",
"Topic :: Multimedia :: Sound/Audio :: Players"
]
packages = [{include = "openfm_qt"}] packages = [{include = "openfm_qt"}]
include = [{path = "openfm_qt/ui_*.py"}]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "~3.11" python = "^3.11"
requests = "^2.31.0" requests = "^2.31.0"
PySide6 = "^6.5.1.1" PySide6 = "^6.5.1.1"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
yapf = "^0.40.1" pyinstaller = "^5.12.0"
isort = "^5.12.0"
[tool.yapf]
based_on_style = "pep8"
[tool.poetry.scripts]
openfm-qt = "openfm_qt.__main__:main"
[tool.poetry.build]
script = "prebuild.py"
[build-system] [build-system]
requires = ["poetry-core"] requires = ["poetry-core"]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 53 KiB