I have a program that have a gui in PyQt in the main thread. It communicates to a photo-detector and gets power readings in another thread, which sends a signal to the main thread to update the gui's power value.
Now I want to use a motor to automatically align my optical fiber, getting feedback from the photo-detector.
So I created a class that controls the motors, but I have to somehow pass the photo-detector readings to that class. First, I tried to access parent's power variable but it didn't work.
Then I created a method in my gui to return the variable's value and tried to access it from the motor class. I got a problem saying that I couldn't use parent's method without using its __init__ first. Is there a way to bypass it? I can't call the gui __init__ again, I just want to use one of its methods from within the child class.
If there is an alternative way to do this, I'd be happy as well.
PS: I guess I can't give the child class the photo-detector object because it is in another thread, right?
--Edit--
The gui code is:
class MyApp(QtGui.QMainWindow, Ui_MainWindow):
self.PDvalue = 0 #initial PD value
self.PDState = 0 #control the PD state (on-off)
self.PDport = self.dialog.pm100d.itemText(self.dialog.pm100d.currentIndex()) #gets pot info
def __init__(self):
... #a lot of other stuff
self.nano = AlgoNanoMax.NanoMax('COM12') #creates the motor object
self.nano_maxX.clicked.connect(self.NanoMaximizeX) #connect its fun to a buttom
self.actionConnect_PM100D.triggered.connect(self.ActionConnect_PM100D) #PD buttom
def NanoMaximizeX(self):
self.nano.maximize_nano_x() #uses motor object function
def ActionConnect_PM100D(self):
if self.PDState == 0: #check if PD is on
self.PD = PDThread(self.PDState, self.PDport) #creates thread
self.PD.valueupdate.connect(self.PDHandler) #signal connect
self.PD.dialogSignal.connect(self.PDdialog) #create error dialog
self.threads = []
self.threads.append(self.PD)
self.PD.start() #start thread
else:
self.PDState = 0
self.PD.state = 0 #stop thread
self.startpd.setText('Start PD') #change buttom name
def PDHandler(self, value):
self.PDvalue = value #slot to get pow from thread
def ReturnPow(self):
return self.PDvalue #return pow (I tried to use this to pass to the motor class)
def PDdialog(self):
self.dialog.set_instrument('PM100D') #I have a dialog that says error and asks you to type the right port
if self.dialog.exec_() == QtGui.QDialog.Accepted: #if Ok buttom try again
ret = self.dialog.pm100d.itemText(self.dialog.pm100d.currentIndex()) #new port
self.PD.port = str(ret)
self.PD.flagWhile = False #change PD stop loop condition to try again
else: #pressed cancel, so it gives up
self.PD.photodetector.__del__() #delete objects
self.PD.terminate() #stop thread
self.PD.quit()
Now the PD class, which is in another thread but in the same file as gui:
class PDThread(QtCore.QThread):
valueupdate = QtCore.pyqtSignal(float) #creating signals
dialogSignal = QtCore.pyqtSignal() #signal in case of error
state = 1 #used to stop thread
def __init__(self, state, port):
QtCore.QThread.__init__(self)
self.photodetector = PM100D() #creates the PD object
self.port = port
def run(self):
while True:
self.flagWhile = True #used to leave while
try:
self.photodetector.connect(self.port) #try to connect
except:
self.dialogSignal.emit() #emit error signal
while self.flagWhile == True:
time.sleep(0.5) #wait here until user press something in the dialog, which is in another thread
else:
break #leave loop when connected
window.PDState = 1 #change state of main gui buttom (change functionality to turn off if pressed again)
window.startpd.setText('Stop PD') #change buttom label
while self.state == 1:
time.sleep(0.016)
value = self.photodetector.get_pow() #get PD pow
self.valueupdate.emit(value) #emit it
The AlgoNanoMax file:
import gui
from NanoMax import Nano
class NanoMax(gui.MyApp): #inheriting parent
def __init__(self, mcontroller_port):
self.mcontroller = Nano(mcontroller_port) #mcontroller is the communication to the motor
def maximize_nano_x(self, step=0.001, spiral_number=3):
''' Alignment procedure with the nano motor X'''
print 'Optimizing X'
power = super(NanoMax, self).ReturnPow() #here I try to read from the photodetector
xpos = self.mcontroller.initial_position_x
position = []
position = [[power, xpos]]
xsign = 1
self.mcontroller.move_relative(self.mcontroller.xaxis, (-1) * spiral_number * step)
print 'X nano move: '+ str((-1) * spiral_number * step * 1000) + ' micrometers'
time.sleep(4)
power = super(NanoMax, self).ReturnPow()
xpos += (-1) * spiral_number * step
position.append([power, xpos])
for _ in xrange(2*spiral_number):
self.mcontroller.move_relative(self.mcontroller.xaxis, xsign * step)
print 'X nano move: '+ str(xsign * step * 1000) + ' micrometers'
time.sleep(5)
power = super(NanoMax, self).ReturnPow()
xpos += xsign * step
position.append([power, xpos])
pospower = [position[i][0] for i in xrange(len(position))]
optimalpoint = pospower.index(max(pospower))
x_shift = (-1) * (xpos - position[optimalpoint][1])
print 'Maximum power: ' + str(max(pospower)) + ' dBm'
print 'Current power: ' + str(super(NanoMax, self).ReturnPow()) + ' dBm'
self.mcontroller.move_relative(self.mcontroller.xaxis, x_shift)
The __init__ for NanoMax and MyApp should call super().__init__() to ensure initialization is done for all levels (if this is Python 2, you can't use no-arg super, so it would be super(NanoMax, self).__init__() and super(MyApp, self).__init__() respectively). This assumes the PyQT was properly written with new-style classes, and correct use of super itself; you're using super in other places, so presumably at least the former is true. Using super appropriately in all classes will ensure all levels are __init__-ed once, while manually listing super classes won't work in certain inheritance patterns, or might call some __init__s multiple times or not at all.
If there is a possibility that many levels might take arguments, you should also accept *args/**kwargs and forward them to the super().__init__ call so the arguments are forwarded where then need to go.
Combining the two, your code should look like:
class MyApp(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, *args, **kwargs):
super(MyApp, self).__init__(*args, **kwargs)
... rest of __init__ ...
class PDThread(QtCore.QThread):
def __init__(self, state, port, *args, **kwargs):
super(PDThread, self).__init__(*args, **kwargs)
...
class NanoMax(gui.MyApp): #inheriting parent
def __init__(self, mcontroller_port, *args, **kwargs):
super(NanoMax, self).__init__(*args, **kwargs)
self.mcontroller = Nano(mcontroller_port) #mcontroller is the communication to the motor
Note: If you've overloaded methods that the super class might call in its __init__ and your overloads depend on state set in your own __init__, you'll need to set up that state before, rather than after the super().__init__(...) call. Cooperative multiple inheritance can be a pain that way. Also note that using positional arguments for anything but the lowest level class can be ugly with multiple inheritance, so it may make sense to pass all arguments by keyword, and only accept and forward **kwargs, not *args, so people don't pass positional arguments in ways that break if the inheritance hierarchy changes slightly.
class MyApp(QtGui.QMainWindow, Ui_MainWindow):
self.PDvalue = 0 #initial PD value
self.PDState = 0 #control the PD state (on-off)
In the above code it is setting a variable outside of a function. To do this in a class don't put the self keyword in front of it. This way you can just have in the class definition
class MyApp(QtGui.QMainWindow, Ui_MainWindow):
PDvalue = 0 #initial PD value
PDState = 0 #control the PD state (on-off)
and in the super line
power = super(NanoMax, self).PDvalue
For example:
>>> class Hi:
H = 5
def __init__(self):
self.g = 6
>>> class Bye(Hi):
def H(self):
print(super(Bye, self).H)
>>> e = Bye()
>>> e.H()
5
>>>
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
Are there any exemplary examples of the GoF Observer implemented in Python? I have a bit code which currently has bits of debugging code laced through the key class (currently generating messages to stderr if a magic env is set). Additionally, the class has an interface for incrementally return results as well as storing them (in memory) for post processing. (The class itself is a job manager for concurrently executing commands on remote machines over ssh).
Currently the usage of the class looks something like:
job = SSHJobMan(hostlist, cmd)
job.start()
while not job.done():
for each in job.poll():
incrementally_process(job.results[each])
time.sleep(0.2) # or other more useful work
post_process(job.results)
An alernative usage model is:
job = SSHJobMan(hostlist, cmd)
job.wait() # implicitly performs a start()
process(job.results)
This all works fine for the current utility. However it does lack flexibility. For example I currently support a brief output format or a progress bar as incremental results, I also support
brief, complete and "merged message" outputs for the post_process() function.
However, I'd like to support multiple results/output streams (progress bar to the terminal, debugging and warnings to a log file, outputs from successful jobs to one file/directory, error messages and other results from non-successful jobs to another, etc).
This sounds like a situation that calls for Observer ... have instances of my class accept registration from other objects and call them back with specific types of events as they occur.
I'm looking at PyPubSub since I saw several references to that in SO related questions. I'm not sure I'm ready to add the external dependency to my utility but I could see value in using their interface as a model for mine if that's going to make it easier for others to use. (The project is intended as both a standalone command line utility and a class for writing other scripts/utilities).
In short I know how to do what I want ... but there are numerous ways to accomplish it. I want suggestions on what's most likely to work for other users of the code in the long run.
The code itself is at: classh.
However it does lack flexibility.
Well... actually, this looks like a good design to me if an asynchronous API is what you want. It usually is. Maybe all you need is to switch from stderr to Python's logging module, which has a sort of publish/subscribe model of its own, what with Logger.addHandler() and so on.
If you do want to support observers, my advice is to keep it simple. You really only need a few lines of code.
class Event(object):
pass
class Observable(object):
def __init__(self):
self.callbacks = []
def subscribe(self, callback):
self.callbacks.append(callback)
def fire(self, **attrs):
e = Event()
e.source = self
for k, v in attrs.items():
setattr(e, k, v)
for fn in self.callbacks:
fn(e)
Your Job class can subclass Observable. When something of interest happens, call self.fire(type="progress", percent=50) or the like.
I think people in the other answers overdo it. You can easily achieve events in Python with less than 15 lines of code.
You simple have two classes: Event and Observer. Any class that wants to listen for an event, needs to inherit Observer and set to listen (observe) for a specific event. When an Event is instantiated and fired, all observers listening to that event will run the specified callback functions.
class Observer():
_observers = []
def __init__(self):
self._observers.append(self)
self._observables = {}
def observe(self, event_name, callback):
self._observables[event_name] = callback
class Event():
def __init__(self, name, data, autofire = True):
self.name = name
self.data = data
if autofire:
self.fire()
def fire(self):
for observer in Observer._observers:
if self.name in observer._observables:
observer._observables[self.name](self.data)
Example:
class Room(Observer):
def __init__(self):
print("Room is ready.")
Observer.__init__(self) # Observer's init needs to be called
def someone_arrived(self, who):
print(who + " has arrived!")
room = Room()
room.observe('someone arrived', room.someone_arrived)
Event('someone arrived', 'Lenard')
Output:
Room is ready.
Lenard has arrived!
A few more approaches...
Example: the logging module
Maybe all you need is to switch from stderr to Python's logging module, which has a powerful publish/subscribe model.
It's easy to get started producing log records.
# producer
import logging
log = logging.getLogger("myjobs") # that's all the setup you need
class MyJob(object):
def run(self):
log.info("starting job")
n = 10
for i in range(n):
log.info("%.1f%% done" % (100.0 * i / n))
log.info("work complete")
On the consumer side there's a bit more work. Unfortunately configuring logger output takes, like, 7 whole lines of code to do. ;)
# consumer
import myjobs, sys, logging
if user_wants_log_output:
ch = logging.StreamHandler(sys.stderr)
ch.setLevel(logging.INFO)
formatter = logging.Formatter(
"%(asctime)s - %(name)s - %(levelname)s - %(message)s")
ch.setFormatter(formatter)
myjobs.log.addHandler(ch)
myjobs.log.setLevel(logging.INFO)
myjobs.MyJob().run()
On the other hand there's an amazing amount of stuff in the logging package. If you ever need to send log data to a rotating set of files, an email address, and the Windows Event Log, you're covered.
Example: simplest possible observer
But you don't need to use any library at all. An extremely simple way to support observers is to call a method that does nothing.
# producer
class MyJob(object):
def on_progress(self, pct):
"""Called when progress is made. pct is the percent complete.
By default this does nothing. The user may override this method
or even just assign to it."""
pass
def run(self):
n = 10
for i in range(n):
self.on_progress(100.0 * i / n)
self.on_progress(100.0)
# consumer
import sys, myjobs
job = myjobs.MyJob()
job.on_progress = lambda pct: sys.stdout.write("%.1f%% done\n" % pct)
job.run()
Sometimes instead of writing a lambda, you can just say job.on_progress = progressBar.update, which is nice.
This is about as simple as it gets. One drawback is that it doesn't naturally support multiple listeners subscribing to the same events.
Example: C#-like events
With a bit of support code, you can get C#-like events in Python. Here's the code:
# glue code
class event(object):
def __init__(self, func):
self.__doc__ = func.__doc__
self._key = ' ' + func.__name__
def __get__(self, obj, cls):
try:
return obj.__dict__[self._key]
except KeyError, exc:
be = obj.__dict__[self._key] = boundevent()
return be
class boundevent(object):
def __init__(self):
self._fns = []
def __iadd__(self, fn):
self._fns.append(fn)
return self
def __isub__(self, fn):
self._fns.remove(fn)
return self
def __call__(self, *args, **kwargs):
for f in self._fns[:]:
f(*args, **kwargs)
The producer declares the event using a decorator:
# producer
class MyJob(object):
#event
def progress(pct):
"""Called when progress is made. pct is the percent complete."""
def run(self):
n = 10
for i in range(n+1):
self.progress(100.0 * i / n)
#consumer
import sys, myjobs
job = myjobs.MyJob()
job.progress += lambda pct: sys.stdout.write("%.1f%% done\n" % pct)
job.run()
This works exactly like the "simple observer" code above, but you can add as many listeners as you like using +=. (Unlike C#, there are no event handler types, you don't have to new EventHandler(foo.bar) when subscribing to an event, and you don't have to check for null before firing the event. Like C#, events do not squelch exceptions.)
How to choose
If logging does everything you need, use that. Otherwise do the simplest thing that works for you. The key thing to note is that you don't need to take on a big external dependency.
How about an implementation where objects aren't kept alive just because they're observing something? Below please find an implementation of the observer pattern with the following features:
Usage is pythonic. To add an observer to a bound method .bar of instance foo, just do foo.bar.addObserver(observer).
Observers are not kept alive by virtue of being observers. In other words, the observer code uses no strong references.
No sub-classing necessary (descriptors ftw).
Can be used with unhashable types.
Can be used as many times you want in a single class.
(bonus) As of today the code exists in a proper downloadable, installable package on github.
Here's the code (the github package or PyPI package have the most up to date implementation):
import weakref
import functools
class ObservableMethod(object):
"""
A proxy for a bound method which can be observed.
I behave like a bound method, but other bound methods can subscribe to be
called whenever I am called.
"""
def __init__(self, obj, func):
self.func = func
functools.update_wrapper(self, func)
self.objectWeakRef = weakref.ref(obj)
self.callbacks = {} #observing object ID -> weak ref, methodNames
def addObserver(self, boundMethod):
"""
Register a bound method to observe this ObservableMethod.
The observing method will be called whenever this ObservableMethod is
called, and with the same arguments and keyword arguments. If a
boundMethod has already been registered to as a callback, trying to add
it again does nothing. In other words, there is no way to sign up an
observer to be called back multiple times.
"""
obj = boundMethod.__self__
ID = id(obj)
if ID in self.callbacks:
s = self.callbacks[ID][1]
else:
wr = weakref.ref(obj, Cleanup(ID, self.callbacks))
s = set()
self.callbacks[ID] = (wr, s)
s.add(boundMethod.__name__)
def discardObserver(self, boundMethod):
"""
Un-register a bound method.
"""
obj = boundMethod.__self__
if id(obj) in self.callbacks:
self.callbacks[id(obj)][1].discard(boundMethod.__name__)
def __call__(self, *arg, **kw):
"""
Invoke the method which I proxy, and all of it's callbacks.
The callbacks are called with the same *args and **kw as the main
method.
"""
result = self.func(self.objectWeakRef(), *arg, **kw)
for ID in self.callbacks:
wr, methodNames = self.callbacks[ID]
obj = wr()
for methodName in methodNames:
getattr(obj, methodName)(*arg, **kw)
return result
#property
def __self__(self):
"""
Get a strong reference to the object owning this ObservableMethod
This is needed so that ObservableMethod instances can observe other
ObservableMethod instances.
"""
return self.objectWeakRef()
class ObservableMethodDescriptor(object):
def __init__(self, func):
"""
To each instance of the class using this descriptor, I associate an
ObservableMethod.
"""
self.instances = {} # Instance id -> (weak ref, Observablemethod)
self._func = func
def __get__(self, inst, cls):
if inst is None:
return self
ID = id(inst)
if ID in self.instances:
wr, om = self.instances[ID]
if not wr():
msg = "Object id %d should have been cleaned up"%(ID,)
raise RuntimeError(msg)
else:
wr = weakref.ref(inst, Cleanup(ID, self.instances))
om = ObservableMethod(inst, self._func)
self.instances[ID] = (wr, om)
return om
def __set__(self, inst, val):
raise RuntimeError("Assigning to ObservableMethod not supported")
def event(func):
return ObservableMethodDescriptor(func)
class Cleanup(object):
"""
I manage remove elements from a dict whenever I'm called.
Use me as a weakref.ref callback to remove an object's id from a dict
when that object is garbage collected.
"""
def __init__(self, key, d):
self.key = key
self.d = d
def __call__(self, wr):
del self.d[self.key]
To use this we just decorate methods we want to make observable with #event. Here's an example
class Foo(object):
def __init__(self, name):
self.name = name
#event
def bar(self):
print("%s called bar"%(self.name,))
def baz(self):
print("%s called baz"%(self.name,))
a = Foo('a')
b = Foo('b')
a.bar.addObserver(b.bar)
a.bar()
From wikipedia:
from collections import defaultdict
class Observable (defaultdict):
def __init__ (self):
defaultdict.__init__(self, object)
def emit (self, *args):
'''Pass parameters to all observers and update states.'''
for subscriber in self:
response = subscriber(*args)
self[subscriber] = response
def subscribe (self, subscriber):
'''Add a new subscriber to self.'''
self[subscriber]
def stat (self):
'''Return a tuple containing the state of each observer.'''
return tuple(self.values())
The Observable is used like this.
myObservable = Observable ()
# subscribe some inlined functions.
# myObservable[lambda x, y: x * y] would also work here.
myObservable.subscribe(lambda x, y: x * y)
myObservable.subscribe(lambda x, y: float(x) / y)
myObservable.subscribe(lambda x, y: x + y)
myObservable.subscribe(lambda x, y: x - y)
# emit parameters to each observer
myObservable.emit(6, 2)
# get updated values
myObservable.stat() # returns: (8, 3.0, 4, 12)
Based on Jason's answer, I implemented the C#-like events example as a fully-fledged python module including documentation and tests. I love fancy pythonic stuff :)
So, if you want some ready-to-use solution, you can just use the code on github.
Example: twisted log observers
To register an observer yourCallable() (a callable that accepts a dictionary) to receive all log events (in addition to any other observers):
twisted.python.log.addObserver(yourCallable)
Example: complete producer/consumer example
From Twisted-Python mailing list:
#!/usr/bin/env python
"""Serve as a sample implementation of a twisted producer/consumer
system, with a simple TCP server which asks the user how many random
integers they want, and it sends the result set back to the user, one
result per line."""
import random
from zope.interface import implements
from twisted.internet import interfaces, reactor
from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver
class Producer:
"""Send back the requested number of random integers to the client."""
implements(interfaces.IPushProducer)
def __init__(self, proto, cnt):
self._proto = proto
self._goal = cnt
self._produced = 0
self._paused = False
def pauseProducing(self):
"""When we've produced data too fast, pauseProducing() will be
called (reentrantly from within resumeProducing's transport.write
method, most likely), so set a flag that causes production to pause
temporarily."""
self._paused = True
print('pausing connection from %s' % (self._proto.transport.getPeer()))
def resumeProducing(self):
self._paused = False
while not self._paused and self._produced < self._goal:
next_int = random.randint(0, 10000)
self._proto.transport.write('%d\r\n' % (next_int))
self._produced += 1
if self._produced == self._goal:
self._proto.transport.unregisterProducer()
self._proto.transport.loseConnection()
def stopProducing(self):
pass
class ServeRandom(LineReceiver):
"""Serve up random data."""
def connectionMade(self):
print('connection made from %s' % (self.transport.getPeer()))
self.transport.write('how many random integers do you want?\r\n')
def lineReceived(self, line):
cnt = int(line.strip())
producer = Producer(self, cnt)
self.transport.registerProducer(producer, True)
producer.resumeProducing()
def connectionLost(self, reason):
print('connection lost from %s' % (self.transport.getPeer()))
factory = Factory()
factory.protocol = ServeRandom
reactor.listenTCP(1234, factory)
print('listening on 1234...')
reactor.run()
OP asks "Are there any exemplary examples of the GoF Observer implemented in Python?"
This is an example in Python 3.7. This Observable class meets the requirement of creating a relationship between one observable and many observers while remaining independent of their structure.
from functools import partial
from dataclasses import dataclass, field
import sys
from typing import List, Callable
#dataclass
class Observable:
observers: List[Callable] = field(default_factory=list)
def register(self, observer: Callable):
self.observers.append(observer)
def deregister(self, observer: Callable):
self.observers.remove(observer)
def notify(self, *args, **kwargs):
for observer in self.observers:
observer(*args, **kwargs)
def usage_demo():
observable = Observable()
# Register two anonymous observers using lambda.
observable.register(
lambda *args, **kwargs: print(f'Observer 1 called with args={args}, kwargs={kwargs}'))
observable.register(
lambda *args, **kwargs: print(f'Observer 2 called with args={args}, kwargs={kwargs}'))
# Create an observer function, register it, then deregister it.
def callable_3():
print('Observer 3 NOT called.')
observable.register(callable_3)
observable.deregister(callable_3)
# Create a general purpose observer function and register four observers.
def callable_x(*args, **kwargs):
print(f'{args[0]} observer called with args={args}, kwargs={kwargs}')
for gui_field in ['Form field 4', 'Form field 5', 'Form field 6', 'Form field 7']:
observable.register(partial(callable_x, gui_field))
observable.notify('test')
if __name__ == '__main__':
sys.exit(usage_demo())
A functional approach to observer design:
def add_listener(obj, method_name, listener):
# Get any existing listeners
listener_attr = method_name + '_listeners'
listeners = getattr(obj, listener_attr, None)
# If this is the first listener, then set up the method wrapper
if not listeners:
listeners = [listener]
setattr(obj, listener_attr, listeners)
# Get the object's method
method = getattr(obj, method_name)
#wraps(method)
def method_wrapper(*args, **kwags):
method(*args, **kwags)
for l in listeners:
l(obj, *args, **kwags) # Listener also has object argument
# Replace the original method with the wrapper
setattr(obj, method_name, method_wrapper)
else:
# Event is already set up, so just add another listener
listeners.append(listener)
def remove_listener(obj, method_name, listener):
# Get any existing listeners
listener_attr = method_name + '_listeners'
listeners = getattr(obj, listener_attr, None)
if listeners:
# Remove the listener
next((listeners.pop(i)
for i, l in enumerate(listeners)
if l == listener),
None)
# If this was the last listener, then remove the method wrapper
if not listeners:
method = getattr(obj, method_name)
delattr(obj, listener_attr)
setattr(obj, method_name, method.__wrapped__)
These methods can then be used to add a listener to any class method. For example:
class MyClass(object):
def __init__(self, prop):
self.prop = prop
def some_method(self, num, string):
print('method:', num, string)
def listener_method(obj, num, string):
print('listener:', num, string, obj.prop)
my = MyClass('my_prop')
add_listener(my, 'some_method', listener_method)
my.some_method(42, 'with listener')
remove_listener(my, 'some_method', listener_method)
my.some_method(42, 'without listener')
And the output is:
method: 42 with listener
listener: 42 with listener my_prop
method: 42 without listener
It seems that this question is too long for anyone to comment on... I'm trying to print out some text and a progress bar in a module called 'laulau.py'. Here's a test piece of code that shows a simple version. My goal is to have only one thread, and send information to it. My question is what is the best way to do this ?
file1 (test.py)
#!/usr/bin/env python
from laulau import laulau
import time
print "FIRST WAY"
total=107
t=laulau()
t.echo('this is text')
t.setbartotal(total)
for a in range(1,total):
t.updatebar(a)
time.sleep(0.01)
time.sleep(1)
print
print "\ndone loop\n"
t.stop()
time.sleep(1)
print "SECOND WAY"
with laulau().echo("this is text"):
time.sleep(1)
print "\nyes this is working\n"
time.sleep(2)
file2: laulau.py
#!/usr/bin/env python
# vim:fileencoding=utf8
from __future__ import division
import time
import string
import threading
from sys import stdout
class laulau(threading.Thread):
def __init__(self, arg=None):
super(laulau,self).__init__()
self._stop = False
self.block='█'
self.empty='□'
self.TEMPLATE = ('%(progress)s%(empty)s %(percent)3s%%')
self.progress = None
self.percent = 0
self.bar_width=30
self.bartotal=None
def run (self):
# start thread for text
while not self._stop:
if self.bartotal is None:
print self.arg,
stdout.flush()
time.sleep(0.3)
else:
self.progress = int((self.bar_width * self.percent) / 100)
self.data = self.TEMPLATE % {
'percent': self.percent,
'progress': self.block * self.progress,
'empty': self.empty * (self.bar_width - self.progress),
}
stdout.write('\033[%dG'%1 + self.data + self.arg)
stdout.flush()
time.sleep(0.1)
def setbartotal(self,total):
# set progress bar total
if self.bartotal is None:
self.bartotal = total
self.updatebar(0)
def updatebar (self,num):
self.num=num
self.percent = self.percentage(self.num)
def percentage (self,numagain):
return int((numagain/self.bartotal)*100+1)
def echo (self,arg="Default"):
#self.thread_debug()
self.arg=arg
self._stop = False
self.start()
return self
def thread_debug(self):
print "threading enumerate :%s"%threading.enumerate()
print "current thread :%s"%threading.currentThread()
print "thread count (including main thread):%s"%threading.activeCount()
def stop(self):
self._stop = True
def stopped(self):
return self._stop == True
def __enter__(self):
print "\nwe have come through the enter function\n"
return self
def __exit__(self, type, value, traceback):
self._stop = True
print "\nwe have exited through the exit function\n"
return isinstance(value, TypeError)
In some cases the second way could work. e.g., when I am printing some text, and just need the thread to die at the end of it, but not in the case of a progress bar when it needs updates sending to it. While this all sort of works, and I learned a lot, I still can't figure out how to encapsulate this class in the way I want. As I only want one thread I don't really need to keep instantiating the class, I just need to do this once.
so e.g. my ideal way would be having three functions only:
1 to control text, turn on progress bar etc (from within one parsed string)
2 to set the progress bar total
3 to set the progress bar iteration
I need to change two variables in the class (for the progress bar)
one for the total
one for the iteration
...and it works out percentage from that.
First I thought I should start the thread by inheriting the class stuff from threading, then after looking at threading.Thread(target=blah,etc) at first I couldn't see how to use more than one function, then I discovered I could just put the class name in there threading.Thread(target=laulau) and that would start a thread with the class in, but then I was stumped on how to send that thread information seeing as I hadn't assigned it to a 'name' as in t=laulau()
My second thought was to have functions outside of the class in my module, but because I need more than one function I got a bit confused there too by adding this to the beginning of laulau.py:
def eko (arg):
t=laulau()
t.echo(arg)
def barupate(iteration):
t.updatebar(a)
def bartotal():
t.setbartotal(a)
the first function made an instance of the class but the preceding functions could not change any variables within that. and then i came across function attributes such as this.
class Foo:
#webmethod
def bar(self, arg1, arg2):
...
def webmethod(func):
func.is_webmethod = True
return func
I then started thinking maybe I could use this somehow but have never come across it before.
Ideally id like something like this:
echo.total(107)
echo('[progressbar] this is text') # starts progress bar and instance of thread if not already there...
for a in range(1,total):
echo.updatebar(a)
time.sleep(0.01)
time.sleep(1)
echo.stop() # bar would stop at the end of iterations but text animations (blinking etc) may still be going at this point...
print
print "\ndone loop\n"
if you know about python you are probably looking at me funny now, but bear in mind that I'm a total beginner non-professional and am learning every day, a lot of it thanks to this site. cheers for any help!
edit: should add that I'm aware of the progress bar module and various other recipes, but I'm making this for learning and fun purposes :)
If you just need to print out a progress bar, use the sys module, like so:
import sys
import time
progress = "0" #this is an int meant to represent 0-100 percent as 0-100
old = "0" #this represents the last updates progress, so that this update only adds the difference and not the full progress
def updatebar(progress,old):
for item in range((progress-old)/2) #this takes the range of progress but divides it by 2, making the progress bar 50 characters long
sys.stdout.write("-") #to change the character used to fill the progress bar change the "-" to something else
sys.stdout.flush()
#you may not want to use a while loop here, this just has an example of how to use the update function as it adds one to the progress bar every second
while True:
progress += 1
updatebar(progress,old)
old = progress #sets the old progress as the current one, because next iteration of the while loop the previous progress will be this one's current progress.
time.sleep(1)