Python command line print on the same lines - python

There are many questions relating to printing on the same line but there aren't any for printing multiple lines on the same line within the terminal.
For example:
ogeno#OH-ogeno-MBP:~|⇒ python my_script.py
Process 1: 5%
Process 2: 14%
Process 3: 55%
I want the progress of these processes to update on the same line rather than printing over and over again. I have looked at other questions that say to use the return carriage character \r and sys.stdout.flush() but it doesn't seem to change the caret to go up a line, just to the end of the current line.
EDIT: My question is different because it's to do with printing MULTIPLE lines on the same lines in the terminal. It's easy if it's just one line.

This can easily be done by using backspace. Following is the sample code that will print the percentage on the same line.
import time
print "Work in progress(0%%)", # Python 2 print without newline
for work_done in range(10):
print "\b\b\b\b\b%2d%%)" % work_done, # Backspace then overwrite
time.sleep(1)

One approach is to use the ANSI escape-code "\033[F" for going to the beginning of the previous line. The following worked well in all my terminals, just writing to the next two lines from the current terminal position:
import time
import sys
progress_1 = 'Process 1: {}%'
progress_2 = 'Process 2: {}%'
print
print
for i in range(100):
sys.stdout.write('\033[F')
sys.stdout.write('\033[F')
print(progress_1.format(i))
print(progress_2.format(i))
time.sleep(0.02)

for python 2.7 you can use,
print 2%, 3% # Using comma will print it in same line
for python 3.x
print('2%', end=" ")
Or you can use sys.stdout.write for doing it with sys.stdout.flush()
Please check my below code, I have created a demo progress bar.
"""ProgressBar Module."""
import sys
import time
class ProgressBar(object):
"""Main class for the ProgressBa."""
DEFAULT_BAR_LENGTH = float(30)
def __init__(self, start=0, step=1):
"""Init for the class."""
self.end = ProgressBar.DEFAULT_BAR_LENGTH
self.start = start
self.step = step
self.total = self.end - self.start
self.counts = self.total / self.step
self._barLength = ProgressBar.DEFAULT_BAR_LENGTH
self.set_level(self.start)
self._plotted = False
def set_level_old(self, level, initial=False):
"""Setting Level."""
self._level = level
if level < self.start:
self._level = self.start
if level > self.end:
self._level = self.end
self._ratio = float(
self._level - self.start) / float(self.end - self.start)
self._levelChars = int(self._ratio * self._barLength)
def set_level(self, level, initial=False):
"""Setting Level."""
self._level = level
if level < self.start:
self._level = self.start
if level > self.end:
self._level = self.end
self._ratio = float(self._level) / float(self._barLength)
self._levelChars = int(self._ratio * self._barLength) * self.step
def plot_progress(self):
"""Plotting the bar."""
sys.stdout.write("\r %3i%% |%s%s|" % (
int(self._ratio * self.step * 100.0),
u'\u2588' * int(self._levelChars),
' ' * int(self._barLength - self._levelChars),
))
sys.stdout.flush()
self._plotted = True
def set_and_plot(self, level):
"""Call the plot."""
old_chars = self._levelChars
self.set_level(level)
if (not self._plotted) or (old_chars != self._levelChars):
self.plot_progress()
def __del__(self):
"""Del for the class."""
sys.stdout.write("\n")
if __name__ == "__main__":
pb = ProgressBar(0, 1)
curProgress = 0
pb.plot_progress()
while curProgress <= pb.counts:
pb.set_and_plot(curProgress)
curProgress += 1
time.sleep(0.1)
del pb

Related

How can I reset a list after each loop?

I want to create a new list (self.Input/self.Output) each time the while loop is called. And append this new list to another list at the end of it (self.Input_full/self.Output_full).
I have tried to reset the list at the start of the while loop with either just setting them back to empty: self.Output = [[],[],[],[]]
or deleting the information held by them: del self.Output[:]
but this didn't work, since then I got empty lists in the Full lists
import threading
class PepperCommandEvaluator(object):
def __init__(self):
self.Input = [[],[],[],[]]
self.Input_full = []
self.Output = [[],[],[],[]]
self.Output_full = []
self.count = 0
self.event = threading.Event()
def send_thread(self):
while self.count < 2:
self.count = self.count + 1
self.event.set()
sequence = [[1,1,1],[1,0,1],[1,3,3]]
for cmd in sequence:
rospy.loginfo("sending command")
rospy.Rate(0.5).sleep()
msg = Twist()
msg.linear.x = cmd[0]
msg.linear.y = cmd[1]
msg.angular.z = cmd[2]
t = rospy.get_rostime()
self.Input[0].append(cmd[0])
self.Input[1].append(cmd[1])
self.Input[2].append(cmd[2])
self.Input[3].append(t.secs + t.nsecs * 1e-9)
self.Input_full.append(self.Input)
self.event.clear()
def receive_thread(self,msg):
if self.event.isSet():
self.frame_id = msg.header.frame_id
self.x_odom = msg.pose.pose.position.x
self.y_odom = msg.pose.pose.position.y
self.z_odom = msg.pose.pose.position.z
self.ang_odom = msg.pose.pose.orientation.z
self.time = msg.header.stamp.secs
self.Output[0].append(self.x_odom)
self.Output[1].append(self.y_odom)
self.Output[2].append(self.ang_odom)
self.Output[3].append(self.time)
else:
self.Output_full.append(self.Output)
if __name__ == "__main__":
tros = PepperCommandEvaluator()
tros.send_thread()
My desired output is to get a new self.Input and self.Output_odom with each loop and append this list to the self.Input_full and self.Output_full_odom respectively.
In the end depending on the amount of times n the loop is ran, this should look like this:
self.Output_full = [[self.Output_1,self.Output_2,...,self.Output_n]]
Since lists in python are handled by reference, when you append the reference to Input to Input_full, then delete Input, you also delete the entry in Input_full. To avoid this, you want to append a copy of Input, then clear the real thing. You can also change the reference of Input to an empty list after appending it.
def send_thread(self):
while self.count < 2:
self.count = self.count + 1
self.event.set()
sequence = [[1,1,1],[1,0,1],[1,3,3]]
self.Input = [[],[],[]] # Reassigning to a new list at the top
for cmd in sequence:
rospy.loginfo("sending command")
rospy.Rate(0.5).sleep()
msg = Twist()
msg.linear.x = cmd[0]
msg.linear.y = cmd[1]
msg.angular.z = cmd[2]
t = rospy.get_rostime()
self.Input[0].append(cmd[0])
self.Input[1].append(cmd[1])
self.Input[2].append(cmd[2])
self.Input[3].append(t.secs + t.nsecs * 1e-9)
self.Input_full.append(self.Input)
self.event.clear()

LinkedQueues and simulated printer

I am aware that there is another post asking about simulated printers but I didn't find any actual answers to my own question there.
class LinkedQueue :
class _Node :
__slots__ = '_element', '_next'
def __init__(self, element, next = None):
self._element = element
self._next = next
def __init__(self) :
self._head = None
self._tail = None
self._size = 0
def __len__(self) :
return self._size
def is_empty(self) :
return self._size == 0
def first(self) :
if self.is_empty() :
raise Empty('Queue is empty')
return self._head._element
def dequeue(self) :
if self.is_empty():
raise Empty('Queue is empty')
answer = self._head._element
self._head = self._head._next
self._size -= 1
if self.is_empty() :
self._tail = None
return answer
def enqueue(self, e) :
newest = self._Node(e,None)
if self.is_empty() :
self._head = newest
else :
self._tail._next = newest
self._tail = newest
self._size += 1
class Printer:
def __init__(self, name, job_queue):
self._name = name
self._job_queue
self._current_job = None
class Empty(Exception) :
pass
def main():
p_jobs = LinkedQueue()
red = Printer("Red", p_jobs) # Creates Printer Red
green = Printer("Green", p_jobs) # Creates Printer Green
print("\nOptions:\n 1. Add Job \n 2. Print Pages \n 3. Status \
\n 4. Quit")
i = 0
while True:
n = str(input("\nChoice (Type the number): "))
if n == '1': # Add Job
i += 1
p = int(input("\nHow many pages? "))
j = p_jobs.job_list(i,p,next)
p_jobs.enqueue(j)
if n == '2': # Print Job
print()
p_jobs.dequeue()
i -= 1
if n == '3': # Status
print()
if n == '4': # Quit
print("\nGoodbye!")
break
This is the provided code for us. We are supposed to simulate two printers that print out pages from jobs using LinkedQueue and Node.
I have the main function barebones w/c consists of 4 options:
Add Job, Print Jobs, Status, and Quit
I have trouble understanding how to use (refer) to the enqueue and dequeue methods. Can somebody break down each part of this program so I can at least understand where to start. I also would appreciate any hints that tell me where to go from here. Thank you.
EDIT: I added my main function w/c is basically just a UI
While this is by no means a complete answer, it shows how to print out what's currently in a LinkedQueue instance.
First, add the following method to the class. It will allow the contents to be iterated.
class LinkedQueue:
def __iter__(self):
if self.is_empty():
return None # No jobs in print queue.
cur = self._head
while cur:
yield cur._element
cur = cur._next
Here's something demonstrating how to use it:
def test():
q = LinkedQueue() # Create a queue.
# Add some jobs to the LinkedQueue.
for j in range(3):
pages = randint(1, 10)
q.enqueue(('job #{}'.format(j), pages))
# Show what's currently in the LinkedQueue.
for v in q:
print(v)
# Modify the LinkedQueue and then show what's left in it.
j = q.dequeue()
print('\nafter removing one job')
for v in q:
print(v)

How does the Random module generate a new number in python

So I have been playing around with linear congruential generators but I am stuck on the idea that once a script has been run, it always generates the same way. If I run a script such as this;
import random
print(random.randrange(0,9))
It will generate a new random number each time the script is run. I'm wondering how it does this.
So this is what I have gotten so far;
from collections import Counter
class linearCongruentialGenerator(object):
def __init__(self, lowBound, highBound, length):
self.lowBound, self.highBound, self.length = lowBound, highBound, length
self.modulus = 20226231145#modulus
self.multiplier = 104743412357321567#multiplier
self.increment = 1049592131123467#increment
self.seed = 43123211114#seed
def __iter__(self):
while self.length > 0:
self.length -= 1
self.seed = (self.multiplier*self.seed + self.increment) % self.modulus
yield (self.seed % self.highBound)
def linearCongruentialGenerator(self, first, last):
self.seed = (self.multiplier*self.seed + self.increment) % self.modulus
return self.seed
def binary(self):
self.seed = (self.multiplier*self.seed + self.increment) % self.modulus
self.seedLength = len(str(seed))
return (self.seed // 10**1 % 10) % 2
if __name__ == "__main__":
random = linearCongruentialGenerator(0, 9, 100)
random1 = linearCongruentialGenerator(0, 9, 100)
list1 = []
list2 = []
for i in random:
list1.append(i)
for j in random1:
list2.append(j)
print(Counter(list1))
print(len(set(list1)))
print(Counter(list2))
print(len(set(list2)))
This script counts the numbers generated by the linearCongruentialGenerator class then prints the distribution: number:count.
I want the script to generate new values every time I run the script. Without using the random class that comes with python. Because that is cheating :D
Not sure how to approach this, any tips?
Just do what we do to ensure this problem doesn't occur in random, set the seed as time.time() in init so it always seeds with the current time seed=time.time()

Countdown with Python and Gtk, problems with timeout_add

I'm trying to make a workout app, so I have to count each push-up or display a countdown for side plank. To do that I tried to use GObject.timeout_add but it appears it doesn't work like I thought it would.
In the current situation, all exercises of a session are run simultaneously, instead of one at a time, in the proper order.
I am certainly missing something, and through my web searching, I still haven't found it.
Here is my code :
#!/usr/bin/python
"""
Work out app to keep track of your progression through the session
"""
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GObject
from Programms import *
from time import sleep
def time_hours(t):
return t // 3600
def time_minutes(t):
return (t % 3600) // 60
def time_seconds(t):
return int((t % 3600) % 60)
def time_format(t):
hours = double_digit(time_hours(t))
minutes = double_digit(time_minutes(t))
seconds = double_digit(time_seconds(t))
return "{}:{}:{}".format(hours, minutes, seconds)
def double_digit(t):
if t == 0:
return "00"
elif t < 10:
return "0{}".format(t)
return t
class StartingLine:
"""
Gtk
"""
def __init__(self):
# main window
self.window = Gtk.Window()
self.window.set_title("Work out !")
self.window.set_size_request(200, 200)
self.window.connect('destroy', lambda x: Gtk.main_quit())
# start button
self.button = Gtk.Button("Start")
self.button.connect('clicked', self.start_work_out)
self.window.add(self.button)
self.window.show_all()
def start_work_out(self, widget):
self.window.hide()
work = Two
duration = work.duration
for exo in work.exercises:
Instructor(exo, duration)
class Instructor:
"""
Gtk
"""
def __init__(self, exo, duration):
# main window
self.window = Gtk.Window()
self.window.set_title("Work out !")
self.window.set_size_request(200, 200)
self.window.connect("destroy", lambda x: Gtk.main_quit())
# timer
self.current_time = 0
self.counter = 0
self.timer_label = Gtk.Label(time_format(self.counter))
# exercise
self.current_exercise = Gtk.Label(exo.name)
# overall progression
self.bar = Gtk.ProgressBar.new()
# hierarchy
grid = Gtk.Grid()
grid.attach(self.timer_label, 1, 0, 1, 1)
grid.attach(self.current_exercise, 1, 1, 1, 1)
grid.attach(self.bar, 1, 2, 1, 1)
self.window.add(grid)
# display
self.window.show_all()
if exo.type == ExoType.Reps:
print('exercise : ', exo.name)
self.counter = 0
self.timer_label.set_label(str(self.counter))
rep_id = GObject.timeout_add(1000*exo.in_between, self.display_rep, exo, duration)
print("rep ID : ", rep_id)
elif exo.type == ExoType.Timer:
self.counter = exo.number
self.timer_label.set_label(time_format(self.counter))
time_id = GObject.timeout_add(1000*exo.in_between, self.display_timer, exo, duration)
print("time ID : ", time_id)
def display_rep(self, exo, duration):
print("current time : ", self.current_time)
print("current exercise : ", exo.name)
print("rep : ", self.counter)
self.counter += 1
self.current_time += exo.in_between
self.timer_label.set_label(str(self.counter)) # update counter
self.current_exercise.set_label(exo.name) # update exercise name
self.bar.set_fraction(self.current_time/duration) # update progression bar
return self.counter < exo.number
def display_timer(self, exo, duration):
print("current time : ", self.current_time)
print("current exercise : ", exo.name)
print("timer : ", self.counter)
self.counter -= 1
self.current_time += exo.in_between
self.timer_label.set_label(time_format(self.counter)) # update counter
self.current_exercise.set_label(exo.name) # update name
self.bar.set_fraction(self.current_time/duration) # update progression bar
return self.counter > 0
if __name__ == "__main__":
StartingLine()
Gtk.main()
In this code I have used two types, which are :
#!/usr/bin/python
"""
define class exercise and class session
"""
class Exercise:
def __init__(self, name, typo, unilateral, number, preparation=0, in_between=1):
"""
:param name: name of the exercise
:param typo: either timer or reps
:param number: either a time in seconds or a a number of reps
:param preparation: time allocated to prepare the exercise, to put yourself in position
:param in_between: time between reps. 1s by default.
"""
self.name = name
self.type = typo
self.unilateral = unilateral
self.number = number
self.prep = preparation
self.in_between = in_between
class ExoType(enumerate):
Reps = 0
Timer = 1
class Session:
def __init__(self, name, exercises):
self.name = name
self.exercises = exercises
self.duration = time_length(exercises)
def time_length(exercises):
t = 0
for exo in exercises:
if exo.type == ExoType.Timer:
t += exo.number
elif exo.type == ExoType.Reps:
t += exo.number*exo.in_between
else:
print("Error : Session duration")
return t
First time I ask a question here, please tell me if I'm doing wrong.
rep_id = GObject.timeout_add(1000*exo.in_between, self.display_rep, exo, duration)
When you timeout_add you tell Mainloop to call function every n seconds.
for exo in work.exercises:
Instructor(exo, duration)
Here you add every exercise to mainloop, which leads to simultaneous run, because you create mainloop once.
There are several solutions and in my opinion none of them includes main_iteration_do.
Reorganise things so that 1 exercise runs it's own mainloop.
Emit a signal when one exercise is finished and in it's handler start another exercise.

Enabling Code Completion in an embedded Python Interpreter

I have got a PyQT widget interpreter working, the code picked up from here is as follows:
import os
import re
import sys
import code
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class MyInterpreter(QWidget):
def __init__(self, parent):
super(MyInterpreter, self).__init__(parent)
hBox = QHBoxLayout()
self.setLayout(hBox)
self.textEdit = PyInterp(self)
# this is how you pass in locals to the interpreter
self.textEdit.initInterpreter(locals())
self.resize(650, 300)
self.centerOnScreen()
hBox.addWidget(self.textEdit)
hBox.setMargin(0)
hBox.setSpacing(0)
def centerOnScreen(self):
# center the widget on the screen
resolution = QDesktopWidget().screenGeometry()
self.move((resolution.width() / 2) - (self.frameSize().width() / 2),
(resolution.height() / 2) - (self.frameSize().height() / 2))
class PyInterp(QTextEdit):
class InteractiveInterpreter(code.InteractiveInterpreter):
def __init__(self, locals):
code.InteractiveInterpreter.__init__(self, locals)
def runIt(self, command):
code.InteractiveInterpreter.runsource(self, command)
def __init__(self, parent):
super(PyInterp, self).__init__(parent)
sys.stdout = self
sys.stderr = self
self.refreshMarker = False # to change back to >>> from ...
self.multiLine = False # code spans more than one line
self.command = '' # command to be ran
self.printBanner() # print sys info
self.marker() # make the >>> or ... marker
self.history = [] # list of commands entered
self.historyIndex = -1
self.interpreterLocals = {}
# setting the color for bg and text
palette = QPalette()
palette.setColor(QPalette.Base, QColor(0, 0, 0))
palette.setColor(QPalette.Text, QColor(0, 255, 0))
self.setPalette(palette)
self.setFont(QFont('Courier', 12))
# initilize interpreter with self locals
self.initInterpreter(locals())
def printBanner(self):
self.write(sys.version)
self.write(' on ' + sys.platform + '\n')
self.write('PyQt4 ' + PYQT_VERSION_STR + '\n')
msg = 'Type !hist for a history view and !hist(n) history index recall'
self.write(msg + '\n')
def marker(self):
if self.multiLine:
self.insertPlainText('... ')
else:
self.insertPlainText('>>> ')
def initInterpreter(self, interpreterLocals=None):
if interpreterLocals:
# when we pass in locals, we don't want it to be named "self"
# so we rename it with the name of the class that did the passing
# and reinsert the locals back into the interpreter dictionary
selfName = interpreterLocals['self'].__class__.__name__
interpreterLocalVars = interpreterLocals.pop('self')
self.interpreterLocals[selfName] = interpreterLocalVars
else:
self.interpreterLocals = interpreterLocals
self.interpreter = self.InteractiveInterpreter(self.interpreterLocals)
def updateInterpreterLocals(self, newLocals):
className = newLocals.__class__.__name__
self.interpreterLocals[className] = newLocals
def write(self, line):
self.insertPlainText(line)
self.ensureCursorVisible()
def clearCurrentBlock(self):
# block being current row
length = len(self.document().lastBlock().text()[4:])
if length == 0:
return None
else:
# should have a better way of doing this but I can't find it
[self.textCursor().deletePreviousChar() for x in xrange(length)]
return True
def recallHistory(self):
# used when using the arrow keys to scroll through history
self.clearCurrentBlock()
if self.historyIndex <> -1:
self.insertPlainText(self.history[self.historyIndex])
return True
def customCommands(self, command):
if command == '!hist': # display history
self.append('') # move down one line
# vars that are in the command are prefixed with ____CC and deleted
# once the command is done so they don't show up in dir()
backup = self.interpreterLocals.copy()
history = self.history[:]
history.reverse()
for i, x in enumerate(history):
iSize = len(str(i))
delta = len(str(len(history))) - iSize
line = line = ' ' * delta + '%i: %s' % (i, x) + '\n'
self.write(line)
self.updateInterpreterLocals(backup)
self.marker()
return True
if re.match('!hist\(\d+\)', command): # recall command from history
backup = self.interpreterLocals.copy()
history = self.history[:]
history.reverse()
index = int(command[6:-1])
self.clearCurrentBlock()
command = history[index]
if command[-1] == ':':
self.multiLine = True
self.write(command)
self.updateInterpreterLocals(backup)
return True
return False
def keyPressEvent(self, event):
if event.key() == Qt.Key_Escape:
# proper exit
self.interpreter.runIt('exit()')
if event.key() == Qt.Key_Down:
if self.historyIndex == len(self.history):
self.historyIndex -= 1
try:
if self.historyIndex > -1:
self.historyIndex -= 1
self.recallHistory()
else:
self.clearCurrentBlock()
except:
pass
return None
if event.key() == Qt.Key_Up:
try:
if len(self.history) - 1 > self.historyIndex:
self.historyIndex += 1
self.recallHistory()
else:
self.historyIndex = len(self.history)
except:
pass
return None
if event.key() == Qt.Key_Home:
# set cursor to position 4 in current block. 4 because that's where
# the marker stops
blockLength = len(self.document().lastBlock().text()[4:])
lineLength = len(self.document().toPlainText())
position = lineLength - blockLength
textCursor = self.textCursor()
textCursor.setPosition(position)
self.setTextCursor(textCursor)
return None
if event.key() in [Qt.Key_Left, Qt.Key_Backspace]:
# don't allow deletion of marker
if self.textCursor().positionInBlock() == 4:
return None
if event.key() in [Qt.Key_Return, Qt.Key_Enter]:
# set cursor to end of line to avoid line splitting
textCursor = self.textCursor()
position = len(self.document().toPlainText())
textCursor.setPosition(position)
self.setTextCursor(textCursor)
line = str(self.document().lastBlock().text())[4:] # remove marker
line.rstrip()
self.historyIndex = -1
if self.customCommands(line):
return None
else:
try:
line[-1]
self.haveLine = True
if line[-1] == ':':
self.multiLine = True
self.history.insert(0, line)
except:
self.haveLine = False
if self.haveLine and self.multiLine: # multi line command
self.command += line + '\n' # + command and line
self.append('') # move down one line
self.marker() # handle marker style
return None
if self.haveLine and not self.multiLine: # one line command
self.command = line # line is the command
self.append('') # move down one line
self.interpreter.runIt(self.command)
self.command = '' # clear command
self.marker() # handle marker style
return None
if self.multiLine and not self.haveLine: # multi line done
self.append('') # move down one line
self.interpreter.runIt(self.command)
self.command = '' # clear command
self.multiLine = False # back to single line
self.marker() # handle marker style
return None
if not self.haveLine and not self.multiLine: # just enter
self.append('')
self.marker()
return None
return None
# allow all other key events
super(PyInterp, self).keyPressEvent(event)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MyInterpreter(None)
win.show()
sys.exit(app.exec_())
Is there an easy way of getting some tab completion going just for local symbols ?
I think you are referring to rlcompleter's Completer object.
You can used it like so:
from rlcompleter import Completer
line = str(...)
completer = Completer(self.interpreter.locals)
suggestion = completer.complete(line, 0)
self.insertPlainText(suggestion)
The numeric argument indicates the n-th suggestion, and you can iterate over it until it returns None.
For example, say we have
>>> my_data = '012345'
then
>>> completer.complete('my_', 0)
'my_data'
>>> completer.complete('my_data.s', 0)
'my_data.split('
>>> completer.complete('my_data.s', 1)
'my_data.splitlines('
Note that while the code above uses interpreter.locals, you can apply a wider search (but be sure to provide a dictionary).
If you want to save yourself some time - take a look at spyderlib, it contains a widget that embeds an interactive Python interpreter with some interaction sugar such as code completion. The bits of specific interest are
spyderlib/widgets/sourcecode/codeeditor.py
spyderlib/shell.py
spyderlib/editor.py
spyderlib/widgets/externalshell/pythonshell.py
spyderlib/utiils/module_completion.py
spyderlib/plugins/externalconsole.py
spyderlib/plugins/console.py
spyderlib/plugins/editor.py
The only caveat I have with spyderlib is that you can't just use that doodad on it's own - somewhere I have a version I extracted that contains the bare minimum of support modules needed to run. If you run into the same problem I did regarding the bloat send me a msg and I'll check my stuff into github for you to grab.
I also seem to remember there's an Qt based interactive Python interpreter widget that is used in NumPy or SciPy - I think it originally came from the ipython project however. It's pretty nice because it actually splits the interpeter from the execution of code - so if your code crashes, your interpreter doesn't crash with it. But, in that case you can't modify the Pythonic contents of other threads.. The spyderlib version can work both ways.
I have an open-source PyQt based Python interpreter that you can find here: http://docs.projexsoftware.com/api/projexui/
The specific class is the XConsoleEdit found in projexui.widgets.xconsoleedit. It has auto-completion built-in.
Hope that helps!
I get auto complete from rlcompleter2, but there are two problems in the following code,
import xxx as yyy auto complete on yyy doesn't work
the locals() are not copied into the interactiveinterpreter, I tried to use this code in Autodesk Maya, eg, run x=3 in maya script editor, and then run x in the pyqt interpreter, it says NameError: name 'x' is not defined. if you do not use maya, this error can be reproduced from external python interpreter as well, first define some variable, then launch this ui, the variable is not copied into the interpreter in the ui.
import os
import re
import sys
import code
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class MyInterpreter(QWidget):
def __init__(self, parent):
super(MyInterpreter, self).__init__(parent)
hBox = QHBoxLayout()
self.setLayout(hBox)
self.textEdit = PyInterp(self)
# this is how you pass in locals to the interpreter
self.textEdit.initInterpreter(locals())
self.resize(850, 400)
# self.centerOnScreen()
hBox.addWidget(self.textEdit)
hBox.setMargin(0)
hBox.setSpacing(0)
def centerOnScreen(self):
# center the widget on the screen
resolution = QDesktopWidget().screenGeometry()
self.move((resolution.width() / 2) - (self.frameSize().width() / 2),
(resolution.height() / 2) - (self.frameSize().height() / 2))
class PyInterp(QTextEdit):
class InteractiveInterpreter(code.InteractiveInterpreter):
def __init__(self, locals):
code.InteractiveInterpreter.__init__(self, locals)
def runIt(self, command):
code.InteractiveInterpreter.runsource(self, command)
def __init__(self, parent):
super(PyInterp, self).__init__(parent)
sys.stdout = self
sys.stderr = self
self.refreshMarker = False # to change back to >>> from ...
self.multiLine = False # code spans more than one line
self.command = '' # command to be ran
self.printBanner() # print sys info
self.marker() # make the >>> or ... marker
self.history = [] # list of commands entered
self.historyIndex = -1
self.interpreterLocals = {}
# setting the color for bg and text
# palette = QPalette()
# palette.setColor(QPalette.Base, QColor(0, 0, 0))
# palette.setColor(QPalette.Text, QColor(0, 255, 0))
# self.setPalette(palette)
self.setFont(QFont('Courier', 10))
# initilize interpreter with self locals
self.initInterpreter(locals())
from rlcompleter2 import Completer
self.completer = Completer()
def printBanner(self):
self.write(sys.version)
self.write(' on ' + sys.platform + '\n')
self.write('PyQt4 ' + PYQT_VERSION_STR + '\n')
# msg = 'Type !hist for a history view and !hist(n) history index recall'
# self.write(msg + '\n')
def marker(self):
if self.multiLine:
self.insertPlainText('... ')
else:
self.insertPlainText('>>> ')
def initInterpreter(self, interpreterLocals=None):
if interpreterLocals:
# when we pass in locals, we don't want it to be named "self"
# so we rename it with the name of the class that did the passing
# and reinsert the locals back into the interpreter dictionary
selfName = interpreterLocals['self'].__class__.__name__
interpreterLocalVars = interpreterLocals.pop('self')
self.interpreterLocals[selfName] = interpreterLocalVars
else:
self.interpreterLocals = interpreterLocals
self.interpreter = self.InteractiveInterpreter(self.interpreterLocals)
def updateInterpreterLocals(self, newLocals):
className = newLocals.__class__.__name__
self.interpreterLocals[className] = newLocals
def write(self, line):
self.insertPlainText(line)
self.ensureCursorVisible()
def clearCurrentBlock(self):
# block being current row
length = len(self.document().lastBlock().text()[4:])
if length == 0:
return None
else:
# should have a better way of doing this but I can't find it
[self.textCursor().deletePreviousChar() for x in xrange(length)]
return True
def recallHistory(self):
# used when using the arrow keys to scroll through history
self.clearCurrentBlock()
if self.historyIndex <> -1:
self.insertPlainText(self.history[self.historyIndex])
return True
def customCommands(self, command):
if command == '!hist': # display history
self.append('') # move down one line
# vars that are in the command are prefixed with ____CC and deleted
# once the command is done so they don't show up in dir()
backup = self.interpreterLocals.copy()
history = self.history[:]
history.reverse()
for i, x in enumerate(history):
iSize = len(str(i))
delta = len(str(len(history))) - iSize
line = line = ' ' * delta + '%i: %s' % (i, x) + '\n'
self.write(line)
self.updateInterpreterLocals(backup)
self.marker()
return True
if re.match('!hist\(\d+\)', command): # recall command from history
backup = self.interpreterLocals.copy()
history = self.history[:]
history.reverse()
index = int(command[6:-1])
self.clearCurrentBlock()
command = history[index]
if command[-1] == ':':
self.multiLine = True
self.write(command)
self.updateInterpreterLocals(backup)
return True
return False
def keyPressEvent(self, event):
if event.key() == Qt.Key_Tab:
line = str(self.document().lastBlock().text())[4:]
self.completer.construct(line)
if len(self.completer.rl_matches) == 1:
self.clearCurrentBlock()
self.insertPlainText(self.completer.rl_matches[0])
else:
print 'repeat:', self.completer.repeated
mod = self.completer.repeated % len(self.completer.completions)
if mod == 0:
# print '\n'.join(self.completer.rl_matches)
col_print(self.completer.rl_matches)
else:
print ' '
print '\n'.join(self.completer.rl_matches)
# print self.completer.rl_matches
self.marker()
self.insertPlainText(line)
return
if event.key() == Qt.Key_Escape:
# proper exit
self.interpreter.runIt('exit()')
if event.key() == Qt.Key_Down:
if self.historyIndex == len(self.history):
self.historyIndex -= 1
try:
if self.historyIndex > -1:
self.historyIndex -= 1
self.recallHistory()
else:
self.clearCurrentBlock()
except:
pass
return None
if event.key() == Qt.Key_Up:
try:
if len(self.history) - 1 > self.historyIndex:
self.historyIndex += 1
self.recallHistory()
else:
self.historyIndex = len(self.history)
except:
pass
return None
if event.key() == Qt.Key_Home:
# set cursor to position 4 in current block. 4 because that's where
# the marker stops
blockLength = len(self.document().lastBlock().text()[4:])
lineLength = len(self.document().toPlainText())
position = lineLength - blockLength
textCursor = self.textCursor()
textCursor.setPosition(position)
self.setTextCursor(textCursor)
return None
if event.key() in [Qt.Key_Left, Qt.Key_Backspace]:
# don't allow deletion of marker
# if qt version < 4.7, have to use position() - block().position()
if self.textCursor().positionInBlock() == 4:
return None
if event.key() in [Qt.Key_Return, Qt.Key_Enter]:
# set cursor to end of line to avoid line splitting
textCursor = self.textCursor()
position = len(self.document().toPlainText())
textCursor.setPosition(position)
self.setTextCursor(textCursor)
line = str(self.document().lastBlock().text())[4:] # remove marker
line.rstrip()
self.historyIndex = -1
if self.customCommands(line):
return None
else:
try:
line[-1]
self.haveLine = True
if line[-1] == ':':
self.multiLine = True
self.history.insert(0, line)
except:
self.haveLine = False
if self.haveLine and self.multiLine: # multi line command
self.command += line + '\n' # + command and line
self.append('') # move down one line
self.marker() # handle marker style
return None
if self.haveLine and not self.multiLine: # one line command
self.command = line # line is the command
self.append('') # move down one line
self.interpreter.runIt(self.command)
self.command = '' # clear command
self.marker() # handle marker style
return None
if self.multiLine and not self.haveLine: # multi line done
self.append('') # move down one line
self.interpreter.runIt(self.command)
self.command = '' # clear command
self.multiLine = False # back to single line
self.marker() # handle marker style
return None
if not self.haveLine and not self.multiLine: # just enter
self.append('')
self.marker()
return None
return None
# allow all other key events
super(PyInterp, self).keyPressEvent(event)
# http://stackoverflow.com/a/30861871/2052889
def col_print(lines, term_width=90, indent=0, pad=2):
n_lines = len(lines)
if n_lines == 0:
return
col_width = max(len(line) for line in lines)
n_cols = int((term_width + pad - indent)/(col_width + pad))
n_cols = min(n_lines, max(1, n_cols))
col_len = int(n_lines/n_cols) + (0 if n_lines % n_cols == 0 else 1)
if (n_cols - 1) * col_len >= n_lines:
n_cols -= 1
cols = [lines[i*col_len: i*col_len + col_len] for i in range(n_cols)]
rows = list(zip(*cols))
rows_missed = zip(*[col[len(rows):] for col in cols[:-1]])
rows.extend(rows_missed)
for row in rows:
print(" "*indent + (" "*pad).join(line.ljust(col_width)
for line in row))
def main():
app = QApplication(sys.argv)
win = MyInterpreter(None)
win.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
desired effect:
https://gfycat.com/DistantScrawnyCivet
current effect:
https://gfycat.com/DeafeningHeavyBoto

Categories

Resources