Skip to content

Instantly share code, notes, and snippets.

@hezicyan
Last active January 10, 2022 09:47
Show Gist options
  • Save hezicyan/3f84a70edf01e2dd3bbd3718ad21b70c to your computer and use it in GitHub Desktop.
Save hezicyan/3f84a70edf01e2dd3bbd3718ad21b70c to your computer and use it in GitHub Desktop.
A base converter supporting fractions, negative number and even negative base, and it has commands and graphical user interface.
#!/usr/bin/env python3.8
# -*- coding: utf-8 -*-
__all__ = ['mod', 'to_decimal', 'to_base', 'convert', 'DEFAULT_TABLE']
from math import ceil, floor
from string import ascii_lowercase, ascii_uppercase, digits
from typing import Tuple, Union
DEFAULT_TABLE = digits + ascii_uppercase + ascii_lowercase
def mod(dividend: int, divisor: int) -> Tuple[int, int]:
quotient, remainder = dividend // divisor, dividend % divisor
if remainder < 0:
quotient += 1
remainder -= divisor
return quotient, remainder
def to_decimal(number: str, base: Union[int, str], table: str = DEFAULT_TABLE) -> Union[int, float]:
base = int(base)
position = number.find('.')
if number[0] == '-':
negative = True
number = number[1:]
else:
negative = False
if position == -1:
result, index = 0, 1
for digit in number[::-1]:
result += index * table.find(digit)
index *= base
else:
integer, fraction = number.split('.')
result, index = to_decimal(integer, base, table), 1.0
for digit in fraction:
index /= base
result += index * table.find(digit)
return -result if negative else result
def to_base(number: Union[int, float], base: Union[int, str], table: str = DEFAULT_TABLE) -> str:
try:
base = int(base)
except ValueError:
pass
if number == 0:
return table[0] if isinstance(number, int) else '{}.{}'.format(table[0], table[0])
result_numbers = []
if number < 0 < base:
negative = True
number = abs(number)
else:
negative = False
if isinstance(number, int):
while number != 0:
number, remainder = mod(number, base)
result_numbers.insert(0, table[remainder])
else:
integer = ceil(number) if base < 0 else floor(number)
fraction = number - integer
result_numbers.append(to_base(integer, base, table) + '.')
times = 0
while fraction != 0 and times < 100:
fraction *= base
integer = ceil(fraction) if base < 0 else floor(fraction)
fraction -= integer
result_numbers.append(table[integer])
times += 1
if times == 0:
result_numbers.append(table[0])
if negative:
result_numbers.insert(0, '-')
return ''.join(result_numbers)
def convert(number: str, original_base: Union[int, str] = 0, result_base: Union[int, str] = 10,
table: str = DEFAULT_TABLE) -> str:
original_base, result_base = int(original_base), int(result_base)
if original_base == result_base:
return number
return to_base(to_decimal(number, original_base, table), result_base, table)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtCore import pyqtSignal
from MainWindow import Ui_MainWindow
import sys
from converter import convert, DEFAULT_TABLE
class MainWindow(QMainWindow, Ui_MainWindow):
number_changed = pyqtSignal(str, name='numberChanged')
result_changed = pyqtSignal(str, name='resultChanged')
def __init__(self, number: str = None, original_base: int = 10, result_base: int = 10, table: str = None):
super().__init__(None)
self.setupUi(self)
self.computing = False
self.number_changed['QString'].connect(self.resultNumberLineEdit.setText)
self.result_changed['QString'].connect(self.originalNumberLineEdit.setText)
self.originalNumberLineEdit.textEdited.connect(lambda: self.compute(True))
self.resultNumberLineEdit.textEdited.connect(lambda: self.compute(False))
self.originalBaseLineEdit.setText(str(original_base))
self.resultBaseLineEdit.setText(str(result_base))
if table and table != DEFAULT_TABLE:
self.usingTableCheckBox.setChecked(True)
self.tableLabel.setEnabled(True)
self.tableLineEdit.setEnabled(True)
self.tableLineEdit.setText(table)
if number:
self.originalNumberLineEdit.setText(number)
self.compute(True)
def compute(self, from_number: bool):
if from_number:
number = self.originalNumberLineEdit.text()
original_base = self.originalBaseLineEdit.text()
result_base = self.resultBaseLineEdit.text()
else:
number = self.resultNumberLineEdit.text()
original_base = self.resultBaseLineEdit.text()
result_base = self.originalBaseLineEdit.text()
table = self.tableLineEdit.text() if self.usingTableCheckBox.isChecked() else DEFAULT_TABLE
try:
result = convert(number, original_base, result_base, table)
except (ValueError, IndexError):
result = ''
if from_number:
self.number_changed[str].emit(result)
else:
self.result_changed[str].emit(result)
def start(number: str = None, original_base: int = 10, result_base: int = 10, table: str = None):
application = QApplication([])
main_window = MainWindow(number, original_base, result_base, table)
main_window.show()
sys.exit(application.exec())
if __name__ == '__main__':
start()
#!/usr/bin/env python3.8
# -*- coding: utf-8 -*-
from typing import Optional
import click
from converter import DEFAULT_TABLE, convert
from gui import start as gui_start
@click.command()
@click.option('-g', '--gui', is_flag=True, help='Open Graphical User Interface.')
@click.option('-n', '--number', type=click.STRING, help='Set the number.')
@click.option('-o', '--original-base', type=click.INT, help='Set original base.')
@click.option('-r', '--result-base', type=click.INT, help='Set result base.')
@click.option('-t', '--table', type=click.STRING, help='Set the table.')
@click.argument('others', nargs=-1)
def process(gui: bool,
number: Optional[str], original_base: Optional[int], result_base: Optional[int], table: Optional[str],
others: tuple):
for arg in others:
if number is None:
number = arg
elif original_base is None:
original_base = arg
elif result_base is None:
result_base = arg
elif table is None:
table = arg
original_base = original_base or 10
result_base = result_base or 10
table = table or DEFAULT_TABLE
if not gui:
click.echo(convert(number, original_base, result_base, table))
else:
gui_start(number, original_base, result_base, table)
if __name__ == '__main__':
process()
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file '.\MainWindow.ui'
#
# Created by: PyQt5 UI code generator 5.14.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(294, 222)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.formLayout = QtWidgets.QFormLayout()
self.formLayout.setObjectName("formLayout")
self.originalNumberLabel = QtWidgets.QLabel(self.centralwidget)
self.originalNumberLabel.setObjectName("originalNumberLabel")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.originalNumberLabel)
self.originalNumberLineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.originalNumberLineEdit.setObjectName("originalNumberLineEdit")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.originalNumberLineEdit)
self.originalBaseLabel = QtWidgets.QLabel(self.centralwidget)
self.originalBaseLabel.setObjectName("originalBaseLabel")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.originalBaseLabel)
self.originalBaseLineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.originalBaseLineEdit.setObjectName("originalBaseLineEdit")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.originalBaseLineEdit)
self.resultBaseLabel = QtWidgets.QLabel(self.centralwidget)
self.resultBaseLabel.setObjectName("resultBaseLabel")
self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.resultBaseLabel)
self.resultBaseLineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.resultBaseLineEdit.setObjectName("resultBaseLineEdit")
self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.resultBaseLineEdit)
self.resultNumberLabel = QtWidgets.QLabel(self.centralwidget)
self.resultNumberLabel.setObjectName("resultNumberLabel")
self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.resultNumberLabel)
self.resultNumberLineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.resultNumberLineEdit.setObjectName("resultNumberLineEdit")
self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.resultNumberLineEdit)
self.usingTableLabel = QtWidgets.QLabel(self.centralwidget)
self.usingTableLabel.setObjectName("usingTableLabel")
self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.usingTableLabel)
self.usingTableCheckBox = QtWidgets.QCheckBox(self.centralwidget)
self.usingTableCheckBox.setObjectName("usingTableCheckBox")
self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.usingTableCheckBox)
self.tableLabel = QtWidgets.QLabel(self.centralwidget)
self.tableLabel.setEnabled(False)
self.tableLabel.setObjectName("tableLabel")
self.formLayout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.tableLabel)
self.tableLineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.tableLineEdit.setEnabled(False)
self.tableLineEdit.setObjectName("tableLineEdit")
self.formLayout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.tableLineEdit)
self.gridLayout.addLayout(self.formLayout, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 294, 26))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
self.usingTableCheckBox.clicked['bool'].connect(self.tableLabel.setEnabled)
self.originalBaseLineEdit.textChanged['QString'].connect(self.originalNumberLineEdit.clear)
self.originalBaseLineEdit.textChanged['QString'].connect(self.resultNumberLineEdit.clear)
self.resultBaseLineEdit.textChanged['QString'].connect(self.originalNumberLineEdit.clear)
self.resultBaseLineEdit.textChanged['QString'].connect(self.resultNumberLineEdit.clear)
self.usingTableCheckBox.clicked['bool'].connect(self.tableLineEdit.setEnabled)
self.tableLineEdit.textChanged['QString'].connect(self.originalNumberLineEdit.clear)
self.tableLineEdit.textChanged['QString'].connect(self.resultNumberLineEdit.clear)
self.usingTableCheckBox.toggled['bool'].connect(self.originalNumberLineEdit.clear)
self.usingTableCheckBox.toggled['bool'].connect(self.resultNumberLineEdit.clear)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "Base Converter"))
self.originalNumberLabel.setText(_translate("MainWindow", "Original Number"))
self.originalBaseLabel.setText(_translate("MainWindow", "Original Base"))
self.resultBaseLabel.setText(_translate("MainWindow", "Result Base"))
self.resultNumberLabel.setText(_translate("MainWindow", "Result Number"))
self.usingTableLabel.setText(_translate("MainWindow", "Using Table"))
self.tableLabel.setText(_translate("MainWindow", "Table"))
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>294</width>
<height>222</height>
</rect>
</property>
<property name="windowTitle">
<string>Base Converter</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="originalNumberLabel">
<property name="text">
<string>Original Number</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="originalNumberLineEdit"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="originalBaseLabel">
<property name="text">
<string>Original Base</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="originalBaseLineEdit"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="resultBaseLabel">
<property name="text">
<string>Result Base</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="resultBaseLineEdit"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="resultNumberLabel">
<property name="text">
<string>Result Number</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="resultNumberLineEdit"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="usingTableLabel">
<property name="text">
<string>Using Table</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="usingTableCheckBox"/>
</item>
<item row="5" column="0">
<widget class="QLabel" name="tableLabel">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Table</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="tableLineEdit">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>294</width>
<height>26</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections>
<connection>
<sender>usingTableCheckBox</sender>
<signal>clicked(bool)</signal>
<receiver>tableLabel</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>143</x>
<y>155</y>
</hint>
<hint type="destinationlabel">
<x>40</x>
<y>177</y>
</hint>
</hints>
</connection>
<connection>
<sender>originalBaseLineEdit</sender>
<signal>textChanged(QString)</signal>
<receiver>originalNumberLineEdit</receiver>
<slot>clear()</slot>
<hints>
<hint type="sourcelabel">
<x>143</x>
<y>75</y>
</hint>
<hint type="destinationlabel">
<x>144</x>
<y>44</y>
</hint>
</hints>
</connection>
<connection>
<sender>originalBaseLineEdit</sender>
<signal>textChanged(QString)</signal>
<receiver>resultNumberLineEdit</receiver>
<slot>clear()</slot>
<hints>
<hint type="sourcelabel">
<x>276</x>
<y>76</y>
</hint>
<hint type="destinationlabel">
<x>275</x>
<y>129</y>
</hint>
</hints>
</connection>
<connection>
<sender>resultBaseLineEdit</sender>
<signal>textChanged(QString)</signal>
<receiver>originalNumberLineEdit</receiver>
<slot>clear()</slot>
<hints>
<hint type="sourcelabel">
<x>168</x>
<y>97</y>
</hint>
<hint type="destinationlabel">
<x>166</x>
<y>42</y>
</hint>
</hints>
</connection>
<connection>
<sender>resultBaseLineEdit</sender>
<signal>textChanged(QString)</signal>
<receiver>resultNumberLineEdit</receiver>
<slot>clear()</slot>
<hints>
<hint type="sourcelabel">
<x>249</x>
<y>101</y>
</hint>
<hint type="destinationlabel">
<x>250</x>
<y>128</y>
</hint>
</hints>
</connection>
<connection>
<sender>usingTableCheckBox</sender>
<signal>clicked(bool)</signal>
<receiver>tableLineEdit</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>273</x>
<y>151</y>
</hint>
<hint type="destinationlabel">
<x>274</x>
<y>171</y>
</hint>
</hints>
</connection>
<connection>
<sender>tableLineEdit</sender>
<signal>textChanged(QString)</signal>
<receiver>originalNumberLineEdit</receiver>
<slot>clear()</slot>
<hints>
<hint type="sourcelabel">
<x>184</x>
<y>170</y>
</hint>
<hint type="destinationlabel">
<x>181</x>
<y>46</y>
</hint>
</hints>
</connection>
<connection>
<sender>tableLineEdit</sender>
<signal>textChanged(QString)</signal>
<receiver>resultNumberLineEdit</receiver>
<slot>clear()</slot>
<hints>
<hint type="sourcelabel">
<x>230</x>
<y>168</y>
</hint>
<hint type="destinationlabel">
<x>231</x>
<y>121</y>
</hint>
</hints>
</connection>
<connection>
<sender>usingTableCheckBox</sender>
<signal>toggled(bool)</signal>
<receiver>originalNumberLineEdit</receiver>
<slot>clear()</slot>
<hints>
<hint type="sourcelabel">
<x>194</x>
<y>148</y>
</hint>
<hint type="destinationlabel">
<x>193</x>
<y>51</y>
</hint>
</hints>
</connection>
<connection>
<sender>usingTableCheckBox</sender>
<signal>toggled(bool)</signal>
<receiver>resultNumberLineEdit</receiver>
<slot>clear()</slot>
<hints>
<hint type="sourcelabel">
<x>214</x>
<y>146</y>
</hint>
<hint type="destinationlabel">
<x>214</x>
<y>123</y>
</hint>
</hints>
</connection>
</connections>
</ui>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment