Created
April 23, 2018 21:42
-
-
Save Sebastian-Nielsen/eb1b460f5ea2f8f599010d5beeb80d97 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import tkinter as tk | |
from tkinter import * | |
from tkinter import messagebox | |
from tkinter.ttk import Combobox, Progressbar, Separator, Style | |
import tkinter.font as font | |
import sys, os, re, platform, bisect, pyperclip, requests, logging, webbrowser, random, getpass, locale, csv | |
from random import randint | |
from bs4 import BeautifulSoup | |
from time import sleep | |
from itertools import cycle | |
from collections import namedtuple, OrderedDict | |
from PIL import ImageTk, Image | |
# Change the locale formatting | |
locale.setlocale(locale.LC_ALL, '') # danish digit grouping TODO Slet alt med digit grouping at gøre | |
# from settings import * | |
###########################Currentpath = sys.argv[0].rsplit('/', 1)[0] + '/' | |
# from openpyxl.utils import get_column_letter, column_index_from_string | |
# from openpyxl.styles import Color, PatternFill, Font, Border | |
# from openpyxl.styles import colors | |
# from openpyxl.cell import Cell | |
# import openpyxl | |
img_data = {'logo': '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rwqPOmCmOoeISDyqH5vXdLpbRESkRVRAF5GYEBjzLPY+9H9id33Gqv+aIHgYmOY6iIiIiIiIiMS9BUAImIVtz/4x9sRmU7eRlfWVeetdhxQRERER8YkK6CISE+or85Z3G5A/GtgfuMh1nlZYAxQApbMqcme6DiMiIiIiIm3M0AHbKlYk0lYC32FbsX8DzAW+I2Ah8H34974BFqpYLiIiIuIZQwB0xL/PCu2x11zE8oHGVlMBXURiRn1l3vxuA/Lvwt7/eR6x8xr2JTAKGFNfmbfYdRgREREREXFiDbbIuXf418Z1IHEuwN6duyH8awM08vOW62s3+b012IL5amAZ8AO2cN50F/A84Mv6sXkbmp1CRERERFxqxM7v5mML1j5seGzHxqt/1rkO40KsFJ9ERACor8yb121A/t3YIvqfsDuzfNWIXcB4sL4yb4TrMCIiIiIi4tRcoBzYjY2FUklsTQuTa9j4NbEBWxxfCawK/7gi/PMV2IL5gvqxeatchxcRERGRiFiLvW7nCeAg7PzPtQ7YeWrTRs2EowK6iMSi2cCj2Hbuqa7DbMVHwP3As66DiIiIiIiIc18Aw7BFU5EmZpOx6a8bNxkN4d9rqB+b1+A6sIiIiIhE1BpgBraI3hF/Ntoa7Dx0hesgLgSuA4iItEa3AfkBkAf8C1tI981/gMeA/9RXqnWeiIiIiIiIiIiIiIhILFABXURiVrcB+TsDVwE3Al1c5wlbBTwDDKmvzKt2HUZERERERERERERERESar73rACIirbWg/vn1XZP++in2DsFjgB0dR5oPVAK311fmfez6+REREREREREREREREZGW0b1bIhLrvgWGA285zvEN8AjwADDPcRYRERERERERERERERFpBbVwF5G40G1AfgowEujl4D//EXA39r7zha6fCxEREREREREREREREWkdFdBFJG50G5D/R+wp8F+04X/2XeDB+sq8510/fhEREREREREREREREdk+ugNdROLGgvrnP+2a9NcVQF+gS5T/c8uBN4Bb6yvzXnf92EVERERERERERERERGT76Q50EYk3r2BbuS+I4n9jNfAUcD0ww/UDFhERERERERERERERkchQC3cRiTvdBuQfCdwMDAQ6RPiPXwMMA/LrK/M+df1YRUREREREREREREREJHLUwl1E4s6C+ud/6Jr014+Aw4BjI/hHzwaGAo/VV+Z94/pxioiIiIiIiIiIiIiISGTpBLqIxK1uA/JPBh4GTtjOP6oBmAQU1Ffmlbt+XCIiIiIiIiIiIiIiIhIdugNdROLZe9gT49tzWnwd8BpwCzDB9QMSERERERERERERERGR6FELdxGJWwvqn6dr0l+/ANYDfYEdWvHHPI0tnlfXV+atd/2YREREREREREREREREJHrUwl1E4l63Afm7An8HrgZ2bub/bBVQCTxSX5n3gevHICIiIiIiIiIiIiIiItGnE+giEvcW1D+/tmvSX2uAPYFuQMdt/E8+Ax4D7qqvzPvadX4RERERERERERERERFpGyqgi0hCWFD//JquSX+dCxwKHA2028y/tgH4FHgAGF5fmbfadW4RERERERERERERERFpO+22/48QEYkZHwAlGD7GwGbG+xhuwTC+vjJvg+uwIiIiIiIiIiIiIiIi0rZ0B7qIJJRu/fM7AenAHcD+m/yjZ4Ci+rF5L7jOKCIiIiIiIiIiIiIiIm6ohbuIJJQF9c83dO3211nAeqAvsA4oAO6rH5s32XU+ERERERERERERERERcUct3EUk4dSPzVtnMOMM5mWDeclghgGzXecSERERERERERERERERt/4P7IuKh4Of/CYAAAAASUVORK5CYII=', | |
'fullscreen':'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAAM1BMVEX///8lb7tFmN5Fmd4AAAAlb7tbtvlVrvFVrfFNo+dFmd9Fmd4+kNY3h884h88xgMj///+0+7hKAAAABXRSTlMAAAAAGaYArnEAAAABYktHRACIBR1IAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH2wUSFikA2jAWAQAAAGtJREFUGNONj0sSgDAIQ0GNLfR3/9tawLrQjVll3pAARB/BxETsxsCZwAYY+XSQRWOiSHKAUts06FUjwujDwGjTkmV5lbJ1/ZN3PRFsaFHaoxTQ2g20WmJtkhIRlRynZ+y+HilOv39azx0vXaiMA1kuoX+tAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE2LTA5LTE3VDE1OjIzOjE1KzA4OjAwxMn81QAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxMS0wNS0xOFQyMjo0MTowMCswODowMM2uG7AAAABNdEVYdHNvZnR3YXJlAEltYWdlTWFnaWNrIDcuMC4xLTYgUTE2IHg4Nl82NCAyMDE2LTA5LTE3IGh0dHA6Ly93d3cuaW1hZ2VtYWdpY2sub3Jn3dmlTgAAABh0RVh0VGh1bWI6OkRvY3VtZW50OjpQYWdlcwAxp/+7LwAAABd0RVh0VGh1bWI6OkltYWdlOjpIZWlnaHQAMTYdr15vAAAAFnRFWHRUaHVtYjo6SW1hZ2U6OldpZHRoADE25QCe4gAAABl0RVh0VGh1bWI6Ok1pbWV0eXBlAGltYWdlL3BuZz+yVk4AAAAXdEVYdFRodW1iOjpNVGltZQAxMzA1NzI5NjYwUrri7QAAABJ0RVh0VGh1bWI6OlNpemUAMy4xMktCKchJ5wAAAFt0RVh0VGh1bWI6OlVSSQBmaWxlOi8vL2hvbWUvd3d3cm9vdC9zaXRlL3d3dy5lYXN5aWNvbi5uZXQvY2RuLWltZy5lYXN5aWNvbi5jbi9zcmMvNDAzLzQwMzE1LnBuZzwbqXIAAAAASUVORK5CYII=','right_blueArrow': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAFzUkdCAK7OHOkAAAAEZ0FNQQAAsY8L/GEFAAAACXBIWXMAAAsSAAALEgHS3X78AAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1MzmNZGAwAAABV0RVh0Q3JlYXRpb24gVGltZQAyLzE3LzA4IJyqWAAABBF0RVh0WE1MOmNvbS5hZG9iZS54bXAAPD94cGFja2V0IGJlZ2luPSIgICAiIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNC4xLWMwMzQgNDYuMjcyOTc2LCBTYXQgSmFuIDI3IDIwMDcgMjI6MTE6NDEgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhhcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyI+CiAgICAgICAgIDx4YXA6Q3JlYXRvclRvb2w+QWRvYmUgRmlyZXdvcmtzIENTMzwveGFwOkNyZWF0b3JUb29sPgogICAgICAgICA8eGFwOkNyZWF0ZURhdGU+MjAwOC0wMi0xN1QwMjozNjo0NVo8L3hhcDpDcmVhdGVEYXRlPgogICAgICAgICA8eGFwOk1vZGlmeURhdGU+MjAwOC0wMy0yNFQxOTowMDo0Mlo8L3hhcDpNb2RpZnlEYXRlPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIj4KICAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9wbmc8L2RjOmZvcm1hdD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgNR1SZAAAAI1JREFUOE9j/P//PwMlgAlKkw1oYwBj3NTNQEyU33C5wIchKBRk0H9GRkYGEMYF8HshMISBIXYK2CW4DCEcBu5ucEOwAfwG/PnDwPD1KwODvg5OQwi7gADAbwALCwMDNzcDw8UrDAyLc7AGAmEX7NyFUzMI4Ddg/Rq8mkEAlwFbGNatJqgZBIZ8ZmJgAAB69SK/wethjgAAAABJRU5ErkJggg==', | |
'left_blueArrow': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAFzUkdCAK7OHOkAAAAEZ0FNQQAAsY8L/GEFAAAACXBIWXMAAAsSAAALEgHS3X78AAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1MzmNZGAwAAABV0RVh0Q3JlYXRpb24gVGltZQAyLzE3LzA4IJyqWAAABBF0RVh0WE1MOmNvbS5hZG9iZS54bXAAPD94cGFja2V0IGJlZ2luPSIgICAiIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNC4xLWMwMzQgNDYuMjcyOTc2LCBTYXQgSmFuIDI3IDIwMDcgMjI6MTE6NDEgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhhcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyI+CiAgICAgICAgIDx4YXA6Q3JlYXRvclRvb2w+QWRvYmUgRmlyZXdvcmtzIENTMzwveGFwOkNyZWF0b3JUb29sPgogICAgICAgICA8eGFwOkNyZWF0ZURhdGU+MjAwOC0wMi0xN1QwMjozNjo0NVo8L3hhcDpDcmVhdGVEYXRlPgogICAgICAgICA8eGFwOk1vZGlmeURhdGU+MjAwOC0wMy0yNFQxOTowMDo0Mlo8L3hhcDpNb2RpZnlEYXRlPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIj4KICAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9wbmc8L2RjOmZvcm1hdD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgNR1SZAAAAKFJREFUOE9j/P//PwMlgAlKkw1oYwAjIyMqjpv6H4g3Q6VRAGEXxE75zxAUCmL5gPloAL8BIM2BIVAOdoDbAJBmdzcoBzfAbgBIs74OA8PXrwwMf/5ABbEDwmFAAGA3YHEOI8PFKwwM3NwMDCwsUEHsALcLQIbs3AXl4AZYDYAnb5Ah69dA2DgATheADAHjRdmMDOtWg4S2gCXQwJDPTAwMAGenLLMBUntVAAAAAElFTkSuQmCC', | |
'afk': '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', | |
'plus': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAADFBMVEUAAAAAbfAAbfAAAAD9vR1+AAAAAnRSTlMAuLMp9oYAAAABYktHRACIBR1IAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QsXFxsxSlNC5QAAAB1JREFUCNdjYGB0YGBgYGrAQ2StWrUSQuBXBzIKAMSvC2nyDAplAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTExLTIzVDIzOjI3OjQ5KzAxOjAwH6vYMwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0xMS0yM1QyMzoyNzo0OSswMTowMG72YI8AAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAAAElFTkSuQmCC', | |
'trash': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAnFBMVEUAAADXACjYACfYACfZACbXACjZACbfACDYACfYACfYACfYACfXACjYACfWACnYACfaACXZACbYACfcACPYACfbACTZACbMADPYACfYACe/AEDZACbYACfYACfXACjYACfYACfXACjVACvYACfZACbZACbZACbXACjYACfZACbXACjXACjZACbYACfYACfMADPZACbYACfYACcAAABkgXASAAAAMnRSTlMAZ/j6cqdxEGjg9e1NwDi4N6DKHdoVeAX+6wTo+/PODeK0HtGZL39Ar2VTekrf6gqt/coYhKoAAAABYktHRACIBR1IAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QsXFyIMHL+DTgAAAKBJREFUGNNFzccCgjAURNFYEBIEBeUhHZUiSHP+/+PEGPHu5myGsU+rNbDZsn/aTtd32m8ZnAuTc1NwbkjYW0t7CfZhyZZwhHDgunAEjhJOOHsggnfGSYJ/CcIPhMHF/95EMUuIEhZH6jdFlhPlGVIFV9zuRPcbrgoKlBaRVaJQUKF+ED1qVAoatM7z6bRoFHToxTCIHp2CcXph7jWN83gDLBwRcbJLtrYAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTctMTEtMjNUMjM6MzQ6MTIrMDE6MDB/9eZuAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE3LTExLTIzVDIzOjM0OjEyKzAxOjAwDqhe0gAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAAASUVORK5CYII=', | |
'write': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAgVBMVEUAAAB5s013sE12sU13qlV2sE13sE13r0x2sU11sE1ttkl2sEx2sU52sE12sE55rkp2sE12sE11s0x2sE54sU53sE12sE6AgIB2sE12sE13sEt1sU52sE13sE10sU50rkx3sE15rlF2sk12r012sU13sE12sE13r056sU52sE0AAAAd3AxlAAAAKXRSTlMAKNW2D8nIndClDrEn57gm5uUlzyTk4wLW80c78qouOfETOHDArMR2F9yf2h0AAAABYktHRACIBR1IAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QsXFy44kb449wAAAHtJREFUGNNVz9kWgjAMBNBQlIKiuLAIIhREcf7/B420p4Z5yp3kJUQ+gQo3JLMFolWjIZsgTnZ7bkLn9AAcf42yzk685EZHmfUZSxJ3f3HG1TovnMt8cXWrV6ZG31tpenTUG+HB8DQab3ryrtPKmyagfTXV/6XpPX/ki1+1aw3T95ovRQAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxNy0xMS0yM1QyMzo0Njo1NiswMTowMCI+CTkAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTctMTEtMjNUMjM6NDY6NTYrMDE6MDBTY7GFAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAABJRU5ErkJggg==', | |
'ove': ''} | |
class CreateToolTip: | |
""" | |
create a tooltip for a given widget | |
""" | |
def __init__(self, widget=None, imgPath=False, data=False, text=False, wraplength=180): | |
self.wraplength = wraplength # pixels | |
self.widget = widget | |
self.imgPath = imgPath | |
self.data = data | |
self.text = text | |
if data: | |
try: | |
self.image = PhotoImage(data=data) | |
except (FileNotFoundError, AttributeError): | |
logging.debug('failed to load pic exiting...') | |
return | |
elif imgPath: | |
# If the tool tip is an img, prepare the image | |
try: | |
# Check if the img exists | |
img = Image.open(self.imgPath) | |
self.image = ImageTk.PhotoImage(img) | |
except (FileNotFoundError, AttributeError): | |
# AttributeError is raised if a nonexistent path is entered | |
logging.debug('failed to load pic exiting...') | |
return | |
"""# Resize img | |
basewidth = 350 | |
wPercent = (basewidth / float(img.size[0])) | |
hSize = int((float(img.size[1]) * float(wPercent))) | |
# !!!! We are not currently resizing the image !!!!# | |
# img = img.resize((basewidth, hSize), Image.ANTIALIAS)""" | |
# waittime before the tooltop appeares when hovering the chosen widget | |
self.waittime = 150 # miliseconds | |
# Bind the widget | |
self.init_bind() | |
def init_bind(self): | |
"""Called along with the __init__ method""" | |
# Bind the widget to the tool tip | |
self.widget.bind("<Enter>", self.enter) | |
self.widget.bind("<Leave>", self.leave) | |
self.widget.bind("<ButtonPress>", self.leave) | |
self.id = None | |
self.tw = None | |
def enter(self, event=None): | |
self.schedule() | |
def leave(self, event=None): | |
self.unschedule() | |
self.hidetip() | |
def schedule(self): | |
self.unschedule() | |
self.id = self.widget.after(self.waittime, self.showtip) | |
def unschedule(self): | |
id = self.id | |
self.id = None | |
if id: | |
self.widget.after_cancel(id) | |
def showtip(self, event=None): | |
x = y = 0 | |
x, y, cx, cy = self.widget.bbox("insert") | |
x += self.widget.winfo_rootx() + 25 | |
y += self.widget.winfo_rooty() + 20 | |
# creates a toplevel window | |
self.tw = tk.Toplevel(self.widget) | |
# Leaves only the label and removes the app window | |
self.tw.wm_overrideredirect(True) | |
self.tw.wm_geometry("+%d+%d" % (x, y)) | |
label = Label(self.tw, justify='left', background="#ffffff", relief='solid', | |
borderwidth=1, wraplength=self.wraplength) | |
if self.imgPath or self.data: | |
# The tooltip is an img | |
label.config(image = self.image) | |
elif self.text: | |
# The tooltip is text | |
label.config(text = self.text) | |
label.pack(ipadx=1) | |
def hidetip(self): | |
tw = self.tw | |
self.tw = None | |
if tw: | |
tw.destroy() | |
def encrypt(plaintext, n, key1, key2): | |
"""Encrypt the string and return the ciphertext""" | |
result = '' | |
for l in plaintext[:int(len(plaintext)/2)]: | |
try: | |
i = (key1.index(l) + n) % len(key1) | |
result += key1[i] | |
except ValueError: | |
result += l | |
for l in plaintext[int(len(plaintext)/2):]: | |
try: | |
i = (key2.index(l) + n) % len(key2) | |
result += key2[i] | |
except ValueError: | |
result += l | |
return result | |
def decrypt(ciphertext, n, key1, key2): | |
"""Encrypt the string and return the ciphertext""" | |
result = '' | |
for l in ciphertext[:int(len(ciphertext)/2)]: | |
try: | |
i = (key1.index(l) - n) % len(key1) | |
result += key1[i] | |
except ValueError: | |
result += l | |
for l in ciphertext[int(len(ciphertext)/2):]: | |
try: | |
i = (key2.index(l) - n) % len(key2) | |
result += key2[i] | |
except ValueError: | |
result += l | |
return result | |
class License: | |
def __init__(self, parent, grey): | |
self.grey = grey | |
self.parent = parent | |
def run(self): | |
print('in licensas dfasdfjælk') | |
# Check if the window already exists | |
try: | |
if self.tw.winfo_exists(): | |
print('it already exist') | |
self.tw.destroy() | |
return | |
except: | |
# expected to pass on first run, since tw isnt initalized yet | |
pass | |
print('creating') | |
# If the window didn't exist - create it now | |
self.tw = tk.Toplevel(root) | |
self.tw.resizable(False, False) | |
# Set span location | |
x = self.parent.winfo_rootx() | |
y = self.parent.winfo_rooty() | |
self.tw.wm_geometry("+%d+%d" % (x, y)) | |
# Set dimensions of window | |
self.tw.geometry('300x75') | |
# Initialize the window | |
self.init_window() | |
def init_window(self): | |
self.frame = Frame(self.tw, bg=self.grey) | |
self.mid_frame = Frame(self.frame, bg=self.grey) | |
self.bot_frame = Frame(self.frame, bg=self.grey) | |
userInput = StringVar(root) | |
self.label = Label(self.frame, text='Indtast license kode', bg=self.grey) | |
self.entry = Entry(self.mid_frame, textvariable=userInput, width=35) | |
self.status = Label(self.mid_frame, bg=self.grey) | |
self.checkBut = Button(self.bot_frame, text='Check', command=lambda: self.check( | |
licCode=userInput.get().strip() | |
)) | |
self.cancel = Button(self.bot_frame, text='Cancel', command=lambda: self.tw.destroy()) | |
# Grid the widgets | |
self.frame.grid(row=0, column=0, sticky='nsew') | |
self.label.grid(row=0, column=0, sticky='nw', padx=(45,0)) | |
self.mid_frame.grid(row=1, column=0, sticky='we') | |
self.entry.grid(row=1, column=0, sticky='n', padx=(2,0), pady=(0,5)) | |
self.status.grid(row=1, column=2, sticky='we', pady=(0,3)) | |
self.bot_frame.grid(row=2, column=0, sticky='ew') | |
self.checkBut.grid(row=0, column=0, sticky='ew') | |
self.cancel.grid(row=0, column=1, sticky='ew') | |
# Configure row and column | |
self.tw.grid_rowconfigure(0, weight=1) | |
self.tw.grid_columnconfigure(0, weight=1) | |
self.frame.grid_rowconfigure(0, weight=1) | |
self.frame.grid_columnconfigure(0, weight=1) | |
self.bot_frame.columnconfigure((0,1), weight=1) | |
self.mid_frame.grid_columnconfigure(1, weight=1) | |
def check(self, licCode): | |
"""Function that handles the check button in the license popup.""" | |
#print('AT CHECK FUNCTION: "{licCode}"') | |
if self.check_license_code(licCode): | |
self.create_license_file() | |
self.display_succes_msg() | |
else: | |
self.display_fail_msg() | |
def display_succes_msg(self): | |
"""Updates the status label to display: 'Succes!'""" | |
self.status.config(bg='lightgreen', fg='green', text='Success!') | |
self.status.grid(padx=(0,14)) | |
def display_fail_msg(self): | |
"""Updates the status label to display: 'Ugyldig license kode'""" | |
self.status.config(bg='#e26a84', fg='black', text='Ugyldig kode') | |
self.status.grid(padx=(0,4), pady=(0,4)) | |
def license_file_info_popup(self): | |
"""A popup that displays some important info to the user. | |
One of the things it talks about is for instance how the user have | |
to keep the license file in the same directory as the executeable file.""" | |
messagebox.showinfo('Success', "Sørg for altid at have license filen i samme mappe som VøBot.exe filen!\n\n"\ | |
" Du rådes til ikke at ændrer på nogle systemvariabler, da VøBot benytter disse til at bestemme om du har delt " \ | |
" licensenskoden med andre personer. Hvis du alligevel skulle gøre dette, vil din license sandsynligvis ikke virke. "\ | |
"I såfald kan du kontakt os for at få en ny - den gamle vil blive stemplet ugyldig.\n\n""") | |
# TODO Skriv hvor lang tid koden er gyldig i | |
def check_license_code(self, licCode): | |
"""Checks if the license code is valid or not | |
returns a boolean depending on the validity of the license code the user typed""" | |
NT_options = sponserW_instance.options | |
for _ in range(*(int(x) for x in NT_options.numberOfTries.split(' '))): | |
licCode = decrypt(ciphertext=licCode, n=NT_options.encryptionOffset, | |
key1=NT_options.charset_1, | |
key2=NT_options.charset_2 | |
) | |
# DEBUG: print(f'{_-3} decrypt: {licCode}') | |
if licCode == NT_options.basisKode: | |
#print(f'The message was broken after {_-5} decrypts') | |
return True | |
else: | |
return False | |
def create_file_content(self): | |
"""Writes the content of the license.txt file""" | |
content = 'Always keep this file in the same directoy as the VøBot.exe file. Do NOT delete this file.\n' | |
# Add random characters to confuse the end user | |
for _ in range(45): | |
content += chr(random.randint(48,123)) | |
for n in (9,34,22,16,12,6): | |
# Add the encrypted version of the systems username | |
content += encrypt(getpass.getuser(), n=n, | |
key1='aA0!bBcC"1dDeE2f#FgG3%hHi&I4/jJ(kK)5l=Lm?M6@nNoO7pPqQ8rRsS9tTuUvVwWxXyYzZ', | |
key2='1aAbBc2CdDeE3fFgG4hHjJ5kKlLm6iIwWMnNo7OpPq8QrRsS9tTuUvVxXyYzZ' | |
) | |
# Continue adding rdm characters | |
for _ in range(67): | |
content += chr(random.randint(48,123)) | |
return content | |
def create_license_file(self): | |
"""This function gets called whenever a correct license code has been entered.""" | |
with open('VøBot License', 'w') as file: | |
content = self.create_file_content() | |
file.write(content) | |
@staticmethod | |
def check_license_file(): | |
"""Returns a boolean dependent on whether the license file is valid or not""" | |
try: | |
file = open('VøBot License', 'r') | |
content = file.readlines()[1] | |
except FileNotFoundError: | |
return False # The file could not be located | |
pivot = 0 | |
usern_L = len(getpass.getuser()) | |
for n in (9,34,22,16,12,6): | |
encrypted_username = content[45 + pivot:45 + usern_L + pivot] | |
username = decrypt(encrypted_username, n=n, | |
key1='aA0!bBcC"1dDeE2f#FgG3%hHi&I4/jJ(kK)5l=Lm?M6@nNoO7pPqQ8rRsS9tTuUvVwWxXyYzZ', | |
key2='1aAbBc2CdDeE3fFgG4hHjJ5kKlLm6iIwWMnNo7OpPq8QrRsS9tTuUvVxXyYzZ' | |
) | |
pivot += usern_L | |
if username != getpass.getuser(): | |
print('<<<<<<FAILED') | |
continue | |
return False | |
else: | |
print('<<<<<<<SUCCESFUL') | |
return True | |
return True if (username == getpass.getuser()) else False | |
class CreateToolTip_dpRectangle(CreateToolTip): | |
def __init__(self, widget, imgPath=False, data=False, text=False, wraplength=180, enable=True, box=None, DPcanvas=None): | |
super().__init__() | |
self.enable = enable | |
self.box = box | |
self.DPcanvas = DPcanvas | |
def init_bind(self): | |
# Du Pont pyramid rectangles | |
DPcanvas.tag_bind(self.box, '<Button-1>', self.enter) | |
DPcanvas.tag_bind(self.box, '<Enter>', self.enter) | |
DPcanvas.tag_bind(self.box, '<Leave>', self.leave) | |
self.id = None | |
self.tw = None | |
def enter(self, event=None): | |
if enable: | |
self.schedule() | |
def showtip(self, event=None): | |
x = y = 0 | |
x, y, cx, cy = self.widget.bbox("insert") | |
x += self.widget.winfo_rootx() + 25 | |
y += self.widget.winfo_rooty() + 20 | |
# creates a toplevel window | |
self.tw = tk.Toplevel(self.widget) | |
# Leaves only the label and removes the app window | |
self.tw.wm_overrideredirect(True) | |
self.tw.wm_geometry("+%d+%d" % (x, y)) | |
label = Label(self.tw, justify='left', background="#ffffff", relief='solid', | |
borderwidth=1, wraplength=self.wraplength) | |
if self.imgPath or self.data: | |
# The tooltip is an img | |
label.config(image=self.image) | |
elif self.text: | |
# The tooltip is text | |
label.config(text=self.text) | |
class SponserWindow: | |
"""1. Look for the image in the dir first, if not found -> | |
2. download the image with request, if it's not possible to download img -> | |
3. show sponsorWindow anyway""" | |
def __init__(self, root, adDuration): | |
self.adDuration = adDuration | |
# request options fra github | |
self.options = self.req_options() | |
print('License file is:', License.check_license_file(), 'at startup.') | |
# Failed to req (no int) github options and the license file was not located or invalid | |
if (self.options == None): | |
if (License.check_license_file() == False): | |
logging.info('>> Invalid license file and no internet.. exiting') | |
#exit() | |
else: | |
# Show root Window | |
root.deiconify() | |
return | |
try: | |
# If an update is available, display a popup with the message | |
if self.options.updateMSG != 'None': | |
self.updateAvailable() | |
# Overwrite the adDuartion with the custom one just downloaded | |
self.adDuration = self.options.adDuration | |
except AttributeError: | |
logging.warning('AttributeError: self.options mangler nogle options eller attribute navne er forkerte') | |
logging.info('adDuration: {}'.format(self.adDuration)) | |
if self.options.showAd: | |
# Load the image and initialize window | |
self.image = self.load_image() | |
self.init_window() | |
else: | |
# if showAd is disable don't start sponsorwindow; Instead show root Window | |
logging.info('showAd = False; skipping sponsorWindow') | |
root.deiconify() | |
def load_image(self): | |
"""Load the image, if that fails it tries to download the image | |
and load it. If anything should fail it returns None""" | |
# if img already in path | |
if 'ad.jpg' in os.listdir(): | |
try: | |
# Load the image | |
img = ImageTk.PhotoImage(file=os.getcwd() + '/ad.jpg') | |
logging.info('Loaded ad') | |
return img | |
except: | |
logging.warning('Failed to load img') | |
# image isn't downloaded | |
self.download_image() | |
try: | |
# load the picture | |
img = ImageTk.PhotoImage(file=os.getcwd() + '/ad.jpg') | |
logging.info('Loaded the saved ad') | |
return img | |
except: | |
logging.warning('Failed to load ad') | |
def req_options(self): | |
"""Henter options fra github og gemmer dem i en namedtuple""" | |
# namedtuple - Options # # # # # # # # # # # # # # # # # # # # # # # | |
#1 # imgURL # link til download af img | |
#2 # updateMSG # Displays messagebox with the updateMSG text | |
#3 # showAd # hvis False så skippes sponsorWindow | |
#4 # adDuration # antal milisekunder reklamen skal vare | |
#5 # basisKode # kode som skal kunne findes frem til ved at decrypt med charset + offset (se 2 næste) | |
#6 # encryptionOffset # Offset til encryption algoritme | |
#7 # charset_1 # Charset key one til encryption algoritme | |
#8 # charset_2 # (key two) - || - | |
#9 # numberOfTries # Number of times to try and decrypt the license code | |
#10+# usedCodes # liste med allerede aktiverede koder for at holde styr på, | |
# # # at en kode ikke bruges af flere personer. | |
Options = namedtuple('Options', 'imgURL updateMSG showAd adDuration basisKode ' | |
'encryptionOffset charset_1 charset_2 ' | |
'numberOfTries usedCodes') | |
gistURL = 'https://gist.github.com/Sebastian-Nielsen/3a717a178d4581983fb711cc35dc90b1' | |
try: | |
# Requests github html | |
html = requests.get(gistURL) | |
soup = BeautifulSoup(html.text, 'html.parser') | |
logging.info('>>Github html received') | |
except: | |
logging.warning('Failed to GET request github options') | |
return None | |
try: | |
# sorter options fra soup | |
gen = (int(x.string) if x.string.isdigit() else x.string | |
for x in soup.findAll("td", {"class": "blob-code blob-code-inner js-file-line"})) | |
# Create the namedtuple 'options' by the options collected from github | |
options = Options(*gen) | |
logging.info(str(options)) | |
except: | |
logging.warn('Failed to handle options and create a namedtuple') | |
return None | |
return options | |
def updateAvailable(self): | |
"""Creates a toplevel window telling the user an update is out""" | |
messagebox.showinfo('Update available', self.options.updateMSG) | |
def download_image(self): | |
"""Downloads the image""" | |
# Download the image | |
try: | |
img = requests.get(self.options.imgURL) | |
logging.info('Downloaded ad') | |
except: | |
logging.warning('Failed to download ad') | |
return | |
# Save the image | |
self.save_img(img) | |
def save_img(self, img): | |
"""Saves the image""" | |
saveLocation = os.getcwd() | |
name = '/ad.jpg' | |
try: | |
with open(saveLocation + name, 'wb') as f: | |
f.write(img.content) | |
logging.info('Saved ad') | |
except: | |
logging.warning('Failed to save ad img') | |
def init_window(self): | |
"""Initializes sponsorWindow and starts timer to show mainWindow and to hide sponsorWindow""" | |
vidste_du = ['Man behøver ikke gøre brug af "digit grouping" \nsom 43.213, du kan skrive 43213 i stedet.', | |
'Test 123'] | |
# Init tw and start countdown to the deletion of the ad | |
self.tw = tk.Toplevel() | |
# Skjul "kryds felt" så ad'en ikke lukkes | |
self.tw.wm_overrideredirect(True) | |
# Luk ned for sponserWindow efter "adDuration" | |
self.tw.after(self.adDuration, self.tw.destroy) | |
# Start op for rootWindow efter "adDuration" | |
root.after(self.adDuration, root.deiconify) | |
# Initialize sponsorWindow layout | |
font = 'arial 20 bold' | |
try: | |
photo = Label(self.tw, image=self.image) | |
photo.image = self.image | |
logging.info('Uploaded ad to sponsorWindow') | |
try: | |
# Billedet er succesful uploaded, count en person har set en ad | |
os = platform.system() | |
if os == 'Windows': | |
headers={'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'} | |
requests.get('https://c.statcounter.com/11546229/0/86e98146/1/', headers=headers) | |
logging.info('Ad viewer count - increased +1') | |
except: | |
logging.warning('Error - Something went wrong; Failed to update ad viewer count') | |
except: | |
photo = Label(self.tw, text='FAILED TO LOAD IMAGE', font='arial 40 bold') | |
logging.warning('Failed to upload ad to sponsorWindow') | |
photo.grid(row=1, column=0) | |
# Vi gør brug af en progress bar i stedet for en label | |
self.topLab = Label(self.tw, font=font, text='Vores sponsorer: {}'.format(int(self.adDuration / 1000))) | |
self.topLab.grid(row=0, column=0, sticky='w') | |
self.botLab = Label(self.tw, font='courier 11', text='Vidste du: ' + random.choice([vidste_du[0]])) | |
self.botLab.grid(row=3, column=0, sticky='w') | |
# Progressbar | |
self.pBar = Progressbar(self.tw, orient="horizontal", mode="determinate") | |
self.pBar.grid(row=2, column=0, sticky='wse') | |
self.pBar["value"] = 0 | |
self.pBar["maximum"] = self.adDuration | |
# Times = hvor mange bider pBar skal opdateres i på adDuration tiden | |
self.times = round(self.adDuration / 6) | |
self.update_progress() | |
self.update_countdown() | |
def update_countdown(self): | |
# Decrease ad duration by one before updating countdown | |
self.adDuration -= 1000 | |
self.topLab.config(text=' Vores sponsorer {:2d}sek. '.format(round(self.adDuration / 1000))) | |
# Update countdown again after 1 second | |
self.tw.after(1000, self.update_countdown) | |
def update_progress(self): | |
self.pBar["value"] += self.times | |
self.tw.after(self.times, self.update_progress) | |
class ExpandoText(tk.Text): | |
"""Used in 'nøgletal_analyse""" | |
def insert(self, *args, **kwargs): | |
result = tk.Text.insert(self, *args, **kwargs) | |
self.update() | |
self.reset_height() | |
def reset_height(self): | |
height = self.tk.call((self._w, "count", "-update", "-displaylines", "1.0", "end")) | |
self.configure(height=height) | |
class CollapsibleFrame(Frame): | |
def __init__(self, master, root, text=None, row=0, column=0, borderwidth=2, width=0, height=16, font=None, interior_padx=0, interior_pady=8, background=None, caption_separation=4, | |
caption_font=None, caption_builder=None, icon_x=5): | |
Frame.__init__(self, master) | |
self.root = root | |
self.row = row | |
self.column = column | |
if background is None: | |
background = self.cget("background") | |
self.configure(background=background) | |
self._is_opened = False | |
self._interior_padx = interior_padx | |
self._interior_pady = interior_pady | |
self._iconOpen = PhotoImage(data="iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAFzUkdCAK7OHOkAAAAEZ0FNQQAAsY8L/GEFAAAACXBIWXMAAAsSAAALEgHS3X78AAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1MzmNZGAwAAABV0RVh0Q3JlYXRpb24gVGltZQAyLzE3LzA4IJyqWAAABBF0RVh0WE1MOmNvbS5hZG9iZS54bXAAPD94cGFja2V0IGJlZ2luPSIgICAiIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNC4xLWMwMzQgNDYuMjcyOTc2LCBTYXQgSmFuIDI3IDIwMDcgMjI6MTE6NDEgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhhcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyI+CiAgICAgICAgIDx4YXA6Q3JlYXRvclRvb2w+QWRvYmUgRmlyZXdvcmtzIENTMzwveGFwOkNyZWF0b3JUb29sPgogICAgICAgICA8eGFwOkNyZWF0ZURhdGU+MjAwOC0wMi0xN1QwMjozNjo0NVo8L3hhcDpDcmVhdGVEYXRlPgogICAgICAgICA8eGFwOk1vZGlmeURhdGU+MjAwOC0wMy0yNFQxOTowMDo0Mlo8L3hhcDpNb2RpZnlEYXRlPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIj4KICAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9wbmc8L2RjOmZvcm1hdD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgNR1SZAAAAI1JREFUOE9j/P//PwMlgAlKkw1oYwBj3NTNQEyU33C5wIchKBRk0H9GRkYGEMYF8HshMISBIXYK2CW4DCEcBu5ucEOwAfwG/PnDwPD1KwODvg5OQwi7gADAbwALCwMDNzcDw8UrDAyLc7AGAmEX7NyFUzMI4Ddg/Rq8mkEAlwFbGNatJqgZBIZ8ZmJgAAB69SK/wethjgAAAABJRU5ErkJggg==") | |
self._iconClose = PhotoImage(data="iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAFzUkdCAK7OHOkAAAAEZ0FNQQAAsY8L/GEFAAAACXBIWXMAAAsSAAALEgHS3X78AAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1MzmNZGAwAAABV0RVh0Q3JlYXRpb24gVGltZQAyLzE3LzA4IJyqWAAABBF0RVh0WE1MOmNvbS5hZG9iZS54bXAAPD94cGFja2V0IGJlZ2luPSIgICAiIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNC4xLWMwMzQgNDYuMjcyOTc2LCBTYXQgSmFuIDI3IDIwMDcgMjI6MTE6NDEgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhhcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyI+CiAgICAgICAgIDx4YXA6Q3JlYXRvclRvb2w+QWRvYmUgRmlyZXdvcmtzIENTMzwveGFwOkNyZWF0b3JUb29sPgogICAgICAgICA8eGFwOkNyZWF0ZURhdGU+MjAwOC0wMi0xN1QwMjozNjo0NVo8L3hhcDpDcmVhdGVEYXRlPgogICAgICAgICA8eGFwOk1vZGlmeURhdGU+MjAwOC0wMy0yNFQxOTowMDo0Mlo8L3hhcDpNb2RpZnlEYXRlPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIj4KICAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9wbmc8L2RjOmZvcm1hdD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgNR1SZAAAAJZJREFUOE9j/P//PwMlgAlKkw2GgQEogcgYN3UzkPKB8HCCLf8XZftC2RgG/GcICoXycIB1qxmABjBCeWgGMALFY6f8ZwgMgQj8+QOhWVgg9Po1KJpBANMAEAAZ4u7GwPD1K4TPzc3AsHMXA8PiHIx0g90AEAAZoq8DYV+8AtYMYhJvAAiADAEBHJpBAMNJpILRpMzAAABzmUMBeP5cOQAAAABJRU5ErkJggg==") | |
height_of_icon = max(self._iconOpen.height(), self._iconClose.height()) | |
width_of_icon = max(self._iconOpen.width(), self._iconClose.width()) | |
containerFrame_pady = ( height_of_icon //2) +1 | |
self._height = height | |
self._width = width | |
self._containerFrame = Frame(self, borderwidth=borderwidth, width=width, height=height, relief=RIDGE, background=background) | |
self._containerFrame.grid(row=row, column=column, pady=(containerFrame_pady, 0)) | |
self.interior = Frame(self._containerFrame, background=background) | |
self._collapseButton = Label(self, borderwidth=0, bg=background, image=self._iconOpen, relief=RAISED) | |
self._collapseButton.bind("<Enter>", lambda *_: self._collapseButton.config(bg='#a2c0ef')) | |
self._collapseButton.bind("<Leave>", lambda *_: self._collapseButton.config(bg=background)) | |
self._collapseButton.place(in_=self._containerFrame, x=icon_x, y=-(height_of_icon // 2), anchor=N + W, bordermode="ignore") | |
self._collapseButton.bind("<Button-1>", lambda event: self.toggle()) | |
if caption_builder is None: | |
if font != None: | |
self._captionLabel = Label(self, anchor=W, borderwidth=1, bg=background, text=text, font=font) | |
else: | |
self._captionLabel = Label(self, anchor=W, borderwidth=1, bg=background, text=text) | |
self._captionLabel.bind("<Enter>", lambda *_: self._captionLabel.config(bg='#a2c0ef')) | |
self._captionLabel.bind("<Leave>", lambda *_: self._captionLabel.config(bg=background)) | |
self._captionLabel.bind("<Button-1>", lambda event: self.toggle()) | |
if caption_font is not None: | |
self._captionLabel.configure(font=caption_font) | |
else: | |
self._captionLabel = caption_builder(self) | |
if not isinstance(self._captionLabel, Widget): | |
raise Exception("'caption_builder' doesn't return a tkinter widget") | |
self.after(0, lambda: self._place_caption(caption_separation, icon_x, width_of_icon)) | |
def update_width(self, width=None): | |
# Update could be devil | |
# http://wiki.tcl.tk/1255 | |
self.after(0, lambda width=width: self._update_width(width)) | |
def _place_caption(self, caption_separation, icon_x, width_of_icon): | |
self.update() | |
x = caption_separation + icon_x + width_of_icon | |
y = -(self._captionLabel.winfo_reqheight() // 2) | |
self._captionLabel.place(in_=self._containerFrame, x=x, y=y, anchor=N + W, bordermode="ignore") | |
def _update_width(self, width): | |
self.update() | |
if width is None: | |
width = self.interior.winfo_reqwidth() | |
if isinstance(self._interior_pady, (list, tuple)): | |
width += self._interior_pady[0] + self._interior_pady[1] | |
else: | |
width += 2 * self._interior_pady | |
width = max(self._width, width) | |
self._containerFrame.configure(width=width) | |
def open(self): | |
self._collapseButton.configure(image=self._iconClose) | |
self._containerFrame.configure(height=self.interior.winfo_reqheight()) | |
self.interior.grid(row=0, column=0, sticky='ew', padx=self._interior_padx, pady=self._interior_pady) | |
self._is_opened = True | |
if not (self.root.winfo_height() >= 850): | |
self.root.geometry('{}x{}'.format(self.root.winfo_width(), self.root.winfo_height()+self.interior.winfo_reqheight())) | |
def close(self): | |
self.interior.grid_forget() | |
self._containerFrame.configure(height=self._height) | |
self._collapseButton.configure(image=self._iconOpen) | |
self._is_opened = False | |
# Resizing of root window size when closing a collapsibleFrame | |
try: | |
# Du Pont Pyramid is not shown - (DDP is not shown if (DPP_switch == True) ) | |
if not next(MainApp.DPP_switch): | |
root_height = self.root.winfo_height() | |
inte_height = self.interior.winfo_reqheight() | |
if root_height - inte_height > 650: | |
self.root.geometry('{}x{}'.format(self.root.winfo_width(), self.root.winfo_height()-self.interior.winfo_reqheight())) | |
next(MainApp.DPP_switch) | |
return | |
next(MainApp.DPP_switch) | |
except AttributeError: | |
pass | |
#print('There does not exist an intance named "MainApp" of the "Nøgletals_analyse" class ') | |
self.root.geometry('{}x{}'.format(self.root.winfo_width(), self.root.winfo_height() - self.interior.winfo_reqheight())) | |
def toggle(self): | |
if self._is_opened: | |
self.close() | |
else: | |
self.open() | |
class MainApplication: | |
def __init__(self, root): | |
root.grid_rowconfigure(1, weight=1) | |
root.grid_columnconfigure(0, weight=1) | |
# create all of the main containers | |
self.top_frame = Frame(root, width=200, height=75, bg='forest green') | |
self.ctr_frame = Frame(root, width=200, bg='black') | |
self.bot_frame = Frame(root, height=50, bg='cyan') | |
# layout all of the main containers | |
self.top_frame.grid(row=0, sticky='ew') | |
self.ctr_frame.grid(row=1, sticky='nsew') | |
self.bot_frame.grid(row=2, sticky='nsew') | |
self.ctr_frame.grid_rowconfigure(0, weight=1) | |
self.ctr_frame.grid_columnconfigure((0, 1), weight=1) | |
# create all child containers | |
self.left_child = Frame(self.ctr_frame, bg='purple') | |
self.right_child = Frame(self.ctr_frame, bg='red') | |
# layout all child containers | |
self.left_child.grid(row=0, column=0, sticky='nsew') | |
self.right_child.grid(row=0, column=1, sticky='nsew') | |
""" def __init__(self, root): | |
root.grid_rowconfigure(1, weight=1) | |
root.grid_columnconfigure(0, weight=1) | |
# create all of the main containers | |
self.top_frame = Frame(root, width=200, height=100, bg='forest green') | |
self.ctr_frame = Frame(root, width=200, bg='black') | |
self.bot_frame = Frame(root, height=50, bg='cyan') | |
# layout all of the main containers | |
self.top_frame.grid(row=0, sticky='ew' ) | |
self.ctr_frame.grid(row=1, sticky='nsew') | |
self.bot_frame.grid(row=2, sticky='nsew') | |
self.ctr_frame.grid_rowconfigure (0, weight=1) | |
self.ctr_frame.grid_columnconfigure(1, weight=1) | |
# create the minor containers | |
self.ctr_left = Frame(self.ctr_frame, width=50, height=200, bg='red' ) | |
self.ctr_mid = Frame(self.ctr_frame, width=600, height=200, bg='light green') | |
self.ctr_right = Frame(self.ctr_frame, width=50, height=200, bg='blue' ) | |
# layout all of the minor containers | |
self.ctr_left.grid( row=0, column=0, sticky='nsew') | |
self.ctr_mid.grid( row=0, column=1, sticky='nsew') | |
self.ctr_right.grid(row=0, column=2, sticky='ns' ) | |
self.ctr_mid.grid_columnconfigure(0, weight=1) | |
self.ctr_mid.grid_rowconfigure((0,1), weight=1) | |
# create objects to - ctr_frame - | |
self.rentabilitet = Button(self.ctr_mid, text='Remtabilitet', padx=35) | |
self.choose_bt = Button(self.ctr_mid, text='Regnskab', padx=35, width=35) | |
# layout all of the ctr_frame objects | |
#self.rentabilitet.grid(row=0, column=0, sticky='nsew') | |
#self.choose_bt.grid( row=1, column=0, sticky='nsew', columnspan=2)""" | |
class Indstillinger_window: | |
def __init__(self, root, grey, blue_active, blue_enter, blue_leave): | |
self.grey = grey | |
self.blue_active = blue_active | |
self.blue_enter = blue_enter | |
self.blue_leave = "#%02x%02x%02x" % (120, 151, 229) | |
#self.blue_active = "#%02x%02x%02x" % (160, 194, 247) | |
#self.blue_enter = "#%02x%02x%02x" % (132, 173, 237) | |
#self.blue_leave = "#%02x%02x%02x" % (100, 151, 229) | |
def run(self): | |
# Check if the window already exists | |
try: | |
if self.tw.winfo_exists(): | |
self.tw.destroy() | |
return | |
except: | |
# expected to pass on first run, since tw isnt initalized yet | |
pass | |
# If the window didn't exist - create it now | |
self.tw = tk.Toplevel(root) | |
self.tw.resizable(False, False) | |
# Set span location | |
x = root.winfo_rootx() | |
y = root.winfo_rooty() | |
self.tw.wm_geometry("+%d+%d" % (x, y)) | |
# Set dimensions of window | |
self.tw.geometry('500x75') | |
self.init_window() | |
def init_window(self): | |
"""Initializes all the widgets in the topLevel window""" | |
frame = Frame(self.tw, bg=self.grey) | |
frame.grid(row=0, column=0, sticky='nsew') | |
# Row and column configure | |
self.tw.grid_columnconfigure(0, weight=1) | |
self.tw.grid_rowconfigure(0, weight=1) | |
frame.grid_columnconfigure((0,1), weight=1) | |
frame.grid_rowconfigure((0,1), weight=1) | |
# Instance of license class | |
license = License(self.tw, self.grey) | |
# Activate License - button | |
actLic = Button(frame, text='Aktiver license kode', activebackground=self.blue_active, | |
bg=self.blue_leave, font='arial 10', bd=1, command=lambda: (license.run(), self.tw.destroy())) | |
actLic.grid(row=0, column=0, sticky='ew') | |
# Save numbers as csv - button | |
saveBut = Button(frame, text='Gem nøgletal', activebackground=self.blue_active, bg=self.blue_leave, | |
font='arial 10', bd=1, command=self.save_numbers_in_csv) | |
saveBut.grid(row=1, column=0, sticky='ew') | |
# Load numbers from csv - button | |
switch = cycle((False, True)) | |
loadBut = Button(frame, text='Load Nøgletal', activebackground=self.blue_active, bg=self.blue_leave, | |
font='arial 10', bd=1, command=lambda: self.on_load_csv(next(switch))) | |
loadBut.grid(row=1, column=1, sticky='ew') | |
# Hidden 'entryPath' for loading csv file | |
sv = StringVar(self.tw) | |
entryPath = Entry(frame, textvariable=sv) | |
entryPath.insert(END, os.getcwd() + '\\VøBot - gemte nøgletal.csv') | |
entryPath.grid(row=2, column=0, columnspan=2, sticky='ew', pady=(5,10)) | |
# Hover colors for (actLic, saveBut) | |
actLic.bind("<Enter>", lambda e: actLic.config(bg=self.blue_enter)) | |
actLic.bind("<Leave>", lambda e: actLic.config(bg=self.blue_leave)) | |
saveBut.bind("<Enter>", lambda e: saveBut.config(bg=self.blue_enter)) | |
saveBut.bind("<Leave>", lambda e: saveBut.config(bg=self.blue_leave)) | |
loadBut.bind("<Enter>", lambda e: loadBut.config(bg=self.blue_enter)) | |
loadBut.bind("<Leave>", lambda e: loadBut.config(bg=self.blue_leave)) | |
def save_numbers_in_csv(self): | |
"""Safes the currently entered numbers in a csv file""" | |
with open('VøBot - gemte nøgletal.csv', 'w') as csvfile: | |
writer = csv.writer(csvfile, delimiter=' ', quotechar='|', quoting=csv.QUOTE_MINIMAL) | |
# Write 'basic nøgletal' and 'gem. nøgletal' til csv | |
writer.writerow( [n.get() for n in MainApp.entries.values()] ) | |
def load_csv(self): | |
"""Loads the numbers from a specified csv file""" | |
def on_load_csv(self, shown): | |
"""Grids an entry in which the path to the csv file is intended to be written.""" | |
# entryPath is already shown -> hide it | |
if shown: | |
os.getcwd() | |
class Nøgletal_Analyse: | |
def __init__(self, root): | |
self.root = root | |
self.root.withdraw() | |
self.nøgletal_labels = ('Afkastningsgrad', 'Overskudsgrad', 'Aktivernes omsætningshastighed', | |
'Gældsrente', 'Egenkapitalens forrentning', 'Gearing', 'Konklusion', 'Indtjeningsevne', | |
'Soliditetsgrad', 'Likviditetsgrad') | |
self.root.grid_rowconfigure(0, weight=1) | |
self.root.grid_columnconfigure(1, weight=1) # super(Analyse_Window, self).__init__() | |
self.grey = 'gray90' | |
self.ggrey = 'grey86' | |
self.bColor_enter = 'grey60' # Button color on enter | |
self.bColor_leave = 'grey70' # Button color on leave | |
self.root.config(bg=self.grey) | |
# Blue colored buttons | |
self.blue_active = "#%02x%02x%02x" % (160, 194, 247) #'SkyBlue1'#'SteelBlue3' | |
self.blue_enter = "#%02x%02x%02x" % (132, 173, 237) #'DodgerBlue2'#'SteelBlue2' | |
self.blue_leave = "#%02x%02x%02x" % (100, 151, 229) #'Dodgerblue1'#'SteelBlue1' | |
# create all of the main containers | |
self.left_frame = Frame(self.root, bg=self.grey) | |
self.right_frame = Frame(self.root, bg=self.grey) | |
self.bot_frame = Frame(self.root, bg=self.ggrey) # Here goes the statusbar label | |
# layout all of the main containers | |
self.left_frame.grid( row=0, column=0, sticky='nsew') | |
self.right_frame.grid(row=0, column=1, sticky='nsew', rowspan=2) | |
self.bot_frame.grid( row=1, column=0, sticky='we') | |
self.right_frame.grid_columnconfigure(0, weight=1) | |
self.right_frame.grid_rowconfigure(1, weight=1) | |
self.bot_frame.grid_columnconfigure(0, weight=1) | |
self.bot_frame.grid_remove() | |
# Statusbar | |
self.statusbar = Label(self.bot_frame, text='status bar text here', bg=self.ggrey) | |
self.statusbar.grid(sticky='w') | |
# create all of the subcontainers | |
self.top_child = Frame(self.left_frame, bg=self.grey) | |
self.left_child = Frame(self.left_frame, bg=self.grey) | |
self.right_child = Frame(self.left_frame, bg=self.grey) | |
self.bot_child = Frame(self.left_frame, bg=self.grey) | |
# layout all of the subcontainers | |
self.top_child.grid(row=0, column=0, columnspan=2, sticky='we') | |
self.left_child.grid(row=1, column=0, sticky='n') | |
self.right_child.grid(row=1, column=1, sticky='n') | |
self.bot_child.grid(row=3, columnspan=2, sticky='ew') | |
self.bot_child.grid_columnconfigure(0, weight=1) | |
# init menu bar | |
#self.init_menu() | |
# create labels | |
self.firmanavnL = Label(self.top_child, bg=self.grey, text='Virksomhedens navn: ') | |
self.årL = Label(self.left_child, bg=self.grey, text='År: (sidste til første)') | |
self.cbut = Checkbutton(self.top_child, bg=self.grey, text='Nyetableret', activebackground=self.grey) | |
# create firma label and trace it | |
self.firmanavn = StringVar(self.top_child) | |
self.firmaE = Entry(self.top_child, textvariable=self.firmanavn) | |
self.firmaE.insert(END, 'FIRMANAVN') | |
self.firmanavn.trace('w', lambda *_: self.update_firmanavn()) | |
# Optionmenu for selecting the business type (produktions-/handelsvirksomhed) | |
options = ['Produktionsvirksomhed', 'Handelsvirksomhed'] | |
self.virkType = StringVar() | |
self.virkType.set('Handelsvirksomhed') | |
temp = OptionMenu(self.top_child, self.virkType, *options) | |
temp.config(bg='grey88', indicatoron=5, width=22, activebackground='grey83') | |
temp.grid(row=0, column=2, sticky='ew', pady=(0,0), padx=(5,0)) | |
#from tkinter import font | |
#f = font.Font(temp, temp.cget("font")) | |
#f.configure(underline=True) | |
# create the 3 entries for the årLabel in a dictionary for itself | |
self.entriesÅr = {} | |
self.svaÅr = {} | |
self.init_årEntries() | |
font = 'courier 9' # TODO <- not used yet - kan slettes | |
# We need two widget in one column, so create a frame to put it in. | |
subFrame = Frame(self.left_child, bg=self.grey) | |
self.overskrift = Label(subFrame, bg=self.grey, width=21, underline="5", text='Rentabilitet', font='Helvetica 8 bold') | |
self.helpBut = Button(subFrame, text='?', width=2, bg=self.grey, font='simsun 9', relief='raised', bd=1, command=self.help_popup) | |
BG1 = Label(self.left_child, bg=self.ggrey, width=35, relief='flat') | |
self.afkastningsgL = Label(self.left_child, bg=self.ggrey, text='Afkastningsgrad,% -------------------------->') | |
self.overskudsgL = Label( self.left_child, bg=self.ggrey, text='Overskudsgrad,% ---------------------------->') | |
self.omsætningshL = Label( self.left_child, bg=self.ggrey, text='Aktivernes omsætningshastighed,gange ---->') | |
self.gældsrenteL = Label( self.left_child, bg=self.ggrey, text='Gældsrente,% ------------------------------->') | |
self.forrentningL = Label( self.left_child, bg=self.ggrey, text='Egenkapitalens forrentning,%---------------->') | |
self.GearingL = Label( self.left_child, bg=self.ggrey, text='Gearing,gange ------------------------------->') | |
BG2 = Label(self.left_child, bg=self.ggrey, width=35, relief='flat') | |
self.overskrift2 = Label( self.left_child, bg=self.grey, width=21, text=' Soliditet og likviditet', font='Helvetica 8 bold') | |
self.solditetsgL = Label( self.left_child, bg=self.ggrey, text='Soliditetsgrad,%') | |
self.likviditetsgL = Label(self.left_child, bg=self.ggrey, text='Likviditetsgrad,%') | |
# create toolstips for each label | |
path = os.getcwd() | |
self.TEST = CreateToolTip(self.afkastningsgL, data = img_data['afk']) | |
CreateToolTip(self.overskudsgL, path + '/formel img/ove.png') | |
CreateToolTip(self.omsætningshL, path + '/formel img/oms.png') | |
CreateToolTip(self.gældsrenteL, path + '/formel img/gæl.png') | |
CreateToolTip(self.forrentningL, path + '/formel img/ege.png') | |
CreateToolTip(self.GearingL, path + '/formel img/gea.png') | |
CreateToolTip(self.solditetsgL, path + '/formel img/sol.png') | |
CreateToolTip(self.likviditetsgL, path + '/formel img/lik.png') | |
# layout all of the labels and entries | |
self.firmanavnL.grid(row=0, column=0, pady=(0, 16)) | |
self.firmaE.grid(row=0, column=1, sticky='nw', pady=(4,0)) | |
self.årL.grid(row=0, column=0) | |
#self.cbut.grid(row=0, column=2, sticky='w', pady=(2,16), padx=(0,80)) | |
CreateToolTip(self.cbut, wraplength=300, | |
text='Er virksomheden nyetableret?\nÆndrer bl.a. på:\n- Acceptabelt for en nyetableret virksomhed at \n have en lav' | |
' soliditetsgrad.\n') | |
# Layout subFrame - we need two in one column | |
subFrame.grid(row=1, column=0, sticky='nsw') | |
self.helpBut.grid(row=1, column=1, sticky='s') | |
self.overskrift.grid(row=1, column=0, sticky='nsw', pady=(10, 1)) | |
self.afkastningsgL.grid(row=2, column=0, sticky='nsw', pady=(2, 0)) | |
self.overskudsgL.grid( row=3, column=0, sticky='nsw', pady=(3, 0)) | |
self.omsætningshL.grid( row=4, column=0, sticky='nsw', pady=(3, 0)) | |
self.gældsrenteL.grid( row=5, column=0, sticky='nsw', pady=(2, 0)) | |
self.forrentningL.grid( row=6, column=0, sticky='nsw', pady=(2, 0)) | |
self.GearingL.grid( row=7, column=0, sticky='nsw', pady=(3, 0)) | |
self.overskrift2.grid( row=8, column=0, sticky='nsw', pady=(12, 0), padx=(5,0)) | |
self.solditetsgL.grid( row=9, column=0, sticky='nsw', pady=(3, 0)) | |
self.likviditetsgL.grid(row=10, column=0,sticky='nsw', pady=(2, 3)) | |
# Background for labels | |
BG1.grid(row=2, column=0, sticky='nsw', columnspan=2, rowspan=6) | |
BG2.grid(row=9, column=0, sticky='nsw', columnspan=2, rowspan=2) | |
# Make 3 entries for each "nøgletal" Label (except headline labels) | |
self.entries = {} | |
self.sva = {} | |
self.init_entries() | |
# Push all entries down to allign them with the labels | |
for i in range(1, 4): | |
self.entries['Afkastningsgrad,%' + str(i)].grid_configure(pady=(4, 4)) | |
# push 6 last entries further down | |
self.entries['Soliditetsgrad,%' + str(i)].grid_configure(pady=(4, 5)) | |
# Create top right frame for buttons such as "expand all" | |
self.right_child_top = Frame(self.right_frame, bg=self.grey, width=700) | |
self.right_child_top.grid(row=0, column=0, sticky='ew') | |
self.right_child_top.grid_columnconfigure(0, weight=1) | |
# Create canvas and scrollbar | |
self.canvas = Canvas(self.right_frame, highlightthickness=0, borderwidth=10, bg='RoyalBlue4', height=700, width=700) | |
self.can_frame = Frame(self.canvas, background=self.grey) | |
self.scrollbar = Scrollbar(self.root, orient='vertical', command=self.canvas.yview) | |
self.canvas.configure(yscrollcommand=self.scrollbar.set) | |
self.canvas.grid_rowconfigure((0, 1, 2, 3), weight=0) | |
self.scrollbar.grid(row=0, column=3, sticky='ns') | |
# canvas | |
self.canvas.grid(row=1, column=0, sticky='nsew') | |
self.can_frame.grid(row=0, column=0, sticky='nsew') | |
self.can_frame.grid_columnconfigure(0, weight=1) | |
self.can_frame.grid_rowconfigure(0, weight=1) | |
self._frame_id = self.canvas.create_window((0, 0), window=self.can_frame, anchor='nw', tags="self.frame") | |
self.canvas.bind_all("<MouseWheel>", self._on_mousewheel) | |
self.canvas.bind("<Configure>", self._resize_frame_width) | |
self.can_frame.bind("<Configure>", self._onFrameConfigure) | |
# Create mini frame for the self.right_child_top | |
self.toolbFrame = Frame(self.right_child_top, bg=self.grey) | |
self.toolbFrame.grid(row=0, column=0, sticky='ew') | |
self.toolbFrame.grid_columnconfigure((0,1), weight=1) | |
# canvas options buttons | |
switch = cycle((True, False)) | |
switch2 = cycle((False, True)) | |
self.co_ex_but = Button(self.toolbFrame, text='Collapse all', bg=self.bColor_leave, command=lambda: self.__collaps_expand_all(next(switch))) | |
self.co_ex_but.grid(row=0, column=0, sticky='we') | |
reorder = Button(self.toolbFrame, text='Reorder text', command=self.__resize_textWidget_height, bg=self.bColor_leave) | |
reorder.grid(row=0, column=1, sticky='we') | |
# Hovor colors of buttons | |
reorder.bind("<Enter>", lambda e: reorder.config(bg=self.bColor_enter)) | |
reorder.bind("<Leave>", lambda e: reorder.config(bg=self.bColor_leave)) | |
self.co_ex_but.bind("<Enter>", lambda e: self.co_ex_but.config(bg=self.bColor_enter)) | |
self.co_ex_but.bind("<Leave>", lambda e: self.co_ex_but.config(bg=self.bColor_leave)) | |
# Initialize the left frame's mid section, here we find: markedsrente; copyBut; collapsible frames | |
self.init_leftFrame_mid_section() | |
# Create and grid "text widgets" and corrosponding buttons to 'Analyse tekst' | |
self._make_TextWidgets() | |
# Initialize(write) 'analyse tekst' and display the 'analyse tekst' in the text widgets in the canvas | |
self.init_analyse_text() | |
self._display_text() | |
# make sure that the text starts "reordered" | |
self.root.after(1500, self.__resize_textWidget_height) | |
self.root.after(4700, self.__resize_textWidget_height) | |
self.canvas.yview_moveto(0) | |
# Stores "self.root.after" id's in order to cancel if needed - used in 'display_statusbar' method | |
self.after_calls = [] | |
def init_menu(self): | |
"""Initializes the menu bar (the top widget of the root window)""" | |
menubar = Menu(self.root, bg='red') | |
self.root.config(menu=menubar) | |
fileMenu = Menu(menubar) | |
fileMenu.add_command(label="Exit") | |
menubar.add_cascade(label="File", menu=fileMenu) | |
def init_DP_collapsibleFrame_and_canvas(self): | |
"""The DP collapsibleFrame is initialized in a function on its own for the sake of simplicity | |
From here it'll: | |
-> make a call to draw the Du Pont Pyramid (in the right_frame -> canvas) | |
-> make a call to initialize the DP entries (in the right_frame -> DP_bot) | |
""" | |
# Du Pont collapsible Frame | |
self.duPontFrame = CollapsibleFrame(self.bot_child, root=self.root, row=1, column=0, width=408, background=self.grey, text='Du Pont Pyramide', font='Ariel 10 bold') | |
self.duPontFrame.grid(row=3, column=0, padx=(2,2), sticky='ew') | |
self.DPP_switch = cycle((True, False)) | |
duPontFrame_topFrame = Frame(self.duPontFrame.interior, bg=self.grey) | |
duPontFrame_topFrame.grid(row=0, column=0, columnspan=2) | |
self.dpButton = Button(duPontFrame_topFrame, text='Vis Du Pont Pyramide', width=20, bg=self.blue_leave, | |
activebackground=self.blue_active, command=lambda *_: self._on_showDP_but(show=next(self.DPP_switch))) | |
self.dpButton.grid(row=0, column=0, sticky='w', padx=(4,250), pady=(10,2)) | |
self.dpButton.bind("<Enter>", lambda e: self.dpButton.config(bg=self.blue_enter)) | |
self.dpButton.bind("<Leave>", lambda e: self.dpButton.config(bg=self.blue_leave)) | |
# Du Pont canvas and options (stuff at self.right_frame) | |
self.DPcanvas = Canvas(self.right_frame, highlightthickness=0)# 'grey84') | |
self.toolbFrameDP = Frame(self.right_frame, bg=self.grey) | |
switch4 = cycle((True, False)) | |
img = PhotoImage(data=img_data['fullscreen']) | |
self.expRightFrame = Button(self.toolbFrameDP, bg=self.ggrey, image=img, | |
command= lambda *_: self.on_expFrame_but(next(switch4)) )#self.left_frame.grid_remove() if next(switch4) else self.left_frame.grid(row=0, column=0, sticky='nsew')) TODO - undo | |
self.expRightFrame.image = img | |
# DPcanvas buttons, frames and entries | |
self.DP_cVar1 = IntVar() | |
self.DP_cVar2 = IntVar() | |
self.DP_botFrame = Frame(self.right_frame, bg='grey85') | |
self.DP_botFrame_but = Button(self.right_frame, text='Hide', bg=self.ggrey, command=self.on_DP_botFrame_but) | |
self.DP_cb1 = Checkbutton(self.duPontFrame.interior, bg=self.grey, activebackground=self.grey, text='Visualiser tal i Du Pont', variable=self.DP_cVar1, command=self.on_DP_cb1) | |
self.DP_cb2 = Checkbutton(self.duPontFrame.interior, bg=self.grey, activebackground=self.grey, text='Disable colors', variable=self.DP_cVar2, command=self.on_DP_cb2) | |
self.DP_botFrame_but.bind("<Enter>", lambda e: self.DP_botFrame_but.config(bg='grey80')) | |
self.DP_botFrame_but.bind("<Leave>", lambda e: self.DP_botFrame_but.config(bg=self.ggrey)) | |
options = ['år1', 'år2', 'år3', 'Udvikling fra: år1 -> år2', 'Udvikling fra: år2 -> år3', 'Udvikling fra: år1 -> år3'] | |
self.DP_dropVar = StringVar() | |
self.DP_dropVar.set('år1') | |
self.DP_dropmenu = OptionMenu(self.DPcanvas, self.DP_dropVar, *options, command=lambda *_: self.on_DP_dropmenu()) | |
self.DP_dropmenu.config(bg='grey98', indicatoron=5) | |
self.DP_dropmenu.grid(row=0, column=0) | |
# trace 'disable colors' cb | |
self.DP_cVar2.trace('w', self.on_DP_disable_colors_cb) | |
# Draw the Du Pont Pyramid | |
self.draw_duPont() | |
# Initialize Du Pont entries | |
self.init_DP_entries() | |
# TODO: where should this be done? - handle clipboard stuff | |
# Bind custom 'ctrl+v' to årstals entries | |
for k, v in self.entriesÅr.items(): | |
v.bind('<<Paste>>', lambda *_, key=k: self.handle_clipboard(key, år_entry=True)) | |
# Bind custom 'ctrl+v' to entries | |
index = 0 | |
for k, v in self.entries.items(): | |
# Check om k er en GEN i så fald set gen_entry til True, ellers set nøgletal til True | |
for label in ('Gennemsnitlig egenkap', 'Gennemsnitlige forpligtel', 'Gennemsnitlige aktiv', 'Resultat'): | |
if k.startswith(label): | |
# gen entry | |
# DEBUG: print(k, 'GEN') | |
v.bind('<<Paste>>', lambda *_, key=k: self.handle_clipboard(key, gen_entry=True)) | |
break | |
else: | |
# None of them matched, it have to be a 'Nøgletals' entry | |
# DEBUG: print(k, 'NØGLETAL') | |
v.bind('<<Paste>>', lambda *_, key=k: self.handle_clipboard(key, nøgletal=True)) | |
def on_DP_disable_colors_cb(self, *_): | |
"""Whenever the disable colors cb is toggled""" | |
state = self.DP_cVar2.get() | |
if state: | |
for label in self.DP_box_labels: | |
self.DPcanvas.itemconfig(self.boxes[label], fill='light blue') | |
else: | |
item = self.DP_dropVar.get() | |
if len(item) == 3: | |
# Hvis kun et år skal vises (fx.'år2') - det vælges i dropdown menuen | |
# Lad 'nr' hvad end året der skal vises er peje til det | |
self.update_analyse_args(Ltext='Everything', nr=item[-1], update='DP') | |
else: | |
self.update_analyse_args(nr=item[-1], Ltext='Everything', update='DP') | |
def on_DP_dropmenu(self): | |
"""Runs a functions depending on the chosen item in the OptionMenu""" | |
# Get the chosen item in the Du Pont optionMenu | |
item = self.DP_dropVar.get() | |
if len(item) == 3: | |
# Hvis kun et år (fx.'år2') skal vises | |
self.update_DP_entries_text(årx=item[-1]) | |
else: | |
# Hvis et år skal vises i forhold til et andet (fx år3-år1) | |
årx = item[17] | |
åry = item[-1] | |
self.update_DP_entries_text(årx=årx, åry=åry) | |
def on_expFrame_but(self, switch): | |
x = self.right_frame.winfo_rootx() | |
y = self.right_frame.winfo_rooty() | |
if switch: | |
self.left_frame.grid_remove() | |
self.root.wm_geometry("+%d+%d" % (x - 6, y - 30)) | |
root.geometry('{}x{}'.format(1015, 700)) | |
else: | |
self.root.wm_geometry("+%d+%d" % (x - 440, y - 30)) | |
self.left_frame.grid(row=0, column=0, sticky='nsew') | |
self.root.geometry('{}x{}'.format(1425, 700)) | |
def on_DP_cb1(self): | |
"""If the Du Pont pyramid is currently shown; | |
show/hide DP_botFrame and DP_botFrame_but""" | |
state = self.DP_cVar1.get() | |
if self.DPcanvas.winfo_exists: | |
# if Du Pont is shown | |
if state: | |
self.DP_cb2.config(state=NORMAL) | |
self.DP_botFrame_but.grid(row=2, column=0, sticky='ew') | |
self.DP_botFrame.grid(row=3, column=0, sticky='ew') | |
self.DP_dropmenu.config(state=NORMAL) | |
# Run 'on_DP_dropmenu' | |
self.on_DP_dropmenu() | |
else: | |
self.DP_botFrame_but.grid_remove() | |
self.DP_botFrame.grid_remove() | |
self.DP_cb2.config(state=DISABLED) | |
self.DP_dropmenu.config(state=DISABLED) | |
self.draw_duPont() | |
def on_DP_cb2(self): | |
"""(This checkbutton can only be activated if cb1 is enabled) | |
Disables/enables DP colors""" | |
def on_DP_botFrame_but(self): | |
"""Show/hide 'DP_bot_frame depending on the state of 'show'""" | |
if self.DP_botFrame.winfo_ismapped(): | |
# If already gridded; hide it | |
self.DP_botFrame.grid_forget() | |
self.DP_botFrame_but.config(text='Show') | |
else: | |
# Show it | |
self.DP_botFrame.grid(row=3, column=0, sticky='ew') | |
self.DP_botFrame_but .config(text='Hide') | |
def update_DP_entries_text(self, årx, åry=None): | |
"""Update the DP_entries' text in the boxes. (see 'init_DP_entries') | |
It does NOT update all boxes' text, only the standard onces! | |
The standard boxes are all the boxes that has to be known in order to fill out the remaining ones.""" | |
# Dict to store value: "årx and åry" with key: "respective box" | |
self.ÅrxÅry = {} | |
if åry: | |
# Der skal bruges 2 entries for hver label | |
for label in self.DP_standard_labels: | |
numx = self._get(label + årx, DP=True) | |
numy = self._get(label + åry, DP=True) | |
label = label[:3].lower() | |
# grad = difference between årx num and åry num in % (indekstal forskel) | |
grad = self.DP_grad(numx, numy) | |
# Forskel mellem tallene | |
num = self.DP_forskel(float(numx), float(numy)) | |
symbol = ('% ⬆ rp' if num > 0 else | |
'% ⬇ rp' if num < 0 else '% rp') | |
if label == 'net': # Nettoomsætning | |
# Da der er 3 nettoom. skal der opdateres 3 tekster med samme tal | |
for i in range(3): | |
self.DPcanvas.itemconfig(self.DP_textID['net' + str(i)], text=self._punctuate_num(str(num))) | |
self.DPcanvas.itemconfig(self.DP_textID_grad['net' + str(i)], text=grad + symbol) | |
self.DP_colorize_box('net' + str(i), num) | |
# Store the value | |
self.ÅrxÅry['net' + str(i)] = (numx, numy) | |
else: | |
self.DPcanvas.itemconfig(self.DP_textID[label], text=self._punctuate_num(str(num))) | |
self.DPcanvas.itemconfig(self.DP_textID_grad[label], text=grad + symbol) | |
self.DP_colorize_box(label, num) | |
# Store the value | |
self.ÅrxÅry[label] = (numx, numy) | |
# Update the box one step higher in the pyramid -> 'Dækningsbidrag' | |
self.DP_update_dækningsbidrag(forhold=True) | |
else: | |
# Udfyld hvert standard box med den tilhørende entry tal | |
for label in self.DP_standard_labels: | |
# Get the number written in the entry | |
num = self._get(label + årx, DP=True) | |
label = label[:3].lower() | |
if label == 'net': # Nettoomsætning | |
# Da der er 3 boxes med label: "nettoom." | |
for i in range(3): | |
self.DPcanvas.itemconfig(self.DP_textID['net' + str(i)], text=self._punctuate_num(str(num))) | |
self.DPcanvas.itemconfig(self.DP_textID_grad['net' + str(i)], text='') | |
# Colorize the box depending on the number | |
self.DP_colorize_box(label + str(i), num) | |
else: | |
self.DPcanvas.itemconfig(self.DP_textID[label], text=self._punctuate_num(str(num))) | |
self.DPcanvas.itemconfig(self.DP_textID_grad[label], text='') | |
# Colorize the box depending on the number | |
self.DP_colorize_box(label, num) | |
# Update the box one step higher in the pyramid -> 'Dækningsbidrag' | |
self.DP_update_dækningsbidrag() | |
def DP_update_dækningsbidrag(self, forhold=False): | |
"""Update dækningsbidrag text and make a call to update resultat af primær drift""" | |
# Vis årx i forhold til åry (åry - årx) | |
if forhold: | |
# Udregn dækningsbidrag | |
dækX = self.ÅrxÅry['net0'][0] - self.ÅrxÅry['var'][0] | |
dækY = self.ÅrxÅry['net0'][1] - self.ÅrxÅry['var'][1] | |
self.ÅrxÅry['dæk'] = (dækX, dækY) | |
# grad = difference between årx num and åry num in % (indekstal forskel) | |
grad = self.DP_grad(dækX, dækY) | |
# Forskel mellem tallene | |
dæk = self.DP_forskel(dækX, dækY) | |
# Opdater text | |
self.DPcanvas.itemconfig(self.DP_textID['dæk'], text=self._punctuate_num(str(dæk))) | |
self.DPcanvas.itemconfig(self.DP_textID_grad['dæk'], text=grad + self.DP_getSymbol(dæk)) | |
self.DP_colorize_box(label='dæk', num=dæk) | |
self.DP_update_RaPD(forhold=True) | |
else: | |
# Udregn dækningsbidrag | |
dæk = self._DPget_text(self.DP_textID['net0']) - self._DPget_text(self.DP_textID['var']) | |
self.DP_entries['dæk'] = dæk | |
# Colorize dækningsbidrag box | |
self.DP_colorize_box(label='dæk', num=dæk) | |
# Update box number | |
self.DPcanvas.itemconfig(self.DP_textID['dæk'], text=self._punctuate_num(str(dæk))) | |
self.DPcanvas.itemconfig(self.DP_textID_grad['dæk'], text='') | |
self.DP_update_RaPD() | |
def DP_update_RaPD(self, forhold=False): | |
"""Update resultat af primær drift and make a call to update Overskudsgrad""" | |
# Vis årx i forhold til åry (åry - årx) | |
if forhold: | |
# Udregn resultat af primær drift | |
resX = self.ÅrxÅry['dæk'][0] - self.ÅrxÅry['kap'][0] | |
resY = self.ÅrxÅry['dæk'][1] - self.ÅrxÅry['kap'][1] | |
self.ÅrxÅry['res'] = (resX, resY) | |
# grad = difference between årx num and åry num in % (indekstal forskel) | |
grad = self.DP_grad(resX, resY) | |
# Forskel mellem tallene | |
res = self.DP_forskel(resX, resY) | |
# Opdater text | |
self.DPcanvas.itemconfig(self.DP_textID['res'], text=self._punctuate_num(str(res))) | |
self.DPcanvas.itemconfig(self.DP_textID_grad['res'], text=grad + self.DP_getSymbol(res)) | |
self.DP_colorize_box(label='res', num=res) | |
self.DP_update_ove(forhold=True) | |
else: | |
# Udregn RaPD | |
res = self._DPget_text(self.DP_textID['dæk']) - self._DPget_text(self.DP_textID['kap']) | |
self.DP_entries['res'] = res | |
# Colorize RaPD box | |
self.DP_colorize_box(label='res', num=res) | |
# Update box number | |
self.DPcanvas.itemconfig(self.DP_textID['res'], text=self._punctuate_num(str(res))) | |
self.DPcanvas.itemconfig(self.DP_textID_grad['res'], text='') | |
# Update the box one step higher in the pyramid -> 'Overskudsgrad' | |
self.DP_update_ove() | |
def DP_update_ove(self, forhold=False): | |
"""Update 'Overskudsgrad' and make a call to update 'Afkastningsgrad' """ | |
# Vis årx i forhold til åry (åry - årx) | |
if forhold: | |
# Udregn overskudsgrad | |
oveX = self.ÅrxÅry['res'][0] / self.ÅrxÅry['net0'][0] | |
oveY = self.ÅrxÅry['res'][1] / self.ÅrxÅry['net0'][1] | |
self.ÅrxÅry['ove'] = (oveX, oveY) | |
# grad = difference between årx num and åry num in % (indekstal forskel) | |
grad = self.DP_grad(oveX, oveY) | |
# Forskel mellem tallene | |
ove = self.DP_forskel(oveX, oveY) | |
# Opdater text | |
self.DPcanvas.itemconfig(self.DP_textID['ove'], text=self._punctuate_num(str(ove)) + ' pp') | |
self.DPcanvas.itemconfig(self.DP_textID_grad['ove'], text=grad + self.DP_getSymbol(ove)) | |
self.DP_colorize_box(label='ove', num=ove) | |
self.DP_update_omsA(forhold=True) | |
else: | |
# Udregn overskudsgrad | |
ove = self._DPget_text(self.DP_textID['res']) / self._DPget_text(self.DP_textID['net0']) | |
self.DP_entries['ove'] = ove | |
# Colorize ove box | |
self.DP_colorize_box(label='ove', num=ove) | |
# Update box number | |
self.DPcanvas.itemconfig(self.DP_textID['ove'], text=self._punctuate_num(str(ove)) + '%') | |
self.DPcanvas.itemconfig(self.DP_textID_grad['ove'], text='') | |
# Update the number of the box 'omsætningsaktiver' (omsA) | |
self.DP_update_omsA() | |
def DP_update_omsA(self, forhold=False): | |
"""Update 'Omsætningsaktiver' and make a call to update 'Aktiver' """ | |
# Vis årx i forhold til åry (åry - årx) | |
if forhold: | |
# Udregn omsætningsaktiver | |
omsAX = self.ÅrxÅry['til'][0] + self.ÅrxÅry['lik'][0] | |
omsAY = self.ÅrxÅry['til'][1] + self.ÅrxÅry['lik'][1] | |
self.ÅrxÅry['omsA'] = (omsAX, omsAY) | |
# grad = difference between årx num and åry num in % (indekstal forskel) | |
grad = self.DP_grad(omsAX, omsAY) | |
# Forskel mellem tallene | |
omsA = self.DP_forskel(omsAX, omsAY) | |
# Opdater text | |
self.DPcanvas.itemconfig(self.DP_textID['omsA'], text=self._punctuate_num(str(omsA))) | |
self.DPcanvas.itemconfig(self.DP_textID_grad['omsA'], text=grad + self.DP_getSymbol(omsA)) | |
self.DP_colorize_box(label='omsA', num=omsA) | |
self.DP_update_akt(forhold=True) | |
else: | |
# Udregn omsætningsaktiver | |
omsA = self._DPget_text(self.DP_textID['til']) + self._DPget_text(self.DP_textID['lik']) | |
self.DP_entries['omsA'] = omsA | |
# Colorize omsA box | |
self.DP_colorize_box(label='omsA', num=omsA) | |
# Update box number | |
self.DPcanvas.itemconfig(self.DP_textID['omsA'], text=self._punctuate_num((str(omsA)))) | |
self.DPcanvas.itemconfig(self.DP_textID_grad['omsA'], text='') | |
# Update the box one step higher in the pyramid -> 'Aktiver' | |
self.DP_update_akt() | |
def DP_update_akt(self, forhold=False): | |
"""Update 'aktiver' and make a call to update 'Aktiver' """ | |
# Vis årx i forhold til åry (åry - årx) | |
if forhold: | |
# Udregn aktiver | |
aktX = self.ÅrxÅry['anl'][0] + self.ÅrxÅry['omsA'][0] | |
aktY = self.ÅrxÅry['anl'][1] + self.ÅrxÅry['omsA'][1] | |
self.ÅrxÅry['akt'] = (aktX, aktY) | |
# grad = difference between årx num and åry num in % (indekstal forskel) | |
grad = self.DP_grad(aktX, aktY) | |
# Forskel mellem tallene | |
akt = self.DP_forskel(aktX, aktY) | |
# Opdater text | |
self.DPcanvas.itemconfig(self.DP_textID['akt'], text=self._punctuate_num(str(akt))) | |
self.DPcanvas.itemconfig(self.DP_textID_grad['akt'], text=grad + self.DP_getSymbol(akt)) | |
self.DP_colorize_box(label='akt', num=akt) | |
self.DP_update_oms(forhold=True) | |
else: | |
# Udregn aktiver | |
akt = self._DPget_text(self.DP_textID['anl']) + self._DPget_text(self.DP_textID['omsA']) | |
self.DP_entries['akt'] = akt | |
# Colorize akt box | |
self.DP_colorize_box(label='akt', num=akt) | |
# Update box number | |
self.DPcanvas.itemconfig(self.DP_textID['akt'], text=self._punctuate_num(str(akt))) | |
self.DPcanvas.itemconfig(self.DP_textID_grad['akt'], text='') | |
# Update the box one step higher in the pyramid -> 'Aktivernes omsætningshastigheder' (oms) | |
self.DP_update_oms() | |
def DP_update_oms(self, forhold=False): | |
"""Update 'Omsætningsaktiver' and make a call to update 'Aktiver' """ | |
# Vis årx i forhold til åry (åry - årx) | |
if forhold: | |
# Udregn aktivernes omsætningshastighed | |
omsX = self.ÅrxÅry['net0'][0] / self.ÅrxÅry['akt'][0] | |
omsY = self.ÅrxÅry['net0'][1] / self.ÅrxÅry['akt'][1] | |
self.ÅrxÅry['oms'] = (omsX, omsY) | |
# grad = difference between årx num and åry num in % (indekstal forskel) | |
grad = self.DP_grad(omsY, omsX) | |
# Forskel mellem tallene | |
oms = self.DP_forskel(omsX, omsY) | |
# Opdater text | |
self.DPcanvas.itemconfig(self.DP_textID['oms'], text=self._punctuate_num(str(oms)) + ' pp') | |
self.DPcanvas.itemconfig(self.DP_textID_grad['oms'], text=grad + self.DP_getSymbol(oms)) | |
self.DP_colorize_box(label='oms', num=oms) | |
# Update afkastningsgraden (the final box that hasn't been updated) | |
self.DP_update_afk(forhold=True) | |
else: | |
# Udregn aktivernes omsætningshastigheder | |
oms = self._DPget_text(self.DP_textID['net0']) / self._DPget_text(self.DP_textID['akt']) | |
self.DP_entries['akt'] = oms | |
# Colorize oms box | |
self.DP_colorize_box(label='oms', num=oms) | |
# Update box number | |
self.DPcanvas.itemconfig(self.DP_textID['oms'], text=self._punctuate_num(str(oms)) + 'g') | |
self.DPcanvas.itemconfig(self.DP_textID_grad['oms'], text='') | |
# Update afkastningsgraden (the final box that hasn't been updated) | |
self.DP_update_afk() | |
def DP_update_afk(self, forhold=False): | |
"""Update 'Afkastningsgrad' and make a call to update 'Omsætningsaktiver' """ | |
# Vis årx i forhold til åry (åry - årx) | |
if forhold: | |
# Udregn afkastningsgraden | |
afkX = self.ÅrxÅry['ove'][0] * self.ÅrxÅry['oms'][0] | |
afkY = self.ÅrxÅry['ove'][1] * self.ÅrxÅry['oms'][1] | |
self.ÅrxÅry['afk'] = (afkX, afkY) | |
# grad = difference between årx num and åry num in percentage (indekstal forskel) | |
grad = self.DP_grad(afkX, afkY) | |
# Forskel mellem tallene | |
afk = self.DP_forskel(afkX, afkY) | |
# Opdater text | |
self.DPcanvas.itemconfig(self.DP_textID['afk'], text=self._punctuate_num(str(afk)) + ' pp') | |
self.DPcanvas.itemconfig(self.DP_textID_grad['afk'], text=grad + self.DP_getSymbol(afk)) | |
self.DP_colorize_box(label='afk', num=afk) | |
else: | |
# Udregn afkastningsgrad | |
ove = float(self.DPcanvas.itemcget(self.DP_textID['ove'], 'text')[:-1].replace('.','').replace(',','.')) | |
oms = float(self.DPcanvas.itemcget(self.DP_textID['oms'], 'text')[:-1].replace('.','').replace(',','.')) | |
afk = ove * oms | |
self.DP_entries['afk'] = afk | |
# Colorize afk box | |
self.DP_colorize_box(label='afk', num=afk) | |
self.DPcanvas.itemconfig(self.DP_textID_grad['afk'], text='') | |
# Update box number | |
self.DPcanvas.itemconfig(self.DP_textID['afk'], text=self._punctuate_num(str(afk)) + '%') | |
def DP_forskel(self, x : float, y : float) -> float: | |
"""returns the difference between x and y (y-x)""" | |
try: | |
if (x < 0 and y < 0): | |
return abs(x) - abs(y) | |
else: | |
return y - x | |
except ZeroDivisionError: | |
return 0 | |
def DP_grad(self, x : float, y : float) -> str: | |
"""returns rp between x and y""" | |
try: | |
if (x < 0): | |
return str(round(-(y*100 / x - 100), 2)) | |
else: | |
return str(round(y*100 / x - 100, 2)) | |
except ZeroDivisionError: | |
return '0' | |
def DP_colorize_box(self, label : str, num : float): | |
"""Farv en box alt efter om num er positiv eller negativ""" | |
# If colors are disabled | |
if self.DP_cVar2.get(): | |
color = 'light blue' # Default DP pyramid color | |
else: | |
# Color = green or red depending on 'num' | |
color = 'light green' if num > 0 else '#e26a84' #(0,self.green,0) if num > 0 else (124,self.green,0) | |
if label == 'Nettoomsætning': | |
for i in range(3): | |
self.DPcanvas.itemconfig(self.boxes['net' + str(i)], fill=color)#'#%02x%02x%02x' % color) | |
else: | |
self.DPcanvas.itemconfig(self.boxes[label], fill=color)#'#%02x%02x%02x' % color ) | |
def _on_showDP_but(self, show): | |
"""When 'grafisk' button is pressed | |
shown = bool: Du Pont is currently shown""" | |
self.root.geometry('{}x{}'.format(1425, self.root.winfo_height())) # 1425)) | |
#print(self.root.winfo_width()) | |
#print(self.root.winfo_height()) # TODO, resizing wwhen pressing "Vis du pont" | |
# SHOW DU PONT PYRAMID | |
if show: | |
# forget the text analyse canvas | |
self.canvas.grid_forget() | |
self.toolbFrame.grid_forget() | |
self.scrollbar.grid_forget() | |
# display expand/collapse right_frame/left_frame | |
self.expRightFrame.grid(row=0, column=0, sticky='nsew') | |
# display dp checkbuttons | |
self.DP_cb1.grid(row=1, column=0) | |
self.DP_cb2.grid(row=1, column=1) | |
# display du pont canvas | |
self.toolbFrameDP.grid(row=0, column=0, sticky='ew') | |
self.DPcanvas.grid( row=1, column=0, sticky='nsew') | |
if self.DP_cVar1.get(): | |
self.DP_botFrame_but.grid(row=2, column=0, sticky='ew') | |
self.DP_botFrame.grid(row=3, column=0, sticky='ew') | |
# Change the text of the button | |
self.dpButton.config(text='Vis analyse text') | |
# HIDE DU PONT PYRAMID | |
elif show == False: | |
# forget the currently shown du pont canvas | |
self.toolbFrameDP.grid_forget() | |
self.DPcanvas.grid_forget() | |
self.DP_botFrame_but.grid_forget() | |
self.DP_botFrame.grid_forget() | |
self.expRightFrame.grid_forget() | |
# forget dp checkbuttons | |
self.DP_cb1.grid_forget() | |
self.DP_cb2.grid_forget() | |
# display the text analyse canvas | |
root.geometry('{}x{}'.format(850, self.root.winfo_height())) | |
self.toolbFrame.grid(row=0, column=0, sticky='ew') | |
self.canvas.grid( row=1, column=0, sticky='nsew') | |
self.scrollbar.grid( row=0, column=3, sticky='ns') | |
# Change the text of the button | |
self.dpButton.config(text='Vis Du Pont Pyramide') | |
def draw_duPont(self): | |
"""Draw objects to DPcanvas and bind the rectangles to a function""" | |
color = 'lightblue' | |
c2 = 'white' | |
pady = 120 | |
height = round(650 / 2) | |
width = round(1000 / 2) | |
width3 = round(1000 / 3) # 1/3 af window width | |
self.boxes = [] | |
# 1. række | |
self.boxes.append(self.DPcanvas.create_rectangle(width - 70, 20 + (pady*0), width + 70, 100 + (pady*0), fill=color, activewidth=2, activeoutline=c2)) # R1B1 | |
# 2. række | |
self.DPcanvas.create_line(width, 100 + (pady*0), width3, 20 + (pady*1)) | |
self.DPcanvas.create_line(width, 100 + (pady*0), (width3*2), 20 + (pady*1)) | |
self.boxes.append(self.DPcanvas.create_rectangle(width3 - 70, 20 + (pady*1), width3 + 70, 100 + (pady*1), fill=color, activewidth=2, activeoutline=c2)) # R2B1 | |
self.boxes.append(self.DPcanvas.create_rectangle((width3 * 2) - 100, 20 + (pady*1), (width3*2) + 134, 100 + (pady*1), fill=color, activewidth=2, activeoutline=c2)) # R2B2 | |
# 3. række | |
self.DPcanvas.create_line(width3, 100 + (pady*1), (width3) - 90, 20 + (pady*2)) # R2B1 -> R3B1 | |
self.DPcanvas.create_line(width3, 100 + (pady*1), (width3) + 90, 20 + (pady*2)) # R2B1 -> R3B2 | |
self.DPcanvas.create_line((width3 * 2), 100 + (pady*1), (width3*2) - 90, 20 + (pady*2)) | |
self.DPcanvas.create_line((width3 * 2), 100 + (pady*1), (width3*2) + 90, 20 + (pady*2)) | |
self.boxes.append(self.DPcanvas.create_rectangle((width3) - 230, 20 + (pady*2), (width3) - 60, 100 + (pady*2), fill=color, activewidth=2, activeoutline=c2)) # R3B1 | |
self.boxes.append(self.DPcanvas.create_rectangle((width3) + 120, 20 + (pady*2), (width3) - 20, 100 + (pady*2), fill=color, activewidth=2, activeoutline=c2)) # R3B2 | |
self.boxes.append(self.DPcanvas.create_rectangle((width3 * 2) - 120, 20 + (pady*2), (width3*2) + 20, 100 + (pady*2), fill=color, activewidth=2, activeoutline=c2)) # R3B3 | |
self.boxes.append(self.DPcanvas.create_rectangle((width3 * 2) + 200, 20 + (pady*2), (width3*2) + 60, 100 + (pady*2), fill=color, activewidth=2, activeoutline=c2)) # R3B4 | |
R3B1 = (width3) - 105 # Række 3, Box 1: boxens midte width | |
R3B4 = (width3 * 2) + 90 # Række 4, Box 4: boxens midte width | |
# 4. række | |
self.DPcanvas.create_line(R3B1, 100 + (pady*2), R3B1 - 90, 20 + (pady*3)) | |
self.DPcanvas.create_line(R3B1, 100 + (pady*2), R3B1 + 90, 20 + (pady*3)) | |
self.DPcanvas.create_line(R3B4, 100 + (pady*2), R3B4 - 90, 20 + (pady*3)) | |
self.DPcanvas.create_line(R3B4, 100 + (pady*2), R3B4 + 90, 20 + (pady*3)) | |
self.boxes.append(self.DPcanvas.create_rectangle(R3B1 - 160, 20 + (pady*3), R3B1 - 20, 100 + (pady*3), fill=color, activewidth=2, activeoutline=c2)) # R4B1 | |
self.boxes.append(self.DPcanvas.create_rectangle(R3B1 + 196, 20 + (pady*3), R3B1 + 20, 100 + (pady*3), fill=color, activewidth=2, activeoutline=c2)) # R4B2 | |
self.boxes.append(self.DPcanvas.create_rectangle(R3B4 - 170, 20 + (pady*3), R3B4 - 20, 100 + (pady*3), fill=color, activewidth=2, activeoutline=c2)) # R4B3 | |
self.boxes.append(self.DPcanvas.create_rectangle(R3B4 + 170, 20 + (pady*3), R3B4 + 20, 100 + (pady*3), fill=color, activewidth=2, activeoutline=c2)) # R4B4 | |
R4B1 = R3B1 - 90 | |
R4B4 = R3B4 + 90 | |
# 5. række | |
self.DPcanvas.create_line(R4B1, 100 + (pady*3), R4B1 - 90, 20 + (pady*4)) # R4B1 -> R5B1 | |
self.DPcanvas.create_line(R4B1, 100 + (pady*3), R4B1 + 90, 20 + (pady*4)) # R4B1 -> R5B2 | |
self.DPcanvas.create_line(R4B4, 100 + (pady*3), R4B4 - 90, 20 + (pady*4)) | |
self.DPcanvas.create_line(R4B4, 100 + (pady*3), R4B4 + 90, 20 + (pady*4)) | |
self.boxes.append(self.DPcanvas.create_rectangle(R4B1 - 100, 20 + (pady*4), R4B1 + 40, 100 + (pady*4), fill=color, activewidth=2, activeoutline=c2)) # R5B1 | |
self.boxes.append(self.DPcanvas.create_rectangle(R4B1 + 245, 20 + (pady*4), R4B1 + 80, 100 + (pady*4), fill=color, activewidth=2, activeoutline=c2)) # color | |
self.boxes.append(self.DPcanvas.create_rectangle(R4B4 - 325, 20 + (pady*4), R4B4 - 80, 100 + (pady*4), fill=color, activewidth=2, activeoutline=c2)) # R5B3 | |
self.boxes.append(self.DPcanvas.create_rectangle(R4B4 + 130, 20 + (pady*4), R4B4 - 40, 100 + (pady*4), fill=color, activewidth=2, activeoutline=c2)) # R5B4 | |
# Text setting | |
f = 'Purisa 11' | |
f2 = 'Purisa 10' | |
padxT = 10 | |
padyT = 20 | |
# R1 | |
self.DPcanvas.create_text(width - 70 + padxT, (20 + (pady*0)) + padyT, font=f, anchor='w', text="Afkastningsgrad") # item 30 | |
# R2 | |
self.DPcanvas.create_text((width3 - 70) + padxT, (20 + (pady*1)) + padyT, font=f, anchor='w', text="Overskudsgrad") # item 31 | |
self.DPcanvas.create_text((width3 * 2 - 100) + padxT, (20 + (pady*1)) + padyT, font=f, anchor='w', text="Aktivernes omsætningshastighed") # item 32 | |
# R3 | |
self.DPcanvas.create_text((width3 - 230) + padxT, (20 + (pady*2)) + padyT, font=f, anchor='w', text="Resultat af primær drift") # item 33 | |
self.DPcanvas.create_text((width3 - 20) + padxT, (20 + (pady*2)) + padyT, font=f, anchor='w', text="Nettoomsætning") # item 34 | |
self.DPcanvas.create_text((width3 * 2 - 120) + padxT,(20 + (pady*2)) + padyT, font=f, anchor='w', text="Nettoomsætning") # item 35 | |
self.DPcanvas.create_text((width3 * 2 + 60) + padxT, (20 + (pady*2)) + padyT, font=f, anchor='w', text="Aktiver") # item 36 | |
# R4 | |
self.DPcanvas.create_text(R3B1 - 160 + padxT, (20 + (pady*3)) + padyT, font=f, anchor='w', text="Dækningsbidrag") # item 37 | |
self.DPcanvas.create_text(R3B1 + 20 + padxT, (20 + (pady*3)) + padyT, font=f, anchor='w', text="Kapacitetsomkostninger") # item 38 | |
self.DPcanvas.create_text(R3B4 - 170 + padxT, (20 + (pady*3)) + padyT, font=f, anchor='w', text="Anlægsaktiver") # item 39 | |
self.DPcanvas.create_text(R3B4 + 20 + padxT, (20 + (pady*3)) + padyT, font=f, anchor='w', text="Omsætningsaktiver") # item 40 | |
# R5 | |
self.DPcanvas.create_text(R4B1 - 100 + padxT, (20 + (pady*4)) + padyT, font=f, anchor='w', text="Nettoomsætning") # item 41 | |
self.DPcanvas.create_text(R4B1 + 80 + padxT, (20 + (pady*4)) + padyT, font=f, anchor='w', text="Variable omkostninger") # item 42 | |
self.DPcanvas.create_text(R4B4 - 325 + padxT, (20 + (pady*4)) + padyT, font=f, anchor='w', text="Tilgodehavender+varebeholdninger") # item 43 | |
self.DPcanvas.create_text(R4B4 - 40 + padxT, (20 + (pady*4)) + padyT, font=f, anchor='w', text="Likvide beholdninger") # item 44 | |
# regnetegn | |
self.DPcanvas.create_text(width, 56 + (pady * 1), font="Purisa 17 ", text='x') # item 45 | |
self.DPcanvas.create_text(width3 - 40, 56 + (pady * 2), font="Purisa 15 bold", text=':') # item 46 | |
self.DPcanvas.create_text(width3 * 2 + 40, 56 + (pady * 2), font="Purisa 15 bold", text=':') # item 47 | |
self.DPcanvas.create_text(R3B1, 57 + (pady * 3), font="Purisa 16 bold", text='‒') # item 48 | |
self.DPcanvas.create_text(R3B4, 60 + (pady * 3), font="Purisa 17", text='+') # item 49 | |
self.DPcanvas.create_text(R4B1 + 60, 57 + (pady * 4), font="Purisa 16 bold", text='‒') # item 50 | |
self.DPcanvas.create_text(R4B4 - 60, 60 + (pady * 4), font="Purisa 17", text='+') # item 51 | |
# Help box | |
self.DP_helpBox = 9999 # Create a dummy to avoid attributeError | |
self.DPcanvas.tag_bind('helpBox', "<Button-1>", self.on_DP_helpBox_click) | |
self.DP_helpBox_lab = self.DPcanvas.create_text((width + 155), 25, anchor='w', font=f2, text='') | |
self.DP_helpBox_årxLab = self.DPcanvas.create_text((width + 155), 45, anchor='w', font='consolas 10', text='') | |
self.DP_helpBox_åryLab = self.DPcanvas.create_text((width + 155), 67, anchor='w', font='consolas 10', text='') | |
#self.DP_helpBox_forskelLab = self.DPcanvas.create_text((width + 155), 89, anchor='w', font=f2, text='Forskel: 23.312.123 kr.') | |
self.DP_labels = ('Afkastningsgrad', 'Overskudsgrad', 'Aktivernes omsætningshastighed', 'Resultat af primær drift', 'Nettoomsætning', 'Nettoomsætning', | |
'Aktiver', 'Dækningsbidrag', 'Kapacitetsomkostninger', 'Anlægsaktiver', 'Omsætningsaktiver', 'Nettoomsætning', 'Variable produktionsomkostninger', | |
'Tilgodehavender+varebeholdninger', 'Likvide beholdninger') | |
self.DP_textID = {} | |
self.DP_textID_grad = {} | |
self.DP_boxTooltips = {} | |
self.DP_box_labels = ('afk', 'ove', 'oms', 'res', 'net0', 'net1', 'akt', 'dæk', 'kap', 'anl', 'omsA', 'net2', 'var', 'til', 'lik') | |
self.boxes = {lab: box for lab, box in zip(self.DP_box_labels, self.boxes)} | |
i = 0 | |
for key, box in self.boxes.items(): | |
coords = self.DPcanvas.coords(box) | |
self.DP_textID[key] = self.DPcanvas.create_text(coords[0] + padxT, coords[1] + padyT*2, font=f, anchor='w', text='') | |
self.DP_textID_grad[key] = self.DPcanvas.create_text(coords[0] + padxT, coords[1] + padyT*2 + 20, font=f, anchor='w', text='') | |
self.DPcanvas.tag_bind(box, "<Enter>", lambda e, key=key, label=self.DP_labels[i]: self.on_box_enter(Ltext=key, label=label)) | |
i += 1 | |
def on_DP_helpBox_click(self, e): | |
"""Remove 'DP_helpbox' rectangle from 'DPcanvas' and set the text inside the rectangle to nothing (invisible)""" | |
self.DPcanvas.delete(self.DP_helpBox) | |
self.DPcanvas.itemconfig(self.DP_helpBox_lab, text='') | |
self.DPcanvas.itemconfig(self.DP_helpBox_årxLab, text='') | |
self.DPcanvas.itemconfig(self.DP_helpBox_åryLab, text='') | |
def on_box_enter(self, Ltext, label): | |
"""If dropmenu is set to compare years -> draw helpbox and write text to it""" | |
item = self.DP_dropVar.get() | |
if len(item) != 3: | |
# Der er blevet valgt en option med sammenligning af år fra "dropmenu" | |
if self.DPcanvas.coords(self.DP_helpBox) == []: | |
# helpBox is not drawn; draw it now and store the id | |
self.DP_helpBox = self.DPcanvas.create_rectangle(650, 15, 980, 120, fill='grey84', activewidth=2, activeoutline='red', tag='helpBox') | |
self.DPcanvas.tag_lower(self.DP_helpBox) | |
# Get år der skal sammenlignes (eg. år1 år3) | |
årx = item[15:18] | |
åry = item[-3:] | |
# Nummeret for hvert år til den tilhørende Ltext | |
årxNum, åryNum = self.ÅrxÅry[Ltext] | |
t1 = 'År ' + self.entriesÅr[årx].get() + ': ' + locale.format("%16.2f", årxNum, grouping=True) | |
t2 = 'År ' + self.entriesÅr[åry].get() + ': ' + locale.format("%16.2f", åryNum, grouping=True) | |
self.DPcanvas.itemconfig(self.DP_helpBox_lab, text=label) | |
self.DPcanvas.itemconfig(self.DP_helpBox_årxLab, text=t1) | |
self.DPcanvas.itemconfig(self.DP_helpBox_åryLab, text=t2) | |
def init_leftFrame_mid_section(self): | |
"""'init_leftFrame_mid_section' initialized in a function on its own for the sake of simplicity | |
In the 'LeftFrame's mid section' we find: | |
# analyse tekst = AT | |
left_frame right_frame | |
------------------------------------------------------------------------------------------------ | |
| <- top_child (AT) -> | canva stuff | | |
|---------------------------------------------------| | | |
| left_child (AT labels) | right_child (AT entries) | | | |
| | | | | |
------------- |------------------------|--------------------------| | | |
mid section ---> | left_frame_mid | | | |
| (markedsrente, copyBut etc.) | | | |
------------- |---------------------------------------------------| | | |
bot section ---> | bot_child | | | |
| (collapsibleFrames such as: årsager, DP) | | | |
------------ |---------------------------------------------------| | | |
| bot_frame (for statusbar) | | | |
------------------------------------------------------------------------------------------------ | |
Markedsrente - label, entry og markedsr. button --------------------------------------> self.left_frame_mid | |
CopyButton - Knap til at kopier analyse teksten. ------------------------------------> self.left_frame_mid | |
CollapsibleFrame - Du Pont Pyramide - Visualiserer tal i en Du Pont pyramide ----------> self.bot_child | |
CollapsibleFrame - årsager - hvis aktiveret skriver årsager til analyse teksten. | |
# Gennemsnitlig(e) -egenkapital, -forplitegelser og -aktiver. | |
""" | |
# Create the 'mid_frame' in the 'left_frame' - (mid section) | |
self.left_frame_mid = Frame(self.bot_child, bg=self.grey) | |
self.left_frame_mid.grid(row=0, column=0, padx=(2,2), sticky='ew', pady=(0,5)) | |
self.left_frame_mid.grid_columnconfigure(0, weight=1) | |
# Populate the mid section # | |
# Create the markedsrente section | |
mrFrame = Frame(self.left_frame_mid, bg=self.ggrey) | |
sv = StringVar(mrFrame) | |
mrLab1 = Label(mrFrame, bg=self.ggrey, text='Markedsrente: ') | |
mrLab2 = Label(mrFrame, bg=self.ggrey, text='%') | |
CreateToolTip(mrLab1, wraplength=310, text="Markedsrenten skal på gymnasieniveau sammenlignes med en markedsrente på 4-5% samt et risikotillæg på 1-2%") | |
self.mrEntry = Entry(mrFrame, width=9, textvariable=sv) | |
self.mrEntry.insert(END, '4.00') | |
self.entries['markedsrente'] = self.mrEntry | |
self.markedsr = Button(mrFrame, text='Oversigt over markedsrenter', width=23, bg=self.blue_leave, activebackground=self.blue_active, command=self.markedsrente_window) | |
# Trace markedsrente entry | |
sv.trace('w', lambda name, *_, sv=sv: self._check_markedsrente(sv, name, 'markedsrente')) | |
# Hover color | |
self.markedsr.bind("<Enter>", lambda event, button=self.markedsr: self.markedsr.config(bg=self.blue_enter)) | |
self.markedsr.bind("<Leave>", lambda event, button=self.markedsr: self.markedsr.config(bg=self.blue_leave)) | |
# Grid markedsrente | |
mrFrame.grid( row=0, column=0, pady=(12,0), sticky='ew') | |
mrLab1.grid( row=0, column=0, pady=(3,3), padx=(0,0)) | |
self.mrEntry.grid( row=0, column=1, pady=(3,3), padx=(0,0), sticky='w') | |
mrLab2.grid( row=0, column=2, pady=(3,3)) | |
self.markedsr.grid(row=0, column=3, pady=(3,3), padx=(92,2)) | |
# Create frame to have "aktLic" og "copy" knapperne i samme column | |
tempFrame = Frame(self.left_frame_mid, background='grey82') #self.grey) | |
tempFrame.grid(row=18, column=0, padx=(20, 20), pady=(10, 10), sticky='we') | |
# Create the 'kopier analyse tekst' button | |
copy = Button(tempFrame, text='Kopier analyse tekst', activebackground='light green', width=16, bg=self.blue_leave, font='arial 10', bd=2, command=self.copy_analysetext_to_clipboard) | |
# Grid copy button | |
copy.grid(row=18, column=0, padx=(18,77), pady=(11, 11), sticky='w') | |
# copyBut hover colors | |
copy.bind("<Enter>", lambda e: copy.config(bg=self.blue_enter)) | |
copy.bind("<Leave>", lambda e: copy.config(bg=self.blue_leave)) | |
#pw = PanedWindow(self.left_frame_mid, bg='black') | |
#pw.grid(row=1, column=0, sticky='we') | |
# Indstillinger | |
inds_window = Indstillinger_window(root, self.grey, self.blue_active, self.blue_enter, self.blue_leave) | |
indsBut = Button(tempFrame, text='Indstillinger', activebackground=self.blue_active, width=16, | |
bg=self.blue_leave, font='arial 10', bd=1, command=lambda: inds_window.run()) | |
indsBut.grid(row=18, column=1) | |
# indsBut hover colors | |
indsBut.bind("<Enter>", lambda e: indsBut.config(bg=self.blue_enter)) | |
indsBut.bind("<Leave>", lambda e: indsBut.config(bg=self.blue_leave)) | |
# Populate the bot section # | |
# # Create the 'CollapsibleFrame' for 'årsager' (gennemsnitlige entries) | |
self.årsagsFrame = CollapsibleFrame(self.bot_child, root=self.root, width=408, background=self.grey, text='Årsager til nøgletallenes ændringer', font='Ariel 10 bold') | |
self.årsagsFrame.grid(row=1, column=0, padx=(2, 2), sticky='ew') | |
# Create a darker grey background | |
BG1 = Label(self.årsagsFrame.interior, bg=self.grey) | |
BG1.grid(row=1, column=0, columnspan=4, rowspan=4, sticky='nsew') | |
# Initialize gen. entries | |
self.init_gen_entries() | |
# create checkbox for gen. and trace it | |
f = Frame(self.årsagsFrame.interior, bg=self.grey) | |
self.iv_gen = IntVar() | |
self.iv_gen2 = IntVar() | |
self.iv_gen.trace( 'w', self.on_gen_checkbox) | |
self.iv_gen2.trace('w', self.on_iv_gen2_RaPD_kendes_ikke) | |
self.gen_cb = Checkbutton(f, text='OFF ', variable=self.iv_gen, bg=self.grey, fg='firebrick2', activebackground=self.grey) | |
self.gen_cb2 = Checkbutton(f, text='Resultat af primær drift kendes ikke', variable=self.iv_gen2, bg=self.grey, activebackground=self.grey) | |
f.grid(row=0, column=0, columnspan=4, sticky='w') | |
self.gen_cb.grid( row=0, column=0, padx=(45,0), sticky='w') | |
self.gen_cb2.grid(row=0, column=1, padx=(65,0)) | |
# # Create the 'CollapsibleFrame' for 'indtjenignsevnen' | |
self.indtjeningsFrame = CollapsibleFrame(self.bot_child, root=self.root, width=408, background=self.grey, text='Indtjeningsevne', font='Ariel 10 bold') | |
self.indtjeningsFrame.grid(row=2, column=0, padx=(2, 2), sticky='ew') | |
# Create an intvariable, trace it, and bind | |
f = Frame(self.indtjeningsFrame.interior, bg=self.grey, width=408) | |
f.grid(row=0, column=0, columnspan=7, sticky='nsew') | |
self.iv_indt = IntVar() | |
self.iv_indt.trace('w', self.on_indt_cb) | |
self.indt_cb = Checkbutton(f, text='OFF ', variable=self.iv_indt, bg='grey89', fg='firebrick2', activebackground=self.grey) | |
self.indt_cb.grid(row=0, column=0, padx=(45,0)) | |
indeks_lab = Label(f, text='Indekstal', bg='grey89', font='Ariel 9') | |
indeks_lab.grid(row=0, column=1, padx=(220,28)) # 45 + 190 = 235 -> 254 = 19 | |
# Initialize labels and respective entries for indekstal | |
self.init_indtjenings_entries() | |
# # Create the 'CollapsibleFrame for | |
self.init_DP_collapsibleFrame_and_canvas() | |
def on_indt_cb(self, *_): | |
"""Triggers whenever the checkbutton is toggled 'OFF/ON' """ | |
state = self.iv_indt.get() | |
# Change color and text | |
if state: | |
self.indt_cb.config(fg='green', text='ON ') | |
else: | |
self.indt_cb.config(fg='firebrick2', text='OFF ') | |
# TODO | |
# TODO | |
# TODO THIS NEEDS TO BE DONE FIRST! | |
# TODO | |
# TODO | |
# Create (evt. update) indtjeningsevne text | |
self.args['Indtjeningsevne']['additional'] = self.create_indtjeningsevne_text() | |
# Display nu ændringerne i args | |
self._display_text() | |
def _paste_nums(self, key_of_focused_entry): | |
... # TODO reduce the handle_clipboard function by adding some of its funcationality to this function | |
def handle_clipboard(self, key_of_focused_entry, år_entry=False, gen_entry=False, nøgletal=False): | |
"""Function to destribute the clipboard data into seperate entries | |
key_of_focused_entry er fx.: | |
hvis år_entry = False | |
# Overskudsgrad,%3, Overskudsgrad,%2, Overskudsgrad,%1 | |
hvis år_entry = True | |
# år3, år2, år1 | |
""" | |
if '\n' not in root.clipboard_get(): | |
# If there is only one line in clipboard, paste it all in the focused cell | |
return | |
# count number of lines (cells) there is in the clipboard | |
clipboard_cells = (cell.strip() for line in root.clipboard_get().split('\n')[:-1] | |
for cell in line.split('\t')) | |
if år_entry: | |
# Vi starter fra den fokuserede cell, og indsætter ind til der ikke er flere entries | |
n = int(key_of_focused_entry[-1]) | |
for entry in (self.entriesÅr['år' + str(n)] for n in range(n, 0, -1)): | |
try: | |
cell = next(clipboard_cells) | |
entry.delete(0, 'end') | |
entry.insert(0, cell) | |
except StopIteration: | |
# There is not more to paste | |
# but still more entries to paste | |
pass | |
# En årsags-entry | |
elif gen_entry: | |
if self.iv_gen2.get(): | |
# RaPD kendes ikke | |
skip = '^Resultat af' # RaPD | |
else: | |
skip = '^Resultat før' # RfFO | |
# Vi starter fra den fokuserede cell, og indsætter ind til der ikke er flere entries | |
index = self.gen_labels.index(key_of_focused_entry) | |
for entry in (self.entries[entry] for entry in self.gen_labels[index:] if not re.search(skip, entry)): | |
try: | |
cell = next(clipboard_cells) | |
entry.delete(0, 'end') | |
entry.insert(0, cell) | |
except StopIteration: | |
# There is not more to paste | |
# but still more entries to paste | |
pass | |
# En nøgletals entry | |
elif nøgletal: | |
# Vi starter fra den fokuserede cell, og indsætter ind til der ikke er flere entries | |
index = self.nøgletal.index(key_of_focused_entry) | |
for entry in (self.entries[entry] for entry in self.nøgletal[index:]): | |
try: | |
cell = next(clipboard_cells) | |
entry.delete(0, 'end') | |
entry.insert(0, cell) | |
except StopIteration: | |
# There is not more to paste | |
# but still more entries to paste | |
pass | |
# DuPont entry | |
else: | |
# Vi starter fra den fokuserede cell, og indsætter ind til der ikke er flere entries | |
index = self.DP_entries_list.index(key_of_focused_entry) | |
for entry in (self.DP_entries[entry] for entry in self.DP_entries_list[index:]): | |
try: | |
cell = next(clipboard_cells) | |
entry.delete(0, 'end') | |
entry.insert(0, cell) | |
except StopIteration: | |
# There is not more to paste | |
# but still more entries to paste | |
pass | |
# Return "break" in order to stop the main "paste" in executing | |
return "break" | |
def markedsrente_window(self): | |
"""Markedsrente window""" | |
try: | |
if self.tw.winfo_exists(): | |
self.tw.destroy() | |
return | |
except: | |
# expected to pass on first run, since tw2 isnt initalized yet | |
pass | |
renter = {2016: '2.0', | |
2015: '2.0', 2014: '2.0', 2013: '2.0', 2012: '2.0', 2011: '2.075', 2010: '2.075', 2009: '3.0', | |
2008: '5.5', 2007: '6.0', 2006: '5.5', 2005: '4.25', 2004: '4.0', 2003: '4.0', 2002: '4.75', | |
2001: '5.25', 2000: '6.75', 1999: '5.0', 1998: '5.5', 1997: '5.5', 1996: '5.25', 1995: '6.25', | |
1994: '7.0', 1993: '8.25', 1992: '11.5', 1991: '11.5', 1990: '10.5', 1989: '9.0', 1988: '9.0', | |
1987: '9.0'} | |
self.tw = tk.Toplevel(root) | |
self.tw.resizable(False, False) | |
# Set span location | |
x = self.markedsr.winfo_rootx() + 250 | |
y = self.markedsr.winfo_rooty() - 300 | |
self.tw.wm_geometry("+%d+%d" % (x, y)) | |
# Set dimensions of window | |
self.tw.geometry('250x702') | |
link = 'http://nationalbanken.statistikbank.dk/nbf/98214' | |
lab1 = Label(self.tw, bg='Burlywood3', text='Nationalbankens Diskonto + 2%', font='system 7 bold') | |
lab2 = Label(self.tw, bg='Burlywood3', text=link, font='arial 8 underline') | |
lab1.grid(row=0, column=0, sticky='we') | |
lab2.grid(row=1, column=0, sticky='we') | |
# Make link (lab2) clickable | |
lab2.bind("<Enter>", lambda e: lab2.config(fg='blue')) | |
lab2.bind("<Leave>", lambda e: lab2.config(fg='black')) | |
lab2.bind("<Button-1>", lambda e: webbrowser.open(link)) | |
row = 2 | |
for år in range(2016, 1986, -1): | |
label = Label(self.tw, relief='solid', font='arial 10', | |
text='Årstal: {a}\t-\t{b:5f} %'.format(a=år, b=float(renter[år]))) | |
label.grid(row=row, column=0, sticky='we') | |
row += 1 | |
# Set colors when focusing/not focusing at the labels | |
label.bind("<Enter>", lambda event, label=label: label.config(bg='light green')) | |
if row % 2 == 1: | |
label.config(bg='Wheat2') | |
label.bind("<Leave>", lambda e, label=label: label.config(bg='Wheat2')) | |
else: | |
label.config(bg='snow') | |
label.bind("<Leave>", lambda e, label=label: label.config(bg='snow')) | |
# If a label is clicked | |
label.bind("<Button-1>", lambda e, label=label: self.on_MR_window_label(label)) | |
def on_MR_window_label(self, label): | |
"""Luk ned for markedsrente window og set markedsrente entry i root window | |
til den valgte labels rentesats.""" | |
# Store the text value first | |
text = label.cget('text') | |
# destroy markedsrente window | |
self.tw.destroy() | |
# Clear markedsrente entry | |
self.mrEntry.delete(0, 'end') | |
# Sorter markedsrenten fra resten af teksten | |
rente_str = re.search(r'\d\.\d+', text).group() | |
rente = round(float(rente_str), 2) | |
# Insert renten i markedsrente entrien | |
self.mrEntry.insert(END, rente) | |
# Show statusbar | |
self.display_statusbar(msg='Markedsrenten er ændret til rentesatsen i år {} -> {} %'.format(re.search(r'\d+', text).group(), | |
rente)) | |
def on_iv_gen2_RaPD_kendes_ikke(self, *_): | |
"""checkbutton that is pressed if RaPD is unknown""" | |
if self.iv_gen2.get(): | |
# Gem 'Resultat af primær drift' | |
self.gen_RaPD_label.grid_forget() | |
for entry in (self.entries['Resultat af primær drift' + str(i)] for i in range(3, 0, -1)): | |
entry.grid_forget() | |
# Show 'Resultat før finansielle omkostninger' | |
self.gen_RfFO_label.grid(row=1, column=0, sticky='w') | |
for i, entry in enumerate(self.entries['Resultat før finansielle omkost.' + str(i)] for i in range(3, 0, -1)): | |
entry.grid(row=1, column=i+1, padx=(0,2)) | |
else: | |
# Gem 'Resultat før finansielle omkostninger | |
self.gen_RfFO_label.grid_forget() | |
for entry in (self.entries['Resultat før finansielle omkost.' + str(i)] for i in range(3, 0, -1)): | |
entry.grid_forget() | |
# Show 'Resultat af primær drift | |
self.gen_RaPD_label.grid(row=1, column=0, sticky='w', padx=(0,15)) | |
for i, entry in enumerate(self.entries['Resultat af primær drift' + str(i)] for i in range(3, 0, -1)): | |
entry.grid(row=1, column=i+1, padx=(0,2)) | |
# Opdater analyse tekst til at vise årsag til RfFO's stigning/fald | |
self.args['Afkastningsgrad']['årsag'] = self._årsag_afkastningsgrad() | |
self.args['Overskudsgrad']['årsag'] = self._årsag_overskudsgrad() | |
# display den opdaterede tekst | |
root.after(1000, self._display_text) | |
def help_popup(self): | |
"""Forklaring af nøgletal window""" | |
try: | |
if self.tw2.winfo_exists(): | |
self.tw2.destroy() | |
return | |
except: | |
# expected to pass on first run, since tw2 isnt initalized | |
pass | |
self.tw2 = Toplevel(self.root, bg='Snow') | |
self.tw2.resizable(False, False) | |
self.tw2.wm_geometry("+1200+1000") | |
# Set spawn location | |
x = self.helpBut.winfo_rootx() + 275 | |
y = self.helpBut.winfo_rooty() - 100 | |
self.tw2.wm_geometry("+{}+{}".format(x, y)) | |
headl = 'Ariel 10 bold' | |
text = 'Ariel 10' | |
bg1 = 'Wheat2' | |
bg2 = 'Wheat2' | |
helpText = [Label(self.tw2, font=headl, bg=bg1, text='Afkastningsgrad'), | |
Label(self.tw2, font=text, bg=bg2, text='Viser, hvor meget man opnår i forrentning af den investerede kapital.'), | |
Label(self.tw2, font=headl, bg=bg1, text='Overskudsgrad'), | |
Label(self.tw2, font=text, bg=bg2, text='Viser, hvor stor en del af omsætningen, der bliver tilbage i indtjening, når virksomhedens driftsomkostninger er dækket.'), | |
Label(self.tw2, font=headl, bg=bg1, text='Aktivernes omsætningshastighed'), | |
Label(self.tw2, font=text, bg=bg2, text='Viser, hvor stor en aktivitet (omsætning) der er skabt i virksomheden på grundlag af den investerede kapital (aktiverne).'), | |
Label(self.tw2, font=headl, bg=bg1, text='Egenkapitalens forrentning'), | |
Label(self.tw2, font=text, bg=bg2, text='Viser, hvor meget ejerne har opnået i forrentning af den kapital, de har investeret i virksomheden.'), | |
Label(self.tw2, font=headl, bg=bg1, text='Gældsrente'), | |
Label(self.tw2, font=text, bg=bg2, text='Viser den rente, som virksomheden i gennemsnit betaler af sine gældsforpligtelser.'), | |
Label(self.tw2, font=headl, bg=bg1, text='Gearing'), | |
Label(self.tw2, font=text, bg=bg2, text='Viser forholdet mellem gældsforpligtelser og egenkapital.'), | |
] | |
for i, label in enumerate(helpText): | |
if i % 2: | |
label.grid(row=i, column=0, sticky='ew', pady=(0,10)) | |
else: | |
label.grid(row=i, column=0, sticky='ew') | |
def on_gen_checkbox(self, *_): | |
"""triggeres whenever the advance options section: 'gennemsnitlig(e) -egenkapital, -forplitegelser og -aktiver' | |
checkbutton is pressed. Remove/show all 'årsager' to the corressponding state""" | |
state = self.iv_gen.get() | |
# Change color and text | |
if state: | |
self.gen_cb.config(fg='green', text='ON ') | |
else: | |
self.gen_cb.config(fg='firebrick2', text='OFF ') | |
# Updater alle args i hvert "gennemsnitlige", de vil blive vist/skjult afhænigigt af state | |
self.update_analyse_args(Ltext='Gennemsnitlig egenkapital', nr=None, update='gennemsnitlig') | |
self.update_analyse_args(Ltext='Gennemsnitlige forpligtelser', nr=None, update='gennemsnitlig') | |
self.update_analyse_args(Ltext='Gennemsnitlige aktiver', nr=None, update='gennemsnitlig') | |
self.update_analyse_args(Ltext='Resultat af primær drift', nr=None, update='gennemsnitlig') | |
# Display nu ændringerne i args | |
self._display_text() | |
def _show_hide_bot_child(self, show): | |
"""Shows/hides bot child (advanced options frame) depending on the show variable""" | |
if show: | |
self.bot_child.grid() | |
self.vis_opt.config(text='v Skjul valgfrie options v') | |
else: | |
self.bot_child.grid_remove() | |
self.vis_opt.config(text='> Vis valgfrie options >') | |
def display_statusbar(self, msg, color='light green', duration=4): | |
"""Displays the statusbar for a given duration""" | |
try: | |
# If there is already an statusbar running cancel the 'root.after' | |
self.root.after_cancel(self.after_calls.pop()) | |
except: | |
pass | |
self.bot_frame.grid() | |
self.bot_frame.config(bg=color) | |
self.statusbar.config(bg=color, text=msg) | |
# Hide the statusbar after x miliseconds | |
self.after_calls.append(self.root.after(duration*1000, lambda: self.bot_frame.grid_remove())) | |
def copy_analysetext_to_clipboard(self): | |
"""Kopter analyse text til clipboard | |
#Important! - vent et stykke tid før der lukkes for tkinter winduet - bug? | |
""" | |
self.root.clipboard_clear() | |
variable = '' | |
for nøgletal in self.nøgletal_labels: | |
variable += nøgletal + '\n' + self.string_templates[nøgletal].format(**self.args[nøgletal]) + '\n\n' | |
pyperclip.copy(variable) | |
messagebox.showinfo('info', 'Analysen er blevet kopieret til clipboard\n\'Ctrl+v\' for at indsætte analysen.') | |
def _resize_frame_width(self, e): | |
# 'e.width-25' da vi gerne vil ypad så widgets'ne ikke skal nå helt ud. | |
self.canvas.itemconfig(self._frame_id, width=e.width - 25) | |
view = self.canvas.yview() | |
if view[0] == 0 and view[1] == 1: | |
self.canvas.bind_all("<MouseWheel>", "break") | |
else: | |
self.canvas.bind_all("<MouseWheel>", self._on_mousewheel) | |
def _onFrameConfigure(self, event): | |
# update scrollregion after starting 'mainloop' | |
# when all widgets are in canvas | |
self.canvas.configure(scrollregion=self.canvas.bbox('all')) | |
# TODO er det nødvendigt med 2 "onFrameConfigure" ? | |
# Referer til self._resize_frame | |
# print('onframe:', event) | |
def __resize_textWidget_height(self): | |
"""Resizes all text widgets' height in order to perfectly fit its content within""" | |
for widget in self.textWidgets.values(): | |
widget.reset_height() | |
# update root so that view is updated | |
self.root.update() | |
# If there is nothing to scroll turn off global mousewheel | |
view = self.canvas.yview() | |
if view[0] == 0 and view[1] == 1: | |
self.canvas.bind_all("<MouseWheel>", "break") | |
else: | |
self.canvas.bind_all("<MouseWheel>", self._on_mousewheel) | |
def _on_mousewheel(self, event): | |
self.canvas.yview_scroll(int(-1 * (event.delta / 120)), 'units') | |
def on_indt_writeBut(self, row : int): | |
"""When the "writeicon" button is pressed (in the "indtjeningsevne collapsible Frame") | |
Display/remove "indt_årsag_entry" depending on if the entry is currently shown. | |
self.indtj_writeMore = [(writeMoreFrame, text), (writeMoreFrame, text), and so on..] | |
self.indtj_writeMore[0][0] == writeMoreFrame # The frame in which we eg. grid the text widget (explained below) | |
self.indtj_writeMore[0][1] == text # The text widget to write an explanation as to why the index numbers increased/decreased as they did | |
""" | |
# Minus 1 fra row, da vi starter med at tælle fra 0 ikke 1 | |
# Divider med 2, da vi stiger med 2 rækker for hver gang vi adder en ny writeMoreFrame | |
# grunden til '//' i stedet for '/' skyldes, at når man dividere får man en float - vi ønsker en 'int' | |
# Man kunne lige så godt have skrevet: int( (row-1)/3) ) | |
# (row-1)//3) | |
index = (row-1)//3 | |
frame = self.indtjenings_list[index]['writeMore'][0] | |
widget = self.indtjenings_list[index]['writeMore'][1] | |
if frame.winfo_ismapped(): | |
# If the writeMoreFrame was shown when the button was pressed - hide | |
frame.grid_remove() | |
else: | |
# If the writeMoreFrame was NOT shown when the button was pressed - show | |
frame.grid() | |
def on_indt_deleteBut(self, row : int): | |
"""When the "trash icon" button is pressed (in the "indtjeningsevne collapsible Frame") | |
Removes everything on the row at which the "trash icon" was clicked. """ | |
#print('Deleting row:', row) | |
# convert row number to corresponding index number in list. | |
# eg. row=1 corressponds to index 0 | |
# eg. row=7 corressponds to index 3 | |
# The index number can be derrived from the following formula: | |
index = (row-1)//3 | |
rowContent = self.indtjenings_list[index] | |
# Delete everything on the row | |
rowContent['label'].destroy() | |
rowContent['writeMore'][0].destroy() | |
rowContent['toDelete'][0].destroy() | |
rowContent['toDelete'][1].destroy() | |
for i in range(1, 4): | |
rowContent['år' + str(i)].destroy() | |
def _indt_create_newline(self, row, label=None): | |
"""To internal use in 'on_indt_addBut' and 'init_indtjenings_entries', | |
this method is entirely dedicated to avoiding DRY code.""" | |
# Create delete button | |
trash_img = PhotoImage(data=img_data['trash']) | |
trash_but = Button(self.indtjeningsFrame.interior, relief='flat', bg='grey89', takefocus=False, image=trash_img, | |
command=lambda row=row: self.on_indt_deleteBut(row=row)) | |
trash_but.grid(row=row, column=0, padx=(2, 0), sticky='w') | |
trash_but.img = trash_img | |
# Create write årsag button | |
write_img = PhotoImage(data=img_data['write']) | |
write_but = Button(self.indtjeningsFrame.interior, relief='flat', bg='grey89', takefocus=False, image=write_img, command=lambda *_, row=row: self.on_indt_writeBut(row)) | |
write_but.grid(row=row, column=1, padx=(2, 0), sticky='w') | |
write_but.img = write_img | |
# Create the "write more entry" - It will be gridded the row below (row +=1) | |
# Create a frame for the "write more entry" and lab in order to not disturb the gridding | |
writeMoreFrame = Frame(self.indtjeningsFrame.interior, bg='grey89') | |
writeMoreLab = Label(writeMoreFrame, text='└─', bg='grey89') | |
text_widget = Text(writeMoreFrame, height=2, width=43, wrap=WORD, font='arial 9') | |
writeMoreFrame.grid(row=row + 1, column=0, columnspan=7, sticky='ew') | |
writeMoreLab.grid(row=row, column=0, padx=(20, 0)) | |
text_widget.grid(row=row, column=1) | |
# Write more framed should not be visable at init | |
writeMoreFrame.grid_remove() | |
# Create the 'label' entry | |
label_entry = Entry(self.indtjeningsFrame.interior, width=40) | |
label_entry.grid(row=row, column=2, sticky='w') | |
if label: # only None when new lines are added by the user | |
label_entry.insert(END, label) | |
# Store this entire row in a namedtuple and add it to 'indjenings_list' | |
self.indtjenings_list.append( | |
{'rowNr': row, 'label': label_entry, | |
'writeMore': (writeMoreFrame, text_widget), | |
'toDelete': (trash_but, write_but)} | |
) | |
år = 3 | |
# We need 3 entries to row | |
for col in range(3, 6): | |
# Create stringvar in order to trace entry | |
sv = StringVar(self.indtjeningsFrame.interior) | |
# Create 'indekstal' entry | |
entry = Entry(self.indtjeningsFrame.interior, width=5, textvariable=sv) | |
entry.insert(END, str(100+(år-1)*10)) | |
entry.grid(row=row, column=col) | |
self.indtjenings_list[-1]['år'+str(år)] = entry | |
# Trace entry - (triggers a cuntion whenever the sv (entry) is being edited) | |
sv.trace('w', lambda name, *_, sv=sv, Ltext=label: self._check_indtjeningsevne_entries(sv, name, Ltext)) # TODO - trace entry | |
år -= 1 | |
def on_indt_addBut(self): | |
"""Adds a new row, to enter additional information eg. 'labelEntry': 'indekstalEntry3', 'indekstalEntry2', indekstalEntry1' """ | |
row = len(self.indtjenings_list)*3 + 1 # The row nr to add to the additional information to ^^ | |
self._indt_create_newline(row) | |
def create_indtjeningsevne_text(self): | |
"""If indtjeningsevne is 'ON'; Creates the body text of the indtjeningsevne based on the information provided; | |
eg. label, indekstal, additonal (writeMore) text """ | |
# Hvis optional options ikke er slået til return | |
if self.iv_indt.get() == False: | |
return '.' # The dot is on purpose | |
x, y = False, False | |
# Locate nettoomsætning | |
for dictionary in self.indtjenings_list: | |
label = dictionary['label'].get() | |
if re.search(r'(netto)?omsætning', label, re.IGNORECASE): | |
oms_1 = float(dictionary['år1'].get()) | |
oms_3 = float(dictionary['år3'].get()) | |
oms_rp = oms_3 - oms_1 | |
x = True | |
elif re.search(r'(vareforbrug|produktionsomkostninger)', label, re.IGNORECASE): | |
omk_label = 'Vareforbruget' if ('vareforbrug' in label) else 'Produktionsomkostningerne' | |
omk_index = (dictionary['rowNr'] - 1) // 3 | |
omk_1 = float(dictionary['år1'].get()) | |
omk_3 = float(dictionary['år3'].get()) | |
omk_rp = omk_3 - omk_1 | |
y = True | |
else: | |
if not y: | |
print('CRITICAL: either "vareforbrug" or "produktionsomkostninger" have not been declared! ----') | |
if not (x and y): | |
... | |
# 'Omsætning' was not found, warn the user that it is mandatory to declare 'omsætning' | |
# TODO | |
# TODO Warn user omsætnings is mandatory to | |
# TODO | |
# Precalculate certain variables to make code more readable and to better the performance | |
pp_ove = self._pp('Overskudsgrad,%', 1, 3) | |
result = ("; dette skyldes, at omsætningen er steget procentuelt {adj} end omkostningerne.\n" | |
"Det fremgår ud fra indekstallene, at omsætningen er {adj2} fra indeks {oms_1} i {år1} til " | |
"indeks {oms_3} i {år3}, hvilket er {sub} på {rp}%. ").format( | |
adj = 'mere' if pp_ove else 'mindre', | |
adj2= self.__binary_stigning_verbum(oms_3-oms_1, førdatid=True), | |
sub = 'en stigning' if pp_ove else 'et fald', | |
år1=self.entriesÅr['år1'].get().strip(), oms_1=oms_1, | |
år3=self.entriesÅr['år3'].get().strip(), oms_3=oms_3, | |
rp = oms_rp # da det er indekstal er rp år3 fratrukket år1 | |
) | |
## Kommentere på omkostningsindekstallenes udvikling ## | |
# Spring den først i listen over, da vi kun kommenter på omkostninger | |
result += ("{label} er {adj} med {rp}%, hvilket er {grad}. " | |
"Det har dermed påvirket overskudsgraden {påvirket}. ").format( | |
label=omk_label, rp=omk_rp, | |
påvirket=self.__påvirkelse(omk_rp - oms_rp, 'indtjeningsevne'), | |
adj = self.__binary_stigning_verbum(omk_rp, førdatid=True), | |
grad = self.__tillægsord_gradbøjet(værdi=omk_rp - oms_rp, | |
tillægsord=['en del mindre end omsætningens stigningstakt', 'en smule mindre end omsætningens stigningstakt', 'den samme stigningstakt som omsætningen', | |
'en anelse mere end omsætningens stigningstakt', 'en del mere end omsætningens stigningstakt'], | |
milestones=[-9, -0.001, 0.001, 9] | |
) | |
) | |
for i in range(1, len(self.indtjenings_list)): | |
if (omk_index == i): # Skip produktionsomkostnings/vareforbug label; er allerede kommenteret på ^^ | |
continue | |
# Da vi har med indekstal at gøre er 'rp' "(indekstal år3 - indekstal år1) = rp" | |
rp = float(self.indtjenings_list[i]['år3'].get()) - float(self.indtjenings_list[i]['år1'].get()) | |
# Get the label to the current line | |
label = self.indtjenings_list[i]['label'].get().capitalize() | |
# Convert label to 'bestemt ental' if it ends on 'omkostninger' | |
label = self._indt_label_to_bestemt_flertal(label) | |
# Varier sætningsopstilling | |
if (i % 2 == 0): | |
result += ("{label} er {adj} med {rp}%. Udviklingen i {label} har derfor påvirket overskudsgraden {påvirket}, idet stigningstakten er {adj2} end " | |
"omsætningens stigningstakt. ").format( | |
label=label, rp=abs(rp), | |
adj=self.__binary_stigning_verbum(rp, førdatid=True), | |
adj2= 'større' if (rp - oms_rp) > 0 else 'mindre', | |
påvirket=self.__påvirkelse(rp, 'indtjeningsevne') | |
) | |
else: | |
result += ("{label} er {adj} med {rp}%, hvilket er {grad}. {label} har altså {retning}. ").format( | |
label=label, rp=rp, | |
adj=self.__binary_stigning_verbum(rp, førdatid=True), | |
grad=self.__tillægsord_gradbøjet(værdi=omk_rp - oms_rp, | |
tillægsord=['en del mindre end stigningen i omsætningen', 'en smule mindre end stigningen i omsætningen', | |
'den samme stigningstakt som omsætningen', 'en anelse mere end stigningen i omsætningen', | |
'en markant mere end stigningen i omsætningen'], | |
milestones=[-9, -0.001, 0.001, 9] | |
), | |
retning=( 'påvirket overskudsgraden i negativ retning' if (omk_rp - oms_rp) > 0 | |
else 'påvirket overskudsgraden i positiv retning' if (omk_rp - oms_rp) < 0 | |
else 'ikke påvirket overskudsgraden') | |
) | |
### Konklusion | |
result += ('\nKonklusion\nAlt i alt viser udviklingen i indtjeningsevnen et {sub}. ' | |
'{imidlertidligt}').format( | |
sub=self.__stigning_sub_bestemt(pp=pp_ove, label='overskudsgraden'), | |
imidlertidligt=('Overskudsgraden er dog imidlertidligt forbedret fra {år2} til {år3}, ' | |
'hvilket lover godt for den fremtidige indtjeningsevne.').format(år2=self.entriesÅr['år2'].get(), | |
år3=self.entriesÅr['år3'].get()) | |
if self._pp('Overskudsgrad,%', 2, 3) > 1 else '' | |
) | |
# 'Årsagen til {sub} er, at {sub2}').format( | |
# sub=self.__stigning_sub_bestemt(pp=pp_ove, label='overskudsgraden') | |
# sub2=self.__stigning_sub_bestemt(pp=pp_ove, label='overskudsgraden') | |
#) | |
return result | |
def create_indtjeningsevne_konklusion(self): | |
### TODO indtjeningsevne konklusion mangler! | |
... | |
def _indt_label_to_bestemt_flertal(self, label): | |
"""Converts a given word if it ends on 'omkostninger' to 'omkostningerne' (bestemt flertal) | |
else return either 'virksomhedens {label}' or '{ejefald(firma)} {label}' """ | |
if label.endswith('nger') or label.endswith('omkostninger'): | |
label += 'ne' | |
else: | |
# if unsure on how to convert the label to 'bestemt flertal', add eg 'FIRMANAVNET(s)/virksomhedens label' | |
label = random.choice(('Virksomhedens', self._firmanavn_ejefald(self.firma.get().strip()).capitalize())) \ | |
+ ' ' \ | |
+ label | |
return label | |
def init_indtjenings_entries(self): # TODO: Complete this method | |
"""Initilization - one time use | |
Makes and places entries in the "advance options" section | |
In the collapsible frame: 'Indtjeningsevne' | |
'self.indtj_maxRow' -> is the max row number of | |
""" | |
self.indtjenings_labels = ['Nettoomsætning', 'Produktionsomkostninger', 'Salgs- og distributionsomkostninger', 'Indeks for administartionsomkostninger'] | |
# For when we need to extract the data | |
self.indtjenings_list = [] | |
row = 1 | |
for label in self.indtjenings_labels: | |
self._indt_create_newline(row, label=label) | |
row += 3 | |
# Nettoomsætning should be a dropdown menu | |
#options = ['år1', 'år2', 'Udvikling fra: år1 -> år3'] | |
#self.qr = StringVar() | |
#self.qr.set('år1') | |
#self.a = OptionMenu(self.indtjeningsFrame.interior, self.qr, *options) | |
#self.a.config(bg='grey98', indicatoron=5, width=40, font="Courier 10") | |
#self.a.grid(row=1, column=2) | |
# Fastgør 'nettoomsætningsrækken' da det er et krav at den er der for at vi kan kommentere på indtjeningsevnen | |
self.indtjenings_list[0]['toDelete'][0].configure(command=int) # Dummy function to overwrite the former one | |
# Create a frame for the separator | |
sepFrame = Frame(self.indtjeningsFrame.interior, bg=self.grey) | |
sepFrame.grid(row=3, columnspan=7, pady=5, sticky='ew') | |
sepFrame.grid_columnconfigure((0,2), weight=1) | |
# Create separator label | |
sep = Separator(sepFrame, orient=HORIZONTAL) | |
lab = Label(sepFrame, text='Omkostninger', bg=self.grey) | |
sep2 = Separator(sepFrame, orient=HORIZONTAL) | |
sep.grid( row=0, column=0, sticky='ew') | |
lab.grid( row=0, column=1, sticky='ew') | |
sep2.grid(row=0, column=2, sticky='ew') | |
# Create an "add new line/row" button | |
add_img = PhotoImage(data=img_data['plus']) | |
self.but = Button(self.indtjeningsFrame.interior, relief='flat', bg='grey89', image=add_img, command= self.on_indt_addBut) | |
self.but.grid(row=99, column=0, columnspan=2, padx=(2,0)) | |
self.but.img = add_img | |
# create an empty disabled entry (for the sake of aesthetic) | |
self.entry = Entry(self.indtjeningsFrame.interior, width=36, state=DISABLED) | |
self.entry.grid(row=99, column=2, sticky='w', padx=(0, 4)) | |
def init_gen_entries(self): | |
"""Initilization - one time use | |
Makes and places entries in the "advance options" section""" | |
# WARNING: Do not remove the extra space in 'gennemsnitlig egekapital' - it is intended | |
self.gen_labels = ['Resultat af primær drift', 'Resultat før finansielle omkost.', 'Gennemsnitlig egenkapital', | |
'Gennemsnitlige forpligtelser', 'Gennemsnitlige aktiver'] | |
for row, label in enumerate(self.gen_labels): | |
lab = Label(self.årsagsFrame.interior, text=label, bg='grey89') | |
lab.grid(row=row + 1, column=0, sticky='w', padx=(0,15)) | |
if label == 'Resultat af primær drift': | |
# We need to save the label in order to hide/show it later 'on_iv_gen2_RaPD_kendes_ikke()' | |
self.gen_RaPD_label = lab | |
elif label == 'Resultat før finansielle omkost.': | |
self.gen_RfFO_label = lab | |
col = 1 | |
for i in range(3, 0, -1): | |
# Create stringvar in order to trace entry | |
sv = StringVar(self.årsagsFrame.interior) | |
self.sva[str(label)+str(i)] = sv | |
# Create entry | |
entry = Entry(self.årsagsFrame.interior, width=12, relief='groove', textvariable=sv) | |
entry.insert(END, i * 100) | |
# Trace entry - (hvornår bliver der skrevet/slettet i entrien) | |
sv.trace('w', lambda name, *_, sv=sv, Ltext=label: self._check_gen_entry(sv, name, Ltext)) | |
entry.grid(row=row + 1, column=col, padx=(0, 2)) | |
self.entries[str(label) + str(i)] = entry | |
col += 1 | |
# Gem 'Resultat før finansielle omkostnigner | |
self.gen_RfFO_label.grid_forget() | |
for entry in (self.entries['Resultat før finansielle omkost.' + str(i)] for i in range(3, 0, -1)): | |
entry.grid_forget() | |
# Overskriv 'gen_labels' | |
self.gen_labels = [label + str(i) for label in self.gen_labels for i in range(3, 0, -1)] | |
# pady resultat af primær drift | |
#temp.config(pady=5) | |
def init_årEntries(self): | |
"""Initilization - one time use | |
Makes and places år entries + stores the objects in a dictionary - self.entriesÅr""" | |
col = 0 | |
år = 0 | |
for i in range(3, 0, -1): | |
# Create and store a StringVariable in a dictionary - self.entriesÅr | |
sv = StringVar(self.right_child) | |
self.svaÅr['år' + str(i)] = sv | |
# Create an entry and insert the year | |
entry = Entry(self.right_child, width=9, textvariable=sv) | |
entry.insert(END, 2017 - år) | |
# Make the StringVariable trace the entry | |
sv.trace('w', lambda name, *_, sv=sv: self._check_årstal(sv, name, 'År: (sidste til første)')) | |
# Place the entry and store the entry in a dictionary - self.svaÅr | |
entry.grid(row=0, column=col, sticky='wn', padx=(0, 2), pady=(1, 32)) | |
self.entriesÅr['år' + str(i)] = entry | |
col += 1 | |
år += 1 | |
def init_entries(self): | |
"""Initilization - one time use | |
makes and places entries + stores the objects in a dictionary - self.entries""" | |
self.labels = ['Afkastningsgrad,%', 'Overskudsgrad,%', 'Aktivernes omsætningshastighed,gange', | |
'Gældsrente,%', 'Egenkapitalens forrentning,%', 'Gearing,gange', | |
'Soliditetsgrad,%', 'Likviditetsgrad,%'] | |
self.nøgletal = [label + str(i) for label in self.labels for i in range(3, 0, -1)] | |
# Create frames in the right_child to organize the entries | |
self.right_child_mid = Frame(self.right_child, bg=self.ggrey) | |
self.right_child_mid.grid(row=1, column=0, columnspan=3) | |
self.right_child_bot = Frame(self.right_child, bg=self.ggrey) | |
self.right_child_bot.grid(row=2, column=0, columnspan=3, pady=(31,0)) | |
for row, label in enumerate(self.labels): | |
if (label == 'Soliditetsgrad,%') or (label == 'Likviditetsgrad,%'): | |
frame = self.right_child_bot | |
else: | |
frame = self.right_child_mid | |
col = 0 | |
for i in range(3, 0, -1): | |
# Create stringvar in order to trace entry | |
sv = StringVar(frame) | |
self.sva[str(label) + str(i)] = sv | |
# Create entry | |
entry = Entry(frame, width=9, relief='groove', textvariable=sv) | |
entry.insert(END, str(i) + '.0') | |
# Trace entry - (hvornår bliver der skrevet/slettet i entrien) | |
sv.trace('w', lambda name, *_, sv=sv, label=label: self._check_entry(sv, name, label, update='nøgletal')) | |
entry.grid(row=row+3, column=col, padx=(0, 2), pady=(0, 4), sticky='n') | |
self.entries[str(label) + str(i)] = entry | |
col += 1 | |
def init_DP_entries(self): | |
self.DP_entries = OrderedDict() | |
self.DP_standard_labels = ('Nettoomsætning', 'Variable omkostninger', 'Kapacitetsomkostninger', | |
'Anlægsaktiver', 'Tilgodehavender+varebeholdninger', 'Likvide beholdninger') | |
# Mandatory entries | |
row = 0 | |
for label in ('Nettoomsætning', 'Variable omkostninger', | |
'Kapacitetsomkostninger'): | |
# Label | |
lab = Label(self.DP_botFrame, text=label, bg='grey85') | |
lab.grid(row=row, column=0, sticky='w') | |
# 3 Entries | |
år = 3 | |
for col in range(1, 4): | |
sv = StringVar(self.DP_botFrame) | |
entry = Entry(self.DP_botFrame, width=17, textvariable=sv) | |
rnd_int = str(år) #str(randint(1023, 9987)) | |
entry.insert(END, rnd_int[0] + '.0' + rnd_int[1:]) | |
entry.grid(row=row, column=col, padx=(0, 2)) | |
sv.trace('w', lambda name, *_, sv=sv, Ltext=label: self._check_DPentry(sv, name, Ltext)) | |
self.DP_entries[label + str(år)] = entry | |
år -= 1 | |
row += 1 | |
row = 0 | |
for label in ('Anlægsaktiver', 'Tilgodehavender+varebeholdninger', 'Likvide beholdninger'): | |
# Label | |
lab = Label(self.DP_botFrame, text=label, bg='grey85') | |
lab.grid(row=row, column=4, sticky='w', padx=(8,2)) | |
# 3 Entries | |
år = 3 | |
for col in range(5, 8): | |
sv = StringVar(self.DP_botFrame) | |
entry = Entry(self.DP_botFrame, width=17, textvariable=sv) | |
rnd_int = str(år) #str(randint(1023, 9987)) | |
entry.insert(END, rnd_int[0] + '.0' + rnd_int[1:]) | |
entry.grid(row=row, column=col, padx=(0, 2)) | |
sv.trace('w', lambda name, *_, sv=sv, Ltext=label: self._check_DPentry(sv, name, Ltext)) | |
self.DP_entries[label + str(år)] = entry | |
år -= 1 | |
row += 1 | |
# Bind custom 'ctrl+v' to DP entries | |
for k, entry in self.DP_entries.items(): | |
entry.bind('<<Paste>>', lambda *_, key=k: self.handle_clipboard(key)) | |
# List of all DP_entry names, is needed to "handle_clipboard" properly | |
self.DP_entries_list = [entry_name for entry_name in self.DP_entries.keys()] | |
def _is_float(self, x): | |
"""Tests if the number is a float. (ignoring thousand seperator (all commas))""" | |
try: | |
flaot(x.replace(',', '')) | |
return True | |
except ValueError: | |
return False | |
def _check_årstal(self, sv, name, Ltext): | |
"""Checks whether the entry is valid and change the color accordingly | |
(text i entry field) (dynamic variable) (Label to the respective 3 entries) | |
eg. sv.get() = '2.0', name = PY_VAR6, Ltext = Overskudsgrad,%""" | |
n = int(name.rsplit('R')[1]) | |
nr = str((n - 2) // 3 * 3 + 3 - (n - 2)) | |
# Check the validity of the entry | |
if re.match(r'^\d+$', sv.get().strip()): | |
self.update_analyse_args(Ltext, nr, update='årstal') | |
root.after(1000, self._display_text) | |
color = 'black' | |
bg = 'white' | |
else: | |
color = 'red' | |
bg = 'pink' | |
self.entriesÅr['år' + nr].config(fg=color, bg=bg) | |
def _check_gen_entry(self, sv, name, Ltext): | |
"""Checks whether the entry is valid and changes the color accordingly | |
(text i entry field) (dynamic variable) (Label to the respective 3 entries) | |
eg. sv.get() = '2.0', name = PY_VAR6, Ltext = Overskudsgrad,%""" | |
n = int(name.rsplit('R')[1]) | |
nr = str((n) // 3 * 3 + 3 - (n)) | |
input = sv.get().strip() | |
if input == '': | |
self.entries[Ltext + nr].config(fg='red', bg='pink') | |
return | |
# Check the validity of the entry | |
if re.search('^[+-]?([0-9]{1,3}(,[0-9]{3})+|[0-9]*)\.?[0-9]*$', input): | |
# Update analyse teksten med det nyt indtastede tal | |
self.update_analyse_args(Ltext, nr, update='gennemsnitlig') | |
# display den opdaterede tekst | |
root.after(3000, self._display_text) | |
color = 'black' | |
bg = 'white' | |
else: | |
color = 'red' | |
bg = 'pink' | |
self.entries[Ltext + nr].config(fg=color, bg=bg) | |
def _check_entry(self, sv, name, Ltext, update): | |
"""Checks whether the entry is valid and changes the color accordingly | |
(text i entry field) (dynamic variable) (Label to the respective 3 entries) | |
eg. sv.get() = '2.0', name = PY_VAR6, Ltext = Overskudsgrad,%""" | |
n = int(name.rsplit('R')[1]) | |
nr = str((n - 2) // 3 * 3 + 3 - (n - 2)) | |
input = sv.get().strip() # 5 should return -> 1 ; 6 should return -> 2 | |
if input == '': | |
self.entries[Ltext + nr].config(fg='red', bg='pink') | |
return | |
# Check if digit grouped is used it is then used correctly (only possible to digit group with comma) | |
# If the number (ignoring commas (digit grouping)) is a float | |
if re.search('^[+-]?([0-9]{1,3}(,[0-9]{3})+|[0-9]*)\.?[0-9]*$', input): | |
color = 'black' | |
bg = 'white' | |
try: | |
# Update analyse teksten med det nyt indtastede tal | |
self.update_analyse_args(Ltext, nr, update=update) | |
# display den opdaterede tekst | |
root.after(3000, self._display_text) | |
except ValueError as e: | |
print('A number from a year is presumably missing..', e) | |
pass | |
else: | |
color = 'red' | |
bg = 'pink' | |
self.entries[Ltext + nr].config(fg=color, bg=bg) | |
def _check_markedsrente(self, sv, name, Ltext): | |
"""Checks whether the entry is valid and changes the color accordingly | |
(text i entry field) (dynamic variable) (Label to the respective 3 entries) | |
eg. sv.get() = '2.0', name = PY_VAR6, Ltext = Overskudsgrad,%""" | |
n = int(name.rsplit('R')[1]) | |
nr = str((n - 1) // 3 * 3 + 3 - (n - 1)) | |
# Check the validity of the entry | |
if re.search(r'^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$', sv.get().strip()): | |
# Update analyse teksten med det nyt indtastede tal | |
self.update_analyse_args(Ltext, nr=None, update='markedsrente') | |
# display den opdaterede tekst | |
root.after(1000, self._display_text) | |
color = 'black' | |
bg = 'white' | |
else: | |
color = 'red' | |
bg = 'pink' | |
self.entries[Ltext].config(fg=color, bg=bg) | |
def _check_DPentry(self, sv, name, Ltext): | |