Skip to content

Commit

Permalink
Merge pull request #2 from meherett/axiom90/desktop
Browse files Browse the repository at this point in the history
Desktop client app
  • Loading branch information
meherett authored Nov 1, 2024
2 parents b899486 + 46b5bc7 commit 2d7a5c0
Show file tree
Hide file tree
Showing 24 changed files with 4,217 additions and 7 deletions.
21 changes: 16 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
# Bitcoin Improvement Proposal - 0038 / BIP38
<h1 align="center" style="border-bottom: none">
<img height="100" alt="BIP38" src="docs/static/svg/bip38.svg"><br>Bitcoin Improvement Proposal - 0038
</h1>

<p align="center">
<a href="https://github.com/meherett/python-bip38/releases" target="_blank">Releases</a> · <a href="https://talonlab.gitbook.io/bip38/manual" target="_blank">Manual</a> · <a href="https://bip38.readthedocs.io" target="_blank">API Docs</a> · <a href="#donations">Donation</a>
</p>

<div align="center">

[![Build Status](https://img.shields.io/github/actions/workflow/status/meherett/python-bip38/build.yml)](https://github.com/meherett/python-bip38/actions/workflows/build.yml)
[![PyPI Version](https://img.shields.io/pypi/v/bip38.svg?color=blue)](https://pypi.org/project/bip38)
Expand All @@ -7,10 +15,14 @@
[![PyPI Python Version](https://img.shields.io/pypi/pyversions/bip38.svg)](https://pypi.org/project/bip38)
[![Coverage Status](https://coveralls.io/repos/github/meherett/python-bip38/badge.svg?branch=master)](https://coveralls.io/github/meherett/python-bip38)

</div>

A Python library for the implementation of Bitcoin Improvement Proposal - 0038 / (BIP38) protocol.
This library supports both [No EC-multiply](https://github.com/bitcoin/bips/blob/master/bip-0038.mediawiki#encryption-when-ec-multiply-flag-is-not-used) and [EC-multiply](https://github.com/bitcoin/bips/blob/master/bip-0038.mediawiki#encryption-when-ec-multiply-mode-is-used) modes and is compatible with over 150+ cryptocurrencies.
It's specifically tailored for Pay-to-PubKey-Hash (P2PKH) address types.

![Desktop Application](docs/static/gif/bip38.gif)

For more info see the [Passphrase-protected private key - BIP38](https://en.bitcoin.it/wiki/BIP_0038) spec.

## Installation
Expand Down Expand Up @@ -417,7 +429,7 @@ BIP38 Decrypted: {
To get started, just fork this repo, clone it locally, and run:

```
pip install -e .[tests,docs]
pip install -e .[desktop,tests,docs]
```

## Testing
Expand Down Expand Up @@ -450,9 +462,8 @@ This module supports more than 150+ cryptocurrencies, including the following:

Buy me a coffee if You found this tool helpful:

- **Bitcoin** - 12uaGVdX1t86FXLQ4yYPrRQDCK7xGGu82r
- **Ethereum / Tether** - 0xCCAad7A87fd81553d0F93F743Fb4Fc6B213b228B
- **Bitcoin / Ethereum / Tether** - With Unstoppable [hd.wallet](https://ud.me/hd.wallet)
- **Bitcoin** - 16c7ajUwHEMaafrceuYSrd35SDjmfVdjoS
- **Ethereum / ERC20** - 0xD3cbCB0B6F82A03C715D665b72dC44CEf54e6D9B

Thank you very much for your support.

Expand Down
89 changes: 89 additions & 0 deletions build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/usr/bin/env python3

# Copyright © 2024, Eyoel Tadesse <[email protected]>
# Distributed under the MIT software license, see the accompanying
# file COPYING or https://opensource.org/license/mit

from cx_Freeze import setup, Executable

import platform

from desktop.info import __version__ as version
from bip38.info import __author__ as author

app_name = "BIP38"

if platform.system() == "Windows":
icon_path = "desktop/ui/images/icon/icon.ico"
else:
icon_path = "desktop/ui/images/svg/logo.svg"

msi_shortcut_table = [
(
"DesktopShortcut", # Shortcut
"DesktopFolder", # Directory_
app_name, # Name that will be show on the link
"TARGETDIR", # Component_
f"[TARGETDIR]{app_name}.exe", # Target exe to exexute
None, # Arguments
None, # Description
None, # Hotkey
None, # Icon
None, # IconIndex
None, # ShowCmd
'TARGETDIR' # WkDir
)
]

msi_directory_table = [
("ProgramMenuFolder", "TARGETDIR", "."),
("BIP38Menu", "ProgramMenuFolder", "BIP38~1|BIP38")
]

msi_data = {
"Shortcut": msi_shortcut_table,
"Directory": msi_directory_table
}

bdist_msi_opt = {
"add_to_path": False,
"data": msi_data,
"initial_target_dir": f"[ProgramFiles64Folder]\\{app_name}",
"install_icon": icon_path,
"upgrade_code": "{E4A369F6-FC76-3013-A420-3BB9B370711C}"
}

build_exe_opt = {
"packages": ["_scrypt"],
"excludes": ["tkinter", "PySide6.QtNetwork", "PySide6.translations"],
"bin_excludes": [
"Qt6Network.dll", "Qt6OpenGL.dll", "Qt6Pdf.dll", "Qt6Qml.dll", "Qt6QmlMeta.dll",
"Qt6QmlModels.dll", "Qt6QmlWorkerScript.dll", "Qt6Quick.dll", "Qt6VirtualKeyboard.dll",
"qgif.dll", "qicns.dll", "qjpeg.dll", "qpdf.dll", "qtga.dll", "qtiff.dll", "qwbmp.dll", "qwebp.dll",
"qtvirtualkeyboardplugin.dll", "qtuiotouchplugin.dll", "qdirect2d.dll", "qoffscreen.dll", "qminimal.dll"
],
"include_msvcr": True
}

executables = [
Executable(
"launch.py",
base="gui",
icon=icon_path,
target_name=app_name,
shortcut_name=app_name,
shortcut_dir="BIP38Menu",
copyright=f"Copyright (C) 2024 {app_name}"
)
]

setup(
name=app_name,
author=author,
version=version,
executables=executables,
options={
"build_exe": build_exe_opt,
"bdist_msi": bdist_msi_opt
}
)
5 changes: 5 additions & 0 deletions desktop/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env python3

# Copyright © 2024, Eyoel Tadesse <[email protected]>
# Distributed under the MIT software license, see the accompanying
# file COPYING or https://opensource.org/license/mit
115 changes: 115 additions & 0 deletions desktop/core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#!/usr/bin/env python3

# Copyright © 2024, Eyoel Tadesse <[email protected]>
# Distributed under the MIT software license, see the accompanying
# file COPYING or https://opensource.org/license/mit

from typing import Optional

import json

from PySide6.QtWidgets import (
QMainWindow, QWidget, QStackedWidget,
QFrame, QComboBox
)
from PySide6.QtCore import (
Qt, QFileSystemWatcher, Signal, QSize
)
from PySide6.QtGui import (
QFontDatabase, QIcon
)

from desktop.utils import (
resolve_path, put_svg
)
from desktop.ui.ui_bip38 import Ui_MainWindow
from desktop.info import __version__ as desktop_ver
from bip38.info import __version__ as library_ver

class Application(QMainWindow):
_instance: Optional['Application'] = None
ui: Ui_MainWindow = None
theme_watcher: QFileSystemWatcher = None
resized: Signal = Signal(object)

def __new__(cls, *args, **kwargs) -> 'Application':
"""
Create a new instance if not already created, implementing the Singleton pattern.
"""
if not cls._instance:
cls._instance = super().__new__(cls)
return cls._instance

def __init__(self) -> None:
"""
Initialize the Application instance if not already initialized.
"""
if not hasattr(self, 'initialized'):
super().__init__()
self.initialize()
self.initialized = True

@classmethod
def instance(cls) -> 'Application':
"""
Get the singleton instance of the Application.
:return: The singleton instance of Application.
"""
if not cls._instance:
cls._instance = cls()
return cls._instance

def initialize(self) -> None:
"""
Perform initialization tasks for the application, such as setting up the UI and loading resources.
"""
self.ui = Ui_MainWindow()
self.ui.setupUi(self)

self.detached_window = None

self.setWindowTitle("Bitcoin Improvement Proposal - 0038")
self.bip38_icon = QIcon(resolve_path("desktop/ui/images/icon/icon.ico"))
self.setWindowIcon(self.bip38_icon)

put_svg(
self.ui.bip38LogoHLayout,
resolve_path("desktop/ui/images/svg/full-logo.svg"),
84,
44
)
self.ui.bip38LogoHLayout.setContentsMargins(0, 0, 5, 0)

css_path = resolve_path("desktop/ui/css/theme.css")
self.theme_watcher = QFileSystemWatcher([css_path])
self.theme_watcher.fileChanged.connect(lambda: self.load_stylesheet(css_path))
QFontDatabase.addApplicationFont(resolve_path("desktop/ui/font/HDWallet.ttf"))
self.load_stylesheet(css_path)

info = {
"library": library_ver,
"desktop": desktop_ver
}
self.ui.outputQTextEdit.setPlaceholderText(json.dumps(info, indent=4))

# remove combo box shadow
for qComboBox in self.findChildren(QComboBox):
qComboBox.findChild(QFrame).setWindowFlags(Qt.Popup | Qt.NoDropShadowWindowHint)

def load_stylesheet(self, path: str) -> None:
"""
Load and apply a stylesheet from the specified path.
:param path: The path to the stylesheet file.
"""
try:
with open(path, 'r', encoding='utf-8') as style_file:
stylesheet = style_file.read()
self.setStyleSheet(stylesheet)
except Exception as e:
print(f"Failed to load stylesheet: {e}")

def resizeEvent(self, event) -> None:
super().resizeEvent(event)
self.resized.emit(event)
7 changes: 7 additions & 0 deletions desktop/info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env python3

# Copyright © 2024, Eyoel Tadesse <[email protected]>
# Distributed under the MIT software license, see the accompanying
# file COPYING or https://opensource.org/license/mit

__version__: str = "v0.1.0"
Loading

0 comments on commit 2d7a5c0

Please sign in to comment.