Cannot Display two custom Progress Bars at once - python

I'm currenty working on a progress bar for another project. I pretty much have everything done and working as intended.
However, i am having an issue, where i can't have two (or more) bars displaying simultaneously. The second one always seems to overwrite the first.
Here's the code (excuse my crappy coding skills):
Progress Bar Class:
import sys
class ProgressBar():
full_size = 100
standard = 50
small = 25
def __init__(self, pretext, length=50, total=100):
self.pretext = pretext
self.length = length
self.total = total
def update_bar(self, fill_amount):
real_fill_amount = int(self.length * fill_amount / self.total)
sys.stdout.write("\r%s |%s%s| %s" % (self.pretext,
"█" * real_fill_amount,
"░" * (self.length - real_fill_amount),
str(fill_amount) + "%"))
sys.stdout.flush()
Example code (CPU and Memory Usage Meter):
from pout_progressbar import ProgressBar
import psutil
import time
bar1 = ProgressBar("CPU Usage", length=ProgressBar.standard, total=100)
bar2 = ProgressBar("Memory Usage", length=ProgressBar.standard, total=100)
while True:
bar1.update_bar(int(psutil.cpu_percent(interval=1)))
bar2.update_bar(int(psutil.virtual_memory().percent))
time.sleep(0.5)
Note: pout_progressbar is the filename of the ProgressBar module.
In the example script, two bars are supposed to be showing, but only the second one is showing.
Is there any way i can fix this ?
Thanks in advance.

That is because your second progress bar output overwrites that of the first. Here is a way to fix that:
import sys
import psutil
import time
class ProgressBar:
full_size = 100
standard = 50
small = 25
def __init__(self, pretext, length=50, total=100):
self.pretext = pretext
self.length = length
self.total = total
def update_bar(self, fill_amount):
real_fill_amount = self.length * fill_amount // self.total
progress = f"{self.pretext} |{'█'*real_fill_amount}" \
f"{'░'*(self.length - real_fill_amount)}| {fill_amount}%"
return progress
bar1 = ProgressBar("CPU Usage")
bar2 = ProgressBar("Memory Usage")
while True:
progress1 = bar1.update_bar(int(psutil.cpu_percent(interval=1)))
progress2 = bar2.update_bar(int(psutil.virtual_memory().percent))
sys.stdout.write(f'\r{progress1}\t{progress2}')
sys.stdout.flush()
time.sleep(0.5)
However, there is another problem. It is not trivial to overwrite more than just the previous line. So for now, both progess bars are displayed in the same line. If that is not ok for you, you need to look into one of the options to overwrite multiple lines, however how to do that is system dependent.

As output of next bar depends on previous one, separating these is not able. So if I write:
class ProgressBars():
full_size = 100
standard = 50
small = 25
def __init__(self, pretexts, length=50, total=100):
self.pretexts = pretexts
self.length = length
self.total = total
def update_bar(self, fill_amounts):
real_fill_amounts = [int(self.length * x / self.total) for x in fill_amounts]
line = "%s |%s%s| %s" % (self.pretext,
"█" * real_fill_amount,
"░" * (self.length - real_fill_amount))
str(fill_amount) + "%"))
sys.stdout.write("\b" * (len(line) + len(fill_amounts))
sys.stdout.write(line)
sys.stdout.flush()
If you wan to control console in advance, There is curses.

Related

take screenshot for label using for loop

I want to take screenshot for each time random create string.
I mad a gui program using python and pyqt5. I have a label which I insert an image into it and I have two other labels which set to set text randomly using random and string module.
so, I want to take screenshot for that label which ( lets consider that) has an image and two label for every time random string set on label.
def createCards(self):
cardsNum = int(self.lineEdit_23.text())
S = 5
while cardsNum != 0:
self.label_8.setFrameShape(QFrame.NoFrame)
self.label_9.setFrameShape(QFrame.NoFrame)
self.label_8.setText(''.join(random.choices(string.ascii_lowercase + string.digits, k = S)))
self.label_9.setText(''.join(random.choices(string.ascii_lowercase + string.digits, k = S)))
pics_path = 'pics/%s.jpg'%(str(pics))
screen = QtWidgets.QApplication.primaryScreen()
screenshot = screen.grabWindow( self.label_7.winId())
screenshot.save(pics_path, 'jpg')
cardsNum -= 1
You have to implement the same logic using signals, for example through a QTimer.
from functools import cached_property
#cached_property
def timer(self):
timer = QTimer()
timer.setInterval(100)
timer.timeout.connect(self.take_screenshot)
return timer
def createCards(self):
self.cardsNum = int(self.lineEdit_23.text())
self.timer.start()
def take_screenshot(self):
S = 5
if self.cardsNum == 0:
self.timer.stop()
else:
self.label_8.setFrameShape(QFrame.NoFrame)
self.label_9.setFrameShape(QFrame.NoFrame)
self.label_8.setText(
"".join(random.choices(string.ascii_lowercase + string.digits, k=S))
)
self.label_9.setText(
"".join(random.choices(string.ascii_lowercase + string.digits, k=S))
)
pics_path = "pics/%s.jpg" % (str(pics))
screen = QtWidgets.QApplication.primaryScreen()
screenshot = screen.grabWindow(self.label_7.winId())
screenshot.save(pics_path, "jpg")
self.cardsNum -= 1

Is it possible to run multiple animations with an overall rate_func?

The following is my current code:
from manimlib.imports import *
class ClockOrganization(VGroup):
CONFIG = {
"numbers" : 4,
"radius" : 3.1,
"color" : WHITE
}
def __init__(self, **kwargs):
digest_config(self, kwargs, locals())
self.generate_nodes()
VGroup.__init__(self, *self.node_list,**kwargs)
def generate_nodes(self):
self.node_list = []
for i in range(self.numbers):
mobject = VMobject()
number = TexMobject(str(i+1))
circle = Circle(radius=0.4,color=self.color)
mobject.add(number)
mobject.add(circle)
mobject.move_to(
self.radius * np.cos((-TAU / self.numbers) * i + 17*TAU / 84) * RIGHT
+ self.radius * np.sin((-TAU / self.numbers) * i + 17*TAU / 84) * UP
)
self.node_list.append(mobject)
def select_node(self, node):
selected_node = self.node_list[node]
selected_node.scale(1.2)
selected_node.set_color(RED)
def deselect_node(self, selected_node):
node = self.node_list[selected_node]
node.scale(0.8)
node.set_color(self.color)
class Testing3(Scene):
def construct(self):
test = ClockOrganization(numbers=21)
self.play(Write(test), run_time=1.5)
animation_steps=[]
for i in range(10):
thing = test.deepcopy()
thing.select_node((19+i)%test.numbers-1)
animation_steps.append(thing)
self.play(Transform(test, animation_steps[0]))
self.wait(2)
for i in range(1,10):
self.play(Transform(test, animation_steps[i]),run_time=0.3)
self.wait()
If you run it currently, it highlights 19, then revolves to 7 at a uniform pace. Is it possible to set a rate_func for this overall process, so it starts revolving slowly, speeds up halfway, then slows its way to 7, like the smooth rate_func but applied over the entire thing?
I have tried changing the above Scene class to
class Testing3(Scene):
def construct(self):
test = ClockOrganization(numbers=21)
self.play(Write(test), run_time=1.5)
animation_steps=[]
for i in range(10):
thing = test.deepcopy()
thing.select_node((19+i)%test.numbers-1)
animation_steps.append(thing)
self.play(Transform(test, animation_steps[0]))
self.wait(2)
for i in range(1,10):
if i in range(1,6):
time_to_run=(sigmoid(10*(-i/4+.75))-sigmoid(-5))/(1-2*sigmoid(-5)+3)+.1
if i in range(6,10):
time_to_run=(sigmoid(10*(i/4-1.75))-sigmoid(-5))/(1-2*sigmoid(-5)+3)+.1
self.play(Transform(test, animation_steps[i]),run_time=time_to_run)
self.wait()
and it comes close to what I'm going for, but the Transforms seem rather choppy.
Or this, start slow, then increase and then slow again
class Testing4(Scene):
def construct(self):
test = ClockOrganization(numbers=21)
self.play(Write(test), run_time=1.5)
animation_steps=[]
#animation_steps.append(test)
num_circ=15
for i in range(num_circ):
thing = test.deepcopy()
thing.select_node((19+i)%test.numbers-1)
animation_steps.append(thing)
test_normal=test.copy()
test.save_state()
self.play(Transform(test, animation_steps[0]))
self.wait(2)
self.play(Restore(test))
anims=[]
theta=180*DEGREES/num_circ
lag_constant=5
for i in range(1,num_circ):
test.node_list[(19+i)%test.numbers-1].generate_target()
test.node_list[(19+i)%test.numbers-1].target.scale(1.2)
test.node_list[(19+i)%test.numbers-1].target.set_color(RED)
stop_smooth=lag_constant*np.sin(i*theta)
anims.append(MoveToTarget(test.node_list[(19+i)%test.numbers-1],rate_func=there_and_back))
anims.append(Animation(Mobject(),run_time=stop_smooth))
self.play(
AnimationGroup(*anims,lag_ratio=0.1)
)
self.wait()
I don't know if you mean this:
class Testing4(Scene):
def construct(self):
test = ClockOrganization(numbers=21)
self.play(Write(test), run_time=1.5)
animation_steps=[]
#animation_steps.append(test)
num_circ=15
for i in range(num_circ):
thing = test.deepcopy()
thing.select_node((19+i)%test.numbers-1)
animation_steps.append(thing)
test_normal=test.copy()
test.save_state()
self.play(Transform(test, animation_steps[0]))
self.wait(2)
self.play(Restore(test))
anims=[]
for i in range(1,num_circ):
test.node_list[(19+i)%test.numbers-1].generate_target()
test.node_list[(19+i)%test.numbers-1].target.scale(1.2)
test.node_list[(19+i)%test.numbers-1].target.set_color(RED)
anims.append(MoveToTarget(test.node_list[(19+i)%test.numbers-1],rate_func=there_and_back))
self.play(
AnimationGroup(*anims,lag_ratio=0.1)
)
self.wait()

python progression bar not in IDE but UI visually after running the program?

I saw many different codes and examples with progression bar. However none of them shows it in a UI format. It only shows it in the IDE which is not going to work if the user is not going to the IDE to run it. I don't have PYQT so I can't use it to create progress bar in that way. Are there any other ways to create progression bar that a user can see it after running the program, not via IDE.
Here is some code for a very basic text progress bar:
class progressBar():
def __init__(self, title, length=40):
self.BAR_LENGTH = length
self.title = title
print('{}\t['.format(self.title) + ' ' * self.BAR_LENGTH + ']', end='')
def update(self, val):
# round from 0 to self.BAR_LENGTH
bars = round(val * self.BAR_LENGTH)
print('\r{}\t['.format(self.title) + '#' * bars + ' ' * (self.BAR_LENGTH - bars) + ']\t{0:.2f}%'.format(
val * 100), end='')
def close(self):
print('')
You use it like this:
bar = progressBar("Text beside progress bar")
while myLoopCondition == True
# loop that you want to show progress of
...
bar.update(new percentage) # decimal number from 0-1
bar.close()
Whatever program you use to make your executable should have a way of displaying a terminal when you run the program.
You should look into getting pyqt for this though
You could use the tqdm library.
You only need to modify your loop to include a counter, see this example:
from tqdm import tqdm
import time
startTime = time.clock()
totalCount = len(listOfItems)
for index, item in enumerate(listOfItems):
stopTime = time.clock()
statusBarText = tqdm.format_meter(index + 1,
totalCount,
stopTime - startTime,
ncols=80, # prints text 80 characters wide
ascii=False)
print(statusBarText, '\b' * 81, end='')
startTime = time.clock()
... rest of the code in your loop goes here ...

Using click.progressbar with multiprocessing in Python

I have a huge list that I need to process, which takes some time, so I divide it into 4 pieces and multiprocess each piece with some function. It still takes a bit of time to run with 4 cores, so I figured I would add some progress bar to the function, so that it could tell me where each processor is at in processing the list.
My dream was to have something like this:
erasing close atoms, cpu0 [######..............................] 13%
erasing close atoms, cpu1 [#######.............................] 15%
erasing close atoms, cpu2 [######..............................] 13%
erasing close atoms, cpu3 [######..............................] 14%
with each bar moving as the loop in the function progresses. But instead, I get a continuous flow:
etc, filling my terminal window.
Here is the main python script that calls the function:
from eraseCloseAtoms import *
from readPDB import *
import multiprocessing as mp
from vectorCalc import *
prot, cell = readPDB('file')
atoms = vectorCalc(cell)
output = mp.Queue()
# setup mp to erase grid atoms that are too close to the protein (dmin = 2.5A)
cpuNum = 4
tasks = len(atoms)
rangeSet = [tasks / cpuNum for i in range(cpuNum)]
for i in range(tasks % cpuNum):
rangeSet[i] += 1
rangeSet = np.array(rangeSet)
processes = []
for c in range(cpuNum):
na, nb = (int(np.sum(rangeSet[:c] + 1)), int(np.sum(rangeSet[:c + 1])))
processes.append(mp.Process(target=eraseCloseAtoms, args=(prot, atoms[na:nb], cell, 2.7, 2.5, output)))
for p in processes:
p.start()
results = [output.get() for p in processes]
for p in processes:
p.join()
atomsNew = results[0] + results[1] + results[2] + results[3]
Below is the function eraseCloseAtoms():
import numpy as np
import click
def eraseCloseAtoms(protein, atoms, cell, spacing=2, dmin=1.4, output=None):
print 'just need to erase close atoms'
if dmin > spacing:
print 'the spacing needs to be larger than dmin'
return
grid = [int(cell[0] / spacing), int(cell[1] / spacing), int(cell[2] / spacing)]
selected = list(atoms)
with click.progressbar(length=len(atoms), label='erasing close atoms') as bar:
for i, atom in enumerate(atoms):
bar.update(i)
erased = False
coord = np.array(atom[6])
for ix in [-1, 0, 1]:
if erased:
break
for iy in [-1, 0, 1]:
if erased:
break
for iz in [-1, 0, 1]:
if erased:
break
for j in protein:
protCoord = np.array(protein[int(j)][6])
trueDist = getMinDist(protCoord, coord, cell, vectors)
if trueDist <= dmin:
selected.remove(atom)
erased = True
break
if output is None:
return selected
else:
output.put(selected)
accepted answer says it's impossible with click and it'd require 'non trivial amount of code to make it work'.
While it's true, there is another module with this functionality out of the box: tqdm
https://github.com/tqdm/tqdm which does exatly what you need.
You can do nested progress bars in docs https://github.com/tqdm/tqdm#nested-progress-bars etc.
I see two issues in your code.
The first one explains why your progress bars are often showing 100% rather than their real progress. You're calling bar.update(i) which advances the bar's progress by i steps, when I think you want to be updating by one step. A better approach would be to pass the iterable to the progressbar function and let it do the updating automatically:
with click.progressbar(atoms, label='erasing close atoms') as bar:
for atom in bar:
erased = False
coord = np.array(atom[6])
# ...
However, this still won't work with multiple processes iterating at once, each with its own progress bar due to the second issue with your code. The click.progressbar documentation states the following limitation:
No printing must happen or the progress bar will be unintentionally destroyed.
This means that whenever one of your progress bars updates itself, it will break all of the other active progress bars.
I don't think there is an easy fix for this. It's very hard to interactively update a multiple-line console output (you basically need to be using curses or a similar "console GUI" library with support from your OS). The click module does not have that capability, it can only update the current line. Your best hope would probably be to extend the click.progressbar design to output multiple bars in columns, like:
CPU1: [###### ] 52% CPU2: [### ] 30% CPU3: [######## ] 84%
This would require a non-trivial amount of code to make it work (especially when the updates are coming from multiple processes), but it's not completely impractical.
For anybody coming to this later. I created this which seems to work okay. It overrides click.ProgressBar fairly minimally, although I had to override an entire method for only a few lines of code at the bottom of the method. This is using \x1b[1A\x1b[2K to clear the progress bars before rewriting them so may be environment dependent.
#!/usr/bin/env python
import time
from typing import Dict
import click
from click._termui_impl import ProgressBar as ClickProgressBar, BEFORE_BAR
from click._compat import term_len
class ProgressBar(ClickProgressBar):
def render_progress(self, in_collection=False):
# This is basically a copy of the default render_progress with the addition of in_collection
# param which is only used at the very bottom to determine how to echo the bar
from click.termui import get_terminal_size
if self.is_hidden:
return
buf = []
# Update width in case the terminal has been resized
if self.autowidth:
old_width = self.width
self.width = 0
clutter_length = term_len(self.format_progress_line())
new_width = max(0, get_terminal_size()[0] - clutter_length)
if new_width < old_width:
buf.append(BEFORE_BAR)
buf.append(" " * self.max_width)
self.max_width = new_width
self.width = new_width
clear_width = self.width
if self.max_width is not None:
clear_width = self.max_width
buf.append(BEFORE_BAR)
line = self.format_progress_line()
line_len = term_len(line)
if self.max_width is None or self.max_width < line_len:
self.max_width = line_len
buf.append(line)
buf.append(" " * (clear_width - line_len))
line = "".join(buf)
# Render the line only if it changed.
if line != self._last_line and not self.is_fast():
self._last_line = line
click.echo(line, file=self.file, color=self.color, nl=in_collection)
self.file.flush()
elif in_collection:
click.echo(self._last_line, file=self.file, color=self.color, nl=in_collection)
self.file.flush()
class ProgressBarCollection(object):
def __init__(self, bars: Dict[str, ProgressBar], bar_template=None, width=None):
self.bars = bars
if bar_template or width:
for bar in self.bars.values():
if bar_template:
bar.bar_template = bar_template
if width:
bar.width = width
def __enter__(self):
self.render_progress()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.render_finish()
def render_progress(self, clear=False):
if clear:
self._clear_bars()
for bar in self.bars.values():
bar.render_progress(in_collection=True)
def render_finish(self):
for bar in self.bars.values():
bar.render_finish()
def update(self, bar_name: str, n_steps: int):
self.bars[bar_name].make_step(n_steps)
self.render_progress(clear=True)
def _clear_bars(self):
for _ in range(0, len(self.bars)):
click.echo('\x1b[1A\x1b[2K', nl=False)
def progressbar_collection(bars: Dict[str, ProgressBar]):
return ProgressBarCollection(bars, bar_template="%(label)s [%(bar)s] %(info)s", width=36)
#click.command()
def cli():
with click.progressbar(length=10, label='bar 0') as bar:
for i in range(0, 10):
time.sleep(1)
bar.update(1)
click.echo('------')
with ProgressBar(iterable=None, length=10, label='bar 1', bar_template="%(label)s [%(bar)s] %(info)s") as bar:
for i in range(0, 10):
time.sleep(1)
bar.update(1)
click.echo('------')
bar2 = ProgressBar(iterable=None, length=10, label='bar 2')
bar3 = ProgressBar(iterable=None, length=10, label='bar 3')
with progressbar_collection({'bar2': bar2, 'bar3': bar3}) as bar_collection:
for i in range(0, 10):
time.sleep(1)
bar_collection.update('bar2', 1)
for i in range(0, 10):
time.sleep(1)
bar_collection.update('bar3', 1)
if __name__ == "__main__":
cli()
It may not be the same as your dream, but you can use imap_unordered with click.progressbar to integrate with multiprocessing.
import multiprocessing as mp
import click
import time
def proc(arg):
time.sleep(arg)
return True
def main():
p = mp.Pool(4)
args = range(4)
results = p.imap_unordered(proc, args)
with click.progressbar(results, length=len(args)) as bar:
for result in bar:
pass
if __name__ == '__main__:
main()
Something like this will work if you are okay with having one progress bar:
import click
import threading
import numpy as np
reallybiglist = []
numthreads = 4
def myfunc(listportion, bar):
for item in listportion:
# do a thing
bar.update(1)
with click.progressbar(length=len(reallybiglist), show_pos=True) as bar:
threads = []
for listportion in np.split(reallybiglist, numthreads):
thread = threading.Thread(target=myfunc, args=(listportion, bar))
thread.start()
threads.append(thread)
for thread in threads:
thread.join()

Why is my JPanel not updating when I run the JPanel.setLocation(x,y) function inside of another function?

So I've been at this for a while now. My swing skills are not bad, but right now I seem to be missing something. I've been experimenting with Jython just recently and I have been utilizing the swing package from within a Jython script.
Let me start with this: My goal is to make a JPanel slide across the JFrame. To keep it to my knowledge, I tried attempting something like this:
x = 0
while panel.getX() < frame.getWidth():
print "panel.getX(): %i" % panel.getX()
panel.setLocation(x,0)
x += 5
time.sleep(0.01)
But here's the gist of my confusion... I ran this in my code and it did exactly what I wanted. The JPanel slid across the JFrame and I could see it do so:
from javax.swing import *
from java.awt import *
from java.awt.event import *
import time
f = JFrame()
p = JPanel()
p.setPreferredSize(Dimension(300,300))
def slide():
x = 0
while p.getX() < f.getWidth():
print "p.getX(): %i" % p.getX()
p.setLocation(x,0)
x += 5
time.sleep(0.5)
p.add(JLabel("hi"))
f.getContentPane().add(p)
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
f.setVisible(True)
f.pack()
slide()
BUT, when I add tad more complexity with events, it has no reaction at all. No updating, repainting or anything:
from javax.swing import *
from java.awt import *
from java.awt.event import *
import time
f = JFrame()
p = JPanel()
p.setPreferredSize(Dimension(300,300))
def slide(event):
x = 0
while p.getX() < f.getWidth():
print "p.getX(): %i" % p.getX()
p.setLocation(x,0)
x += 5
time.sleep(0.5)
b = JButton(actionPerformed=slide)
p.add(JLabel("hi"))
f.getContentPane().setLayout(BoxLayout(f.getContentPane(), BoxLayout.Y_AXIS))
f.getContentPane().add(p)
f.getContentPane().add(b)
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
f.setVisible(True)
f.pack()
Any ideas???
Thanks,
Dave
The loop blocks the event dispatch thread, so no events or drawing can be processed. Use a swing Timer for the sliding. The documentation is for java but hopefully not too difficult to translate to python.

Categories