Add files from 6a34355fda commit of FastLBRY-GTK notabug.org repo by jyamihud without the flbry/lbrynet binary
830 lines
23 KiB
Python
830 lines
23 KiB
Python
#####################################################################
|
|
# #
|
|
# THIS IS A SOURCE CODE FILE FROM A PROGRAM TO INTERACT WITH THE #
|
|
# LBRY PROTOCOL ( lbry.com ). IT WILL USE THE LBRY SDK ( lbrynet ) #
|
|
# FROM THEIR REPOSITORY ( https://github.com/lbryio/lbry-sdk ) #
|
|
# WHICH I GONNA PRESENT TO YOU AS A BINARY. SINCE I DID NOT DEVELOP #
|
|
# IT AND I'M LAZY TO INTEGRATE IN A MORE SMART WAY. THE SOURCE CODE #
|
|
# OF THE SDK IS AVAILABLE IN THE REPOSITORY MENTIONED ABOVE. #
|
|
# #
|
|
# ALL THE CODE IN THIS REPOSITORY INCLUDING THIS FILE IS #
|
|
# (C) J.Y.Amihud and Other Contributors 2021. EXCEPT THE LBRY SDK. #
|
|
# YOU CAN USE THIS FILE AND ANY OTHER FILE IN THIS REPOSITORY UNDER #
|
|
# THE TERMS OF GNU GENERAL PUBLIC LICENSE VERSION 3 OR ANY LATER #
|
|
# VERSION. TO FIND THE FULL TEXT OF THE LICENSE GO TO THE GNU.ORG #
|
|
# WEBSITE AT ( https://www.gnu.org/licenses/gpl-3.0.html ). #
|
|
# #
|
|
# THE LBRY SDK IS UNFORTUNATELY UNDER THE MIT LICENSE. IF YOU ARE #
|
|
# NOT INTENDING TO USE MY CODE AND JUST THE SDK. YOU CAN FIND IT ON #
|
|
# THEIR OFFICIAL REPOSITORY ABOVE. THEIR LICENSE CHOICE DOES NOT #
|
|
# SPREAD ONTO THIS PROJECT. DON'T GET A FALSE ASSUMPTION THAT SINCE #
|
|
# THEY USE A PUSH-OVER LICENSE, I GONNA DO THE SAME. I'M NOT. #
|
|
# #
|
|
# THE LICENSE CHOSEN FOR THIS PROJECT WILL PROTECT THE 4 ESSENTIAL #
|
|
# FREEDOMS OF THE USER FURTHER, BY NOT ALLOWING ANY WHO TO CHANGE #
|
|
# THE LICENSE AT WILL. SO NO PROPRIETARY SOFTWARE DEVELOPER COULD #
|
|
# TAKE THIS CODE AND MAKE THEIR USER-SUBJUGATING SOFTWARE FROM IT. #
|
|
# #
|
|
#####################################################################
|
|
|
|
# Since LBRY wallet is just a long stream of increasing and decreasing
|
|
# numbers. I call this the analytics.
|
|
|
|
import os
|
|
import math
|
|
import json
|
|
import time
|
|
import random
|
|
import colorsys
|
|
import threading
|
|
|
|
from gi.repository import Gtk
|
|
from gi.repository import Gdk
|
|
from gi.repository import GLib
|
|
from gi.repository import cairo
|
|
|
|
from flbry import fetch
|
|
from flbry import settings
|
|
|
|
|
|
|
|
|
|
|
|
def make_colors(amount=4, color=(0.1,0.5,0.8)):
|
|
|
|
# TODO: Figure out how to got the themes prefered active color
|
|
# and use it here.
|
|
|
|
colors = []
|
|
for i in range(amount):
|
|
|
|
nc = colorsys.rgb_to_hsv(*color)
|
|
nc = list(nc)
|
|
|
|
if i != 0:
|
|
nc[1] = 0
|
|
nc[2] = (i)*(1/amount)
|
|
|
|
colors.append(colorsys.hsv_to_rgb(*nc))
|
|
|
|
return colors
|
|
|
|
def window(win):
|
|
|
|
|
|
|
|
|
|
awin = Gtk.Window()
|
|
awin.set_title("FastLBRY GTK: Wallet / Analytics")
|
|
awin.set_size_request(500, 500)
|
|
|
|
|
|
notebook = Gtk.Notebook()
|
|
notebook.set_scrollable(True)
|
|
awin.add(notebook)
|
|
|
|
############################ ANALYTICS SECTION #############################
|
|
|
|
box = Gtk.VBox()
|
|
notebook.append_page(box, Gtk.Label("Totals / Analytics"))
|
|
|
|
# Let's get...
|
|
raw_balance = fetch.lbrynet("wallet_balance")
|
|
|
|
# ... and format the balance information.
|
|
balance = {
|
|
"Spendable":raw_balance["available"],
|
|
"Claim Bids":raw_balance["reserved_subtotals"]["claims"],
|
|
"Supports":raw_balance["reserved_subtotals"]["supports"],
|
|
"Tips":raw_balance["reserved_subtotals"]["tips"],
|
|
}
|
|
|
|
|
|
|
|
# Pie chart for balance
|
|
def conv(t):
|
|
return str(t) + " LBC"
|
|
box.pack_start(pie_chart(win, balance, "Totals", converter=conv), 0, 0 , 0)
|
|
|
|
minus_30_days = 60*60*24*30*-1
|
|
|
|
transactions = {"items":[], # The list of items
|
|
"zoom":[0,0], #minus_30_days,0], # The range of the selection ( by timestamp )
|
|
"allow_negative":True
|
|
}
|
|
|
|
def getting_all_transactions(data, win):
|
|
|
|
# This is a small function funnin in a separate thread, loading
|
|
# all transactions so the graph could draw them.
|
|
|
|
# TODO: this commented code works perfectly fine to get the kind of
|
|
# data that I want. But it's hell'a heavy on my computer. Last time
|
|
# I tried, it froze. So I need to figure out something about this.
|
|
|
|
# cash_file = settings.get_settings_folder()+"GTK-transactions-graph.json"
|
|
|
|
# try:
|
|
# with open(cash_file) as json_file:
|
|
# data["items"] = json.load(json_file)
|
|
# except Exception as e:
|
|
# print(e)
|
|
|
|
|
|
|
|
# def add(out, addpage):
|
|
# ret = False
|
|
# added = 0
|
|
# count = 0
|
|
# for i in out["items"]:
|
|
|
|
# # Removing unnesesary data
|
|
|
|
# a = i.copy()
|
|
# # a["amount"] = i["amount"]
|
|
# # a["timestamp"] = i["timestamp"]
|
|
# if a["timestamp"] == None: # Too recent
|
|
# a["timestamp"] = int(time.time())
|
|
# # a["txid"] = i["txid"]
|
|
# # a["name"] = i.get("name", "")
|
|
|
|
# if a not in data["items"]:
|
|
# added += 1
|
|
# data["items"].append(a)
|
|
# elif not addpage:
|
|
# count += 1
|
|
# ret = True
|
|
|
|
# return ret
|
|
|
|
|
|
# Fetching
|
|
|
|
|
|
|
|
# out = fetch.lbrynet("transaction_list", { "page_size":50})
|
|
# all_of_them = out["total_items"]
|
|
# addpage = 0
|
|
# add(out, 0)
|
|
# for i in range(out["total_pages"]):
|
|
# out = fetch.lbrynet("transaction_list", {"page":addpage+i+2,
|
|
# #"exclude_internal_transfers": True,
|
|
# #"no_totals":True,
|
|
# "page_size":50})
|
|
|
|
# if add(out, addpage):
|
|
# addpage = int((len(data["items"]))/50) - 2
|
|
|
|
|
|
# # Save to cash
|
|
# with open(cash_file, 'w') as fp:
|
|
# json.dump(data["items"], fp)
|
|
|
|
# if not win.keep_loading_the_wallet_graph or all_of_them == len(data["items"]):
|
|
# return
|
|
|
|
# THE MEANWHILE SOLUTION
|
|
|
|
out = fetch.lbrynet("txo_plot", { "days_back":1000, # Fetch 100 days of txo
|
|
"exclude_internal_transfers":True, # Without crap
|
|
"is_not_my_input":True, # Not from me ( as in support only )
|
|
})
|
|
|
|
|
|
|
|
for i in out:
|
|
|
|
a = {}
|
|
a["amount"] = i["total"]
|
|
a["timestamp"] = int(time.mktime(time.strptime(i["day"],"%Y-%m-%d")))
|
|
|
|
|
|
data["items"].append(a)
|
|
|
|
|
|
|
|
win.keep_loading_the_wallet_graph = True
|
|
t = threading.Thread(target=getting_all_transactions, args=(transactions, win))
|
|
t.setDaemon(True)
|
|
t.start()
|
|
def kill_graph(w):
|
|
win.keep_loading_the_wallet_graph = False
|
|
awin.connect("destroy", kill_graph)
|
|
|
|
# Graph with the history of all transactions
|
|
the_graph = graph(win, transactions, "Totals")
|
|
|
|
box.pack_start(the_graph, 1, 1 , 0)
|
|
|
|
|
|
|
|
awin.show_all()
|
|
|
|
def box_of_color(color):
|
|
|
|
def on_draw(widget, cr, data):
|
|
|
|
width = widget.get_allocated_width()
|
|
height = widget.get_allocated_height()
|
|
|
|
cr.set_source_rgb(*color)
|
|
cr.rectangle(0, 0, width, height)
|
|
cr.fill()
|
|
|
|
area = Gtk.DrawingArea()
|
|
area.set_size_request(40, 40)
|
|
area.connect("draw", on_draw, color)
|
|
return area
|
|
|
|
def pie_chart_draw(d, main_layer, win, data, colors):
|
|
|
|
|
|
# Need to know how big is our chart
|
|
w = d.get_allocated_width()
|
|
h = d.get_allocated_height()
|
|
|
|
# We want our circle to fit, so we find which one is
|
|
# smaller.
|
|
smaller = min(w, h)
|
|
|
|
|
|
|
|
last = 0
|
|
whole = float(math.pi*2)
|
|
|
|
sum_of_data = 0
|
|
|
|
for i in data:
|
|
sum_of_data += float(data[i])
|
|
|
|
for n, i in enumerate(data):
|
|
|
|
this = whole* ( float(data[i]) / sum_of_data )
|
|
|
|
main_layer.move_to(w/2, h/2)
|
|
main_layer.arc(w/2, h/2, smaller/2, last, last+this)
|
|
main_layer.close_path()
|
|
main_layer.set_source_rgb(*colors[n%len(colors)])
|
|
main_layer.fill()
|
|
|
|
last = last+this
|
|
|
|
def pie_chart(win, data, title="", converter=False):
|
|
|
|
ret = Gtk.HBox(True)
|
|
|
|
colors = make_colors(len(data))
|
|
|
|
da = Gtk.DrawingArea()
|
|
da.connect("draw", pie_chart_draw, win, data, colors)
|
|
|
|
ret.pack_start(da, 1,1,5)
|
|
|
|
lbox = Gtk.VBox()
|
|
ret.pack_start(lbox, 1,1,5)
|
|
|
|
sum_of_data = 0
|
|
|
|
for i in data:
|
|
sum_of_data += float(data[i])
|
|
|
|
if converter:
|
|
sum_of_data = converter(sum_of_data)
|
|
|
|
lbox.pack_start(Gtk.Label(" Total : "+str(sum_of_data)+" "), 0,0,1)
|
|
|
|
for n, i in enumerate(data):
|
|
ibox = Gtk.HBox()
|
|
lbox.pack_start(ibox, 0,0,3)
|
|
|
|
ibox.pack_start(box_of_color(colors[n%len(colors)]), 0,0,0)
|
|
show_size = data[i]
|
|
if converter:
|
|
show_size = converter(show_size)
|
|
ibox.pack_start(Gtk.Label(" "+i+": "+str(show_size)+" "), 0,0,1)
|
|
|
|
|
|
|
|
return ret
|
|
|
|
def graph_draw(d, main_layer, win, data, currancy, graph_addition):
|
|
|
|
|
|
data["items"] = sorted(data["items"], key=lambda k: k["timestamp"])
|
|
|
|
# Need to know how big is our graph
|
|
w = d.get_allocated_width()
|
|
h = d.get_allocated_height()
|
|
|
|
if data.get("allow_negative", False):
|
|
zero_at = h / 2
|
|
else:
|
|
zero_at = h
|
|
|
|
# The mouse position of a given frame
|
|
mx = d.get_pointer()[0]
|
|
my = d.get_pointer()[1]
|
|
|
|
# Test of the mouse position
|
|
# main_layer.move_to(0,0)
|
|
# main_layer.line_to(mx, my)
|
|
# main_layer.stroke()
|
|
|
|
|
|
len_day = 60*60*24 # Seconds in a day
|
|
len_hour = 60*60 # Seconds in an hour
|
|
len_minute = 60 # Seconds in a minute
|
|
|
|
# Here we are getting the latest and the earliest
|
|
# timestamp, so we could calculate the step of the
|
|
# graph. ( So the data will be readable )
|
|
|
|
latest = 0
|
|
try:
|
|
earliest = data["items"][0]["timestamp"]
|
|
except:
|
|
earliest = 0
|
|
|
|
for i in data["items"]:
|
|
if i.get("timestamp", 0) > latest:
|
|
latest = i.get("timestamp", 0)
|
|
if i.get("timestamp", 0) < earliest:
|
|
earliest = i.get("timestamp", 0)
|
|
|
|
# Now let's look at our zoom value
|
|
|
|
to_zoom = data["zoom"][0] != earliest or data["zoom"][1] != latest
|
|
|
|
if data["zoom"][0] < earliest or data["zoom"][0] == 0:
|
|
data["zoom"][0] = earliest
|
|
if data["zoom"][1] > latest or data["zoom"][1] == 0:
|
|
data["zoom"][1] = latest
|
|
|
|
earliest, latest = data["zoom"]
|
|
|
|
# Now I want to make a scale of dates from left
|
|
# to right.
|
|
|
|
main_layer.select_font_face("Monospace")
|
|
main_layer.set_font_size(10)
|
|
|
|
full_date = "%Y-%m-%d %H:%M:%S"
|
|
only_date = "%Y-%m-%d"
|
|
|
|
if latest - earliest > 10 * len_day:
|
|
show_format = only_date
|
|
count = int( w / (len("xxxx-xx-xx")*6+12) )
|
|
else:
|
|
show_format = full_date
|
|
count = int( w / (len("xxxx-xx-xx xx:xx:xx")*6+12) )
|
|
|
|
|
|
# Now I want to show the current date / time for
|
|
# the area where the user is hovering.
|
|
|
|
suglen = len("xxxx-xx-xx xx:xx:xx")*6+12
|
|
|
|
thexm = mx-suglen/2
|
|
if thexm < 2:
|
|
thexm = 2
|
|
elif thexm > w - suglen - 2:
|
|
thexm = w - suglen - 2
|
|
|
|
try:
|
|
res_date = int( ( latest - earliest ) / w * mx + earliest )
|
|
show_date = time.strftime(full_date, time.gmtime(res_date))
|
|
except:
|
|
show_date = "0000-00-00"
|
|
|
|
main_layer.set_source_rgba(0.1,0.1,0.1,0.5)
|
|
main_layer.rectangle(2+thexm,2,len(show_date)*6+2, 14)
|
|
main_layer.fill()
|
|
|
|
main_layer.move_to(3+thexm,12)
|
|
main_layer.set_source_rgba(1,1,1,1)
|
|
|
|
main_layer.show_text(str(show_date))
|
|
|
|
|
|
# main_layer.set_source_rgba(0.7,0.7,0.7,1)
|
|
# main_layer.move_to( mx, 20 )
|
|
# main_layer.line_to( mx, h )
|
|
# main_layer.stroke()
|
|
|
|
# main_layer.set_dash([10,10])
|
|
# main_layer.set_source_rgba(0.2,0.2,0.2,1)
|
|
# main_layer.move_to( mx, 20 )
|
|
# main_layer.line_to( mx, h )
|
|
# main_layer.stroke()
|
|
# main_layer.set_dash([1])
|
|
|
|
# And the rest of the dates
|
|
|
|
for date in range(count):
|
|
|
|
try:
|
|
res_date = int( ( latest - earliest ) / count * date + earliest )
|
|
show_date = time.strftime(show_format, time.gmtime(res_date))
|
|
except:
|
|
show_date = "0000-00-00"
|
|
|
|
thex = w / count * date
|
|
|
|
|
|
|
|
# If not in range of the mouse ( so I could show the current day
|
|
# for that specific area ).
|
|
if int(thex) not in range(int(thexm-suglen/2), int(thexm+suglen)):
|
|
|
|
main_layer.set_source_rgba(0.1,0.1,0.1,0.5)
|
|
main_layer.rectangle(2+thex,2,len(show_date)*6+2, 14)
|
|
main_layer.fill()
|
|
|
|
main_layer.move_to(3+thex,12)
|
|
main_layer.set_source_rgba(1,1,1,1)
|
|
|
|
main_layer.show_text(str(show_date))
|
|
|
|
|
|
|
|
|
|
|
|
# A step is how often will there be a data point
|
|
# of the graph. Step of one minute, means every
|
|
# point on the graph will consist all the data
|
|
# happened in this minute.
|
|
|
|
|
|
|
|
step = (latest - earliest) / (w / 2) # A second
|
|
|
|
|
|
# Now we need the smallest and biggest value in a
|
|
# given step
|
|
|
|
values = []
|
|
times = []
|
|
|
|
pstep = earliest
|
|
s = 0
|
|
av = []
|
|
|
|
for n, i in enumerate(data["items"]):
|
|
|
|
if i.get("timestamp", 0) < earliest:
|
|
continue
|
|
|
|
if graph_addition == "add":
|
|
s += float(i.get("amount", i.get("value", 0)))
|
|
elif graph_addition == "average":
|
|
av.append( float(i.get("amount", i.get("value", 0))) )
|
|
elif graph_addition == "last":
|
|
s = float(i.get("amount", i.get("value", 0)))
|
|
|
|
if i.get("timestamp", 0) > pstep + step-1:
|
|
|
|
pstep = i.get("timestamp", n)
|
|
|
|
if graph_addition == "average":
|
|
try:
|
|
values.append(sum(av)/len(av))
|
|
except:
|
|
values.append(0)
|
|
else:
|
|
values.append(s)
|
|
times.append(pstep)
|
|
s = 0
|
|
av = []
|
|
|
|
if i.get("timestamp", 0) > latest:
|
|
break
|
|
|
|
|
|
|
|
|
|
|
|
# Finding the farthest point from the center
|
|
# center being the 0 (zero)
|
|
try:
|
|
biggest = max(values)
|
|
if min(values) * -1 > biggest:
|
|
biggest = min(values) * -1 # Multuply by -1 reverses the - to a +
|
|
except Exception as e:
|
|
biggest = 1
|
|
|
|
|
|
|
|
# Now let's draw it
|
|
|
|
main_layer.set_line_cap(cairo.LineCap.ROUND)
|
|
|
|
# POSITIVE VALUE
|
|
|
|
try:
|
|
toy = ( zero_at ) - ( ( zero_at ) / biggest * values[0] ) *0.9
|
|
except:
|
|
toy = zero_at
|
|
#toy = min(toy, zero_at)
|
|
|
|
main_layer.rectangle(0,0,w,zero_at)
|
|
main_layer.clip()
|
|
|
|
main_layer.move_to(0, toy)
|
|
|
|
|
|
|
|
prex = 0
|
|
prey = toy
|
|
|
|
toxes = []
|
|
toyes = []
|
|
|
|
for n, i in enumerate(values):
|
|
|
|
tox = w / (latest - earliest) * (times[n]-earliest)
|
|
try:
|
|
toy = ( zero_at ) - ( ( zero_at ) / biggest * i ) *0.9
|
|
except:
|
|
toy = zero_at
|
|
|
|
toxes.append(tox)
|
|
toyes.append(toy)
|
|
|
|
#toy = min(toy, zero_at)
|
|
|
|
|
|
|
|
main_layer.curve_to(
|
|
tox - (tox - prex)/2,
|
|
prey,
|
|
|
|
prex + (tox - prex)/2,
|
|
toy,
|
|
|
|
tox,
|
|
toy)
|
|
|
|
prex = tox
|
|
prey = toy
|
|
|
|
main_layer.line_to( w, zero_at)
|
|
main_layer.line_to( 0, zero_at)
|
|
main_layer.set_source_rgba(0.2,0.8,0.2,0.5)
|
|
main_layer.fill_preserve()
|
|
main_layer.set_source_rgba(0.2,0.8,0.2,1)
|
|
main_layer.stroke()
|
|
|
|
# NEGATIVE VALUE
|
|
|
|
try:
|
|
toy = ( zero_at ) - ( ( zero_at ) / biggest * values[0] ) *0.9
|
|
except:
|
|
toy = zero_at
|
|
#toy = max(toy, zero_at)
|
|
|
|
main_layer.reset_clip()
|
|
main_layer.rectangle(0,zero_at,w,h)
|
|
main_layer.clip()
|
|
|
|
|
|
main_layer.move_to(0, toy)
|
|
|
|
prex = 0
|
|
prey = toy
|
|
|
|
|
|
for n, i in enumerate(values):
|
|
|
|
tox = w / (latest - earliest) * (times[n]-earliest)
|
|
try:
|
|
toy = ( zero_at ) - ( ( zero_at ) / biggest * i ) *0.9
|
|
except:
|
|
toy = zero_at
|
|
#toy = max(toy, zero_at)
|
|
|
|
main_layer.curve_to(
|
|
|
|
tox - (tox - prex)/2,
|
|
prey,
|
|
|
|
prex + (tox - prex)/2,
|
|
toy,
|
|
|
|
|
|
tox,
|
|
toy)
|
|
|
|
prex = tox
|
|
prey = toy
|
|
|
|
main_layer.line_to( w, zero_at)
|
|
main_layer.line_to( 0, zero_at)
|
|
main_layer.set_source_rgba(0.8,0.2,0.2,0.5)
|
|
main_layer.fill_preserve()
|
|
main_layer.set_source_rgba(0.8,0.2,0.2,1)
|
|
main_layer.stroke()
|
|
|
|
main_layer.reset_clip()
|
|
|
|
|
|
|
|
|
|
# Reference line
|
|
|
|
main_layer.set_source_rgba(0.7,0.7,0.7,1)
|
|
main_layer.move_to( 0, zero_at )
|
|
main_layer.line_to( w, zero_at )
|
|
main_layer.stroke()
|
|
|
|
main_layer.set_dash([10,10])
|
|
main_layer.set_source_rgba(0.2,0.2,0.2,1)
|
|
main_layer.move_to( 0, zero_at )
|
|
main_layer.line_to( w, zero_at )
|
|
main_layer.stroke()
|
|
|
|
main_layer.set_dash([1])
|
|
|
|
# MOUSE OVER SELECTOR
|
|
|
|
def closest(l, v):
|
|
distances = []
|
|
for i in l:
|
|
distances.append(max(i-v, v-i))
|
|
try:
|
|
return l[distances.index(min(distances))]
|
|
except:
|
|
return 0
|
|
|
|
|
|
selectx = closest(toxes, mx)
|
|
if selectx:
|
|
selecty = toyes[toxes.index(selectx)]
|
|
|
|
|
|
# Litte circle
|
|
|
|
main_layer.arc(selectx, selecty, 8, 0, math.pi*2)
|
|
|
|
main_layer.set_source_rgba(0.2,0.8,0.2,1)
|
|
if selecty > zero_at:
|
|
main_layer.set_source_rgba(0.8,0.2,0.2,1)
|
|
main_layer.fill()
|
|
|
|
# Line from that circle downwards
|
|
|
|
main_layer.move_to(selectx, selecty)
|
|
main_layer.line_to(selectx, zero_at)
|
|
main_layer.stroke()
|
|
|
|
# Data about this time frame
|
|
|
|
to_data = times[toxes.index(selectx)]
|
|
from_data = to_data - step
|
|
|
|
try:
|
|
from_data = time.strftime(show_format, time.gmtime(from_data))
|
|
except:
|
|
from_data = "0000-00-00"
|
|
try:
|
|
to_data = time.strftime(show_format, time.gmtime(to_data))
|
|
except:
|
|
to_data = "0000-00-00"
|
|
|
|
|
|
|
|
# Counting the largest thing
|
|
plist = ["From: "+from_data,
|
|
"To: "+to_data,
|
|
"Total: "+currancy+" "+str(round(values[toxes.index(selectx)], 2)) ]
|
|
|
|
leng = 0
|
|
for thing in plist:
|
|
if len(str(thing))*6+2 > leng:
|
|
leng = len(str(thing))*6+2
|
|
|
|
if selectx > w/2:
|
|
recx = selectx - leng - 10
|
|
else:
|
|
recx = selectx + 10
|
|
|
|
if selecty + len(plist)*15 > h:
|
|
recy = selecty - len(plist)*15
|
|
else:
|
|
recy = selecty
|
|
|
|
main_layer.set_source_rgba(0.1,0.1,0.1,0.7)
|
|
main_layer.rectangle(recx, recy, leng, len(plist)*15)
|
|
main_layer.fill()
|
|
|
|
for n, thing in enumerate(plist):
|
|
main_layer.move_to(recx+2, recy+12+(15*n))
|
|
main_layer.set_source_rgba(1,1,1,1)
|
|
main_layer.show_text(thing)
|
|
|
|
# Now let's get the values ( to the side of the graph )
|
|
|
|
for i in range(int(h/20)):
|
|
|
|
# TODO: This has to be tuned a bit. It's not perfect. But it's
|
|
# very close.
|
|
|
|
they = i*20+20
|
|
|
|
try:
|
|
value_is = round( biggest / zero_at * (zero_at - they), 2)
|
|
except Exception as e:
|
|
print("what", e)
|
|
value_is = 0
|
|
|
|
show_value = currancy + " " + str(value_is)
|
|
if mx > w / 2:
|
|
main_layer.set_source_rgba(0.1,0.1,0.1,0.5)
|
|
main_layer.rectangle(2, 2+they,len(show_value)*6+4, 14)
|
|
main_layer.fill()
|
|
main_layer.move_to(3,12+they)
|
|
main_layer.set_source_rgba(1,1,1,1)
|
|
main_layer.show_text(show_value)
|
|
|
|
else:
|
|
main_layer.set_source_rgba(0.1,0.1,0.1,0.5)
|
|
main_layer.rectangle(w-len(show_value)*6-4, 2+they,len(show_value)*6+4, 14)
|
|
main_layer.fill()
|
|
main_layer.move_to(w-len(show_value)*6-3,12+they)
|
|
main_layer.set_source_rgba(1,1,1,1)
|
|
main_layer.show_text(show_value)
|
|
|
|
|
|
# Render a little pressed selector
|
|
|
|
if "pressed" in data:
|
|
|
|
for i in [data["pressed"], mx]:
|
|
|
|
main_layer.set_source_rgba(0.7,0.7,0.7,1)
|
|
main_layer.move_to( i, 0 )
|
|
main_layer.line_to( i, h )
|
|
main_layer.stroke()
|
|
|
|
main_layer.set_dash([10,10])
|
|
main_layer.set_source_rgba(0.2,0.2,0.2,1)
|
|
main_layer.move_to( i, 0 )
|
|
main_layer.line_to( i, h )
|
|
main_layer.stroke()
|
|
|
|
main_layer.set_dash([1])
|
|
|
|
# Keep redrawing the graph
|
|
d.queue_draw()
|
|
|
|
def graph_button_press(w, e, data, da):
|
|
|
|
data["pressed"] = e.x
|
|
|
|
print(data["zoom"])
|
|
print(e.x, e.y)
|
|
|
|
def graph_button_release(w, e, data, da):
|
|
|
|
if "pressed" in data:
|
|
x = data["pressed"]
|
|
|
|
# If there was no motion
|
|
if x-2 < e.x < x+2:
|
|
data["zoom"] = [0,0]
|
|
|
|
else:
|
|
w = da.get_allocated_width()
|
|
zoom0 = data["zoom"][0] + ((data["zoom"][1] - data["zoom"][0]) / w * min(x, e.x))
|
|
zoom1 = data["zoom"][0] + ((data["zoom"][1] - data["zoom"][0]) / w * max(x, e.x))
|
|
|
|
data["zoom"] = [zoom0, zoom1]
|
|
|
|
print(data["zoom"])
|
|
del data["pressed"]
|
|
|
|
def graph(win, data, title="", currancy="$", add_now=True, add_value="Same", graph_addition="add"):
|
|
|
|
# adding one more data point for "now"
|
|
if add_now:
|
|
try:
|
|
data["items"] = sorted(data["items"], key=lambda k: k["timestamp"])
|
|
last = data["items"][-1].copy()
|
|
last["timestamp"] = int(time.time())
|
|
if not add_value == "Same":
|
|
last["amount"] = add_value
|
|
data["items"].append(last)
|
|
|
|
except:
|
|
pass
|
|
|
|
|
|
event_box = Gtk.EventBox()
|
|
da = Gtk.DrawingArea()
|
|
da.set_size_request(100,100)
|
|
da.connect("draw", graph_draw, win, data, currancy, graph_addition)
|
|
event_box.connect("button-press-event", graph_button_press, data, da)
|
|
event_box.connect("button-release-event", graph_button_release, data, da)
|
|
event_box.add(da)
|
|
|
|
|
|
return event_box
|