diff --git a/bitcoin_safe/gui/qt/category_manager/category_list.py b/bitcoin_safe/gui/qt/category_manager/category_list.py index 23ede797..eee63df0 100644 --- a/bitcoin_safe/gui/qt/category_manager/category_list.py +++ b/bitcoin_safe/gui/qt/category_manager/category_list.py @@ -44,6 +44,7 @@ from bitcoin_safe.category_info import CategoryInfo from bitcoin_safe.config import UserConfig +from bitcoin_safe.fx import FX from bitcoin_safe.gui.qt.category_manager.category_core import CategoryCore from bitcoin_safe.gui.qt.drag_info import AddressDragInfo from bitcoin_safe.gui.qt.my_treeview import ( @@ -63,7 +64,7 @@ class CategoryList(MyTreeView[CategoryInfo]): - VERSION = "0.0.2" + VERSION = "0.0.3" known_classes = { **BaseSaveableClass.known_classes, MyTreeView.__name__: MyTreeView, @@ -79,6 +80,7 @@ class Columns(MyTreeView.Columns): CATEGORY = enum.auto() TXO_BALANCE = enum.auto() UTXO_BALANCE = enum.auto() + FIAT_UTXO_BALANCE = enum.auto() filter_columns = [ Columns.ADDRESS_COUNT, @@ -92,6 +94,7 @@ class Columns(MyTreeView.Columns): Columns.COLOR: Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter, Columns.TXO_BALANCE: Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter, Columns.UTXO_BALANCE: Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter, + Columns.FIAT_UTXO_BALANCE: Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter, } column_widths: dict[MyTreeView.Columns, int] = {} @@ -102,14 +105,17 @@ class Columns(MyTreeView.Columns): def cls_kwargs( signals: Signals, config: UserConfig, + fx: FX, ): return { "signals": signals, "config": config, + "fx": fx, } def __init__( self, + fx: FX, config: UserConfig, signals: Signals, category_core: CategoryCore | None = None, @@ -148,6 +154,7 @@ def __init__( _scroll_position=_scroll_position, ) self.category_core = category_core + self.fx = fx self.setTextElideMode(Qt.TextElideMode.ElideRight) self._source_model = MyStandardItemModel( @@ -171,6 +178,7 @@ def __init__( # signals self.signals.any_wallet_updated.connect(self.update_with_filter) + self.fx.signal_data_updated.connect(self.on_update_fx_rates) def set_category_core(self, category_core: CategoryCore | None): """Set category core.""" @@ -179,6 +187,7 @@ def set_category_core(self, category_core: CategoryCore | None): def get_headers(self) -> dict[MyTreeView.Columns, QStandardItem]: """Get headers.""" + currency_symbol = self.fx.get_currency_symbol() return { self.Columns.ADDRESS_COUNT: header_item(self.tr("Addresses")), self.Columns.UTXO_COUNT: header_item( @@ -193,8 +202,13 @@ def get_headers(self) -> dict[MyTreeView.Columns, QStandardItem]: self.tr("Received"), tooltip=self.tr("Total received (possibly already spent again)") ), self.Columns.UTXO_BALANCE: header_item(self.tr("Balance"), tooltip=self.tr("Current Balance")), + self.Columns.FIAT_UTXO_BALANCE: header_item(currency_symbol + " " + self.tr("Value")), } + def on_update_fx_rates(self) -> None: + """Update fiat values after FX data changes.""" + self.update_with_filter(UpdateFilter(refresh_all=True, reason=UpdateFilterReason.NewFxRates)) + @time_logger def update_with_filter(self, update_filter: UpdateFilter) -> None: """Update with filter.""" @@ -294,6 +308,20 @@ def refresh_row(self, key: Any, row: int): items[column].setData(balance, MyItemDataRole.ROLE_SORT_ORDER) items[column].setData(balance, MyItemDataRole.ROLE_CLIPBOARD_DATA) + fiat_value = self.fx.btc_to_fiat(key.utxo_balance) + fiat_balance_str = ( + self.fx.fiat_to_str(fiat_value, use_currency_symbol=False) if fiat_value is not None else "" + ) + fiat_color = ( + self.palette().color(self.foregroundRole()) + if key.utxo_balance + else QColor(255 // 2, 255 // 2, 255 // 2) + ) + items[self.Columns.FIAT_UTXO_BALANCE].setText(fiat_balance_str) + items[self.Columns.FIAT_UTXO_BALANCE].setForeground(QBrush(fiat_color)) + items[self.Columns.FIAT_UTXO_BALANCE].setData(fiat_value, MyItemDataRole.ROLE_SORT_ORDER) + items[self.Columns.FIAT_UTXO_BALANCE].setData(fiat_value, MyItemDataRole.ROLE_CLIPBOARD_DATA) + color = category_color(key.category) items[self.Columns.COLOR].setText(color.name()) items[self.Columns.COLOR].setData(color.name(), MyItemDataRole.ROLE_CLIPBOARD_DATA) @@ -393,7 +421,7 @@ def get_selected_values(self) -> list[int]: @classmethod def from_dump_migration(cls, dct: dict[str, Any]) -> dict[str, Any]: """From dump migration.""" - if fast_version(str(dct["VERSION"])) < fast_version("0.0.2"): + if fast_version(str(dct["VERSION"])) < fast_version("0.0.3"): if "hidden_columns_enum" in dct: del dct["hidden_columns_enum"] diff --git a/bitcoin_safe/gui/qt/category_manager/category_manager.py b/bitcoin_safe/gui/qt/category_manager/category_manager.py index f8bc872c..9b1cf36a 100644 --- a/bitcoin_safe/gui/qt/category_manager/category_manager.py +++ b/bitcoin_safe/gui/qt/category_manager/category_manager.py @@ -37,6 +37,7 @@ from PyQt6.QtWidgets import QDialogButtonBox, QPushButton, QVBoxLayout, QWidget from bitcoin_safe.config import UserConfig +from bitcoin_safe.fx import FX from bitcoin_safe.gui.qt.category_manager.category_core import ( CategoryCore, prompt_merge_category, @@ -57,7 +58,7 @@ class CategoryManager(QWidget): - def __init__(self, config: UserConfig, category_core: CategoryCore, wallet_id: str): + def __init__(self, config: UserConfig, fx: FX, category_core: CategoryCore, wallet_id: str): """Initialize instance.""" super().__init__() self.setWindowIcon(svg_tools.get_QIcon("logo.svg")) @@ -68,12 +69,14 @@ def __init__(self, config: UserConfig, category_core: CategoryCore, wallet_id: s self.category_list = CategoryList( config=config, + fx=fx, category_core=category_core, signals=category_core.signals, hidden_columns_enum=[ CategoryList.Columns.COLOR, CategoryList.Columns.TXO_BALANCE, CategoryList.Columns.UTXO_BALANCE, + CategoryList.Columns.FIAT_UTXO_BALANCE, CategoryList.Columns.UTXO_COUNT, CategoryList.Columns.TXO_COUNT, ], diff --git a/bitcoin_safe/gui/qt/qt_wallet.py b/bitcoin_safe/gui/qt/qt_wallet.py index e99434ce..bb3e514d 100644 --- a/bitcoin_safe/gui/qt/qt_wallet.py +++ b/bitcoin_safe/gui/qt/qt_wallet.py @@ -493,6 +493,7 @@ def from_file( CategoryList.__name__: CategoryList.cls_kwargs( signals=wallet_functions.signals, config=config, + fx=fx, ), } class_kwargs.update( @@ -1590,7 +1591,7 @@ def _create_addresses_tab( ) -> tuple[SearchableTab, AddressList, SidebarNode, CategoryManager, AddressListWithToolbar]: """Create addresses tab.""" category_manager = CategoryManager( - config=self.config, category_core=self.category_core, wallet_id=self.wallet.id + config=self.config, fx=self.fx, category_core=self.category_core, wallet_id=self.wallet.id ) if address_list_with_toolbar: diff --git a/bitcoin_safe/gui/qt/ui_tx/ui_tx_creator.py b/bitcoin_safe/gui/qt/ui_tx/ui_tx_creator.py index 35ff933c..63200a1c 100644 --- a/bitcoin_safe/gui/qt/ui_tx/ui_tx_creator.py +++ b/bitcoin_safe/gui/qt/ui_tx/ui_tx_creator.py @@ -177,6 +177,7 @@ def __init__( else: self.category_list = CategoryList( config=self.config, + fx=self.fx, category_core=category_core, signals=self.signals, )