Уведомления об изменении цен на бирже Bybit в приложении Flex на Python


Создание пользовательского интерфейса для отображения детальной информация по торговым парам с биржи Bybit в приложении Flet Python.
- Процент изменнеия цены (float)
- Интервал проверки цены (в секундах)
- Выкл. / Вкл. получение уведомлений
Для удобства можно добавить храниение состояния торговой пары в БД с помошью SQLite. Подробнее можно ознакомиться в статье Работа с базой данных SQLite в Python: примеры использования
Или можно хранить даные о состоянии в файле. Подробнее можно ознакомиться в статье Рекурсивное создание директорий и сохранение данных в файл на Python
Установка
Прежде чем мы начнем, убедитесь, что у вас установлены необходимые библиотеки:
pip install websocket-client requests flet
pip plyer
Код
# -*- coding: utf-8 -*-
import flet as ft
import time, websocket, json, threading, requests, re
from functools import partial
import plyer
sybolsList = {}
class bybit():
def __init__(self):
try:
self.mTickers = {}
self.ticker_socket = {}
self.start()
except Exception as e:
print(e)
# Данные от сокета
def on_message(self, _wsa, wsData, prm):
varData = json.loads(wsData)
if 'op' in varData:
if varData['op'] == 'subscribe' and varData['success'] == False:
mPairs = re.findall(r'\[([^]]*)\]', varData['ret_msg'])
for elem in mPairs:
del(self.ticker_socket[prm['key']]['listPair'][elem])
if len(self.ticker_socket[prm['key']]['listPair']) == 0:
self.ticker_socket[prm['key']]['exit'] = 'true'
self.ticker_socket[prm['key']]['status'] = 'off'
_wsa.close()
else:
self.mTickers[varData['data']['symbol']] = varData['data']
# Отлов ошибок/выключения сокета
def on_close(self, _wsa, wsData, prm):
_wsa.close()
self.ticker_socket[prm['key']]['status'] = 'off'
# Открытие соединения
def on_open(self, _wsa, prm):
try:
args = list(self.ticker_socket[prm['key']]['listPair'].values())
_wsa.send(json.dumps({"op": "subscribe", "args": args}))
except Exception as e:
_wsa.close()
# Запуск сокета
def runWebSockPl(self, key):
while True:
try:
wss = 'wss://stream.bybit.com/v5/public/spot'
wsa = websocket.WebSocketApp(wss,
on_open = partial(self.on_open, prm={'key': key}),
on_message = partial(self.on_message, prm={'key': key}),
on_error = partial(self.on_close, prm={'key': key}),
on_close = partial(self.on_close, prm={'key': key}))
self.ticker_socket[key]['connect'] = wsa
self.ticker_socket[key]['status'] = 'on'
print(u'Установлено соединение Web Socket ключ '+key)
wsa.run_forever()
if self.ticker_socket[key]['exit'] == 'true':
break
raise Exception('Exit')
except Exception as e:
print(u'Разрыв соединения Web Socket ключ '+key)
self.ticker_socket[key]['status'] = 'off'
time.sleep(10)
def start(self):
response = requests.get("https://api.bybit.com/v5/market/instruments-info?category=spot")
data = response.json()
i = 1
listPair = {}
for elem in data['result']['list']:
listPair['tickers.'+elem['symbol']] = 'tickers.'+elem['symbol']
if len(listPair) >= 10:
key = str(i)
self.ticker_socket[key] = {'connect': '', 'status': 'off', 'exit': 'false', 'listPair': listPair}
listPair = {}
ws = threading.Thread(target=self.runWebSockPl, kwargs={'key': key}, daemon=True)
ws.start()
i = i + 1
if len(listPair) > 0:
key = str(i)
self.ticker_socket[key] = {'connect': '', 'status': 'off', 'exit': 'false', 'listPair': listPair}
ws = threading.Thread(target=self.runWebSockPl, kwargs={'key': key}, daemon=True)
ws.start()
def main(page: ft.Page):
# Обновление торговых пар
def update_data_pair():
while True:
time.sleep(1)
for keyElem, dataElem in sybolsList.items():
sybolsList[keyElem]['container'].bgcolor = None
det_title_con.bgcolor = None
for keyElem, dataElem in bb.mTickers.items():
if keyElem in sybolsList:
if mPage['detailed']['container'].visible == True:
if det_title.value == keyElem:
det_lastPrice.value = dataElem['lastPrice']
det_highPrice24h.value = dataElem['highPrice24h']
det_lowPrice24h.value = dataElem['lowPrice24h']
det_prevPrice24h.value = dataElem['prevPrice24h']
det_volume24h.value = dataElem['volume24h']
det_turnover24h.value = dataElem['turnover24h']
if float(dataElem['lastPrice']) > float(sybolsList[keyElem]['lastPrice']):
det_title_con.bgcolor = ft.colors.GREEN_700
if float(dataElem['lastPrice']) < float(sybolsList[keyElem]['lastPrice']):
det_title_con.bgcolor = ft.colors.RED
sybolsList[keyElem]['text'].value = keyElem+' '+str(dataElem['lastPrice'])
if float(dataElem['lastPrice']) > float(sybolsList[keyElem]['lastPrice']):
sybolsList[keyElem]['container'].bgcolor = ft.colors.GREEN_700
if float(dataElem['lastPrice']) < float(sybolsList[keyElem]['lastPrice']):
sybolsList[keyElem]['container'].bgcolor = ft.colors.RED
sybolsList[keyElem]['lastPrice'] = float(dataElem['lastPrice'])
if sybolsList[keyElem]['alarm']['status'] == True and int(time.time()) >= sybolsList[keyElem]['alarm']['nextCheck']:
sybolsList[keyElem]['alarm']['nextCheck'] = int(time.time()) + sybolsList[det_title.value]['alarm']['interval']
percentage_change = ((float(sybolsList[keyElem]['lastPrice']) - float(sybolsList[keyElem]['alarm']['price'])) / float(sybolsList[keyElem]['alarm']['price'])) * 100
if percentage_change >= sybolsList[keyElem]['alarm']['percent']:
position = 'увеличилась' if float(sybolsList[keyElem]['lastPrice']) > float(sybolsList[keyElem]['alarm']['price']) else 'уменьшилась'
text_msg = 'Цена '+position+' на '+str(sybolsList[keyElem]['alarm']['percent'])+'%'
plyer.notification.notify(message = text_msg, app_name='Bybit - торговые пары', title='Торговая пара '+str(keyElem))
sybolsList[keyElem]['alarm']['price'] = sybolsList[keyElem]['lastPrice']
page.update()
# Нижнее меню
def bottom_menu(e):
if e.control.selected_index == 0:
mPage['main']['container'].visible = True
mPage['detailed']['container'].visible = False
formSearch.visible = True
page.update()
# Детальная информация
def detailed_open(symbol):
if symbol in bb.mTickers:
det_title.value = symbol
det_lastPrice.value = bb.mTickers[symbol]['lastPrice']
det_highPrice24h.value = bb.mTickers[symbol]['highPrice24h']
det_lowPrice24h.value = bb.mTickers[symbol]['lowPrice24h']
det_prevPrice24h.value = bb.mTickers[symbol]['prevPrice24h']
det_volume24h.value = bb.mTickers[symbol]['volume24h']
det_turnover24h.value = bb.mTickers[symbol]['turnover24h']
tf_percent.value = sybolsList[det_title.value]['alarm']['percent']
tf_interval.value = sybolsList[det_title.value]['alarm']['interval']
sw_start_alarm.value = sybolsList[det_title.value]['alarm']['status']
mPage['detailed']['container'].visible = True
mPage['main']['container'].visible = False
formSearch.visible = False
page.update()
# Филтрация списка
def filter_proccess(e):
serchVar = search.value.upper()
for keyElem, dataElem in sybolsList.items():
sybolsList[keyElem]['container'].visible = False
if serchVar in keyElem:
sybolsList[keyElem]['container'].visible = True
page.update()
# Отмена фильтрации
def clear_filter(e):
search.value = ''
for keyElem, dataElem in sybolsList.items():
sybolsList[keyElem]['container'].visible = True
page.update()
# Обновление конфига уведомления
def proccess_upd_alarm(e):
sybolsList[det_title.value]['alarm']['percent'] = float(tf_percent.value)
sybolsList[det_title.value]['alarm']['interval'] = int(tf_interval.value)
sybolsList[det_title.value]['alarm']['status'] = sw_start_alarm.value
sybolsList[det_title.value]['alarm']['nextCheck'] = int(time.time())
sybolsList[det_title.value]['alarm']['price'] = sybolsList[det_title.value]['lastPrice']
# Генерация главной страницы
def main_page():
response = requests.get("https://api.bybit.com/v5/market/instruments-info?category=spot")
data = response.json()
lastPrice = 0.00000000
rv = ft.ResponsiveRow([])
for elem in data['result']['list']:
var_text = ft.Text(elem['symbol']+' '+str(lastPrice))
var_elem = ft.Container(
ft.CupertinoButton(
content=var_text,
opacity_on_click=0.3,
on_click=lambda e, symbol=elem['symbol']: detailed_open(symbol),
color="black"
),
padding=5,
col={"sm": 6, "md": 4, "xl": 2},
border = ft.border.all(1, ft.colors.PRIMARY)
)
sybolsList[elem['symbol']] = {'container': var_elem, 'text': var_text, 'lastPrice': lastPrice, 'alarm': {'price': 0, 'percent': 0, 'interval': 0, 'status': False, 'nextCheck': False}}
rv.controls.append(var_elem)
mPage['main']['container'].controls.append(rv)
page.update()
# Деальная информация
det_title = ft.Text("--/--", size = 50, weight = ft.FontWeight.BOLD)
det_title_con = ft.Container(det_title,padding = 10, alignment=ft.alignment.center, border = ft.border.all(1, ft.colors.PRIMARY))
det_lastPrice = ft.Text("0.00000000", size = 20, weight = ft.FontWeight.BOLD)
det_highPrice24h = ft.Text("0.00000000", size = 20, weight = ft.FontWeight.BOLD)
det_lowPrice24h = ft.Text("0.00000000", size = 20, weight = ft.FontWeight.BOLD)
det_prevPrice24h = ft.Text("0.00000000", size = 20, weight = ft.FontWeight.BOLD)
det_volume24h = ft.Text("0.00000000", size = 20, weight = ft.FontWeight.BOLD)
det_turnover24h = ft.Text("0.00000000", size = 20, weight = ft.FontWeight.BOLD)
# Форма детальная информация
con_det = ft.Container(
ft.Column([
ft.ResponsiveRow([ft.Text("Последняя цена: ", size = 20), det_lastPrice]),
ft.ResponsiveRow([ft.Text("Самая высокая цена за 24 часа: ", size = 20), det_highPrice24h]),
ft.ResponsiveRow([ft.Text("Самая низкая цена за 24 часа: ", size = 20), det_lowPrice24h]),
ft.ResponsiveRow([ft.Text("Процентное изменение рыночной цены: ", size = 20), det_prevPrice24h]),
ft.ResponsiveRow([ft.Text("Объем за 24 часа: ", size = 20), det_volume24h]),
ft.ResponsiveRow([ft.Text("Оборот за 24 часа: ", size = 20), det_turnover24h])
]),
col={"sm": 6, "md": 6, "xl": 4},
padding = 10, alignment=ft.alignment.center, border = ft.border.all(1, ft.colors.PRIMARY)
)
# Форма включение уведомлений
tf_percent = ft.TextField(label="Процент изменения цены", value="0.5", border_color = ft.colors.PRIMARY)
tf_interval = ft.TextField(label="Интервал проверки цены (в секундах)", value="5", border_color = ft.colors.PRIMARY)
sw_start_alarm = ft.Switch(label="Выкл. / Вкл.")
btn_upd_alarm = ft.CupertinoFilledButton(
content=ft.Text("Применить"),
opacity_on_click = 0.3,
on_click = proccess_upd_alarm,
)
con_alarm = ft.Container(
ft.Column([
tf_percent,
tf_interval,
sw_start_alarm,
btn_upd_alarm
]),
col={"sm": 6, "md": 6, "xl": 4},
padding = 10, alignment=ft.alignment.center, border = ft.border.all(1, ft.colors.PRIMARY)
)
rvd = ft.ResponsiveRow([con_det, con_alarm])
mPage = {
'main': {'container': ft.ListView(expand = 1, spacing = 10, padding = 10)},
'detailed': {'container': ft.ListView(expand = 1, spacing = 10, padding = 10)}
}
mPage['detailed']['container'].controls.append(det_title_con)
mPage['detailed']['container'].controls.append(rvd)
mPage['main']['container'].visible = True
mPage['detailed']['container'].visible = False
# Фильтр
search = ft.TextField(label="Поиск", on_change=filter_proccess)
searchContainer = ft.Container(search ,padding=10)
btnFilter = ft.IconButton(ft.icons.FILTER_ALT_OFF, icon_size=30,tooltip = "Отмена фильтра", on_click = clear_filter)
formSearch = ft.Row([searchContainer, btnFilter])
# Верхнее меню
page.appbar = ft.AppBar(
title=ft.Text("Bybit - торговые пары"),
center_title=False,
bgcolor=ft.colors.SURFACE_VARIANT,
actions=[formSearch],
)
# Нижнее меню
page.navigation_bar = ft.CupertinoNavigationBar(
bgcolor=ft.colors.SURFACE_VARIANT,
on_change=lambda e: bottom_menu(e),
destinations=[ft.NavigationBarDestination(icon=ft.icons.HOME_ROUNDED, label="Главная")]
)
page.add(mPage['main']['container'], mPage['detailed']['container'])
# Генерация главной страницы
ws = threading.Thread(target = main_page, daemon=True)
ws.start()
# Запуск ws
bb = bybit()
# Запуск обновления торговых пар
ws = threading.Thread(target = update_data_pair, daemon=True)
ws.start()
ft.app(target=main)
Заключение
В этом примере мы продемонстрировали, как можно реализовать получение системных уведомлений от приложения. Надеюсь, эта статья была полезной для вас.
- 12.10.2024
- 39
- 0
Уведомления об изменении цен на бирже Bybit в приложении Flex на Python
- Процент изменнеия цены (float)
- Интервал проверки цены (в секундах)
- Выкл. / Вкл. получение уведомлений
Для удобства можно добавить храниение состояния торговой пары в БД с помошью SQLite. Подробнее можно ознакомиться в статье Работа с базой данных SQLite в Python: примеры использования
Или можно хранить даные о состоянии в файле. Подробнее можно ознакомиться в статье Рекурсивное создание директорий и сохранение данных в файл на Python
Установка
Прежде чем мы начнем, убедитесь, что у вас установлены необходимые библиотеки:
pip install websocket-client requests flet
pip plyer
Код
# -*- coding: utf-8 -*-
import flet as ft
import time, websocket, json, threading, requests, re
from functools import partial
import plyer
sybolsList = {}
class bybit():
def __init__(self):
try:
self.mTickers = {}
self.ticker_socket = {}
self.start()
except Exception as e:
print(e)
# Данные от сокета
def on_message(self, _wsa, wsData, prm):
varData = json.loads(wsData)
if 'op' in varData:
if varData['op'] == 'subscribe' and varData['success'] == False:
mPairs = re.findall(r'\[([^]]*)\]', varData['ret_msg'])
for elem in mPairs:
del(self.ticker_socket[prm['key']]['listPair'][elem])
if len(self.ticker_socket[prm['key']]['listPair']) == 0:
self.ticker_socket[prm['key']]['exit'] = 'true'
self.ticker_socket[prm['key']]['status'] = 'off'
_wsa.close()
else:
self.mTickers[varData['data']['symbol']] = varData['data']
# Отлов ошибок/выключения сокета
def on_close(self, _wsa, wsData, prm):
_wsa.close()
self.ticker_socket[prm['key']]['status'] = 'off'
# Открытие соединения
def on_open(self, _wsa, prm):
try:
args = list(self.ticker_socket[prm['key']]['listPair'].values())
_wsa.send(json.dumps({"op": "subscribe", "args": args}))
except Exception as e:
_wsa.close()
# Запуск сокета
def runWebSockPl(self, key):
while True:
try:
wss = 'wss://stream.bybit.com/v5/public/spot'
wsa = websocket.WebSocketApp(wss,
on_open = partial(self.on_open, prm={'key': key}),
on_message = partial(self.on_message, prm={'key': key}),
on_error = partial(self.on_close, prm={'key': key}),
on_close = partial(self.on_close, prm={'key': key}))
self.ticker_socket[key]['connect'] = wsa
self.ticker_socket[key]['status'] = 'on'
print(u'Установлено соединение Web Socket ключ '+key)
wsa.run_forever()
if self.ticker_socket[key]['exit'] == 'true':
break
raise Exception('Exit')
except Exception as e:
print(u'Разрыв соединения Web Socket ключ '+key)
self.ticker_socket[key]['status'] = 'off'
time.sleep(10)
def start(self):
response = requests.get("https://api.bybit.com/v5/market/instruments-info?category=spot")
data = response.json()
i = 1
listPair = {}
for elem in data['result']['list']:
listPair['tickers.'+elem['symbol']] = 'tickers.'+elem['symbol']
if len(listPair) >= 10:
key = str(i)
self.ticker_socket[key] = {'connect': '', 'status': 'off', 'exit': 'false', 'listPair': listPair}
listPair = {}
ws = threading.Thread(target=self.runWebSockPl, kwargs={'key': key}, daemon=True)
ws.start()
i = i + 1
if len(listPair) > 0:
key = str(i)
self.ticker_socket[key] = {'connect': '', 'status': 'off', 'exit': 'false', 'listPair': listPair}
ws = threading.Thread(target=self.runWebSockPl, kwargs={'key': key}, daemon=True)
ws.start()
def main(page: ft.Page):
# Обновление торговых пар
def update_data_pair():
while True:
time.sleep(1)
for keyElem, dataElem in sybolsList.items():
sybolsList[keyElem]['container'].bgcolor = None
det_title_con.bgcolor = None
for keyElem, dataElem in bb.mTickers.items():
if keyElem in sybolsList:
if mPage['detailed']['container'].visible == True:
if det_title.value == keyElem:
det_lastPrice.value = dataElem['lastPrice']
det_highPrice24h.value = dataElem['highPrice24h']
det_lowPrice24h.value = dataElem['lowPrice24h']
det_prevPrice24h.value = dataElem['prevPrice24h']
det_volume24h.value = dataElem['volume24h']
det_turnover24h.value = dataElem['turnover24h']
if float(dataElem['lastPrice']) > float(sybolsList[keyElem]['lastPrice']):
det_title_con.bgcolor = ft.colors.GREEN_700
if float(dataElem['lastPrice']) < float(sybolsList[keyElem]['lastPrice']):
det_title_con.bgcolor = ft.colors.RED
sybolsList[keyElem]['text'].value = keyElem+' '+str(dataElem['lastPrice'])
if float(dataElem['lastPrice']) > float(sybolsList[keyElem]['lastPrice']):
sybolsList[keyElem]['container'].bgcolor = ft.colors.GREEN_700
if float(dataElem['lastPrice']) < float(sybolsList[keyElem]['lastPrice']):
sybolsList[keyElem]['container'].bgcolor = ft.colors.RED
sybolsList[keyElem]['lastPrice'] = float(dataElem['lastPrice'])
if sybolsList[keyElem]['alarm']['status'] == True and int(time.time()) >= sybolsList[keyElem]['alarm']['nextCheck']:
sybolsList[keyElem]['alarm']['nextCheck'] = int(time.time()) + sybolsList[det_title.value]['alarm']['interval']
percentage_change = ((float(sybolsList[keyElem]['lastPrice']) - float(sybolsList[keyElem]['alarm']['price'])) / float(sybolsList[keyElem]['alarm']['price'])) * 100
if percentage_change >= sybolsList[keyElem]['alarm']['percent']:
position = 'увеличилась' if float(sybolsList[keyElem]['lastPrice']) > float(sybolsList[keyElem]['alarm']['price']) else 'уменьшилась'
text_msg = 'Цена '+position+' на '+str(sybolsList[keyElem]['alarm']['percent'])+'%'
plyer.notification.notify(message = text_msg, app_name='Bybit - торговые пары', title='Торговая пара '+str(keyElem))
sybolsList[keyElem]['alarm']['price'] = sybolsList[keyElem]['lastPrice']
page.update()
# Нижнее меню
def bottom_menu(e):
if e.control.selected_index == 0:
mPage['main']['container'].visible = True
mPage['detailed']['container'].visible = False
formSearch.visible = True
page.update()
# Детальная информация
def detailed_open(symbol):
if symbol in bb.mTickers:
det_title.value = symbol
det_lastPrice.value = bb.mTickers[symbol]['lastPrice']
det_highPrice24h.value = bb.mTickers[symbol]['highPrice24h']
det_lowPrice24h.value = bb.mTickers[symbol]['lowPrice24h']
det_prevPrice24h.value = bb.mTickers[symbol]['prevPrice24h']
det_volume24h.value = bb.mTickers[symbol]['volume24h']
det_turnover24h.value = bb.mTickers[symbol]['turnover24h']
tf_percent.value = sybolsList[det_title.value]['alarm']['percent']
tf_interval.value = sybolsList[det_title.value]['alarm']['interval']
sw_start_alarm.value = sybolsList[det_title.value]['alarm']['status']
mPage['detailed']['container'].visible = True
mPage['main']['container'].visible = False
formSearch.visible = False
page.update()
# Филтрация списка
def filter_proccess(e):
serchVar = search.value.upper()
for keyElem, dataElem in sybolsList.items():
sybolsList[keyElem]['container'].visible = False
if serchVar in keyElem:
sybolsList[keyElem]['container'].visible = True
page.update()
# Отмена фильтрации
def clear_filter(e):
search.value = ''
for keyElem, dataElem in sybolsList.items():
sybolsList[keyElem]['container'].visible = True
page.update()
# Обновление конфига уведомления
def proccess_upd_alarm(e):
sybolsList[det_title.value]['alarm']['percent'] = float(tf_percent.value)
sybolsList[det_title.value]['alarm']['interval'] = int(tf_interval.value)
sybolsList[det_title.value]['alarm']['status'] = sw_start_alarm.value
sybolsList[det_title.value]['alarm']['nextCheck'] = int(time.time())
sybolsList[det_title.value]['alarm']['price'] = sybolsList[det_title.value]['lastPrice']
# Генерация главной страницы
def main_page():
response = requests.get("https://api.bybit.com/v5/market/instruments-info?category=spot")
data = response.json()
lastPrice = 0.00000000
rv = ft.ResponsiveRow([])
for elem in data['result']['list']:
var_text = ft.Text(elem['symbol']+' '+str(lastPrice))
var_elem = ft.Container(
ft.CupertinoButton(
content=var_text,
opacity_on_click=0.3,
on_click=lambda e, symbol=elem['symbol']: detailed_open(symbol),
color="black"
),
padding=5,
col={"sm": 6, "md": 4, "xl": 2},
border = ft.border.all(1, ft.colors.PRIMARY)
)
sybolsList[elem['symbol']] = {'container': var_elem, 'text': var_text, 'lastPrice': lastPrice, 'alarm': {'price': 0, 'percent': 0, 'interval': 0, 'status': False, 'nextCheck': False}}
rv.controls.append(var_elem)
mPage['main']['container'].controls.append(rv)
page.update()
# Деальная информация
det_title = ft.Text("--/--", size = 50, weight = ft.FontWeight.BOLD)
det_title_con = ft.Container(det_title,padding = 10, alignment=ft.alignment.center, border = ft.border.all(1, ft.colors.PRIMARY))
det_lastPrice = ft.Text("0.00000000", size = 20, weight = ft.FontWeight.BOLD)
det_highPrice24h = ft.Text("0.00000000", size = 20, weight = ft.FontWeight.BOLD)
det_lowPrice24h = ft.Text("0.00000000", size = 20, weight = ft.FontWeight.BOLD)
det_prevPrice24h = ft.Text("0.00000000", size = 20, weight = ft.FontWeight.BOLD)
det_volume24h = ft.Text("0.00000000", size = 20, weight = ft.FontWeight.BOLD)
det_turnover24h = ft.Text("0.00000000", size = 20, weight = ft.FontWeight.BOLD)
# Форма детальная информация
con_det = ft.Container(
ft.Column([
ft.ResponsiveRow([ft.Text("Последняя цена: ", size = 20), det_lastPrice]),
ft.ResponsiveRow([ft.Text("Самая высокая цена за 24 часа: ", size = 20), det_highPrice24h]),
ft.ResponsiveRow([ft.Text("Самая низкая цена за 24 часа: ", size = 20), det_lowPrice24h]),
ft.ResponsiveRow([ft.Text("Процентное изменение рыночной цены: ", size = 20), det_prevPrice24h]),
ft.ResponsiveRow([ft.Text("Объем за 24 часа: ", size = 20), det_volume24h]),
ft.ResponsiveRow([ft.Text("Оборот за 24 часа: ", size = 20), det_turnover24h])
]),
col={"sm": 6, "md": 6, "xl": 4},
padding = 10, alignment=ft.alignment.center, border = ft.border.all(1, ft.colors.PRIMARY)
)
# Форма включение уведомлений
tf_percent = ft.TextField(label="Процент изменения цены", value="0.5", border_color = ft.colors.PRIMARY)
tf_interval = ft.TextField(label="Интервал проверки цены (в секундах)", value="5", border_color = ft.colors.PRIMARY)
sw_start_alarm = ft.Switch(label="Выкл. / Вкл.")
btn_upd_alarm = ft.CupertinoFilledButton(
content=ft.Text("Применить"),
opacity_on_click = 0.3,
on_click = proccess_upd_alarm,
)
con_alarm = ft.Container(
ft.Column([
tf_percent,
tf_interval,
sw_start_alarm,
btn_upd_alarm
]),
col={"sm": 6, "md": 6, "xl": 4},
padding = 10, alignment=ft.alignment.center, border = ft.border.all(1, ft.colors.PRIMARY)
)
rvd = ft.ResponsiveRow([con_det, con_alarm])
mPage = {
'main': {'container': ft.ListView(expand = 1, spacing = 10, padding = 10)},
'detailed': {'container': ft.ListView(expand = 1, spacing = 10, padding = 10)}
}
mPage['detailed']['container'].controls.append(det_title_con)
mPage['detailed']['container'].controls.append(rvd)
mPage['main']['container'].visible = True
mPage['detailed']['container'].visible = False
# Фильтр
search = ft.TextField(label="Поиск", on_change=filter_proccess)
searchContainer = ft.Container(search ,padding=10)
btnFilter = ft.IconButton(ft.icons.FILTER_ALT_OFF, icon_size=30,tooltip = "Отмена фильтра", on_click = clear_filter)
formSearch = ft.Row([searchContainer, btnFilter])
# Верхнее меню
page.appbar = ft.AppBar(
title=ft.Text("Bybit - торговые пары"),
center_title=False,
bgcolor=ft.colors.SURFACE_VARIANT,
actions=[formSearch],
)
# Нижнее меню
page.navigation_bar = ft.CupertinoNavigationBar(
bgcolor=ft.colors.SURFACE_VARIANT,
on_change=lambda e: bottom_menu(e),
destinations=[ft.NavigationBarDestination(icon=ft.icons.HOME_ROUNDED, label="Главная")]
)
page.add(mPage['main']['container'], mPage['detailed']['container'])
# Генерация главной страницы
ws = threading.Thread(target = main_page, daemon=True)
ws.start()
# Запуск ws
bb = bybit()
# Запуск обновления торговых пар
ws = threading.Thread(target = update_data_pair, daemon=True)
ws.start()
ft.app(target=main)
Заключение
В этом примере мы продемонстрировали, как можно реализовать получение системных уведомлений от приложения. Надеюсь, эта статья была полезной для вас.