- Timestamp:
- Dec 5, 2013 11:04:36 AM (9 years ago)
- Location:
- pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/account.py
r4677 r4686 52 52 self.deleting = False 53 53 54 def findBuddy(self, uri): 54 """ 55 def findBuddy2(self, uri): 55 56 # TODO: proper URI comparison 56 57 for bud in self.buddyList: … … 58 59 return bud 59 60 return None 60 61 def findChat(self, buddy, call_inst = None): 61 """ 62 63 def findChat(self, uri_str, call_inst = None): 64 uri = ch.ParseSipUri(uri_str) 65 if not uri: return None 66 62 67 for chat in self.chatList: 63 if chat.is BuddyParticipant(buddy):68 if chat.isUriParticipant(uri): 64 69 if call_inst and chat.isCallRegistered(call_inst): 65 70 return chat … … 68 73 return None 69 74 70 def newChat(self, buddy): 71 chat = ch.Chat(self.app, self, buddy) 75 def newChat(self, uri_str): 76 uri = ch.ParseSipUri(uri_str) 77 if not uri: return None 78 79 chat = ch.Chat(self.app, self, uri) 72 80 self.chatList.append(chat) 81 self.app.updateWindowMenu() 73 82 return chat 74 83 … … 113 122 c.answer(call_prm) 114 123 115 # create chat instance 116 bud = self.findBuddy(ci.remoteUri) 117 if not bud: 118 print "=== Incoming call from '%s': cannot find buddy" % ci.remoteUri 119 return 120 chat = self.findChat(bud) 121 if not chat: chat = self.newChat(bud) 124 # find/create chat instance 125 chat = self.findChat(ci.remoteUri) 126 if not chat: chat = self.newChat(ci.remoteUri) 122 127 123 128 chat.showWindow() 124 chat.registerCall( bud, c)129 chat.registerCall(ci.remoteUri, c) 125 130 chat.updateCallState(c, ci) 126 131 else: … … 128 133 129 134 def onInstantMessage(self, prm): 130 bud = self.findBuddy(prm.fromUri) 131 if not bud: 132 print "=== Incoming IM from '%s': cannot find buddy" % prm.fromUri 133 return 134 chat = self.findChat(bud) 135 if not chat: chat = self.newChat(bud) 136 135 chat = self.findChat(prm.fromUri) 136 if not chat: chat = self.newChat(prm.fromUri) 137 137 138 chat.showWindow() 138 chat.addMessage( bud.cfg.uri, prm.msgBody)139 chat.addMessage(prm.fromUri, prm.msgBody) 139 140 140 141 def onInstantMessageStatus(self, prm): 141 142 if prm.code/100 == 2: return 142 143 143 bud = self.findBuddy(prm.toUri)144 if not bud: return145 chat = self.findChat(bud)146 if not chat:return147 148 chat.addMessage(None, "Failed sending message to '%s': %s" % ( bud.cfg.uri, prm.reason))144 chat = self.findChat(prm.toUri) 145 if not chat: 146 print "=== IM status to '%s' cannot find chat" % prm.toUri 147 return 148 149 chat.addMessage(None, "Failed sending message to '%s': %s" % (prm.toUri, prm.reason)) 149 150 150 151 def onTypingIndication(self, prm): 151 bud = self.findBuddy(prm.fromUri) 152 if not bud: 153 print "=== Incoming typing indication from '%s': cannot find buddy" % prm.fromUri 154 return 155 chat = self.findChat(bud) 156 if not chat: return 157 158 chat.setTypingIndication(bud.cfg.uri, prm.isTyping) 152 chat = self.findChat(prm.fromUri) 153 if not chat: 154 print "=== Incoming typing indication from '%s' cannot find chat" % prm.fromUri 155 return 156 157 chat.setTypingIndication(prm.fromUri, prm.isTyping) 159 158 160 159 -
pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/application.py
r4679 r4686 209 209 file_menu.add_command(label="Quit", command=self._onMenuQuit) 210 210 211 # Window menu 212 self.window_menu = tk.Menu(self.menubar, tearoff=False) 213 self.menubar.add_cascade(label="Window", menu=self.window_menu) 214 211 215 # Help menu 212 216 help_menu = tk.Menu(self.menubar, tearoff=False) 213 217 self.menubar.add_cascade(label="Help", menu=help_menu) 214 218 help_menu.add_command(label="About", underline=2, command=self._onMenuAbout) 219 220 def _showChatWindow(self, chat_inst): 221 chat_inst.showWindow() 222 223 def updateWindowMenu(self): 224 # Chat windows 225 self.window_menu.delete(0, tk.END) 226 for acc in self.accList: 227 for c in acc.chatList: 228 cmd = lambda arg=c: self._showChatWindow(arg) 229 self.window_menu.add_command(label=c.title, command=cmd) 215 230 216 231 def _createContextMenu(self): … … 312 327 bud = self._getSelectedBuddy() 313 328 acc = bud.account 314 chat = acc.findChat(bud )329 chat = acc.findChat(bud.cfg.uri) 315 330 if not chat: 316 chat = acc.newChat(bud )331 chat = acc.newChat(bud.cfg.uri) 317 332 chat.showWindow() 318 333 … … 377 392 378 393 if label=='Audio call': 379 chat = acc.findChat(bud )380 if not chat: chat = acc.newChat(bud )394 chat = acc.findChat(bud.cfg.uri) 395 if not chat: chat = acc.newChat(bud.cfg.uri) 381 396 chat.showWindow() 382 397 chat.startCall() 383 398 elif label=='Send instant message': 384 chat = acc.findChat(bud )385 if not chat: chat = acc.newChat(bud )399 chat = acc.findChat(bud.cfg.uri) 400 if not chat: chat = acc.newChat(bud.cfg.uri) 386 401 chat.showWindow() 387 402 elif label=='Subscribe': … … 493 508 494 509 if __name__ == '__main__': 495 print pj496 510 main() -
pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/call.py
r4679 r4686 32 32 import pjsua2 as pj 33 33 import application 34 import endpoint as ep 34 35 35 36 # Call class … … 44 45 self.chat = chat 45 46 self.connected = False 47 self.onhold = False 46 48 47 49 def onCallState(self, prm): … … 58 60 mi.status == pj.PJSUA_CALL_MEDIA_REMOTE_HOLD): 59 61 m = self.getMedia(mi.index) 60 print m 62 am = pj.AudioMedia.typecastFromMedia(m) 63 # connect ports 64 ep.Endpoint.instance.audDevManager().getCaptureDevMedia().startTransmit(am) 65 am.startTransmit(ep.Endpoint.instance.audDevManager().getPlaybackDevMedia()) 66 67 if mi.status == pj.PJSUA_CALL_MEDIA_REMOTE_HOLD and not self.onhold: 68 self.chat.addMessage(None, "'%s' sets call onhold" % (self.peerUri)) 69 self.onhold = True 70 elif mi.status == pj.PJSUA_CALL_MEDIA_ACTIVE and self.onhold: 71 self.chat.addMessage(None, "'%s' sets call active" % (self.peerUri)) 72 self.onhold = False 61 73 62 74 def onInstantMessage(self, prm): -
pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/chat.py
r4677 r4686 27 27 import ttk 28 28 29 import buddy 29 30 import call 30 31 import chatgui as gui 32 import endpoint as ep 31 33 import pjsua2 as pj 34 import re 35 36 SipUriRegex = re.compile('(sip|sips):([^:;>\@]*)@?([^:;>]*):?([^:;>]*)') 37 ConfIdx = 1 38 39 # Simple SIP uri parser, input URI must have been validated 40 def ParseSipUri(sip_uri_str): 41 m = SipUriRegex.search(sip_uri_str) 42 if not m: 43 assert(0) 44 return None 45 46 scheme = m.group(1) 47 user = m.group(2) 48 host = m.group(3) 49 port = m.group(4) 50 if host == '': 51 host = user 52 user = '' 53 54 return SipUri(scheme.lower(), user, host.lower(), port) 55 56 class SipUri: 57 def __init__(self, scheme, user, host, port): 58 self.scheme = scheme 59 self.user = user 60 self.host = host 61 self.port = port 62 63 def __cmp__(self, sip_uri): 64 if self.scheme == sip_uri.scheme and self.user == sip_uri.user and self.host == sip_uri.host: 65 # don't check port, at least for now 66 return 0 67 return -1 68 69 def __str__(self): 70 s = self.scheme + ':' 71 if self.user: s += self.user + '@' 72 s += self.host 73 if self.port: s+= ':' + self.port 74 return s 32 75 33 76 class Chat(gui.ChatObserver): 34 def __init__(self, app, acc, bud, call_inst=None):77 def __init__(self, app, acc, uri, call_inst=None): 35 78 self._app = app 36 79 self._acc = acc 37 self._participantList = [] 38 self._callList = [] 80 self.title = '' 81 82 global ConfIdx 83 self.confIdx = ConfIdx 84 ConfIdx += 1 85 86 # each participant call/buddy instances are stored in call list 87 # and buddy list with same index as in particpant list 88 self._participantList = [] # list of SipUri 89 self._callList = [] # list of Call 90 self._buddyList = [] # list of Buddy 91 39 92 self._gui = gui.ChatFrame(self) 40 self.addParticipant( bud, call_inst)93 self.addParticipant(uri, call_inst) 41 94 42 95 def _updateGui(self): 43 96 if self.isPrivate(): 44 bud = self._participantList[0]45 self._gui.title(bud.cfg.uri)46 else:47 self._gui.title('Conference (%d participants)' % (len(self._participantList)))48 49 def _getCallFromBuddy(self, bud):50 try:51 idx = self._participantList.index(bud)52 the_call = self._callList[idx]53 except:97 self.title = str(self._participantList[0]) 98 else: 99 self.title = 'Conference #%d (%d participants)' % (self.confIdx, len(self._participantList)) 100 self._gui.title(self.title) 101 self._app.updateWindowMenu() 102 103 def _getCallFromUriStr(self, uri_str, op = ''): 104 uri = ParseSipUri(uri_str) 105 if uri not in self._participantList: 106 print "=== %s cannot find participant with URI '%s'" % (op, uri_str) 54 107 return None 55 return the_call 56 57 def _getBuddyFromUri(self, uri): 58 for bud in self._participantList: 59 if uri == bud.cfg.uri: 60 return bud 108 idx = self._participantList.index(uri) 109 if idx < len(self._callList): 110 return self._callList[idx] 61 111 return None 62 63 def _getCallFromUri(self, uri, op = ''): 64 for idx, bud in enumerate(self._participantList): 65 if uri == bud.cfg.uri: 66 if idx < len(self._callList): 67 return self._callList[idx] 68 return None 69 print "=== %s cannot find buddy URI '%s'" % (op, uri) 70 return None 71 72 def _sendTypingIndication(self, is_typing): 112 113 def _getActiveMediaIdx(self, thecall): 114 ci = thecall.getInfo() 115 for mi in ci.media: 116 if mi.type == pj.PJMEDIA_TYPE_AUDIO and \ 117 (mi.status != pj.PJSUA_CALL_MEDIA_NONE and \ 118 mi.status != pj.PJSUA_CALL_MEDIA_ERROR): 119 return mi.index 120 return -1 121 122 def _getAudioMediaFromUriStr(self, uri_str): 123 c = self._getCallFromUriStr(uri_str) 124 if not c: return None 125 126 idx = self._getActiveMediaIdx(c) 127 if idx < 0: return None 128 129 m = c.getMedia(idx) 130 am = pj.AudioMedia.typecastFromMedia(m) 131 return am 132 133 def _sendTypingIndication(self, is_typing, sender_uri_str=''): 134 sender_uri = ParseSipUri(sender_uri_str) if sender_uri_str else None 73 135 type_ind_param = pj.SendTypingIndicationParam() 74 136 type_ind_param.isTyping = is_typing 75 for bud in self._participantList: 76 c = self._getCallFromBuddy(bud) 77 try: 78 if c and c.connected: 79 c.sendTypingIndication(type_ind_param) 80 else: 81 bud.sendTypingIndication(type_ind_param) 82 except: 83 pass 84 85 def _sendInstantMessage(self, msg, sender_uri=''): 86 send_im_param = pj.SendInstantMessageParam() 87 send_im_param.content = str(msg) 88 for bud in self._participantList: 137 for idx, p in enumerate(self._participantList): 89 138 # don't echo back to the original sender 90 if sender_uri and bud.cfg.uri== sender_uri:139 if sender_uri and p == sender_uri: 91 140 continue 92 141 93 142 # send via call, if any, or buddy 94 c = self._getCallFromBuddy(bud) 143 sender = None 144 if self._callList[idx] and self._callList[idx].connected: 145 sender = self._callList[idx] 146 else: 147 sender = self._buddyList[idx] 148 assert(sender) 149 95 150 try: 96 if c and c.connected: 97 c.sendInstantMessage(send_im_param) 98 else: 99 bud.sendInstantMessage(send_im_param) 151 sender.sendTypingIndication(type_ind_param) 152 except: 153 pass 154 155 def _sendInstantMessage(self, msg, sender_uri_str=''): 156 sender_uri = ParseSipUri(sender_uri_str) if sender_uri_str else None 157 send_im_param = pj.SendInstantMessageParam() 158 send_im_param.content = str(msg) 159 for idx, p in enumerate(self._participantList): 160 # don't echo back to the original sender 161 if sender_uri and p == sender_uri: 162 continue 163 164 # send via call, if any, or buddy 165 sender = None 166 if self._callList[idx] and self._callList[idx].connected: 167 sender = self._callList[idx] 168 else: 169 sender = self._buddyList[idx] 170 assert(sender) 171 172 try: 173 sender.sendInstantMessage(send_im_param) 100 174 except: 101 175 # error will be handled via Account::onInstantMessageStatus() … … 105 179 return len(self._participantList) <= 1 106 180 107 def is BuddyParticipant(self, bud):108 return budin self._participantList181 def isUriParticipant(self, uri): 182 return uri in self._participantList 109 183 110 184 def isCallRegistered(self, call_inst): 111 185 return call_inst in self._callList 112 186 113 def registerCall(self, bud, call_inst): 187 def registerCall(self, uri_str, call_inst): 188 uri = ParseSipUri(uri_str) 114 189 try: 115 idx = self._participantList.index(bud) 116 if len(self._callList) < idx+1: 117 self._callList.append(call_inst) 118 else: 119 self._callList[idx] = call_inst 120 190 idx = self._participantList.index(uri) 191 bud = self._buddyList[idx] 192 self._callList[idx] = call_inst 121 193 call_inst.chat = self 122 194 call_inst.peerUri = bud.cfg.uri 123 195 except: 124 pass196 assert(0) # idx must be found! 125 197 126 198 def showWindow(self): … … 133 205 print b.cfg.uri 134 206 135 def addParticipant(self, bud, call_inst=None):207 def addParticipant(self, uri, call_inst=None): 136 208 # avoid duplication 137 if self.isBuddyParticipant(bud): return 138 for b in self._participantList: 139 if bud.cfg.uri == b.cfg.uri: return 140 209 if self.isUriParticipant(uri): return 210 211 uri_str = str(uri) 212 213 # find buddy, create one if not found (e.g: for IM/typing ind), 214 # it is a temporary one and not really registered to acc 215 bud = None 216 try: 217 bud = self._acc.findBuddy(uri_str) 218 except: 219 bud = buddy.Buddy(None) 220 bud_cfg = pj.BuddyConfig() 221 bud_cfg.uri = uri_str 222 bud_cfg.subscribe = False 223 bud.create(self._acc, bud_cfg) 224 bud.cfg = bud_cfg 225 bud.account = self._acc 226 227 # update URI from buddy URI 228 uri = ParseSipUri(bud.cfg.uri) 229 141 230 # add it 142 self._participantList.append(bud) 143 if call_inst: 144 self._callList.append(call_inst) 145 self._gui.addParticipant(bud.cfg.uri) 146 231 self._participantList.append(uri) 232 self._callList.append(call_inst) 233 self._buddyList.append(bud) 234 self._gui.addParticipant(str(uri)) 147 235 self._updateGui() 148 236 149 def kickParticipant(self, bud): 150 if bud in self._participantList: 151 idx = self._participantList.index(bud) 152 self._participantList.remove(bud) 153 self._gui.delParticipant(bud.cfg.uri) 154 155 # also clear call, if any 156 if self._callList: del self._callList[idx] 157 237 def kickParticipant(self, uri): 238 if (not uri) or (uri not in self._participantList): 239 assert(0) 240 return 241 242 idx = self._participantList.index(uri) 243 del self._participantList[idx] 244 del self._callList[idx] 245 del self._buddyList[idx] 246 self._gui.delParticipant(str(uri)) 247 158 248 if self._participantList: 159 249 self._updateGui() 160 250 else: 161 # will remove entry from list eventually destroy this chat? 162 self._acc.chatList.remove(self) 163 164 # let's destroy GUI manually 165 self._gui.destroy() 166 #self.destroy() 167 168 def addMessage(self, from_uri, msg): 169 if from_uri: 170 msg = from_uri + ': ' + msg 251 self.onCloseWindow() 252 253 def addMessage(self, from_uri_str, msg): 254 if from_uri_str: 255 # print message on GUI 256 msg = from_uri_str + ': ' + msg 171 257 self._gui.textAddMessage(msg) 172 self._sendInstantMessage(msg, from_uri) 258 # now relay to all participants 259 self._sendInstantMessage(msg, from_uri_str) 173 260 else: 174 261 self._gui.textAddMessage(msg, False) 175 262 176 def setTypingIndication(self, from_uri, is_typing): 177 self._gui.textSetTypingIndication(from_uri, is_typing) 263 def setTypingIndication(self, from_uri_str, is_typing): 264 # notify GUI 265 self._gui.textSetTypingIndication(from_uri_str, is_typing) 266 # now relay to all participants 267 self._sendTypingIndication(is_typing, from_uri_str) 178 268 179 269 def startCall(self): … … 182 272 call_param.opt.audioCount = 1 183 273 call_param.opt.videoCount = 0 184 for idx, budin enumerate(self._participantList):274 for idx, p in enumerate(self._participantList): 185 275 # just skip if call is instantiated 186 276 if len(self._callList)>=idx+1 and self._callList[idx]: 187 277 continue 278 279 uri_str = str(p) 280 c = call.Call(self._acc, uri_str, self) 281 self._callList[idx] = c 282 self._gui.audioUpdateState(uri_str, gui.AudioState.INITIALIZING) 283 284 try: 285 c.makeCall(uri_str, call_param) 286 except: 287 self._callList[idx] = None 288 self._gui.audioUpdateState(bud.cfg.uri, gui.AudioState.FAILED) 188 289 189 c = call.Call(self._acc, bud.cfg.uri, self) 190 if len(self._callList) < idx+1: 191 self._callList.append(c) 192 else: 193 self._callList[idx] = c 194 195 self._gui.audioUpdateState(bud.cfg.uri, gui.AudioState.INITIALIZING) 196 197 try: 198 c.makeCall(bud.cfg.uri, call_param) 199 except: 200 #self._callList[idx] = None 201 #self._gui.audioUpdateState(bud.cfg.uri, gui.AudioState.FAILED) 202 self.kickParticipant(bud) 290 # kick the disconnected participant, but the last (avoid zombie chat) 291 if not self.isPrivate(): 292 self.kickParticipant(p) 203 293 204 294 def stopCall(self): 205 for bud in self._participantList: 206 self._gui.audioUpdateState(bud.cfg.uri, gui.AudioState.DISCONNECTED) 295 for p in self._participantList: 296 self._gui.audioUpdateState(str(p), gui.AudioState.DISCONNECTED) 297 if True or not self.isPrivate(): 298 self.kickParticipant(p) 207 299 208 300 # clear call list, calls should be auto-destroyed by GC (and hungup by destructor) 209 del self._callList[:] 301 #for idx, c in enumerate(self._callList): 302 # self._callList[idx] = None 210 303 211 304 def updateCallState(self, thecall, info = None): 212 305 # info is optional here, just to avoid calling getInfo() twice (in the caller and here) 213 306 if not info: info = thecall.getInfo() 307 214 308 if info.state < pj.PJSIP_INV_STATE_CONFIRMED: 215 309 self._gui.audioUpdateState(thecall.peerUri, gui.AudioState.INITIALIZING) 216 310 elif info.state == pj.PJSIP_INV_STATE_CONFIRMED: 217 311 self._gui.audioUpdateState(thecall.peerUri, gui.AudioState.CONNECTED) 312 med_idx = self._getActiveMediaIdx(thecall) 313 si = thecall.getStreamInfo(med_idx) 314 stats_str = "Audio codec: %s/%s\n..." % (si.codecName, si.codecClockRate) 315 self._gui.audioSetStatsText(thecall.peerUri, stats_str) 218 316 elif info.state == pj.PJSIP_INV_STATE_DISCONNECTED: 219 317 if info.lastStatusCode/100 != 2: … … 233 331 # kick the disconnected participant, but the last (avoid zombie chat) 234 332 if not self.isPrivate(): 235 bud = self._getBuddyFromUri(thecall.peerUri) 236 if bud: self.kickParticipant(bud) 333 self.kickParticipant(ParseSipUri(thecall.peerUri)) 237 334 238 335 … … 250 347 251 348 # Audio 252 def onHangup(self, peer_uri ):253 c = self._getCallFromUri (peer_uri, "onHangup()")349 def onHangup(self, peer_uri_str): 350 c = self._getCallFromUriStr(peer_uri_str, "onHangup()") 254 351 if not c: return 255 352 call_param = pj.CallOpParam() 256 353 c.hangup(call_param) 257 354 258 def onHold(self, peer_uri ):259 c = self._getCallFromUri (peer_uri, "onHold()")355 def onHold(self, peer_uri_str): 356 c = self._getCallFromUriStr(peer_uri_str, "onHold()") 260 357 if not c: return 261 358 call_param = pj.CallOpParam() 262 359 c.setHold(call_param) 263 360 264 def onUnhold(self, peer_uri ):265 c = self._getCallFromUri (peer_uri, "onUnhold()")361 def onUnhold(self, peer_uri_str): 362 c = self._getCallFromUriStr(peer_uri_str, "onUnhold()") 266 363 if not c: return 364 267 365 call_param = pj.CallOpParam() 366 call_param.opt.audioCount = 1 367 call_param.opt.videoCount = 0 368 call_param.opt.flag |= pj.PJSUA_CALL_UNHOLD 268 369 c.reinvite(call_param) 269 370 270 def onRxMute(self, peer_uri, is_muted): 271 pass 272 def onRxVol(self, peer_uri, vol_pct): 273 pass 274 def onTxMute(self, peer_uri, is_muted): 275 pass 371 def onRxMute(self, peer_uri_str, mute): 372 am = self._getAudioMediaFromUriStr(peer_uri_str) 373 if not am: return 374 if mute: 375 am.stopTransmit(ep.Endpoint.instance.audDevManager().getPlaybackDevMedia()) 376 self.addMessage(None, "Muted audio from '%s'" % (peer_uri_str)) 377 else: 378 am.startTransmit(ep.Endpoint.instance.audDevManager().getPlaybackDevMedia()) 379 self.addMessage(None, "Unmuted audio from '%s'" % (peer_uri_str)) 380 381 def onRxVol(self, peer_uri_str, vol_pct): 382 am = self._getAudioMediaFromUriStr(peer_uri_str) 383 if not am: return 384 # pjsua volume range = 0:mute, 1:no adjustment, 2:100% louder 385 am.adjustRxLevel(vol_pct/50.0) 386 self.addMessage(None, "Adjusted volume level audio from '%s'" % (peer_uri_str)) 387 388 def onTxMute(self, peer_uri_str, mute): 389 am = self._getAudioMediaFromUriStr(peer_uri_str) 390 if not am: return 391 if mute: 392 ep.Endpoint.instance.audDevManager().getCaptureDevMedia().stopTransmit(am) 393 self.addMessage(None, "Muted audio to '%s'" % (peer_uri_str)) 394 else: 395 ep.Endpoint.instance.audDevManager().getCaptureDevMedia().startTransmit(am) 396 self.addMessage(None, "Unmuted audio to '%s'" % (peer_uri_str)) 276 397 277 398 # Chat room … … 281 402 if dlg.doModal(): 282 403 for bud in buds: 283 self.addParticipant(bud) 284 self.startCall() 404 uri = ParseSipUri(bud.cfg.uri) 405 self.addParticipant(uri) 406 if not self.isPrivate(): 407 self.startCall() 285 408 286 409 def onStartAudio(self): … … 289 412 def onStopAudio(self): 290 413 self.stopCall() 414 415 def onCloseWindow(self): 416 self.stopCall() 417 # will remove entry from list eventually destroy this chat? 418 if self in self._acc.chatList: self._acc.chatList.remove(self) 419 self._app.updateWindowMenu() 420 # destroy GUI 421 self._gui.destroy() 291 422 292 423 -
pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/chatgui.py
r4677 r4686 164 164 self._btnHold['text'] = 'Hold' 165 165 self._btnHold.config(state=tk.NORMAL) 166 self._rxMute = False 167 self._txMute = False 168 self.btnRxMute['text'] = 'Mute' 169 self.btnTxMute['text'] = 'Mute' 170 self.rxVol.set(5.0) 166 171 167 172 # save last state 168 173 self._state = state 174 175 def setStatsText(self, stats_str): 176 self.stat.config(state=tk.NORMAL) 177 self.stat.delete("0.0", tk.END) 178 self.stat.insert(tk.END, stats_str) 179 self.stat.config(state=tk.DISABLED) 169 180 170 181 def _onHold(self): … … 187 198 self._rxMute = not self._rxMute 188 199 self._observer.onRxMute(self.peerUri, self._rxMute) 200 self.btnRxMute['text'] = 'Unmute' if self._rxMute else 'Mute' 189 201 190 202 def _onRxVol(self, event): 191 203 # notify app 192 204 vol = self.rxVol.get() 193 self._observer.onRxVol(self.peerUri, vol )205 self._observer.onRxVol(self.peerUri, vol*10.0) 194 206 195 207 def _onTxMute(self): … … 197 209 self._txMute = not self._txMute 198 210 self._observer.onTxMute(self.peerUri, self._txMute) 211 self.btnTxMute['text'] = 'Unmute' if self._txMute else 'Mute' 199 212 200 213 def _createInitWidgets(self): … … 231 244 self.rxVolFrm.pack(side=tk.LEFT, fill=tk.Y) 232 245 233 self.btnRxMute = ttk.Button(self.rxVolFrm, width= 5, text='Mute', command=self._onRxMute)246 self.btnRxMute = ttk.Button(self.rxVolFrm, width=8, text='Mute', command=self._onRxMute) 234 247 self.btnRxMute.pack(side=tk.LEFT) 235 248 self.rxVol = tk.Scale(self.rxVolFrm, orient=tk.HORIZONTAL, from_=0.0, to=10.0, showvalue=0) #, tickinterval=10.0, showvalue=1) … … 241 254 self.txVolFrm.pack(side=tk.RIGHT, fill=tk.Y) 242 255 243 self.btnTxMute = ttk.Button(self.txVolFrm, width= 5, text='Mute', command=self._onTxMute)256 self.btnTxMute = ttk.Button(self.txVolFrm, width=8, text='Mute', command=self._onTxMute) 244 257 self.btnTxMute.pack(side=tk.LEFT) 245 258 246 259 # stat 247 self.stat = tk.Text(self._callFrame, width= 20, height=5, font=("Arial", "10"))260 self.stat = tk.Text(self._callFrame, width=10, height=2, bg='lightgray', relief=tk.FLAT, font=("Arial", "9")) 248 261 self.stat.insert(tk.END, 'stat here') 249 262 self.stat.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=1) … … 256 269 pass 257 270 def onStopAudio(self): 271 pass 272 def onCloseWindow(self): 258 273 pass 259 274 … … 264 279 def __init__(self, observer): 265 280 tk.Toplevel.__init__(self) 266 self.protocol("WM_DELETE_WINDOW", lambda: self.withdraw())281 self.protocol("WM_DELETE_WINDOW", self._onClose) 267 282 self._observer = observer 268 283 … … 331 346 self._observer.onStopAudio() 332 347 self.enableAudio(self._audioEnabled) 348 349 def _onClose(self): 350 self._observer.onCloseWindow() 333 351 334 352 # APIs … … 381 399 else: 382 400 self.enableAudio(True) 383 401 402 def audioSetStatsText(self, participant_uri, stats_str): 403 for aud_frm in self._audioFrames: 404 if participant_uri == aud_frm.peerUri: 405 aud_frm.setStatsText(stats_str) 406 break 407 384 408 if __name__ == '__main__': 385 409 root = tk.Tk()
Note: See TracChangeset
for help on using the changeset viewer.