Last active
December 9, 2015 14:38
-
-
Save glurp/f9908b851b57332e47e1 to your computer and use it in GitHub Desktop.
show cotations, with curves
This file contains hidden or 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
############################################################## | |
# bourse.rb | |
############################################################## | |
require 'json' | |
require 'httpclient' | |
require 'yahoo-finance' | |
unless Dir.exists?("dsl-gtk") | |
require 'Ruiby' | |
else | |
require_relative 'dsl-gtk/lib/Ruiby.rb' | |
load 'dsl-gtk/lib/ruiby_gtk/dsl/canvas.rb' # for dynamic reloading+plot debug | |
end | |
require 'thread' | |
require 'date' | |
require 'pp' | |
$rfirst=! defined?($rfirst) | |
$cotes={ | |
"360114899" => {name: "Airbus" , param1: 56.5 ,param2: 58.6, api: :lecho}, | |
"AIR.PA" => {name: "YAirbus" , param1: 56.5 ,param2: 58.6, api: :yahoo}, | |
"360194025" => {name: "EDF" , param1: 17.7 ,param2: 17.8, api: :lecho}, | |
"360017060" => {name: "L'Oreal" , param1: 145.1 ,param2: 145.2, api: :lecho}, | |
"360114910" => {name: "Sanofi" , param1: 87.7 ,param2: 87.8, api: :lecho}, | |
"360115975" => {name: "Vinci" , param1: 55.5 ,param2: 56.6, api: :lecho}, | |
"360015511" => {name: "CAC40" , param1: 80.7 ,param2: 90.8, api: :lecho}, | |
"GOOG" => {name: "Google" , param1: 630.0 ,param2: 635.0, api: :yahoo}, | |
"MSFT" => {name: "Mi$" , param1: 43.0 ,param2: 45.0, api: :yahoo}, | |
"GM" => {name: "GM" , param1: 29.0 ,param2: 32.0, api: :yahoo}, | |
"ATML" => {name: "Atmel" , param1: 7.0 ,param2: 9.0, api: :yahoo}, | |
"VLKPY" => {name: "VolksW" , param1: 20.0 ,param2: 30.0, api: :yahoo}, | |
} if $rfirst | |
$file_config="bourse_config.rb" | |
$cotes=eval(File.read($file_config)) if File.exist?($file_config) && $rfirst | |
def calc_alarme(cote,values) | |
return [false,false] unless values["last"] | |
value=values["last"].to_f | |
p1=cote[:param1] | |
p2=cote[:param2] | |
c1=(value < p1) ? "#FF4050" : (value > p2) ? "#7F7" : $BGNOALARME | |
c2=(value < p1*0.99) ? "#FF4050" : (value > p2*1.01) ? "#7F7" : $BGNOALARME | |
[c1,c2] | |
end | |
$nb_colonnes=ARGV.first || 2 | |
$fr_ouverture=9..18 # heure ouverture cotations cac40 | |
$us_ouverture=15..23 # heure ouverture cotations Nyse | |
$periode=60_000 # acquisition periode, ms | |
$BG="#383838" # background color window | |
$PLOT0,$PLOT1="#111","#101010" # background plots zones (stroke/fill) | |
$TXTPLOT="#FFF" # color text min/value/max un plot | |
$CVPLOT="#AAA" # shadow color of each curve | |
$BGNOALARME="#083838" # alarme color non-active | |
$url_lecho="http://1.ajax.lecho.be/rtq/?reqtype=simple"es=360015511&lightquotes=&group=g30_q_p" | |
$query_lecho={reqtype: "simple", quotes: 360015511, lightquotes: "", group: "g30_q_p"} | |
$url_yahoo="http://finance.yahoo.com" | |
$bgcolor=Ruiby_dsl.html_color($BG) | |
$hcotes={} if $rfirst | |
$COTES=$cotes.keys | |
#require 'nokogiri' | |
=begin | |
Data acquired with ajax.lecho.be : | |
try { _parseRtq( | |
{"delay":60,"serverTime":1441820894526, | |
"stocks":{ "360017060":{"volume":684414,"pct":"1.1573","high":"150.9500", | |
"last":"148.6000","low":"148.5500","prev":"146.9000","ask":"148.6000", "time":"17:35:00","bid":"148.5500","open":"150.1000"}, | |
.... | |
} | |
}) | |
} catch(err) { if (console) console.error(err); } | |
=end | |
######################### sauvegardes courbes/restitution au demarrage | |
DATA_CURVES="curves2.data" | |
def save_curve(data) | |
File.open(DATA_CURVES,"wb") { |f| Marshal.dump(data,f) } | |
end | |
def load_curve() | |
a={} | |
if File.exists?(DATA_CURVES) | |
File.open(DATA_CURVES,"rb") { |f| a=Marshal.load(f) } | |
else | |
a=$COTES.each_with_object({}) {|name,h| h[name]=[[100]*150,[100]*150] } | |
end | |
a | |
end | |
###################### Acqisition CAC40 aupres de lecho.be : javascript=>json=>data | |
def get_data_lecho() | |
h = HTTPClient.new() | |
json=h.get($url_lecho,$query_lecho).body | |
if json =~ /_parseRtq\(([^)]*?)\)\s*}\s*catch/ | |
JSON.parse($1)["stocks"] rescue (p $! ; {}) | |
else | |
puts "noecho: no data" | |
{} | |
end | |
end | |
###################### Acqisition Yahoo Finance | |
def get_data_yahoo(lcotes) | |
fields=[ | |
:name, | |
:last_trade_price,:last_trade_time, | |
:volume,:previous_close, | |
:change_in_percent, | |
:ask,:open,:bid | |
] | |
yahoo_client = YahooFinance::Client.new | |
l=yahoo_client.quotes(lcotes,fields) | |
ret= l.zip(lcotes).each_with_object({}) {|(data,n),h| | |
if data | |
h[n]={ | |
"bname" => data[:name], | |
"volume" => nai(data[:volume]), | |
"pct" => naf(data[:change_in_percent].gsub(/%\s*$/,"")), | |
"last" => naf(data[:last_trade_price]), | |
"time" => ftime(data[:last_trade_time]), | |
"ask" => naf(data[:ask]), | |
"open" => naf(data[:open]), | |
"bid" => naf(data[:bid]), | |
"prev" => naf(data[:previous_close]), | |
"per" => naf(data[:pe_ratio]), # ratio capitalisation/resultat-annuel | |
"peg" => naf(data[:peg_ratio]), # ratio capitalisation/resultat-glissant | |
"unit" => '$', | |
} | |
end | |
#gui_invoke {alert("#{n} => #{h[n].inspect}") } if $first | |
} | |
pp ret | |
return ret | |
end | |
def nai(s) s.to_i rescue 0 end | |
def naf(s) s.to_f rescue 0.0 end | |
def ftime(h) # 10:35am => 10:35, 02:35pm => 14:35 | |
case h | |
when /(\d+):(\d+)am$/ then "#{$1}:#{$2}:00" | |
when /(\d+):(\d+)pm$/ then "#{$1.to_i+12}:#{$2}:00" | |
else | |
puts "unknown time : #{h}" | |
"00:00:00" | |
end | |
end | |
##################### Moteur d'aquisition | |
$first=true | |
def get_update(app) | |
return if Thread.list.size>2 | |
Thread.new { | |
now=Time.now | |
if $first || $fr_ouverture.member?(Time.now.hour) | |
h=(get_data_lecho rescue nil) | |
next if !h || h.size==0 || !(Hash === h) | |
hh=$COTES.each_with_object({}) {|cote,r| r[cote]=h[cote].merge({"unit"=>'€'}) if h[cote]} | |
$hcotes.merge!(hh) | |
end | |
names=$COTES.select {|k| $cotes[k][:api]==:yahoo} | |
if names.size>0 | |
h=(get_data_yahoo(names) rescue p $! ) | |
next if !h || h.size==0 || !(Hash === h) | |
$hcotes.merge!(h) | |
end | |
$first=false | |
gui_invoke { push_data($hcotes) ; @cv.redraw() rescue p $!} | |
Historique.memo(now,$hcotes) | |
} | |
end | |
####################### Archivage | |
Dir.mkdir("tradingdata") unless Dir.exists?("tradingdata") | |
class Historique | |
class << self | |
def to_dir(top) | |
dir="tradingdata/#{top.gmtime.strftime("%Y_%m")}" | |
Dir.mkdir(dir) unless Dir.exists?(dir) | |
dir | |
end | |
def to_sec(top) (top.gmtime.to_i % (24*3600)) end | |
def from_i(gm_timestamp) | |
dt=DateTime.strptime(gm_timestamp.to_s,'%s') | |
end | |
def memo(time,data) | |
dir=to_dir(time) | |
top=to_sec(time) | |
hdata=data.clone | |
hdata['_time']=top | |
hdata['_timestamp']=time.gmtime.to_i | |
filename="#{dir}/#{'%02d' % time.day}.data" | |
File.open(filename,"a+") { |f| f.puts(hdata.inspect) } | |
end | |
def read(app,t0,t1,lcotes) | |
avance=DynVar.new(0) | |
longer=(t1-t0).to_i>3600*48 | |
gui_invoke_wait { avance=start_avance "Reading files..."} if longer | |
date0,date1=t0.gmtime.strftime("%Y_%m"),t1.gmtime.strftime("%Y_%m") | |
t0gmt,t1gmt=t0.gmtime.to_i,t1.gmtime.to_i | |
lvalues=(0..lcotes.size-1).map {[]} | |
p ["****",date0,date1,t0,t1] | |
lfile=(date0..date1).each_with_object([]) do |date,a| | |
afn=Dir.glob("tradingdata/#{date}/*.data").select {|fn| | |
y,mo,day=fn.scan(/\d+/).map(&:to_i) | |
p Date.new(y,mo,day).to_time.gmtime,date0,date1 | |
p [" ",y,mo,day,Date.new(y,mo,day).to_time] | |
(t0.to_i..t1.to_i).member?(Date.new(y,mo,day).to_time.gmtime.to_i) | |
} | |
a.push(*afn) | |
end | |
p lfile | |
lfile.each_with_index do |filename,ifile| | |
avance.value=(1.0*ifile)/lfile.size | |
numday=File.basename(filename).gsub(".data","").gsub(/^0/,"").to_i | |
lv=File.readlines(filename).map do |line| | |
h=eval(line) | |
tm=h['_timestamp'] | |
next if tm<t0gmt | |
return(lvalues) if tm>t1gmt | |
lcotes.each_with_index {|cote,i| | |
next unless h[cote] | |
val=h[cote]["pct"].to_f | |
if lvalues[i].size==0 | |
lvalues[i] << [val,tm ] | |
else | |
lastv= lvalues[i].last[0] | |
if val!=lvalues[i].last[0] | |
( lvalues[i] << [lastv,tm] ) if lvalues[i].last.last<(tm-60*4) | |
lvalues[i] << [val,tm] | |
end | |
end | |
} | |
end # end read each line | |
end # end lfile | |
avance.value=3 if longer | |
return(lvalues) | |
end # end read | |
def show(app) | |
profondeur=DynVar.new(1) | |
calage_zero=false | |
masquage=true | |
ok=app.instance_eval do | |
#======================== dialog choix cotes et periode | |
dialog("Cotes") do | |
stack do | |
labeli " Choisir les cotes a extraire " | |
separator | |
$cotes.values.each {|d| | |
d[:ok]=false | |
check_button(d[:name],false) { |ok| d[:ok]=ok } | |
} | |
separator | |
check_button("Calage courbe sur 0 ?",false) { |ok| calage_zero=ok } | |
check_button("masquage zones mortes?",true) { |ok| masquage=ok} | |
separator | |
stacki { | |
labeli "Profondeur d'extration (nb jour):" | |
islider(profondeur,:min=>0,:max=>20,:by => 1) | |
wnbj=labeli "1 jour" | |
profondeur.observ {|v| wnbj.label= "#{v} Jours"} | |
} | |
end | |
end | |
end | |
return unless ok | |
lcotes=$cotes.each_with_object([]) {|(k,v),a| a<<k if v[:ok]} | |
return unless lcotes.size>0 | |
#======================= extration datas dans un thread | |
Thread.new do | |
delta= 24*3600+(profondeur.value>0 ? (profondeur.value*24*3600) : (6*3600)) | |
datas=Historique.read(app,Time.now.gmtime-delta,Time.now.gmtime,lcotes) | |
datas.each {|a| a[0][0]=0 } if calage_zero | |
datas.reject! {|a| a.size<3} | |
next if datas.size==0 | |
if masquage | |
#============== suprimer les trous: zone(s) ou toutes les courbes sont plattes | |
ai=(1..datas.size).map { 0 } # index de chaque courbes | |
cum_delta=0 | |
time=datas.zip(ai).map {|a,i| a[i].last}.min | |
adelta=[[time,0]] | |
loop do | |
time=datas.zip(ai).map {|a,i| a[i].last}.max | |
delta=datas.zip(ai).map {|a,i| a[i+1].last}.min - time | |
#=== si plat de plus d'une heure, decalage | |
if delta>3600 | |
delta=[delta-60*60,60*60].max # garder un plat < 60 minutes | |
puts "decalage de #{delta}" | |
cum_delta+=delta | |
adelta << [time+60,cum_delta] | |
datas.each_with_index do |a,j| | |
puts " decale #{j} de #{delta}" | |
((ai[j]+1)..(a.size-1)).each {|i| a[i][1]-=delta} | |
end | |
puts "decalage fait" | |
end | |
break if datas.zip(ai).any? {|a,i| (i == (a.size-1))} | |
#=== avance | |
datas.each_with_index { |a,j| ai[j]+=1 if a[ai[j]+1].last == time+delta} | |
break if datas.zip(ai).any? {|a,i| (i == (a.size-1))} | |
end | |
adelta << [time,cum_delta] | |
recale= proc do |x| | |
d=0 | |
adelta.each_cons(2) {|(t0,d0),(t1,d1)| if x>=t0 && x<=t1 then d=d0 ; break; end} | |
Time.at(x+d).to_s | |
end | |
else | |
recale= proc do |x| Time.at(x).to_s end | |
end | |
#========= preparation descripteurs courbes pour ploter | |
xminmax=datas[0].minmax_by {|(y,x)| x}.map {|(y,x)| x} | |
yminmax=lcotes.each_with_index.map { |cote,i| | |
datas[i].minmax_by {|(y,x)| y}.map {|(y,x)| y} | |
}.flatten.minmax | |
hc=lcotes.each_with_object({}) { |cote,h| | |
#yminmax=datas[h.size].minmax_by {|(y,x)| y}.map {|(y,x)| y} | |
#yminmax[1]=yminmax[1]+(yminmax.last-yminmax.first)/10.0 | |
s={ | |
name: $cotes[cote][:bname] || $cotes[cote][:name], | |
data: datas[h.size], | |
color: %w{#FF0 #F99 #F0F #0FF #AFF #FAA}[h.size%6], | |
xminmax: xminmax, | |
yminmax: yminmax | |
} | |
h[cote]=s | |
} | |
hrules=lcotes.each_with_object({}) { |cote,h| | |
s={ | |
d: $cotes[cote], | |
data: datas[h.size], | |
} | |
h[cote]=s | |
} | |
#======================= Affichage courbes | |
gui_invoke do | |
panel_async("Archives curves : #{profondeur.value} days") do | |
c=nil | |
stack { | |
flow { | |
cc=canvas(70,300) { # labels des cotes | |
on_canvas_draw { |w,ctx| | |
cc.draw_rectangle(0,0,70,300,0,$BG,$BG,0) | |
hc.each_with_index { |(k,d),i| | |
cc.draw_text(3,(i+1)*16,d[:name],1.2,d[:color]) | |
} | |
} | |
} | |
c=plot(800,300,{},{ | |
bg: $BG, | |
grid: 40, | |
grid_color: "#555", | |
tracker: [proc {|x| "Date: #{recale.call(x)}"},proc {|name,y| "#{name}: #{y} %"}] | |
}) | |
hc.each {|k,d| c.add_curve(k,d) } | |
} | |
flowi { | |
button("Rejeux") do | |
hrules.each do |k,d| | |
last,v="",{} | |
ll=d[:data].map {|(c,x)| | |
v["last"]=c | |
[x,calc_alarme(d[:d],v).first] | |
} | |
c.add_bar("rules #{k}",ll) | |
end | |
end | |
button(">>") | |
} | |
} | |
end # end dialog curves | |
end # end gui_invoke | |
end # end thread | |
end # end def show | |
end #class self | |
end | |
################################################################ | |
# C o n f i g u r a t i o n | |
################################################################ | |
module Ruiby_dsl | |
def dialog_minmax(wbutton,h,value) | |
f1=DynVar.new(h[:param1]) | |
f2=DynVar.new(h[:param2]) | |
ok=dialog_async("Saisie Seuils #{h['name']}", response: proc { | |
if f1.value<f2.value | |
wbutton.label="#{f1.value}..#{f2.value}" | |
h[:param1]=f1.value | |
h[:param2]=f2.value | |
File.write($file_config,$cotes.inspect) | |
true | |
else | |
false | |
end} | |
) do | |
stack do | |
label("#{h[:name]} : #{value}",font: "Arial bold 33px") | |
separator | |
flowi { entry(f1) ; entry(f2) } | |
a=label('',font: "Arial 33") | |
f1.observ {|v| a.text= v<f2.value ? "Ok":"Nok: p1>p2 !"} | |
f2.observ {|v| a.text= v>f1.value ? "Ok":"Nok: p2<p1 !"} | |
stacki { | |
fslider(f1,:min=>(f1.value*0.8),:max=>(f2.value*1.1),:by => 0.1,:decimal=>2) | |
fslider(f2,:min=>(f1.value*0.8),:max=>(f2.value*1.1),:by => 0.1,:decimal=>2) | |
} | |
end | |
end | |
if ok | |
end | |
end | |
def configuration_trading() | |
dialog_async("Configuration",{response: proc { | |
#alert($cotes) | |
true | |
}}) do | |
stack do table(0,0) do | |
row { | |
cell(label('clef'));cell(label('name')); | |
cell(label('param 1&2'));cell(label('api')) | |
} | |
$cotes.each do |k,v| | |
row do | |
cell(label(k));cell(label(v[:name])) | |
x=nil | |
x=cell(button("#{v[:param1]}..#{v[:param2]}") { |w| | |
dialog_minmax(w,v,$hcotes[k]["last"]) | |
}) | |
cell(label(v[:api].to_s)) | |
end # end row | |
end # end each | |
end end # end stack | |
end # end dialog | |
end | |
end | |
################################################### | |
## D e s s i n f e n e t r e | |
################################################### | |
module Ruiby_dsl | |
def expose_trading(cv,ctx) | |
return if $hcotes.size<1 | |
top=Time.now | |
@sx,@sy=size() | |
@wcurve=@sx/@nb_col | |
@hcurve=((@sy-12-2-12-15-24-3)/($hcotes.size*2) - 14)*@nb_col | |
@cv.draw_rectangle(0,0,@sx,@sy,1,"#AAA",$BG,3) | |
y=10 | |
x=2 | |
############# Rappel des alertes | |
$cotes.keys.each_with_index do |k,i| | |
v=$hcotes[k] | |
car=$cotes[k][:name][0,1] | |
calc_alarme($cotes[k],v).each_with_index {|color,col| | |
x,y=2,y+12 if x>=@sx-20 | |
if col==0 | |
@cv.draw_text(x,y,car,0.8,"#FFF") | |
x+=7 | |
end | |
@cv.draw_rectangle(x,y-5,6,3,0,$BGNOALARME, color,0) | |
x+=7 | |
} | |
x+=5 | |
end | |
y+=2 | |
x+=3 | |
@cv.draw_line([7,y,@sx-7,y],"#FFF",2) | |
y+=16 | |
y00=y | |
############# Zone detailles cotations: alerte+nom+valeur+courbes | |
x=5 | |
$cotes.keys.each do |k| v=$hcotes[k] | |
if y>@sy-30-@hcurve*2 | |
y=y00 | |
x+=@wcurve+2 | |
@cv.draw_line([x-1,y,x-1,@sy],"#FFF",1) | |
#alert(" changement colonne: #{x} #{y}") | |
end | |
calc_alarme($cotes[k],v).each_with_index {|color,ii| | |
@cv.draw_rectangle(x+2+11*(ii),y-10,10,10,4,$BGNOALARME, color,2) | |
} | |
@cv.draw_text(x+24,y+2,"#{$cotes[k][:name]}",1.7,"#FFE") | |
@cv.draw_text_left( | |
x+@wcurve-2,y+2, | |
"#{v["unit"]}#{v["last"].to_f.round(2)} #{v["pct"].to_f.round(0)}%", | |
1.5, | |
"#FFE",$BG | |
) | |
y+=10 | |
y=draw_curve1(@lcurve[k][0] ,0 ,x,y, "€" , :brute , v["prev"].to_f)+3 | |
y=draw_curve1(@lcurve[k][1] ,1 ,x,y, "vol" , :delta, 0) | |
y+=13 | |
end | |
###################### bs de page: heure derniere cotation | |
y+=10 | |
@cv.draw_text(@sx/2-30,y-8,$hcotes.values.first["time"],1.0,"#FFE") if $hcotes.size>0 | |
y+=3 | |
if false | |
@cv.set_size_request(@sx-2,y) | |
move(1,1) | |
end | |
save_curve(@lcurve) | |
rescue Exception => e | |
log(e) | |
end | |
def push_data(h) | |
h.each_with_index {|(k,v),i| | |
next unless Hash === v | |
begin | |
@lcurve[k]=[[100]*150,[100]*150] unless @lcurve[k] && @lcurve[k].size==2 | |
curve=@lcurve[k][0] ; curve.push(v["last"].to_f) ; curve.shift while curve.size>150 | |
curve=@lcurve[k][1] ; curve.push(v["volume"]) ; curve.shift while curve.size>150 | |
rescue Exception => e | |
p e | |
p k,v,i | |
end | |
} | |
end | |
def draw_curve1(curve,i,x0,y0,unit, type, vy) | |
w=@sx-4 | |
h=@hcurve | |
@cv.draw_rectangle(x0,y0,@wcurve,h,1,$PLOT0,$PLOT1,2) | |
coul="##{%w{FF4 4F4 6060FF FF6060 44F 4FF}[i]}" | |
data= if type==:brute | |
curve | |
else | |
a=curve.each_cons(2).map { |a,b| b-a} | |
min,max=a.minmax | |
i=2 | |
a.each_cons(5) {|l| | |
m=l[2] | |
mm=(l[0]+l[1]+l[3]+l[4])/4.0 | |
a[i]=mm if (m==min || m==max) && (m-mm).abs> (max-min)/5.0 | |
i+=1 | |
} | |
a | |
end | |
min,max=data.minmax | |
if unit=="€" | |
min,p,max=[min,vy,max].sort | |
end | |
if unit=="€" && (max-min).abs<1 | |
min,max=min-1,max+1 | |
end | |
min,max=min-50,min+50 if min==max | |
dx=1.0*@wcurve/data.size | |
lxy=data.each_with_index.map {|v,t| [x0+dx*t,y0+h-1.0*(v-min)*h/(max-min)]}.flatten | |
@cv.draw_line(lxy,$CVPLOT,3) | |
@cv.draw_line(lxy,coul,1) | |
if unit=="€" | |
y=y0+h-1.0*(vy-min)*h/(max-min) | |
@cv.draw_line([x0+2,y,x0+@wcurve-2,y],"#F00",1) | |
end | |
@cv.draw_text(x0+5,y0+(1)*8,"#{unit} #{min} .. #{max}",0.8,$TXTPLOT) | |
(y0+h) | |
end | |
end | |
############################################################### | |
# M A I N : fenetre et moteur d'acquisition | |
############################################################### | |
Ruiby.app width: 150*$nb_colonnes,height: 600, title: "Boursicotons" do | |
def cv_redraw() @cv.redraw() end | |
chrome(false) | |
@chrome=false | |
set_resizable(true) | |
move(10,30) | |
@lv=[] | |
@s={} | |
@lv_last=[] | |
@bddtr={} | |
@marche_ok=false | |
@lcurve=load_curve() | |
@nb_col=$nb_colonnes | |
stack do | |
@cv=canvas(150,600) do | |
on_canvas_draw { |w,ctx| expose_trading(w,ctx) } | |
on_canvas_button_press { |w,e| | |
@chrome=!@chrome | |
chrome(@chrome) | |
false # event is not consume; di the popup... | |
} | |
end | |
flowi { | |
button("Load") { load(__FILE__); } | |
button("Arch") { Historique.show(self) } | |
button("Conf.") { configuration_trading() } | |
button("Ref.") { after(1) { get_update(self) } } | |
buttoni("Exit") { exit!(0) } | |
} | |
end | |
def_style <<EEND | |
* {background: #{$BG}} | |
.button { | |
background: #{$BG} ; | |
background-image: none; | |
font: Sans bold 8px; | |
color: #CCC; | |
border-radius: 5px; | |
padding: 3px 7px 2px 5px; | |
border-top-left-radius: 12px; | |
border-width: 1px; | |
-GtkButton-shadow-type:none; | |
-GtkWidget-focus-line-width: 0; | |
} | |
GtkSeparator { color: #FFC ; padding: 20px 0px 10px 0px;} | |
GtkLabel { background:transparent; color: #FFC ; font: Sans 10px;} | |
GtkEntry { background:transparent; color: #FFC ; font: Sans bold 10px;} | |
GtkProgressBar { | |
background-image: -gtk-gradient (linear,left bottom, right top, from(#EEF), to(#00F)); | |
} | |
EEND | |
after(100) { get_update(self) ; anim($periode) { get_update(self) } } # update periodique | |
anim(2000) { set_keep_above(true) if $nb_colonne==1} # maintin en avant plan de la fenetre | |
def start_avance(text) | |
@dpp=panel_progress(text="Starting...") {|v| v=(v*100.0).to_i ; "Done %d%%" % v} | |
end | |
def end_avance() @dpp.value=200.0 end | |
end if $rfirst | |
$rfirst=false | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment