473,748 Members | 5,849 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Command config, quitting, binary, Timer

Hello, I have four things to ask or to suggest, sorry if they seem
basic or already discussed.

-------------------

I am still ignorant about Tkinter. This little program, after pressing
the "Go" eats more and more RAM, is it normal? Can it be avoided? (In
normal programs this is isn't a real problem).

! import Tkinter
! def dogo():
! while 1:
! b.config(comman d=lambda:None)
! root = Tkinter.Tk()
! b = Tkinter.Button( root, text="Go", command=dogo)
! b.pack()
! root.mainloop()

Note: I have found this problem because in a small program I have a
Start button that becomes a Stop, so the bound command must change each
time. (Maybe there is some other solution, like hiding/unhiding the
buttons, I don't know.)

-------------------

When I quit a Tkinter program (clicking the [X] button of the window)
while it's running, it stops with something like this:

TclError: invalid command name ".9970192"

There is a way to avoid it, or to intercept that quitting command to
stop the program more gracefully?

-------------------

When I have to convert numbers to binary I use something like this (the
management of negative numbers is removed). It's my faster Python
version (derived by a Hettinger's version):

! from collections import deque
! def binary_conv(n):
! if n == 0:
! return 0
! else:
! absn = abs(n)
! conv = ["0", "1"]
! result = deque()
! _app = result.appendle ft
! while absn:
! _app( conv[absn & 1] )
! absn >>= 1
! return int("".join(res ult))

But converting to binary is a quite common operation, so I think it can
be added as a binary function to Python, for example adding "b" to the
the conversion types of the % formatting.

-------------------

Sometimes I need something like the Timer of Java, it generates "ticks"
and calls a function for each tick. The tick frequency can be set, or
they can be stopped.

In another Newsgroup someone has suggested me that the callLater of
Twisted can solve my problem, but I think it's a quite common thing, so
I think that maybe it can be added to the threading standard module.

This is a rough Python version of mine (without comments), it's not a
true Metronome because it counts the delay time after the end of the
last function call. This class also seems fragile, sometimes it gives
me problems, and I cannot use too much concurrent metronomes, etc. It's
quite

Maybe someone can suggest me how to improve it.
! from threading import Timer
!
! class Metronome(objec t):
! def __init__(self, interval, fun, *args, **kwargs):
! self.interval = interval # seconds.
! self.fun = fun
! self.args = args
! self.kwargs = kwargs
! self._running = False
! def _go(self):
! # Call the function with the stored values
! self.fun(*self. args, **self.kwargs)
! if self._running:
! self._call = Timer(self.inte rval, self._go)
! self._call.star t()
! def start(self):
! if not self._running:
! self._running = True
! self._go()
! def stop(self):
! if self._running:
! self._call.canc el()
! self._running = False

Thank you,
bearophile

Sep 4 '05 #1
8 2172
be************@ lycos.com wrote:
I am still ignorant about Tkinter. This little program, after pressing
the "Go" eats more and more RAM, is it normal? Can it be avoided? (In
normal programs this is isn't a real problem).

! import Tkinter
! def dogo():
! while 1:
! b.config(comman d=lambda:None)
! root = Tkinter.Tk()
! b = Tkinter.Button( root, text="Go", command=dogo)
! b.pack()
! root.mainloop()


What did you expect to happen with the infinite loop inside dogo()?
Wouldn't it call b.config() over and over again, never returning? Is
that really what you wanted?

-Peter
Sep 4 '05 #2
On 3 Sep 2005 18:44:11 -0700, be************@ lycos.com declaimed the
following in comp.lang.pytho n:

! import Tkinter
! def dogo():
! while 1:
! b.config(comman d=lambda:None)
Well, this infinite loop can't be good for memory usage (I'm no
expert -- what does .config() do? [If I interpret the skimpy help
system, basically this one is replacing the original "dogo" method with
an anonymous function that "returns" None -- being in an endless loop,
you are creating an endless number of anonymous functions; they may not
be garbage collected until the loop ends]
Note: I have found this problem because in a small program I have a
Start button that becomes a Stop, so the bound command must change each
time. (Maybe there is some other solution, like hiding/unhiding the
buttons, I don't know.)
Would it not be easier to have a single callback, but change the
button Label, and have the callback branch based on the current label?
-------------------

When I quit a Tkinter program (clicking the [X] button of the window)
while it's running, it stops with something like this:

TclError: invalid command name ".9970192"

There is a way to avoid it, or to intercept that quitting command to
stop the program more gracefully?
No idea -- my only Tkinter program (at work, so I can't test it),
doesn't do anything like this. -------------------

When I have to convert numbers to binary I use something like this (the
management of negative numbers is removed). It's my faster Python
version (derived by a Hettinger's version):

! from collections import deque
! def binary_conv(n):
! if n == 0:
! return 0
! else:
! absn = abs(n)
! conv = ["0", "1"]
! result = deque()
! _app = result.appendle ft
! while absn:
! _app( conv[absn & 1] )
! absn >>= 1
! return int("".join(res ult))

But converting to binary is a quite common operation, so I think it can
be added as a binary function to Python, for example adding "b" to the
the conversion types of the % formatting.
Ugh...

And exactly what are you trying to return? That last line implies
you are returning an integer... If I understand the logic, you are
taking, say an integer of 3 (in binary "00000011"b , assuming only a byte
in length), creating a string of 0/1 ("11"), and then converting this
string back to an integer with the value 11 (binary "10000011") ...
NYBBLES = { "0" : "0000",
"1" : "0001",
"2" : "0010",
"3" : "0011",
"4" : "0100",
"5" : "0101",
"6" : "0110",
"7" : "0111",
"8" : "1000",
"9" : "1001",
"A" : "1010",
"B" : "1011",
"C" : "1100",
"D" : "1101",
"E" : "1110",
"F" : "1111" }

def conv2b(n):
tstr = "%X" % n
olst = []
for nyb in tstr:
olst.append(NYB BLES[nyb])
return "".join(ols t)

print
print "3 is ", conv2b(3)
print "11 is ", conv2b(11)
print "3.141 is ", conv2b(3.141)
print "-3 is ",conv2b(-3)
print "40558 is ", conv2b(40558)

-=-=-=-=-=-=-
3 is 0011
11 is 1011
3.141 is 0011
E:\UserData\Den nis Lee Bieber\My Documents\Pytho n Progs\Script1.p y:20:
FutureWarning: %u/%o/%x/%X of negative int will return a signed string
in Python 2.4 and up
tstr = "%X" % n
-3 is 111111111111111 111111111111111 01
40558 is 100111100110111 0

I've not tested it, but I somehow suspect it is faster than a mass
of push/pop and shifts...


-------------------

Sometimes I need something like the Timer of Java, it generates "ticks"
and calls a function for each tick. The tick frequency can be set, or
they can be stopped.

In another Newsgroup someone has suggested me that the callLater of
Twisted can solve my problem, but I think it's a quite common thing, so
I think that maybe it can be added to the threading standard module.

This is a rough Python version of mine (without comments), it's not a
true Metronome because it counts the delay time after the end of the
last function call. This class also seems fragile, sometimes it gives
me problems, and I cannot use too much concurrent metronomes, etc. It's
quite
Main problem I see with this is that each call to threading.Timer ()
is creating a NEW thread just for one tick. Lots of overhead involved.
I'd create a single thread whose main run method is a continuous loop,
using time.sleep() to suspend between ticks. .Timer() is optimized for a
one-time callback.

Note that using _nextInterval allows me perform an adjustment for
the time taken by the callback function.

-=-=-=-=-=-=-
import time
import threading

class Metronome(objec t):
def __init__(self, interval, func, *args, **kwargs):
self._func = func
self._args = args
self._kwargs = kwargs
self._interval = interval
self._running = False
self._Timer = None
self._nextInter val = 0

def Start(self):
if not self._running:
print "Creating worker at %s" % time.time()
self._Timer = threading.Threa d(None, self._worker, (), {})
self._nextInter val = time.time() + self._interval
self._running = True
self._Timer.sta rt()

def Stop(self):
if self._running:
print "Stopping worker at %s" % time.time()
self._running = False
self._Timer.joi n()
print "thread joined"

def _worker(self):
while self._running:
time.sleep(self ._nextInterval - time.time())
self._nextInter val += self._interval
self._func(*sel f._args, **self._kwargs)
def aTick():
print "A tick happened at: %s" % time.time()

if __name__ == "__main__":
m = Metronome(5.0, aTick)
m.Start()
time.sleep(30)
m.Stop()
time.sleep(2.5)
m.Start()
time.sleep(20)
m.Stop()
-- =============== =============== =============== =============== == <
wl*****@ix.netc om.com | Wulfraed Dennis Lee Bieber KD6MOG <
wu******@dm.net | Bestiaria Support Staff <
=============== =============== =============== =============== == <
Home Page: <http://www.dm.net/~wulfraed/> <
Overflow Page: <http://wlfraed.home.ne tcom.com/> <

Sep 4 '05 #3
> ! import Tkinter
! def dogo():
! while 1:
! b.config(comman d=lambda:None)
! root = Tkinter.Tk()
! b = Tkinter.Button( root, text="Go", command=dogo)
! b.pack()
! root.mainloop()

I guess tkinter has to keep a name-reference pair (some days a
discussion about this arose and /F said something like that). It
certainly is somewhat ugly - but there might be very good reasons to do so.
Note: I have found this problem because in a small program I have a
Start button that becomes a Stop, so the bound command must change each
time. (Maybe there is some other solution, like hiding/unhiding the
buttons, I don't know.)
Had that to do myself - my solution was to use the buttons text for
swich-casing. I guess you change the buttons text anyways (otherwise
nobody would know that its semantic changed), so do something like this
(untetsted)

def start_stop_call back():
if b.get_config("t ext") == "Start":
b.config(text=" Stop")
....
else:
b.config(text=" Start")
....

Sometimes I need something like the Timer of Java, it generates "ticks"
and calls a function for each tick. The tick frequency can be set, or
they can be stopped.

In another Newsgroup someone has suggested me that the callLater of
Twisted can solve my problem, but I think it's a quite common thing, so
I think that maybe it can be added to the threading standard module.

This is a rough Python version of mine (without comments), it's not a
true Metronome because it counts the delay time after the end of the
last function call. This class also seems fragile, sometimes it gives
me problems, and I cannot use too much concurrent metronomes, etc. It's
quite

Maybe someone can suggest me how to improve it.


Instead of several metronomes, use one thread and compute the gresatest
common divisor for all scheduled timings. Use that as sleep interval
(your "ticks"), and count ticks for each timer. You should introduce a
lower thrshold on tick-length, otherwise you can get into trouble.
Diez
Sep 4 '05 #4
Witn your suggestions and with some tests and work I've solved most of
the problems, thank you all for the comments.

Peter Hansen:
What did you expect to happen with the infinite loop inside dogo()?<
I expected that the same memory used by the b.config(comman d=...) can
be used by the successive update of the command. I am ignorant about
Tkinter, but to me it seems that no new memory is really needed each
loop.

-------------------

Dennis Lee Bieber:
you are creating an endless number of anonymous functions; they may not be garbage collected until the loop ends<
I don't know, usually the garbage collector is quite efficient :-)

Would it not be easier to have a single callback, but change the button Label, and have the callback branch based on the current label?<
Right, thank you. It's not a perfect solution, but often it's good
enough.

No idea -- my only Tkinter program (at work, so I can't test it), doesn't do anything like this.<
With more tests and some googling I have found the problem and the
solution. It seems that the problem was produced by commands like this:
self.canvas.del ete(self.id)
Maybe they are called after the Tkinter stop, or window close, or
something like that.

To solve the problem I've added this to the main window:
self.root.proto col('WM_DELETE_ WINDOW', self.quitAll)

And inside the self.quitAll method I fix something before calling
self.root.quit( )

I somehow suspect it is faster than a mass of push/pop and shifts...<
Your version is very interesting, thank you. I'll use this modified
version:

! _nibbles = {"0":"0000", "1":"0001", "2":"0010", "3":"0011",
! "4":"0100", "5":"0101", "6":"0110", "7":"0111",
! "8":"1000", "9":"1001", "A":"1010", "B":"1011",
! "C":"1100", "D":"1101", "E":"1110", "F":"1111",
! "-":"-"}
!
! def to_base2(number ):
! result = [_nibbles[nibble] for nibble in "%X"%number]
! return int("".join(res ult))

This is faster than my version (but if you are using Psyco for all the
tests, for small numbers my version is faster).
This version is expecially faster for very long numbers.
But the difference between the two versions is quite little still, a C
function like the base2 conversion of GMPY is much faster, so I think
that such common operation can be added to the % formatting sintax.

Main problem I see with this is that each call to threading.Timer () is creating a NEW thread just for one tick. Lots of overhead involved. I'd create a single thread whose main run method is a continuous loop, using time.sleep() to suspend between ticks. .Timer() is optimized for a one-time callback. Note that using _nextInterval allows me perform an adjustment for the time taken by the callback function.<
Oh, very nice, you are very gentle, thank you. I'll add few suggestion
by Roggisch, and I think it will be quite good. If I obtain something
good I'll even think about putting it in the cookbook (with your names
in it).

-------------------

Diez B. Roggisch:
It certainly is somewhat ugly - but there might be very good reasons to do so.<
Okay.

Instead of several metronomes, use one thread and compute the gresatest common divisor for all scheduled timings. Use that as sleep interval (your "ticks"), and count ticks for each timer. You should introduce a lower thrshold on tick-length, otherwise you can get into trouble.<


Okay, I'll follow your suggestions.

Thank you all and hugs to Dennis Lee Bieber,
bearophile

Sep 4 '05 #5
On 4 Sep 2005 08:48:33 -0700, be************@ lycos.com declaimed the
following in comp.lang.pytho n:

you are creating an endless number of anonymous functions; they may not be garbage collected until the loop ends<
I don't know, usually the garbage collector is quite efficient :-)

Yes, but only when ref-counts go to 0... it may be that this tight
loop never allowed stuff to go to 0 ref-counts. It definitely never
returned control, so besides eating memory that way, any events for the
GUI framework were also not being handled and had to be queued.
Your version is very interesting, thank you. I'll use this modified
version:

! _nibbles = {"0":"0000", "1":"0001", "2":"0010", "3":"0011",
! "4":"0100", "5":"0101", "6":"0110", "7":"0111",
! "8":"1000", "9":"1001", "A":"1010", "B":"1011",
! "C":"1100", "D":"1101", "E":"1110", "F":"1111",
! "-":"-"}
!
! def to_base2(number ):
! result = [_nibbles[nibble] for nibble in "%X"%number]
! return int("".join(res ult))
I'm still concerned about your use of that int() call... What is the
real use case for this conversion?

You take in a number -- which, for all but a few languages, is
internally binary (the exceptions being: true REXX -- using character
strings for everything, COBOL -- traditionally using BCD arithmetic
unless one declared a variable usage as COMPutational, and maybe PL/1).
You then convert this to a human readable representation of binary
(using character 1 and 0 for each bit)... So far I understand it. But
then you feed these string representation of a binary number to a
function that treats it as a string representation of a DECIMAL number,
and converts said decimal back to an internal binary format which bears
no resemblence to the 0/1 bits you started with.

You can't do arithmetic with the result: Say 2 + 3 were the
originals...

2d => 0010b
3d => 0011b
+(in native binary)
5d => 0101b

but in your method, feeding back through int()

2d => 0010b => 10d (1010b)
3d => 0011b => 11d (1011b)
+ +
5d => 0101b !-> 21d (00010101b)

If the only purpose is to get a displayable binary value, leave the
result as a string and don't call int(). Actually, with leading 0s, that
int() call is probably interpreting the string as OCTAL, rather than
DECIMAL.
This is faster than my version (but if you are using Psyco for all the
tests, for small numbers my version is faster).
For moderate numbers, mine is using the, hopefully quite optimized
string formatting operations (which should be nearly as fast as doing
the same in C: sprintf(stringb uff, "%X", n);), and then doing hopefully
fast dictionary lookups to get the pieces.

-- =============== =============== =============== =============== == <
wl*****@ix.netc om.com | Wulfraed Dennis Lee Bieber KD6MOG <
wu******@dm.net | Bestiaria Support Staff <
=============== =============== =============== =============== == <
Home Page: <http://www.dm.net/~wulfraed/> <
Overflow Page: <http://wlfraed.home.ne tcom.com/> <

Sep 4 '05 #6
Dennis Lee Bieber:
Yes, but only when ref-counts go to 0... it may be that this tight loop never allowed stuff to go to 0 ref-counts. It definitely never returned control, so besides eating memory that way, any events for the GUI framework were also not being handled and had to be queued.<
This memory allocation problem is present even without the loop. In
this other example if you keep cliking on the Go button, the memory
used keep growing:

from Tkinter import *
def dogo(): b.config(comman d=dogo)
root = Tk()
b = Button(root, text="Go", command=dogo)
b.pack()
root.mainloop()

with leading 0s, that int() call is probably interpreting the string as OCTAL, rather than DECIMAL.<
This can be fixed with a different dictionary that doesn't contain the
leading 0s, to be used just for the first (not the "-") nibble.

What is the real use case for this conversion?<


You are right, I'll remove the int conversion.

Thank you, a bear hug,
Bearophile

Sep 6 '05 #7
Bearophile>This can be fixed with a different dictionary that doesn't
contain the leading 0s,<

No other dict is necessary:

! _nibbles = {"0":"0000", "1":"0001", "2":"0010", "3":"0011",
! "4":"0100", "5":"0101", "6":"0110", "7":"0111",
! "8":"1000", "9":"1001", "A":"1010", "B":"1011",
! "C":"1100", "D":"1101", "E":"1110", "F":"1111",
! "-":"-"}
!
! def toBase2(number) :
! """toBase2(numb er): given an int/long, converts it
! to a string containing the number in base 2."""
! # From a suggestion by Dennis Lee Bieber.
! if number == 0:
! return "0"
! result = [_nibbles[nibble] for nibble in "%X"%number]
! result[number<0] = result[number<0].lstrip("0")
! return "".join(res ult)
!
! for i in xrange(-20, 21):
! print toBase2(i)

Bye,
Bearophile

Sep 6 '05 #8
On 6 Sep 2005 06:01:40 -0700, be************@ lycos.com declaimed the
following in comp.lang.pytho n:

This memory allocation problem is present even without the loop. In
this other example if you keep cliking on the Go button, the memory
used keep growing:
No idea then -- I'd hope the .mainloop process wasn't that
inefficient to keep references to all historical states <G>
-- =============== =============== =============== =============== == <
wl*****@ix.netc om.com | Wulfraed Dennis Lee Bieber KD6MOG <
wu******@dm.net | Bestiaria Support Staff <
=============== =============== =============== =============== == <
Home Page: <http://www.dm.net/~wulfraed/> <
Overflow Page: <http://wlfraed.home.ne tcom.com/> <

Sep 6 '05 #9

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

1
1477
by: Gitte Wange | last post by:
Hello, I'm trying to create an application that needs a Timer() to do various checks. My class has a self.timer = Timer(900, self.sendDNSRequest) The timer itself works fine but whenever I try to exit the application, it simply hangs and I have to kill the proces using `kill pid` I have tried uncommenting the timer stuff, and then the app works as expected.
7
4736
by: Steve M | last post by:
I'm trying to invoke a Java command-line program from my Python program on Windows XP. I cannot get the paths in one of the arguments to work right. The instructions for the program describe the following for the command-line arguments: java -jar sforcedataloader.jar -Dsalesforce.config.dir=CONFIG_DIRECTORY They also give an example:
8
1575
by: n3crius | last post by:
hi, i just got a web host with asp.net , seemed really cool. aspx with the c# or vb IN the actual main page run fine, but when i use codebehind and make another source file ( a .cs) to go with the aspx (as you would realistically) I get this : Details: To enable the details of this specific error message to be viewable on remote machines, please create a <customErrors> tag within a "web.config" configuration file located in the root...
4
5006
by: Bit byte | last post by:
I have a project that I normally build (without problems) from the DevStudio IDE. However, I have embarked on automating all my builds (this test project being one of several). The project creates a DLL. I am able to build the project without any probs in the IDE, however - when I build the project from the command line (using the same options shown in the 'Command line' node in the 'Project Settings' dialog box), I get the following...
0
1362
by: hazz | last post by:
I want to load Service.exe.config into a DataGrid, change the timer interval value from 10000 to 555555, then update the original exe.config. The code below shows the code at step 1 and 2 before creating ServiceNew.exe.config below. The initial line, '<?xml version="1.0" encoding="utf-8" ?' is missing and the nodes are not hiearchical with indentation from the root to the innermost nodes as the original config file. How can I add this...
5
3399
by: Rajesh | last post by:
Hi, The '-I<path>' option adds the path to the list of directories that contains modules that can be included in a script. I can use it as "#!/ usr/bin/perl -I<path_to_my_modules>" thereby not asking the user of the script to set the <path_to_my_modulesin their environment. Is there any equivalent command-line option to the python binary or a command-line version of PYTHONPATH?
13
4511
by: Chris Carlen | last post by:
Hi: Having completed enough serial driver code for a TMS320F2812 microcontroller to talk to a terminal, I am now trying different approaches to command interpretation. I have a very simple command set consisting of several single letter commands which take no arguments. A few additional single letter commands take arguments:
40
29345
by: =?Utf-8?B?Um9iZXJ0IEUuIEZsYWhlcnR5?= | last post by:
What is the C# command to wait for a specified period of time? I am writing a windows service that will process a file once it has beed created or changed. I'm using the fileSystemWatcher to detect when a specific file has been created or changed. That works fine to a point. The event does fire when the file is being created or changed but I then blow up when I attempt to open the file because the creation or changing has not...
4
3756
by: enggwaqas | last post by:
I have an Oracle stored procedure that takes approx 3 minutes to execute and I am using .net 2.0 data access provider for oracle. Now i want to cancel the execution of stored procedure from .net after few seconds, the code i wrote for this is: //Start main function System.Data.OracleClient.OracleDataAdapter objAdapter; System.Data.OracleClient.OracleConnection con; System.Threading.Timer objTimer = null;
0
8984
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
8823
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
1
9312
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
9238
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
8237
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
0
4864
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
3300
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
2775
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
3
2206
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.