Changeset 4677


Ignore:
Timestamp:
Dec 4, 2013 12:19:48 PM (7 years ago)
Author:
nanang
Message:

Re #1708: Implement adding participants, also various misc updates/fixes.

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

    r4675 r4677  
    6969         
    7070        def newChat(self, buddy): 
    71                 chat = ch.Chat(self, buddy) 
     71                chat = ch.Chat(self.app, self, buddy) 
    7272                self.chatList.append(chat) 
    7373                return chat 
     
    109109                ci = c.getInfo() 
    110110                msg = "Incoming call for account '%s'" % self.cfg.idUri 
    111                 if msgbox.askquestion(msg, "Accept call from '%s'?" % (ci.remoteURI), default=msgbox.YES) == u'yes': 
     111                if msgbox.askquestion(msg, "Accept call from '%s'?" % (ci.remoteUri), default=msgbox.YES) == u'yes': 
    112112                        call_prm.statusCode = 200 
    113113                        c.answer(call_prm) 
    114114                         
    115115                        # create chat instance 
    116                         bud = self.findBuddy(ci.remoteURI) 
    117                         if not bud: return 
     116                        bud = self.findBuddy(ci.remoteUri) 
     117                        if not bud: 
     118                                print "=== Incoming call from '%s': cannot find buddy" % ci.remoteUri 
     119                                return 
    118120                        chat = self.findChat(bud) 
    119                         if not chat: chat = self.newChat(bud, c) 
    120                          
     121                        if not chat: chat = self.newChat(bud) 
     122                         
     123                        chat.showWindow() 
    121124                        chat.registerCall(bud, c) 
    122                         chat.showWindow() 
     125                        chat.updateCallState(c, ci) 
    123126                else: 
    124127                        c.hangup(call_prm) 
     
    126129        def onInstantMessage(self, prm): 
    127130                bud = self.findBuddy(prm.fromUri) 
    128                 if not bud: return 
     131                if not bud: 
     132                        print "=== Incoming IM from '%s': cannot find buddy" % prm.fromUri 
     133                        return 
    129134                chat = self.findChat(bud) 
    130135                if not chat: chat = self.newChat(bud) 
    131136                         
     137                chat.showWindow() 
    132138                chat.addMessage(bud.cfg.uri, prm.msgBody) 
    133                 chat.showWindow() 
    134139                 
    135140        def onInstantMessageStatus(self, prm): 
     
    145150        def onTypingIndication(self, prm): 
    146151                bud = self.findBuddy(prm.fromUri) 
    147                 if not bud: return 
     152                if not bud: 
     153                        print "=== Incoming typing indication from '%s': cannot find buddy" % prm.fromUri 
     154                        return 
    148155                chat = self.findChat(bud) 
    149156                if not chat: return 
  • pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/application.py

    r4675 r4677  
    5858                 
    5959                # GUI variables 
    60                 self.showLogWindow = tk.IntVar(value=1) 
     60                self.showLogWindow = tk.IntVar(value=0) 
    6161                self.quitting = False  
    6262                 
     
    378378                        if not chat: chat = acc.newChat(bud) 
    379379                        chat.showWindow() 
    380                         chat.startAudio() 
     380                        chat.startCall() 
    381381                elif label=='Send instant message': 
    382382                        chat = acc.findChat(bud) 
  • pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/call.py

    r4671 r4677  
    5151                        self.chat.updateCallState(self, ci) 
    5252                         
     53        def onCallMediaState(self, prm): 
     54                ci = self.getInfo() 
     55                for mi in ci.media: 
     56                        if mi.type == pj.PJMEDIA_TYPE_AUDIO and \ 
     57                          (mi.status == pj.PJSUA_CALL_MEDIA_ACTIVE or \ 
     58                           mi.status == pj.PJSUA_CALL_MEDIA_REMOTE_HOLD): 
     59                                m = self.getMedia(mi.index) 
     60                                print m 
     61                         
    5362        def onInstantMessage(self, prm): 
    5463                # chat instance should have been initalized 
  • pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/chat.py

    r4671 r4677  
    3232         
    3333class Chat(gui.ChatObserver): 
    34         def __init__(self, acc, bud, call_inst=None): 
     34        def __init__(self, app, acc, bud, call_inst=None): 
     35                self._app = app 
    3536                self._acc = acc 
    3637                self._participantList = [] 
     
    5455                return the_call 
    5556                 
     57        def _getBuddyFromUri(self, uri): 
     58                for bud in self._participantList: 
     59                        if uri == bud.cfg.uri: 
     60                                return bud 
     61                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                 
    5672        def _sendTypingIndication(self, is_typing): 
    5773                type_ind_param = pj.SendTypingIndicationParam() 
     
    6076                        c = self._getCallFromBuddy(bud) 
    6177                        try: 
    62                                 if c: 
     78                                if c and c.connected: 
    6379                                        c.sendTypingIndication(type_ind_param) 
    6480                                else: 
     
    6783                                pass 
    6884 
     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: 
     89                        # don't echo back to the original sender 
     90                        if sender_uri and bud.cfg.uri == sender_uri: 
     91                                continue 
     92                                 
     93                        # send via call, if any, or buddy 
     94                        c = self._getCallFromBuddy(bud) 
     95                        try: 
     96                                if c and c.connected: 
     97                                        c.sendInstantMessage(send_im_param) 
     98                                else: 
     99                                        bud.sendInstantMessage(send_im_param) 
     100                        except: 
     101                                # error will be handled via Account::onInstantMessageStatus() 
     102                                pass 
     103 
    69104        def isPrivate(self): 
    70105                return len(self._participantList) <= 1 
    71106                 
    72         def isBuddyParticipant(self, bud, call_inst): 
     107        def isBuddyParticipant(self, bud): 
    73108                return bud in self._participantList 
    74109                 
     
    79114                try: 
    80115                        idx = self._participantList.index(bud) 
    81                         print "registering call...", idx, self._participantList 
    82116                        if len(self._callList) < idx+1: 
    83117                                self._callList.append(call_inst) 
    84118                        else: 
    85119                                self._callList[idx] = call_inst 
     120 
     121                        call_inst.chat = self 
     122                        call_inst.peerUri = bud.cfg.uri 
    86123                except: 
    87                         return None 
     124                        pass 
    88125                 
    89126        def showWindow(self): 
    90127                self._gui.bringToFront() 
    91128 
     129        # helper 
     130        def dumpParticipantList(self): 
     131                print "Number of participants: %d" % (len(self._participantList)) 
     132                for b in self._participantList: 
     133                        print b.cfg.uri 
     134                 
    92135        def addParticipant(self, bud, call_inst=None): 
     136                # avoid duplication 
     137                if self.isBuddyParticipant(bud): return 
     138                for b in self._participantList: 
     139                        if bud.cfg.uri == b.cfg.uri: return 
     140                         
     141                # add it 
    93142                self._participantList.append(bud) 
    94143                if call_inst: 
     
    97146 
    98147                self._updateGui() 
    99                 if not self.isPrivate(): 
    100                         self.startCall() 
    101                         self._gui.enableAudio() 
    102          
    103         def kickParticipant(bud): 
     148         
     149        def kickParticipant(self, bud): 
    104150                if bud in self._participantList: 
    105151                        idx = self._participantList.index(bud) 
     
    113159                        self._updateGui() 
    114160                else: 
    115                         # will this eventually destroy itself? 
     161                        # will remove entry from list eventually destroy this chat? 
    116162                        self._acc.chatList.remove(self) 
     163                         
     164                        # let's destroy GUI manually 
     165                        self._gui.destroy() 
     166                        #self.destroy() 
    117167                         
    118168        def addMessage(self, from_uri, msg): 
     
    120170                        msg = from_uri + ': ' + msg 
    121171                        self._gui.textAddMessage(msg) 
     172                        self._sendInstantMessage(msg, from_uri) 
    122173                else: 
    123174                        self._gui.textAddMessage(msg, False) 
     
    127178                 
    128179        def startCall(self): 
     180                self._gui.enableAudio() 
    129181                call_param = pj.CallOpParam() 
    130182                call_param.opt.audioCount = 1 
     
    146198                                c.makeCall(bud.cfg.uri, call_param) 
    147199                        except: 
    148                                 self._gui.audioUpdateState(bud.cfg.uri, gui.AudioState.FAILED) 
     200                                #self._callList[idx] = None 
     201                                #self._gui.audioUpdateState(bud.cfg.uri, gui.AudioState.FAILED) 
     202                                self.kickParticipant(bud) 
    149203                         
    150204        def stopCall(self): 
     
    155209                del self._callList[:] 
    156210                 
    157         def updateCallState(self, thecall, info): 
    158                 if info.state == pj.PJSIP_INV_STATE_CONFIRMED: 
     211        def updateCallState(self, thecall, info = None): 
     212                # info is optional here, just to avoid calling getInfo() twice (in the caller and here) 
     213                if not info: info = thecall.getInfo() 
     214                if info.state < pj.PJSIP_INV_STATE_CONFIRMED: 
     215                        self._gui.audioUpdateState(thecall.peerUri, gui.AudioState.INITIALIZING) 
     216                elif info.state == pj.PJSIP_INV_STATE_CONFIRMED: 
    159217                        self._gui.audioUpdateState(thecall.peerUri, gui.AudioState.CONNECTED) 
    160218                elif info.state == pj.PJSIP_INV_STATE_DISCONNECTED: 
    161                         self._gui.audioUpdateState(thecall.peerUri, gui.AudioState.DISCONNECTED) 
     219                        if info.lastStatusCode/100 != 2: 
     220                                self._gui.audioUpdateState(thecall.peerUri, gui.AudioState.FAILED) 
     221                        else: 
     222                                self._gui.audioUpdateState(thecall.peerUri, gui.AudioState.DISCONNECTED) 
     223                         
    162224                        # reset entry in the callList 
    163225                        try: 
     
    166228                        except: 
    167229                                pass 
     230                         
     231                        self.addMessage(None, "Call to '%s' disconnected: %s" % (thecall.peerUri, info.lastReason)) 
     232                         
     233                        # kick the disconnected participant, but the last (avoid zombie chat) 
     234                        if not self.isPrivate(): 
     235                                bud = self._getBuddyFromUri(thecall.peerUri) 
     236                                if bud: self.kickParticipant(bud) 
    168237 
    169238                         
     
    172241        # Text 
    173242        def onSendMessage(self, msg): 
    174                 send_im_param = pj.SendInstantMessageParam() 
    175                 send_im_param.content = str(msg) 
    176                 for bud in self._participantList: 
    177                         # send via call, if any, or buddy 
    178                         c = self._getCallFromBuddy(bud) 
    179                         try: 
    180                                 if c: 
    181                                         c.sendInstantMessage(send_im_param) 
    182                                 else: 
    183                                         bud.sendInstantMessage(send_im_param) 
    184                         except: 
    185                                 # error will be handled via Account::onInstantMessageStatus() 
    186                                 pass 
     243                self._sendInstantMessage(msg) 
    187244 
    188245        def onStartTyping(self): 
     
    193250                 
    194251        # Audio 
    195         def onRetry(self, peer_uri): 
    196                 pass 
    197         def onKick(self, peer_uri): 
    198                 pass 
     252        def onHangup(self, peer_uri): 
     253                c = self._getCallFromUri(peer_uri, "onHangup()") 
     254                if not c: return 
     255                call_param = pj.CallOpParam() 
     256                c.hangup(call_param) 
     257 
    199258        def onHold(self, peer_uri): 
    200                 pass 
     259                c = self._getCallFromUri(peer_uri, "onHold()") 
     260                if not c: return 
     261                call_param = pj.CallOpParam() 
     262                c.setHold(call_param) 
     263 
     264        def onUnhold(self, peer_uri): 
     265                c = self._getCallFromUri(peer_uri, "onUnhold()") 
     266                if not c: return 
     267                call_param = pj.CallOpParam() 
     268                c.reinvite(call_param) 
     269                 
    201270        def onRxMute(self, peer_uri, is_muted): 
    202271                pass 
     
    208277        # Chat room 
    209278        def onAddParticipant(self): 
    210                 #self.addParticipant() 
    211                 pass 
     279                buds = [] 
     280                dlg = AddParticipantDlg(None, self._app, buds) 
     281                if dlg.doModal(): 
     282                        for bud in buds: 
     283                                self.addParticipant(bud) 
     284                        self.startCall() 
     285                                 
    212286        def onStartAudio(self): 
    213287                self.startCall() 
     
    215289        def onStopAudio(self): 
    216290                self.stopCall() 
     291 
     292 
     293class AddParticipantDlg(tk.Toplevel): 
     294        """ 
     295        List of buddies 
     296        """ 
     297        def __init__(self, parent, app, bud_list): 
     298                tk.Toplevel.__init__(self, parent) 
     299                self.title('Add participants..') 
     300                self.transient(parent) 
     301                self.parent = parent 
     302                self._app = app 
     303                self.buddyList = bud_list 
     304                 
     305                self.isOk = False 
     306                 
     307                self.createWidgets() 
     308         
     309        def doModal(self): 
     310                if self.parent: 
     311                        self.parent.wait_window(self) 
     312                else: 
     313                        self.wait_window(self) 
     314                return self.isOk 
     315                 
     316        def createWidgets(self): 
     317                # buddy list 
     318                list_frame = ttk.Frame(self) 
     319                list_frame.pack(side=tk.TOP, fill=tk.BOTH, expand=1, padx=20, pady=20) 
     320                #scrl = ttk.Scrollbar(self, orient=tk.VERTICAL, command=list_frame.yview) 
     321                #list_frame.config(yscrollcommand=scrl.set) 
     322                #scrl.pack(side=tk.RIGHT, fill=tk.Y) 
     323                 
     324                # draw buddy list 
     325                self.buddies = [] 
     326                for acc in self._app.accList: 
     327                        self.buddies.append((0, acc.cfg.idUri)) 
     328                        for bud in acc.buddyList: 
     329                                self.buddies.append((1, bud)) 
     330                 
     331                self.bud_var = [] 
     332                for idx,(flag,bud) in enumerate(self.buddies): 
     333                        self.bud_var.append(tk.IntVar()) 
     334                        if flag==0: 
     335                                s = ttk.Separator(list_frame, orient=tk.HORIZONTAL) 
     336                                s.pack(fill=tk.X) 
     337                                l = tk.Label(list_frame, anchor=tk.W, text="Account '%s':" % (bud)) 
     338                                l.pack(fill=tk.X) 
     339                        else: 
     340                                c = tk.Checkbutton(list_frame, anchor=tk.W, text=bud.cfg.uri, variable=self.bud_var[idx]) 
     341                                c.pack(fill=tk.X) 
     342                s = ttk.Separator(list_frame, orient=tk.HORIZONTAL) 
     343                s.pack(fill=tk.X) 
     344 
     345                # Ok/cancel buttons 
     346                tail_frame = ttk.Frame(self) 
     347                tail_frame.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=1) 
     348                 
     349                btnOk = ttk.Button(tail_frame, text='Ok', default=tk.ACTIVE, command=self.onOk) 
     350                btnOk.pack(side=tk.LEFT, padx=20, pady=10) 
     351                btnCancel = ttk.Button(tail_frame, text='Cancel', command=self.onCancel) 
     352                btnCancel.pack(side=tk.RIGHT, padx=20, pady=10) 
     353                 
     354        def onOk(self): 
     355                self.buddyList[:] = [] 
     356                for idx,(flag,bud) in enumerate(self.buddies): 
     357                        if not flag: continue 
     358                        if self.bud_var[idx].get() and not (bud in self.buddyList): 
     359                                self.buddyList.append(bud) 
     360                         
     361                self.isOk = True 
     362                self.destroy() 
     363                 
     364        def onCancel(self): 
     365                self.destroy() 
  • pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/chatgui.py

    r4671 r4677  
    7474                self.columnconfigure(1, weight=0) 
    7575                 
    76                 self._text = tk.Text(self, font=("Arial", "10")) 
     76                self._text = tk.Text(self, width=50, height=30, font=("Arial", "10")) 
    7777                self._text.grid(row=0, column=0, sticky='nswe') 
    7878                self._text.config(state=tk.DISABLED) 
     
    8383                scrl.grid(row=0, column=1, sticky='nsw') 
    8484                 
    85                 self._typingBox = tk.Text(self, height=1, font=("Arial", "10")) 
     85                self._typingBox = tk.Text(self, width=50, height=1, font=("Arial", "10")) 
    8686                self._typingBox.grid(row=1, columnspan=2, sticky='we', pady=0) 
    8787                 
     
    112112                         
    113113class AudioObserver: 
    114         def onRetry(self, peer_uri): 
    115                 pass 
    116         def onKick(self, peer_uri): 
     114        def onHangup(self, peer_uri): 
    117115                pass 
    118116        def onHold(self, peer_uri): 
     117                pass 
     118        def onUnhold(self, peer_uri): 
    119119                pass 
    120120        def onRxMute(self, peer_uri, is_muted): 
     
    135135                self._rxMute = False 
    136136                self._txMute = False 
    137  
    138                 # internal state: 0:init - 1:established - 2:failure/rejected 3:normal cleared 
    139137                self._state = AudioState.NULL 
    140138                 
     
    149147                        self._callFrame.pack_forget() 
    150148                        self._initFrame.pack(fill=tk.BOTH) 
    151                         self._btnRetry.pack_forget() 
    152149                        self._btnCancel.pack(side=tk.TOP) 
    153150                        self._lblInitState['text'] = 'Intializing..' 
     
    161158                        if state == AudioState.FAILED: 
    162159                                self._lblInitState['text'] = 'Failed' 
    163                                 self._btnRetry.pack() 
    164160                        else: 
    165161                                self._lblInitState['text'] = 'Normal cleared' 
    166162                                self._btnCancel.pack_forget() 
     163                         
     164                        self._btnHold['text'] = 'Hold' 
     165                        self._btnHold.config(state=tk.NORMAL) 
    167166                 
    168167                # save last state 
    169168                self._state = state 
    170169                 
    171         def _onRetry(self): 
    172                 self._btnRetry.pack_forget() 
    173                 self._state = 0 
    174                 # notify app 
    175                 self._observer.onRetry(self.peerUri) 
    176  
    177         def _onKick(self): 
    178                 # notify app 
    179                 self._observer.onKick(self.peerUri) 
    180                  
    181170        def _onHold(self): 
    182                 # notify app 
    183                 self._observer.onHold(self.peerUri) 
     171                self._btnHold.config(state=tk.DISABLED) 
     172                # notify app 
     173                if self._btnHold['text'] == 'Hold': 
     174                        self._observer.onHold(self.peerUri) 
     175                        self._btnHold['text'] = 'Unhold' 
     176                else: 
     177                        self._observer.onUnhold(self.peerUri) 
     178                        self._btnHold['text'] = 'Hold' 
     179                self._btnHold.config(state=tk.NORMAL) 
    184180 
    185181        def _onHangup(self): 
    186182                # notify app 
    187                 self._observer.onKick(self.peerUri) 
     183                self._observer.onHangup(self.peerUri) 
    188184 
    189185        def _onRxMute(self): 
     
    210206                self._lblInitState.pack(side=tk.TOP, fill=tk.X, expand=1) 
    211207                 
    212                 # Operation: retry, cancel/kick 
    213                 self._btnRetry = ttk.Button(self._initFrame, text = 'Retry', command=self._onRetry) 
    214                 self._btnRetry.pack(side=tk.TOP) 
    215                 self._btnCancel = ttk.Button(self._initFrame, text = 'Cancel', command=self._onKick) 
     208                # Operation: cancel/kick 
     209                self._btnCancel = ttk.Button(self._initFrame, text = 'Cancel', command=self._onHangup) 
    216210                self._btnCancel.pack(side=tk.TOP) 
    217211                                 
     
    383377                                aud_frm.updateState(state) 
    384378                                break 
     379                if state >= AudioState.DISCONNECTED and len(self._audioFrames) == 1: 
     380                        self.enableAudio(False) 
     381                else: 
     382                        self.enableAudio(True) 
    385383                                                 
    386384if __name__ == '__main__': 
Note: See TracChangeset for help on using the changeset viewer.