scroller.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. import time
  2. import argparse
  3. import config
  4. import sys
  5. import select
  6. import signal
  7. signal.signal(signal.SIGPIPE, signal.SIG_DFL)
  8. class InputReceived(Exception):
  9. pass
  10. parser = argparse.ArgumentParser(description=config.description)
  11. parser.add_argument(
  12. '-i', '--interval',
  13. dest='interval',
  14. default=0.2,
  15. type=float,
  16. help='specify scroll speed in seconds'
  17. )
  18. parser.add_argument(
  19. '-m', '--mutate',
  20. action='store_true',
  21. default=False,
  22. dest='mutate',
  23. help='scroll the text in place'
  24. )
  25. parser.add_argument(
  26. '-s', '--separator',
  27. dest='sep',
  28. default=' ',
  29. help='append a separator to input string before animating, \
  30. used as padding'
  31. )
  32. parser.add_argument(
  33. '-l', '--length',
  34. dest='len',
  35. type=int,
  36. default=0,
  37. help='scroll text only if its length is greater than or \
  38. equal to this value'
  39. )
  40. parser.add_argument(
  41. '-c', '--count',
  42. dest='count',
  43. type=int,
  44. default=float('inf'),
  45. help='specify number of characters to scroll'
  46. )
  47. parser.add_argument(
  48. '-r', '--reverse',
  49. dest='reverse',
  50. default=False,
  51. action='store_true',
  52. help='scroll text in the opposite direction'
  53. )
  54. parser.add_argument(
  55. '-o', '--open',
  56. dest='open',
  57. default=False,
  58. action='store_true',
  59. help='keep stdin open and reload on any new input'
  60. )
  61. parser.add_argument(
  62. '-p', '--persist',
  63. dest='persist',
  64. default=False,
  65. action='store_true',
  66. help='if using --open flag, \
  67. do not exit after stdin is closed'
  68. )
  69. parser.add_argument(
  70. '-v', '--version',
  71. dest='version',
  72. default=False,
  73. action='store_true',
  74. help='print version and exit'
  75. )
  76. parser.add_argument(
  77. '-a', '--after',
  78. dest='postfix',
  79. default='',
  80. help='append a static postfix to the text'
  81. )
  82. parser.add_argument(
  83. '-b', '--before',
  84. dest='prefix',
  85. default='',
  86. help='prepend a static prefix to the text'
  87. )
  88. def permute(string, rev=False):
  89. return (string[-1] + string[:-1] if rev else string[1:] + string[0])
  90. def scroll(string, rev=False, sep='', static=False):
  91. string = string + sep
  92. if static:
  93. while True:
  94. yield string
  95. else:
  96. while True:
  97. string = permute(string, rev=rev)
  98. yield string
  99. def scroller(string,
  100. static=False, count=float('inf'),
  101. rev=False, sep=''):
  102. for i, permutation in enumerate(scroll(string,
  103. rev=rev,
  104. sep=sep,
  105. static=static)):
  106. if i >= count:
  107. break
  108. yield permutation
  109. def main(string=None, args=None):
  110. if args is None:
  111. args = parser.parse_args()
  112. if args.version:
  113. print("Scroller {}".format(config.version))
  114. return
  115. if string is None:
  116. try:
  117. string = input()
  118. except KeyboardInterrupt:
  119. print(end='\r')
  120. return
  121. static = False
  122. if args.len >= len(string):
  123. static = True
  124. end = '\n'
  125. if args.mutate:
  126. end = '\r'
  127. interval = args.interval
  128. if args.interval < 0:
  129. interval = 0.2
  130. try:
  131. if not args.open:
  132. for permutation in scroller(string,
  133. static,
  134. args.count,
  135. args.reverse,
  136. args.sep):
  137. if args.len:
  138. permutation = permutation[:args.len + 1]
  139. print(args.prefix + permutation + args.postfix, end=end)
  140. sys.stdout.flush()
  141. time.sleep(interval)
  142. else:
  143. try:
  144. for permutation in scroller(string,
  145. static,
  146. args.count,
  147. args.reverse,
  148. args.sep):
  149. r, _, _ = select.select([sys.stdin], [], [], 0)
  150. if sys.stdin in r:
  151. raise InputReceived
  152. if args.len:
  153. permutation = permutation[:args.len - 1]
  154. print(args.prefix + permutation + args.postfix, end=end)
  155. sys.stdout.flush()
  156. time.sleep(interval)
  157. except InputReceived:
  158. try:
  159. string = input()
  160. except EOFError:
  161. if args.persist:
  162. args.open = False
  163. main(string, args)
  164. else:
  165. return
  166. if args.mutate:
  167. sys.stdout.write('\033[K')
  168. main(string, args)
  169. except KeyboardInterrupt:
  170. print(end='\r')
  171. if __name__ == '__main__':
  172. main()