On 7/11/2018 10:09 AM, jkn wrote:
Hi All
This is more of a Tkinter question rather than a python one, I think, but
anyway...
I have a Python simulator program with a Model-View_Controller architecture. I
have written the View part using Tkinter in the first instance; later I plan
to use Qt.
However I also want to be able to offer an alternative of a console-only
operation. So I have a variant View with the beginnings of this.
Naturally I want to keep this as similar as possible to my Tkinter-based view. I
had thought that I had seen a guide somewhere to using Tk/Tkinter in a non-GUI
form.
The only reason I can think of to use tkinter in a text application is
to its multiple simultaneous timer loops.
I don't seem to be able to track this down now, but I have at least been
successful in hiding ('withdrawing') the main Frame, and running a main loop.
At the end of this response is an experiment in driving text-output
coroutines with tkinter, where I did as you say above.
The bit which I am now stumbling on is trying to bind key events to my view,
and I am wondering if this actually makes any sense. In the absence of a GUI I
want to accept keypresses to control the simulation. But in a console app I will
have no visible or in focus window, and therefore at what level would any
keys be bound? Not at the widget level, nor the frame, and I am not sure if the
the root makes sense either.
You cannot use the tkinter widget bind command. If you can use curses
or something to move the cursor around the screen, you can emulate
widgets and key binding with characters, an after loop, and key handlers.
+---------------------------------------+
| Find: _ |
| Ignore case [ ] Up ( ) Down ( ) |
+---------------------------------------+
keyhandle = entry
def keyhit():
if kbhit(): # Windows, someone else gave linux equivalent
keyhandle(getch())
root.after(50, keyhit)
root.after(50, keyhit) # Start key polling
keyhandler is initially entry, which echoes printable chars and moves
cursor to Ignore case [_] on tab (assuming you have gui box do the same)
and changes keyhandle to check. Check treats [space] as a left click
and call whatever the gui checkbutton command is. Ditto for radio and
the binding to radiobuttons.
So I am looking for confirmation of this, and/or whether there is any way of
running a Tkinter application in 'console' mode, running a main loop and
both outputting data and accepting, and acting on, key presses.
Brett's countdown.py (see link below) implements an event loop using
time.sleep and a priority queue. A 'keyhit loop could be added to that,
but the result is still to text apps.
---
"""https://snarky.ca/how-the-heck-does-async-await-work-in-python-3-5/
How the heck does async/await work in Python 3.5?
Bret Cannon
"""
import datetime
import types
from time import perf_counter
from tkinter import Tk
class SleepingLoop:
"""An event loop focused on delaying execution of coroutines.
Think of this as being like asyncio.BaseEventLoop/curio.Kernel.
"""
def __init__(self, *coros):
self.coros = coros
self.waiting = set()
self.root = Tk()
self.root.withdraw()
def callback(self, coro):
try:
# It's time to resume the coroutine.
seconds = coro.send(perf_counter())
self.root.after(int(seconds * 1000), self.callback, coro)
except StopIteration:
# The coroutine is done.
self.waiting.remove(coro)
if not self.waiting:
self.root.quit()
def run_until_complete(self):
# Start all the coroutines (async generators)
for coro in self.coros:
self.waiting.add(coro)
seconds = coro.send(None)
self.root.after(int(seconds * 1000), self.callback, coro)
self.root.mainloop()
@types.coroutine
def sleep(seconds):
"""Pause a coroutine for the specified number of seconds.
Think of this as being like asyncio.sleep()/curio.sleep().
"""
now = perf_counter()
actual = yield seconds
# Resume the execution stack, sending back how long we actually waited.
return actual - now
async def countdown(label, length, *, delay=0):
"""Countdown a launch for `length` seconds, waiting `delay` seconds.
This is what a user would typically write.
"""
print(label, 'waiting', delay, 'seconds before starting countdown')
delta = await sleep(delay)
print(label, 'starting after waiting', delta)
while length:
print(label, 'T-minus', length)
waited = await sleep(1)
length -= 1
print(label, 'lift-off!')
def main():
"""Start the event loop, counting down 3 separate launches.
This is what a user would typically write.
"""
loop = SleepingLoop(countdown('A', 5, delay=2), countdown(' B', 3,
delay=2),
countdown(' C', 4, delay=1))
start = perf_counter()
loop.run_until_complete()
print('Total elapsed time is', perf_counter() - start)
if __name__ == '__main__':
main()
--
Terry Jan Reedy
--
https://mail.python.org/mailman/listinfo/python-list