* mainloop.py: added facility for calling select(). Also added
embryonic facility for pseudo-modal dialogs. * stdwinevents.py: added modifier masks for key/mouse events * renamed exceptions in nntplib.py * Changed string.join() to call string.joinfields() to profit of strop.joinfields()
This commit is contained in:
parent
c89705d697
commit
18fc5696c8
|
@ -13,6 +13,11 @@ from stdwinevents import *
|
|||
windows = []
|
||||
|
||||
|
||||
# Last window that ever received an event
|
||||
#
|
||||
last_window = None
|
||||
|
||||
|
||||
# Function to register a window.
|
||||
#
|
||||
def register(win):
|
||||
|
@ -28,6 +33,9 @@ def register(win):
|
|||
# (this is useful for cleanup actions).
|
||||
#
|
||||
def unregister(win):
|
||||
global last_window
|
||||
if win == last_window:
|
||||
last_window = None
|
||||
if win in windows:
|
||||
windows.remove(win) # Not in 0.9.1
|
||||
# 0.9.1 solution:
|
||||
|
@ -49,6 +57,65 @@ def anywindow():
|
|||
return None
|
||||
|
||||
|
||||
# NEW: register any number of file descriptors
|
||||
#
|
||||
fdlist = []
|
||||
select_args = None
|
||||
select_handlers = None
|
||||
#
|
||||
def registerfd(fd, mode, handler):
|
||||
if mode not in ('r', 'w', 'x'):
|
||||
raise ValueError, 'mode must be r, w or x'
|
||||
if type(fd) <> type(0):
|
||||
fd = fd.fileno() # If this fails it's not a proper select arg
|
||||
for i in range(len(fdlist)):
|
||||
if fdlist[i][:2] == (fd, mode):
|
||||
raise ValueError, \
|
||||
'(fd, mode) combination already registered'
|
||||
fdlist.append((fd, mode, handler))
|
||||
make_select_args()
|
||||
#
|
||||
def unregisterfd(fd, *args):
|
||||
if type(fd) <> type(0):
|
||||
fd = fd.fileno() # If this fails it's not a proper select arg
|
||||
args = (fd,) + args
|
||||
n = len(args)
|
||||
for i in range(len(fdlist)):
|
||||
if fdlist[i][:n] == args:
|
||||
del fdlist[i]
|
||||
make_select_args()
|
||||
#
|
||||
def make_select_args():
|
||||
global select_args, select_handlers
|
||||
rlist, wlist, xlist = [], [], []
|
||||
rhandlers, whandlers, xhandlers = {}, {}, {}
|
||||
for fd, mode, handler in fdlist:
|
||||
if mode == 'r':
|
||||
rlist.append(fd)
|
||||
rhandlers[`fd`] = handler
|
||||
if mode == 'w':
|
||||
wlist.append(fd)
|
||||
whandlers[`fd`] = handler
|
||||
if mode == 'x':
|
||||
xlist.append(fd)
|
||||
xhandlers[`fd`] = handler
|
||||
if rlist or wlist or xlist:
|
||||
select_args = rlist, wlist, xlist
|
||||
select_handlers = rhandlers, whandlers, xhandlers
|
||||
else:
|
||||
select_args = None
|
||||
select_handlers = None
|
||||
#
|
||||
def do_select():
|
||||
import select
|
||||
reply = apply(select.select, select_args)
|
||||
for mode in 0, 1, 2:
|
||||
list = reply[mode]
|
||||
for fd in list:
|
||||
handler = select_handlers[mode][`fd`]
|
||||
handler(fd, 'rwx'[mode])
|
||||
|
||||
|
||||
# Event processing main loop.
|
||||
# Return when there are no windows left, or when an unhandled
|
||||
# exception occurs. (It is safe to restart the main loop after
|
||||
|
@ -57,17 +124,111 @@ def anywindow():
|
|||
# into KeyboardInterrupt exceptions; these are turned back in events.
|
||||
#
|
||||
def mainloop():
|
||||
while windows:
|
||||
stdwin_select_handler() # Process events already in stdwin queue
|
||||
fd = stdwin.fileno()
|
||||
while 1:
|
||||
if windows:
|
||||
registerfd(fd, 'r', stdwin_select_handler)
|
||||
try:
|
||||
while windows:
|
||||
do_select()
|
||||
stdwin_select_handler()
|
||||
finally:
|
||||
unregisterfd(fd)
|
||||
elif fdlist:
|
||||
while fdlist and not windows:
|
||||
do_select()
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
# Handle stdwin events until none are left
|
||||
#
|
||||
def stdwin_select_handler(*args):
|
||||
while 1:
|
||||
try:
|
||||
dispatch(stdwinq.getevent())
|
||||
event = stdwinq.pollevent()
|
||||
except KeyboardInterrupt:
|
||||
dispatch(WE_COMMAND, stdwin.getactive(), WC_CANCEL)
|
||||
event = (WE_COMMAND, None, WC_CANCEL)
|
||||
if event is None:
|
||||
break
|
||||
dispatch(event)
|
||||
|
||||
|
||||
# Run a modal dialog loop for a window. The dialog window must have
|
||||
# been registered first. This prohibits most events (except size/draw
|
||||
# events) to other windows. The modal dialog loop ends when the
|
||||
# dialog window unregisters itself.
|
||||
#
|
||||
passthrough = WE_SIZE, WE_DRAW
|
||||
beeping = WE_MOUSE_DOWN, WE_COMMAND, WE_CHAR, WE_KEY, WE_CLOSE, WE_MENU
|
||||
#
|
||||
def modaldialog(window):
|
||||
if window not in windows:
|
||||
raise ValueError, 'modaldialog window not registered'
|
||||
while window in windows:
|
||||
try:
|
||||
event = stdwinq.getevent()
|
||||
except KeyboardInterrupt:
|
||||
event = WE_COMMAND, None, WC_CANCEL
|
||||
etype, ewindow, edetail = event
|
||||
if etype not in passthrough and ewindow <> window:
|
||||
if etype in beeping:
|
||||
stdwin.fleep()
|
||||
continue
|
||||
dispatch(event)
|
||||
|
||||
|
||||
# Dispatch a single event.
|
||||
# Events for the no window in particular are sent to the active window
|
||||
# or to the last window that received an event (these hacks are for the
|
||||
# WE_LOST_SEL event, which is directed to no particular window).
|
||||
# Windows not in the windows list don't get their events:
|
||||
# events for such windows are silently ignored.
|
||||
#
|
||||
def dispatch(event):
|
||||
if event[1] in windows:
|
||||
event[1].dispatch(event)
|
||||
global last_window
|
||||
if event[1] == None:
|
||||
active = stdwin.getactive()
|
||||
if active: last_window = active
|
||||
else:
|
||||
last_window = event[1]
|
||||
if last_window in windows:
|
||||
last_window.dispatch(event)
|
||||
|
||||
|
||||
# Dialog base class
|
||||
#
|
||||
class Dialog:
|
||||
#
|
||||
def init(self, title):
|
||||
self.window = stdwin.open(title)
|
||||
self.window.dispatch = self.dispatch
|
||||
register(self.window)
|
||||
return self
|
||||
#
|
||||
def close(self):
|
||||
unregister(self.window)
|
||||
del self.window.dispatch
|
||||
self.window.close()
|
||||
#
|
||||
def dispatch(self, event):
|
||||
etype, ewindow, edetail = event
|
||||
if etype == WE_CLOSE:
|
||||
self.close()
|
||||
|
||||
|
||||
# Standard modal dialogs
|
||||
# XXX implemented using stdwin dialogs for now
|
||||
#
|
||||
def askstr(prompt, default):
|
||||
return stdwin.askstr(prompt, default)
|
||||
#
|
||||
def askync(prompt, yesorno):
|
||||
return stdwin.askync(prompt, yesorno)
|
||||
#
|
||||
def askfile(prompt, default, new):
|
||||
return stdwin.askfile(prompt, default, new)
|
||||
#
|
||||
def message(msg):
|
||||
stdwin.message(msg)
|
||||
|
|
|
@ -44,3 +44,18 @@ WC_RETURN = 9 # return or enter key
|
|||
WS_CLIPBOARD = 0
|
||||
WS_PRIMARY = 1
|
||||
WS_SECONDARY = 2
|
||||
|
||||
# Modifier masks in key and mouse events
|
||||
|
||||
WM_SHIFT = (1 << 0)
|
||||
WM_LOCK = (1 << 1)
|
||||
WM_CONTROL = (1 << 2)
|
||||
WM_META = (1 << 3)
|
||||
WM_OPTION = (1 << 4)
|
||||
WM_NUM = (1 << 5)
|
||||
|
||||
WM_BUTTON1 = (1 << 8)
|
||||
WM_BUTTON2 = (1 << 9)
|
||||
WM_BUTTON3 = (1 << 10)
|
||||
WM_BUTTON4 = (1 << 11)
|
||||
WM_BUTTON5 = (1 << 12)
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
# Example:
|
||||
#
|
||||
# >>> from nntp import NNTP
|
||||
# >>> from nntplib import NNTP
|
||||
# >>> s = NNTP().init('charon')
|
||||
# >>> resp, count, first, last, name = s.group('nlnet.misc')
|
||||
# >>> print 'Group', name, 'has', count, 'articles, range', first, 'to', last
|
||||
|
@ -32,12 +32,12 @@ import socket
|
|||
import string
|
||||
|
||||
|
||||
# Exception raiseds when an error or invalid response is received
|
||||
# Exception raised when an error or invalid response is received
|
||||
|
||||
error_reply = 'nntp.error_reply' # unexpected [123]xx reply
|
||||
error_function = 'nntp.error_function' # 4xx errors
|
||||
error_form = 'nntp.error_form' # 5xx errors
|
||||
error_protocol = 'nntp.error_protocol' # response does not begin with [1-5]
|
||||
error_reply = 'nntplib.error_reply' # unexpected [123]xx reply
|
||||
error_temp = 'nntplib.error_temp' # 4xx errors
|
||||
error_perm = 'nntplib.error_perm' # 5xx errors
|
||||
error_proto = 'nntplib.error_proto' # response does not begin with [1-5]
|
||||
|
||||
|
||||
# Standard port used by NNTP servers
|
||||
|
@ -119,11 +119,11 @@ class NNTP:
|
|||
if self.debugging: print '*resp*', `resp`
|
||||
c = resp[:1]
|
||||
if c == '4':
|
||||
raise error_function, resp
|
||||
raise error_temp, resp
|
||||
if c == '5':
|
||||
raise error_form, resp
|
||||
raise error_perm, resp
|
||||
if c not in '123':
|
||||
raise error_protocol, resp
|
||||
raise error_proto, resp
|
||||
return resp
|
||||
|
||||
# Internal: get a response plus following text from the server.
|
||||
|
@ -342,7 +342,7 @@ class NNTP:
|
|||
|
||||
def ihave(self, id, f):
|
||||
resp = self.shortcmd('IHAVE ' + id)
|
||||
# Raises error_function if the server already has it
|
||||
# Raises error_??? if the server already has it
|
||||
if resp[0] <> '3':
|
||||
raise error_reply, resp
|
||||
while 1:
|
||||
|
|
|
@ -13,6 +13,11 @@ from stdwinevents import *
|
|||
windows = []
|
||||
|
||||
|
||||
# Last window that ever received an event
|
||||
#
|
||||
last_window = None
|
||||
|
||||
|
||||
# Function to register a window.
|
||||
#
|
||||
def register(win):
|
||||
|
@ -28,6 +33,9 @@ def register(win):
|
|||
# (this is useful for cleanup actions).
|
||||
#
|
||||
def unregister(win):
|
||||
global last_window
|
||||
if win == last_window:
|
||||
last_window = None
|
||||
if win in windows:
|
||||
windows.remove(win) # Not in 0.9.1
|
||||
# 0.9.1 solution:
|
||||
|
@ -49,6 +57,65 @@ def anywindow():
|
|||
return None
|
||||
|
||||
|
||||
# NEW: register any number of file descriptors
|
||||
#
|
||||
fdlist = []
|
||||
select_args = None
|
||||
select_handlers = None
|
||||
#
|
||||
def registerfd(fd, mode, handler):
|
||||
if mode not in ('r', 'w', 'x'):
|
||||
raise ValueError, 'mode must be r, w or x'
|
||||
if type(fd) <> type(0):
|
||||
fd = fd.fileno() # If this fails it's not a proper select arg
|
||||
for i in range(len(fdlist)):
|
||||
if fdlist[i][:2] == (fd, mode):
|
||||
raise ValueError, \
|
||||
'(fd, mode) combination already registered'
|
||||
fdlist.append((fd, mode, handler))
|
||||
make_select_args()
|
||||
#
|
||||
def unregisterfd(fd, *args):
|
||||
if type(fd) <> type(0):
|
||||
fd = fd.fileno() # If this fails it's not a proper select arg
|
||||
args = (fd,) + args
|
||||
n = len(args)
|
||||
for i in range(len(fdlist)):
|
||||
if fdlist[i][:n] == args:
|
||||
del fdlist[i]
|
||||
make_select_args()
|
||||
#
|
||||
def make_select_args():
|
||||
global select_args, select_handlers
|
||||
rlist, wlist, xlist = [], [], []
|
||||
rhandlers, whandlers, xhandlers = {}, {}, {}
|
||||
for fd, mode, handler in fdlist:
|
||||
if mode == 'r':
|
||||
rlist.append(fd)
|
||||
rhandlers[`fd`] = handler
|
||||
if mode == 'w':
|
||||
wlist.append(fd)
|
||||
whandlers[`fd`] = handler
|
||||
if mode == 'x':
|
||||
xlist.append(fd)
|
||||
xhandlers[`fd`] = handler
|
||||
if rlist or wlist or xlist:
|
||||
select_args = rlist, wlist, xlist
|
||||
select_handlers = rhandlers, whandlers, xhandlers
|
||||
else:
|
||||
select_args = None
|
||||
select_handlers = None
|
||||
#
|
||||
def do_select():
|
||||
import select
|
||||
reply = apply(select.select, select_args)
|
||||
for mode in 0, 1, 2:
|
||||
list = reply[mode]
|
||||
for fd in list:
|
||||
handler = select_handlers[mode][`fd`]
|
||||
handler(fd, 'rwx'[mode])
|
||||
|
||||
|
||||
# Event processing main loop.
|
||||
# Return when there are no windows left, or when an unhandled
|
||||
# exception occurs. (It is safe to restart the main loop after
|
||||
|
@ -57,17 +124,111 @@ def anywindow():
|
|||
# into KeyboardInterrupt exceptions; these are turned back in events.
|
||||
#
|
||||
def mainloop():
|
||||
while windows:
|
||||
stdwin_select_handler() # Process events already in stdwin queue
|
||||
fd = stdwin.fileno()
|
||||
while 1:
|
||||
if windows:
|
||||
registerfd(fd, 'r', stdwin_select_handler)
|
||||
try:
|
||||
while windows:
|
||||
do_select()
|
||||
stdwin_select_handler()
|
||||
finally:
|
||||
unregisterfd(fd)
|
||||
elif fdlist:
|
||||
while fdlist and not windows:
|
||||
do_select()
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
# Handle stdwin events until none are left
|
||||
#
|
||||
def stdwin_select_handler(*args):
|
||||
while 1:
|
||||
try:
|
||||
dispatch(stdwinq.getevent())
|
||||
event = stdwinq.pollevent()
|
||||
except KeyboardInterrupt:
|
||||
dispatch(WE_COMMAND, stdwin.getactive(), WC_CANCEL)
|
||||
event = (WE_COMMAND, None, WC_CANCEL)
|
||||
if event is None:
|
||||
break
|
||||
dispatch(event)
|
||||
|
||||
|
||||
# Run a modal dialog loop for a window. The dialog window must have
|
||||
# been registered first. This prohibits most events (except size/draw
|
||||
# events) to other windows. The modal dialog loop ends when the
|
||||
# dialog window unregisters itself.
|
||||
#
|
||||
passthrough = WE_SIZE, WE_DRAW
|
||||
beeping = WE_MOUSE_DOWN, WE_COMMAND, WE_CHAR, WE_KEY, WE_CLOSE, WE_MENU
|
||||
#
|
||||
def modaldialog(window):
|
||||
if window not in windows:
|
||||
raise ValueError, 'modaldialog window not registered'
|
||||
while window in windows:
|
||||
try:
|
||||
event = stdwinq.getevent()
|
||||
except KeyboardInterrupt:
|
||||
event = WE_COMMAND, None, WC_CANCEL
|
||||
etype, ewindow, edetail = event
|
||||
if etype not in passthrough and ewindow <> window:
|
||||
if etype in beeping:
|
||||
stdwin.fleep()
|
||||
continue
|
||||
dispatch(event)
|
||||
|
||||
|
||||
# Dispatch a single event.
|
||||
# Events for the no window in particular are sent to the active window
|
||||
# or to the last window that received an event (these hacks are for the
|
||||
# WE_LOST_SEL event, which is directed to no particular window).
|
||||
# Windows not in the windows list don't get their events:
|
||||
# events for such windows are silently ignored.
|
||||
#
|
||||
def dispatch(event):
|
||||
if event[1] in windows:
|
||||
event[1].dispatch(event)
|
||||
global last_window
|
||||
if event[1] == None:
|
||||
active = stdwin.getactive()
|
||||
if active: last_window = active
|
||||
else:
|
||||
last_window = event[1]
|
||||
if last_window in windows:
|
||||
last_window.dispatch(event)
|
||||
|
||||
|
||||
# Dialog base class
|
||||
#
|
||||
class Dialog:
|
||||
#
|
||||
def init(self, title):
|
||||
self.window = stdwin.open(title)
|
||||
self.window.dispatch = self.dispatch
|
||||
register(self.window)
|
||||
return self
|
||||
#
|
||||
def close(self):
|
||||
unregister(self.window)
|
||||
del self.window.dispatch
|
||||
self.window.close()
|
||||
#
|
||||
def dispatch(self, event):
|
||||
etype, ewindow, edetail = event
|
||||
if etype == WE_CLOSE:
|
||||
self.close()
|
||||
|
||||
|
||||
# Standard modal dialogs
|
||||
# XXX implemented using stdwin dialogs for now
|
||||
#
|
||||
def askstr(prompt, default):
|
||||
return stdwin.askstr(prompt, default)
|
||||
#
|
||||
def askync(prompt, yesorno):
|
||||
return stdwin.askync(prompt, yesorno)
|
||||
#
|
||||
def askfile(prompt, default, new):
|
||||
return stdwin.askfile(prompt, default, new)
|
||||
#
|
||||
def message(msg):
|
||||
stdwin.message(msg)
|
||||
|
|
|
@ -44,3 +44,18 @@ WC_RETURN = 9 # return or enter key
|
|||
WS_CLIPBOARD = 0
|
||||
WS_PRIMARY = 1
|
||||
WS_SECONDARY = 2
|
||||
|
||||
# Modifier masks in key and mouse events
|
||||
|
||||
WM_SHIFT = (1 << 0)
|
||||
WM_LOCK = (1 << 1)
|
||||
WM_CONTROL = (1 << 2)
|
||||
WM_META = (1 << 3)
|
||||
WM_OPTION = (1 << 4)
|
||||
WM_NUM = (1 << 5)
|
||||
|
||||
WM_BUTTON1 = (1 << 8)
|
||||
WM_BUTTON2 = (1 << 9)
|
||||
WM_BUTTON3 = (1 << 10)
|
||||
WM_BUTTON4 = (1 << 11)
|
||||
WM_BUTTON5 = (1 << 12)
|
||||
|
|
|
@ -82,10 +82,7 @@ def splitfields(s, sep):
|
|||
|
||||
# Join words with spaces between them
|
||||
def join(words):
|
||||
res = ''
|
||||
for w in words:
|
||||
res = res + (' ' + w)
|
||||
return res[1:]
|
||||
return joinfields(words, ' ')
|
||||
|
||||
# Join fields with separator
|
||||
def joinfields(words, sep):
|
||||
|
|
|
@ -82,10 +82,7 @@ def splitfields(s, sep):
|
|||
|
||||
# Join words with spaces between them
|
||||
def join(words):
|
||||
res = ''
|
||||
for w in words:
|
||||
res = res + (' ' + w)
|
||||
return res[1:]
|
||||
return joinfields(words, ' ')
|
||||
|
||||
# Join fields with separator
|
||||
def joinfields(words, sep):
|
||||
|
|
Loading…
Reference in New Issue