Package ts3widgets :: Module serverview
[hide private]
[frames] | no frames]

Source Code for Module ts3widgets.serverview

   1  from . import _errprint 
   2   
   3  from ts3plugin import PluginHost 
   4   
   5  import ts3lib 
   6  import ts3defines 
   7   
   8  import ts3client 
   9   
  10  import re 
  11   
  12  from PythonQt.QtCore import Qt, QAbstractItemModel, QModelIndex 
  13  from PythonQt.QtGui import (QStyledItemDelegate, QStyle, QFontMetrics, 
  14                              QApplication, QIcon, QColor, QTreeView) 
15 16 17 -class ServerViewRoles:
18 """ 19 Additional roles used in ServerviewModel to deliver icons and spacer 20 properties. 21 """ 22 23 itemtype = Qt.UserRole 24 statusicons = Qt.UserRole + 1 25 isspacer = Qt.UserRole + 2 26 spacertype = Qt.UserRole + 3 27 spaceralignment = Qt.UserRole + 4 28 spacercustomtext = Qt.UserRole + 5
29
30 31 -class Channel(object):
32 """ 33 Object wrapper for a channel on a TS3 server. 34 """ 35
36 - def __init__(self, schid, cid):
37 super().__init__() 38 self.schid = schid 39 self.cid = cid 40 self.parentNode = None 41 self.subchans = [] 42 self.allsubchans = {} 43 self.clients = [] 44 45 self.update()
46
47 - def _appendClient(self, obj):
48 self.clients.insert(self.rowOf(obj) - len(self.subchans), obj)
49
50 - def _appendChannel(self, obj, sort=True):
51 if sort: 52 so = obj.sortOrder 53 if so == 0: 54 self.subchans.insert(0, obj) 55 else: 56 self.subchans.insert(self.subchans.index( 57 self.allsubchans[so]) + 1, obj) 58 # if exists, the sortorder of the previous successor is not 59 # valid anymore, as long it is not updated 60 else: 61 self.subchans.append(obj) 62 63 self.allsubchans[obj.cid] = obj
64
65 - def append(self, obj, sort=True):
66 if type(obj) is Client: 67 self._appendClient(obj) 68 else: 69 assert type(obj) is Channel 70 self._appendChannel(obj, sort) 71 72 obj.parentNode = self
73
74 - def rowOf(self, obj=None, pretend=False):
75 if obj is None: 76 return self.parentNode.rowOf(self) 77 else: 78 if type(obj) is Client: 79 if obj not in self.clients: 80 if len(self.clients) == 0: 81 return len(self.subchans) 82 83 for i in range(len(self.clients)): 84 if self.clients[i] < obj: 85 continue 86 87 return len(self.subchans) + i 88 89 return len(self.subchans) + len(self.clients) 90 else: 91 return len(self.subchans) + self.clients.index(obj) 92 else: 93 assert type(obj) is Channel 94 if obj not in self.subchans or pretend: 95 if obj.sortOrder == 0: 96 return 0 97 else: 98 return self.subchans.index( 99 self.allsubchans[obj.sortOrder]) + 1 100 else: 101 return self.subchans.index(obj)
102
103 - def remove(self, obj):
104 if type(obj) is Client: 105 self.clients.remove(obj) 106 else: 107 assert type(obj) is Channel 108 self.subchans.remove(obj)
109
110 - def update(self):
111 self.cache = {}
112 113 @property
114 - def name(self):
115 if "name" in self.cache: 116 return self.cache["name"] 117 118 err, n = ts3lib.getChannelVariableAsString(self.schid, self.cid, 119 ts3defines.ChannelProperties.CHANNEL_NAME) 120 if err != ts3defines.ERROR_ok: 121 _errprint("Error getting channel name", err, self.schid, self.cid) 122 return "ERROR_GETTING_NAME: %s" % err 123 else: 124 self.cache["name"] = n 125 return n
126 127 @property
128 - def sortOrder(self):
129 if "sortOrder" in self.cache: 130 return self.cache["sortOrder"] 131 132 err, so = ts3lib.getChannelVariableAsUInt64(self.schid, self.cid, 133 ts3defines.ChannelProperties.CHANNEL_ORDER) 134 if err != ts3defines.ERROR_ok: 135 _errprint("Error getting channel sortorder", err, self.schid, 136 self.cid) 137 return 0 138 else: 139 self.cache["sortOrder"] = so 140 return so
141 142 @property
143 - def isPermanent(self):
144 if "isPermanent" in self.cache: 145 return self.cache["isPermanent"] 146 147 err, permanent = ts3lib.getChannelVariableAsInt(self.schid, self.cid, 148 ts3defines.ChannelProperties.CHANNEL_FLAG_PERMANENT) 149 if err != ts3defines.ERROR_ok: 150 _errprint("Error getting channel ispermanent flag", err, 151 self.schid, self.cid) 152 return True 153 else: 154 self.cache["isPermanent"] = permanent == 1 155 return permanent == 1
156
157 - def _updateSpacer(self):
158 done = False 159 if self.isPermanent: 160 m = re.match(r'\[([clr\*])spacer\d*](.*)', self.name) 161 if m: 162 self.cache["isSpacer"] = True 163 164 al = m.group(1) 165 if al == "l": 166 self.cache["spacerAlignment"] = Qt.AlignLeft 167 elif al == "r": 168 self.cache["spacerAlignment"] = Qt.AlignRight 169 elif al == "c": 170 self.cache["spacerAlignment"] = Qt.AlignHCenter 171 elif al == "*": 172 self.cache["spacerAlignment"] = Qt.AlignJustify 173 174 st = m.group(2) 175 self.cache["spacerCustomtext"] = "" 176 if st == "___": 177 self.cache["spacerType"] = Qt.SolidLine 178 elif st == "---": 179 self.cache["spacerType"] = Qt.DashLine 180 elif st == "...": 181 self.cache["spacerType"] = Qt.DotLine 182 elif st == "-.-": 183 self.cache["spacerType"] = Qt.DashDotLine 184 elif st == "-..": 185 self.cache["spacerType"] = Qt.DashDotDotLine 186 else: 187 self.cache["spacerType"] = Qt.CustomDashLine 188 self.cache["spacerCustomtext"] = st 189 190 done = True 191 192 if not done: 193 self.cache["isSpacer"] = False 194 self.cache["spacerAlignment"] = Qt.AlignLeft 195 self.cache["spacerType"] = Qt.SolidLine 196 self.cache["spacerCustomtext"] = ""
197 198 @property
199 - def isSpacer(self):
200 if "isSpacer" not in self.cache: 201 self._updateSpacer() 202 203 return self.cache["isSpacer"]
204 205 @property
206 - def spacerAlignment(self):
207 if "spacerAlignment" not in self.cache: 208 self._updateSpacer() 209 210 return self.cache["spacerAlignment"]
211 212 @property
213 - def spacerType(self):
214 if "spacerType" not in self.cache: 215 self._updateSpacer() 216 217 return self.cache["spacerType"]
218 219 @property
220 - def spacerCustomtext(self):
221 if "spacerCustomtext" not in self.cache: 222 self._updateSpacer() 223 224 return self.cache["spacerCustomtext"]
225 226 @property
227 - def isPasswordProtected(self):
228 if "isPasswordProtected" in self.cache: 229 return self.cache["isPasswordProtected"] 230 231 err, p = ts3lib.getChannelVariableAsInt(self.schid, self.cid, 232 ts3defines.ChannelProperties.CHANNEL_FLAG_PASSWORD) 233 if err != ts3defines.ERROR_ok: 234 _errprint("Error getting channel ispasswordprotected flag", err, 235 self.schid, self.cid) 236 return False 237 else: 238 self.cache["isPasswordProtected"] = p == 1 239 return p == 1
240 241 @property
242 - def isSubscribed(self):
243 if "isSubscribed" in self.cache: 244 return self.cache["isSubscribed"] 245 246 err, sub = ts3lib.getChannelVariableAsInt(self.schid, self.cid, 247 ts3defines.ChannelPropertiesRare.CHANNEL_FLAG_ARE_SUBSCRIBED) 248 if err != ts3defines.ERROR_ok: 249 _errprint("Error getting channel issubscribed flag", err, 250 self.schid, self.cid) 251 return False 252 else: 253 self.cache["isSubscribed"] = sub == 1 254 return sub == 1
255 256 @property
257 - def neededTalkPower(self):
258 if "neededTalkPower" in self.cache: 259 return self.cache["neededTalkPower"] 260 261 err, p = ts3lib.getChannelVariableAsInt(self.schid, self.cid, 262 ts3defines.ChannelPropertiesRare.CHANNEL_NEEDED_TALK_POWER) 263 if err != ts3defines.ERROR_ok: 264 _errprint("Error getting channel neededtalkpower", err, self.schid, 265 self.cid) 266 return 0 267 else: 268 self.cache["neededTalkPower"] = p 269 return p
270 271 @property
272 - def isDefault(self):
273 if "isDefault" in self.cache: 274 return self.cache["isDefault"] 275 276 err, d = ts3lib.getChannelVariableAsInt(self.schid, self.cid, 277 ts3defines.ChannelProperties.CHANNEL_FLAG_DEFAULT) 278 if err != ts3defines.ERROR_ok: 279 _errprint("Error getting channel isdefault flag", err, self.schid, 280 self.cid) 281 return False 282 else: 283 self.cache["isDefault"] = d == 1 284 return d == 1
285 286 @property
287 - def iconID(self):
288 if "iconID" in self.cache: 289 return self.cache["iconID"] 290 291 err, i = ts3lib.getChannelVariableAsUInt64(self.schid, self.cid, 292 ts3defines.ChannelPropertiesRare.CHANNEL_ICON_ID) 293 if err != ts3defines.ERROR_ok: 294 _errprint("Error getting channel iconid", err, self.schid, 295 self.cid) 296 return 0 297 else: 298 if i < 0: 299 i = pow(2, 32) + i 300 301 self.cache["iconID"] = i 302 return i
303 304 @property
305 - def maxClients(self):
306 if "maxClients" in self.cache: 307 return self.cache["maxClients"] 308 309 err, m = ts3lib.getChannelVariableAsInt(self.schid, self.cid, 310 ts3defines.ChannelPropertiesRare.CHANNEL_FLAG_MAXCLIENTS_UNLIMITED) 311 if err != ts3defines.ERROR_ok: 312 _errprint("Error getting channel maxclientsunlimited flag", err, 313 self.schid, self.cid) 314 return 300 315 else: 316 if m == 1: 317 self.cache["maxClients"] = -1 318 return -1 319 320 err, m = ts3lib.getChannelVariableAsInt(self.schid, self.cid, 321 ts3defines.ChannelProperties.CHANNEL_MAXCLIENTS) 322 if err != ts3defines.ERROR_ok: 323 _errprint("Error getting channel maxclients", err, self.schid, 324 self.cid) 325 return 0 326 else: 327 self.cache["maxClients"] = m 328 return m
329 330 @property
331 - def codec(self):
332 if "codec" in self.cache: 333 return self.cache["codec"] 334 335 err, c = ts3lib.getChannelVariableAsInt(self.schid, self.cid, 336 ts3defines.ChannelProperties.CHANNEL_CODEC) 337 if err != ts3defines.ERROR_ok: 338 _errprint("Error getting channel codec", err, self.schid, self.cid) 339 return 0 340 else: 341 self.cache["codec"] = c 342 return c
343
344 - def isFull(self):
345 if self.maxClients == -1: 346 return False 347 348 return self.maxClients <= len(self.clients)
349
350 - def iconVariable(self):
351 if self.isSpacer: 352 return "" 353 354 if self.isFull(): 355 if self.isSubscribed: 356 return "CHANNEL_RED_SUBSCRIBED" 357 else: 358 return "CHANNEL_RED" 359 360 if self.isPasswordProtected: 361 if self.isSubscribed: 362 return "CHANNEL_YELLOW_SUBSCRIBED" 363 else: 364 return "CHANNEL_YELLOW" 365 366 if self.isSubscribed: 367 return "CHANNEL_GREEN_SUBSCRIBED" 368 else: 369 return "CHANNEL_GREEN"
370
371 - def count(self):
372 return len(self.clients) + len(self.subchans)
373
374 - def child(self, row):
375 if row >= len(self.subchans): 376 return self.clients[row - len(self.subchans)] 377 else: 378 return self.subchans[row]
379
380 - def sort(self):
381 newsubchans = [] 382 i = 0 383 next = 0 384 while len(self.subchans) > 0: 385 if self.subchans[i].sortOrder == next: 386 newsubchans.append(self.subchans.pop(i)) 387 next = newsubchans[-1].cid 388 else: 389 i += 1 390 391 if i >= len(self.subchans): 392 i = 0 393 394 self.subchans = newsubchans
395
396 - def __iter__(self):
397 for c in self.subchans: 398 yield c 399 400 for c in self.clients: 401 yield c
402
403 404 -class Server(Channel):
405 """ 406 Object wrapper for a TS3 server connection. 407 """ 408
409 - def __init__(self, schid):
410 super().__init__(schid, 0) 411 412 self.update()
413
414 - def update(self):
415 self.cache = {}
416 417 @property
418 - def name(self):
419 if "name" in self.cache: 420 return self.cache["name"] 421 422 err, n = ts3lib.getServerVariableAsString(self.schid, 423 ts3defines.VirtualServerProperties.VIRTUALSERVER_NAME) 424 if err != ts3defines.ERROR_ok: 425 _errprint("Error getting server name", err, self.schid) 426 return "ERROR_UNABLE_TO_GET_SERVERNAME" 427 else: 428 self.cache["name"] = n 429 return n
430 431 @property
432 - def iconID(self):
433 if "iconID" in self.cache: 434 return self.cache["iconID"] 435 436 err, i = ts3lib.getServerVariableAsUInt64(self.schid, 437 ts3defines.VirtualServerPropertiesRare.VIRTUALSERVER_ICON_ID) 438 if err != ts3defines.ERROR_ok: 439 _errprint("Error getting server iconid", err, self.schid) 440 return 0 441 else: 442 if i < 0: 443 i = pow(2, 32) + i 444 445 self.cache["iconID"] = i 446 return i
447
448 - def rowOf(self, obj=None):
449 if obj is None: 450 return 0 451 else: 452 return super().rowOf(obj)
453
454 - def iconVariable(self):
455 return "SERVER_GREEN"
456
457 458 -class Client(object):
459 """ 460 Object wrapper for a connected client on a TS3 server. 461 """ 462
463 - def __init__(self, schid, clid, isme):
464 super().__init__() 465 self.schid = schid 466 self.clid = clid 467 self.isme = isme 468 self.parentNode = None 469 self._isTalking = False 470 471 self.update()
472
473 - def update(self):
474 self.cache = {}
475
476 - def count(self):
477 return 0
478
479 - def rowOf(self):
480 return self.parentNode.rowOf(self)
481
482 - def __lt__(self, other):
483 assert type(other) is Client 484 485 if self.talkPower != other.talkPower: 486 return other.talkPower < self.talkPower 487 else: 488 return self.name.lower() < other.name.lower()
489
490 - def __gt__(self, other):
491 return other < self
492 493 @property
494 - def name(self):
495 if "name" in self.cache: 496 return self.cache["name"] 497 498 err, n = ts3lib.getClientVariableAsString(self.schid, self.clid, 499 ts3defines.ClientProperties.CLIENT_NICKNAME) 500 if err != ts3defines.ERROR_ok: 501 _errprint("Error getting client name", err, self.schid, self.clid) 502 return "ERROR_GETTING_NICKNAME: %s" % err 503 else: 504 self.cache["name"] = n 505 return n
506 507 @property
508 - def displayName(self):
509 if "displayName" in self.cache: 510 return self.cache["displayName"] 511 512 err, n = ts3lib.getClientDisplayName(self.schid, self.clid) 513 if err != ts3defines.ERROR_ok: 514 _errprint("Error getting client displayname", err, self.schid, 515 self.clid) 516 return "ERROR_GETTING_DISPLAYNAME: %s" % err 517 else: 518 self.cache["displayName"] = n 519 return n
520 521 @property
522 - def talkPower(self):
523 if "talkPower" in self.cache: 524 return self.cache["talkPower"] 525 526 err, p = ts3lib.getClientVariableAsInt(self.schid, self.clid, 527 ts3defines.ClientPropertiesRare.CLIENT_TALK_POWER) 528 if err != ts3defines.ERROR_ok: 529 _errprint("Error getting client talkpower", err, self.schid, 530 self.clid) 531 return 0 532 else: 533 self.cache["talkPower"] = p 534 return p
535 536 @property
537 - def isRecording(self):
538 if "isRecording" in self.cache: 539 return self.cache["isRecording"] 540 541 err, rec = ts3lib.getClientVariableAsInt(self.schid, self.clid, 542 ts3defines.ClientProperties.CLIENT_IS_RECORDING) 543 if err != ts3defines.ERROR_ok: 544 _errprint("Error getting client isrecording flag", err, self.schid, 545 self.clid) 546 return False 547 else: 548 self.cache["isRecording"] = rec == 1 549 return rec == 1
550 551 @property
552 - def isChannelCommander(self):
553 if "isChannelCommander" in self.cache: 554 return self.cache["isChannelCommander"] 555 556 err, cc = ts3lib.getClientVariableAsInt(self.schid, self.clid, 557 ts3defines.ClientPropertiesRare.CLIENT_IS_CHANNEL_COMMANDER) 558 if err != ts3defines.ERROR_ok: 559 _errprint("Error getting client channelcommander flag", err, 560 self.schid, self.clid) 561 return False 562 else: 563 self.cache["isChannelCommander"] = cc == 1 564 return cc == 1
565 566 @property
567 - def isTalking(self):
568 return self._isTalking
569 570 @isTalking.setter
571 - def isTalking(self, val):
572 self._isTalking = val
573 574 @property
575 - def iconID(self):
576 if "iconID" in self.cache: 577 return self.cache["iconID"] 578 579 err, i = ts3lib.getClientVariableAsUInt64(self.schid, self.clid, 580 ts3defines.ClientPropertiesRare.CLIENT_ICON_ID) 581 if err != ts3defines.ERROR_ok: 582 _errprint("Error getting client iconid", err, self.schid, 583 self.clid) 584 return 0 585 else: 586 if i < 0: 587 i = pow(2, 32) + i 588 589 self.cache["iconID"] = i 590 return i
591 592 @property
593 - def isPrioritySpeaker(self):
594 if "isPrioritySpeaker" in self.cache: 595 return self.cache["isPrioritySpeaker"] 596 597 err, p = ts3lib.getClientVariableAsInt(self.schid, self.clid, 598 ts3defines.ClientPropertiesRare.CLIENT_IS_PRIORITY_SPEAKER) 599 if err != ts3defines.ERROR_ok: 600 _errprint("Error getting client ispriorityspeaker flag", err, 601 self.schid, self.clid) 602 return False 603 else: 604 self.cache["isPrioritySpeaker"] = p == 1 605 return p == 1
606 607 @property
608 - def isAway(self):
609 if "isAway" in self.cache: 610 return self.cache["isAway"] 611 612 err, a = ts3lib.getClientVariableAsInt(self.schid, self.clid, 613 ts3defines.ClientPropertiesRare.CLIENT_AWAY) 614 if err != ts3defines.ERROR_ok: 615 _errprint("Error getting client isaway flag", err, self.schid, 616 self.clid) 617 return False 618 else: 619 self.cache["isAway"] = a == 1 620 return a == 1
621 622 @property
623 - def country(self):
624 if "country" in self.cache: 625 return self.cache["country"] 626 627 err, c = ts3lib.getClientVariableAsString(self.schid, self.clid, 628 ts3defines.ClientPropertiesRare.CLIENT_COUNTRY) 629 if err != ts3defines.ERROR_ok: 630 _errprint("Error getting client country", err, self.schid, 631 self.clid) 632 return "" 633 else: 634 self.cache["country"] = c 635 return c
636 637 @property
638 - def isRequestingTalkPower(self):
639 if "isRequestingTalkPower" in self.cache: 640 return self.cache["isRequestingTalkPower"] 641 642 err, r = ts3lib.getClientVariableAsInt(self.schid, self.clid, 643 ts3defines.ClientPropertiesRare.CLIENT_TALK_REQUEST) 644 if err != ts3defines.ERROR_ok: 645 _errprint("Error getting client isrequestingtalkpower flag", err, 646 self.schid, self.clid) 647 return False 648 else: 649 self.cache["isRequestingTalkPower"] = r == 1 650 return r == 1
651 652 @property
653 - def isTalker(self):
654 if "isTalker" in self.cache: 655 return self.cache["isTalker"] 656 657 err, t = ts3lib.getClientVariableAsInt(self.schid, self.clid, 658 ts3defines.ClientPropertiesRare.CLIENT_IS_TALKER) 659 if err != ts3defines.ERROR_ok: 660 _errprint("Error getting client istalker flag", err, self.schid, 661 self.clid) 662 return False 663 else: 664 self.cache["isTalker"] = t == 1 665 return t == 1
666 667 @property
668 - def outputMuted(self):
669 if "outputMuted" in self.cache: 670 return self.cache["outputMuted"] 671 672 err, o = ts3lib.getClientVariableAsInt(self.schid, self.clid, 673 ts3defines.ClientProperties.CLIENT_OUTPUT_MUTED) 674 if err != ts3defines.ERROR_ok: 675 _errprint("Error getting client outputmuted flag", err, self.schid, 676 self.clid) 677 return False 678 else: 679 self.cache["outputMuted"] = o == 1 680 return o == 1
681 682 @property
683 - def inputMuted(self):
684 if "inputMuted" in self.cache: 685 return self.cache["inputMuted"] 686 687 err, i = ts3lib.getClientVariableAsInt(self.schid, self.clid, 688 ts3defines.ClientProperties.CLIENT_INPUT_MUTED) 689 if err != ts3defines.ERROR_ok: 690 _errprint("Error getting client inputmuted flag", err, self.schid, 691 self.clid) 692 return False 693 else: 694 self.cache["inputMuted"] = i == 1 695 return i == 1
696 697 @property
698 - def hardwareInputMuted(self):
699 if "hardwareInputMuted" in self.cache: 700 return self.cache["hardwareInputMuted"] 701 702 err, i = ts3lib.getClientVariableAsInt(self.schid, self.clid, 703 ts3defines.ClientProperties.CLIENT_INPUT_HARDWARE) 704 if err != ts3defines.ERROR_ok: 705 _errprint("Error getting client hardwareinputmuted flag", err, 706 self.schid, self.clid) 707 return False 708 else: 709 self.cache["hardwareInputMuted"] = i == 0 710 return i == 0
711 712 @property
713 - def hardwareOutputMuted(self):
714 if "hardwareOutputMuted" in self.cache: 715 return self.cache["hardwareOutputMuted"] 716 717 err, o = ts3lib.getClientVariableAsInt(self.schid, self.clid, 718 ts3defines.ClientProperties.CLIENT_OUTPUT_HARDWARE) 719 if err != ts3defines.ERROR_ok: 720 _errprint("Error getting client hardwareoutputmuted flag", err, 721 self.schid, self.clid) 722 return False 723 else: 724 self.cache["hardwareOutputMuted"] = o == 0 725 return o == 0
726 727 @property
728 - def inputDeactivated(self):
729 if "inputDeactivated" in self.cache: 730 return self.cache["inputDeactivated"] 731 732 err, i = ts3lib.getClientVariableAsInt(self.schid, self.clid, 733 ts3defines.ClientProperties.CLIENT_INPUT_DEACTIVATED) 734 if err != ts3defines.ERROR_ok: 735 _errprint("Error getting client inputdeactivated flag", err, 736 self.schid, self.clid) 737 return False 738 else: 739 self.cache["hardwareOutputMuted"] = i == 1 740 return i == 1
741 742 @property
743 - def channelGroup(self):
744 if "channelGroup" in self.cache: 745 return self.cache["channelGroup"] 746 747 err, g = ts3lib.getClientVariableAsUInt64(self.schid, self.clid, 748 ts3defines.ClientPropertiesRare.CLIENT_CHANNEL_GROUP_ID) 749 if err != ts3defines.ERROR_ok: 750 _errprint("Error getting client channelgroup", err, self.schid, 751 self.clid) 752 return 0 753 else: 754 self.cache["channelGroup"] = g 755 return g
756 757 @property
758 - def serverGroups(self):
759 if "serverGroups" in self.cache: 760 return self.cache["serverGroups"] 761 762 err, gs = ts3lib.getClientVariableAsString(self.schid, self.clid, 763 ts3defines.ClientPropertiesRare.CLIENT_SERVERGROUPS) 764 if err != ts3defines.ERROR_ok: 765 _errprint("Error getting client servergroups", err, self.schid, 766 self.clid) 767 return [] 768 else: 769 self.cache["serverGroups"] = list(map(int, gs.split(','))) 770 return self.cache["serverGroups"]
771
772 - def iconVariable(self):
773 if self.isAway: 774 return "AWAY" 775 776 if self.hardwareOutputMuted: 777 return "HARDWARE_OUTPUT_MUTED" 778 779 if self.outputMuted: 780 return "OUTPUT_MUTED" 781 782 if self.inputDeactivated: 783 return "INPUT_MUTED_LOCAL" 784 785 if self.hardwareInputMuted: 786 return "HARDWARE_INPUT_MUTED" 787 788 if self.inputMuted: 789 return "INPUT_MUTED" 790 791 if self.isChannelCommander: 792 if self.isTalking: 793 return "PLAYER_COMMANDER_ON" 794 else: 795 return "PLAYER_COMMANDER_OFF" 796 797 if self.isTalking: 798 return "PLAYER_ON" 799 else: 800 return "PLAYER_OFF"
801
802 803 -class ServerviewModel(QAbstractItemModel):
804 """ 805 ItemModel to deliver data of a serverview to ItemWidgets. The data is 806 delivered in one column. 807 Limitations: no badges, no friend/foe status 808 """ 809
810 - def __init__(self, schid, iconpack=None, parent=None):
811 """ 812 Instantiates a new ServerviewModel object. This raises an exception if 813 the iconpack could not be opened. The object registers itself as 814 callbackproxy to the PythonHost. 815 @param schid: the ID of the serverconnection 816 @type schid: int 817 @param iconpack: the iconpack to use icons from. defaults to None to 818 use the TS3 client's current IconPack 819 @type iconpack: ts3client.IconPack 820 @param parent: the QObject-parent. defaults to None 821 @type parent: QObject 822 """ 823 super().__init__(parent) 824 825 self.schid = schid 826 self.root = Server(schid) 827 828 self.allchans = {} 829 self.allclients = {} 830 self.objs = {} 831 832 self.cgicons = {} 833 self.sgicons = {} 834 835 self._reload() 836 837 try: 838 self.icons = ts3client.ServerCache(self.schid) 839 840 self.countries = ts3client.CountryFlags() 841 self.countries.open() 842 except Exception as e: 843 self.delete() 844 raise e 845 846 self.iconpackcreated = False 847 if not iconpack: 848 try: 849 self.iconpack = ts3client.IconPack.current() 850 self.iconpack.open() 851 self.iconpackcreated = True 852 except Exception as e: 853 self.delete() 854 raise e 855 else: 856 self.iconpack = iconpack 857 858 PluginHost.registerCallbackProxy(self)
859
860 - def __del__(self):
861 PluginHost.unregisterCallbackProxy(self) 862 self.allchans = {} 863 self.objs = {} 864 865 if self.iconpackcreated: 866 self.iconpack.close() 867 868 self.countries.close()
869
870 - def setServerConnectionHandlerId(self, schid):
871 self.schid = schid 872 self._reload()
873
874 - def _reload(self):
875 err, self.myid = ts3lib.getClientID(self.schid) 876 877 err = ts3lib.requestServerGroupList(self.schid) 878 if err != ts3defines.ERROR_ok: 879 _errprint("Error requesting servergrouplist", err, self.schid) 880 err = ts3lib.requestChannelGroupList(self.schid) 881 if err != ts3defines.ERROR_ok: 882 _errprint("Error requesting channelgroups", err, self.schid) 883 884 self.beginResetModel() 885 886 self.allchans = {} 887 self.objs = {} 888 889 self._reloadServerinfo() 890 self._reloadChannellist() 891 self._reloadClientlist() 892 893 self.endResetModel()
894
895 - def _reloadServerinfo(self):
896 self.onServerEditedEvent(self.schid, 0, "", "")
897
898 - def _reloadChannellist(self):
899 err, cids = ts3lib.getChannelList(self.schid) 900 if err != ts3defines.ERROR_ok: 901 _errprint("Error getting channellist", err, self.schid) 902 return 903 904 unhandled = {} 905 for c in cids: 906 err, pcid = ts3lib.getParentChannelOfChannel(self.schid, c) 907 908 if err != ts3defines.ERROR_ok: 909 _errprint("Error getting channel parent", err, self.schid, c) 910 pass 911 912 if pcid == 0: 913 parent = self.root 914 elif pcid in self.allchans: 915 parent = self.allchans[pcid] 916 else: 917 if pcid in unhandled: 918 unhandled[pcid].append(c) 919 else: 920 unhandled[pcid] = [c] 921 continue 922 923 obj = Channel(self.schid, c) 924 self.allchans[c] = obj 925 parent.append(obj, False) 926 927 self._addChilds(self.root, unhandled) 928 self.root.sort() 929 assert len(unhandled) == 0
930
931 - def _addChilds(self, node, d):
932 for chan in node: 933 for scid in d.pop(chan.cid, []): 934 obj = Channel(self.schid, scid) 935 self.allchans[scid] = obj 936 chan.append(obj, False) 937 938 chan.sort() 939 940 self._addChilds(chan, d)
941
942 - def _reloadClientlist(self):
943 err, clids = ts3lib.getClientList(self.schid) 944 if err != ts3defines.ERROR_ok: 945 _errprint("Error getting clientlist", err, self.schid) 946 return 947 948 for c in clids: 949 err, parent = ts3lib.getChannelOfClient(self.schid, c) 950 if err == ts3defines.ERROR_ok: 951 self.onClientMoveEvent(self.schid, c, 0, parent, 952 ts3defines.Visibility.ENTER_VISIBILITY, 953 "") 954 else: 955 _errprint("Error getting client channel", err, self.schid, c) 956 pass
957
958 - def onServerEditedEvent(self, schid, editerID, editerName, editerUID):
959 if schid != self.schid: 960 return 961 962 self.root.update() 963 idx = self._createIndex(0, 0, self.root) 964 self.dataChanged(idx, idx)
965
966 - def onNewChannelEvent(self, schid, cid, pcid):
967 if schid != self.schid: 968 return 969 970 if pcid == 0: 971 parent = self.root 972 elif pcid in self.allchans: 973 parent = self.allchans[pcid] 974 else: 975 _errprint("Error, event on unrecognised channel", 0, self.schid, 976 pcid) 977 return 978 979 obj = Channel(schid, cid) 980 self.allchans[cid] = obj 981 982 newrow = parent.rowOf(obj) 983 self.beginInsertRows(self._createIndex(parent.rowOf(), 0, parent), 984 newrow, newrow) 985 parent.append(obj) 986 self.endInsertRows()
987
988 - def onNewChannelCreatedEvent(self, schid, cid, parent, invokerID, 989 invokerName, invokerUniqueIdentifier):
990 self.onNewChannelEvent(schid, cid, parent)
991
992 - def onUpdateChannelEditedEvent(self, schid, cid, invokerID, invokerName, 993 invokerUniqueIdentifier):
994 if schid != self.schid: 995 return 996 997 chan = self.allchans[cid] 998 oldsort = chan.sortOrder 999 oldrow = chan.rowOf() 1000 1001 chan.update() 1002 1003 if oldsort != chan.sortOrder: 1004 # the channel (propably) has moved inside the parent 1005 newrow = chan.parentNode.rowOf(chan, True) 1006 pidx = self._createIndex(chan.parentNode.rowOf(), 0, 1007 chan.parentNode) 1008 self.beginMoveRows(pidx, oldrow, oldrow, pidx, newrow) 1009 chan.parentNode.remove(chan) 1010 chan.parentNode.append(chan) 1011 self.endMoveRows() 1012 else: 1013 row = chan.rowOf() 1014 1015 idx = self._createIndex(row, 0, chan) 1016 self.dataChanged(idx, idx)
1017
1018 - def onChannelMoveEvent(self, schid, cid, newpcid, invokerID, invokerName, 1019 invokerUniqueIdentifier):
1020 if schid != self.schid: 1021 return 1022 1023 chan = self.allchans[cid] 1024 oldpidx = self._createIndex(chan.parentNode.rowOf(), 0, 1025 chan.parentNode) 1026 oldrow = chan.rowOf() 1027 newpar = self.allchans[newpcid] 1028 newpidx = self._createIndex(newpar.rowOf(), 0, newpar) 1029 1030 self.beginMoveRows(oldpidx, oldrow, oldrow, newpidx, 1031 newpar.rowOf(chan)) 1032 chan.parentNode.remove(chan) 1033 newpar.append(chan) 1034 self.endMoveRows()
1035
1036 - def onDelChannelEvent(self, schid, cid, invokerID, invokerName, 1037 invokerUID):
1038 if schid != self.schid: 1039 return 1040 1041 chan = self.allchans[cid] 1042 row = chan.rowOf() 1043 paridx = self._createIndex(chan.parentNode.rowOf(), 0, chan.parentNode) 1044 1045 self.beginRemoveRows(paridx, row, row) 1046 chan.parentNode.remove(chan) 1047 del self.allchans[cid] 1048 if id(chan) in self.objs: 1049 del self.objs[id(chan)] 1050 self.endRemoveRows()
1051
1052 - def onClientMoveEvent(self, schid, clientID, oldChannelID, newChannelID, 1053 visibility, moveMessage):
1054 if schid != self.schid: 1055 return 1056 1057 if visibility == ts3defines.Visibility.ENTER_VISIBILITY: 1058 obj = Client(schid, clientID, clientID == self.myid) 1059 self.allclients[clientID] = obj 1060 chan = self.allchans[newChannelID] 1061 1062 newrow = chan.rowOf(obj) 1063 self.beginInsertRows(self._createIndex(chan.rowOf(), 0, chan), 1064 newrow, newrow) 1065 chan.append(obj) 1066 self.endInsertRows() 1067 elif visibility == ts3defines.Visibility.RETAIN_VISIBILITY: 1068 obj = self.allclients[clientID] 1069 oldchan = self.allchans[oldChannelID] 1070 oldidx = self._createIndex(oldchan.rowOf(), 0, oldchan) 1071 oldrow = oldchan.rowOf(obj) 1072 newchan = self.allchans[newChannelID] 1073 newidx = self._createIndex(newchan.rowOf(), 0, newchan) 1074 newrow = newchan.rowOf(obj) 1075 1076 self.beginMoveRows(oldidx, oldrow, oldrow, newidx, newrow) 1077 oldchan.remove(obj) 1078 newchan.append(obj) 1079 self.endMoveRows() 1080 else: 1081 obj = self.allclients[clientID] 1082 oldchan = self.allchans[oldChannelID] 1083 oldidx = self._createIndex(oldchan.rowOf(), 0, oldchan) 1084 oldrow = oldchan.rowOf(obj) 1085 1086 self.beginRemoveRows(oldidx, oldrow, oldrow) 1087 oldchan.remove(obj) 1088 del self.allclients[clientID] 1089 if id(obj) in self.objs: 1090 del self.objs[id(obj)] 1091 self.endRemoveRows()
1092
1093 - def onChannelUnsubscribeEvent(self, schid, channelID):
1094 if schid != self.schid: 1095 return 1096 1097 chan = self.allchans[channelID] 1098 idx = self._createIndex(chan.rowOf(), 0, chan) 1099 1100 self.beginRemoveRows(idx, len(chan.subchans), chan.count()) 1101 clients = chan.clients 1102 for c in clients: 1103 chan.remove(c) 1104 del self.allclients[c.clid] 1105 if id(c) in self.objs: 1106 del self.objs[id(c)] 1107 self.endRemoveRows() 1108 1109 chan.update() 1110 self.dataChanged(idx, idx)
1111
1112 - def onChannelSubscribeEvent(self, schid, channelID):
1113 if schid != self.schid: 1114 return 1115 1116 chan = self.allchans[channelID] 1117 idx = self._createIndex(chan.rowOf(), 0, chan) 1118 chan.update() 1119 self.dataChanged(idx, idx)
1120
1121 - def onClientMoveMovedEvent(self, schid, clientID, oldChannelID, 1122 newChannelID, visibility, moverID, moverName, 1123 moverUniqueIdentifier, moveMessage):
1124 self.onClientMoveEvent(schid, clientID, oldChannelID, newChannelID, 1125 visibility, "")
1126
1127 - def onUpdateClientEvent(self, schid, clientID, invokerID, invokerName, 1128 invokerUniqueIdentifier):
1129 if schid != self.schid: 1130 return 1131 1132 client = self.allclients[clientID] 1133 idx = self._createIndex(client.rowOf(), 0, client) 1134 client.update() 1135 self.dataChanged(idx, idx)
1136
1137 - def onClientSelfVariableUpdateEvent(self, schid, flag, oldValue, newValue):
1138 self.onUpdateClientEvent(schid, self.myid, 0, "", "")
1139
1140 - def onClientMoveSubscriptionEvent(self, schid, clientID, oldChannelID, 1141 newChannelID, visibility):
1142 self.onClientMoveEvent(schid, clientID, oldChannelID, newChannelID, 1143 visibility, "")
1144
1145 - def onClientMoveTimeoutEvent(self, schid, clientID, oldChannelID, 1146 newChannelID, visibility, timeoutMessage):
1147 self.onClientMoveEvent(schid, clientID, oldChannelID, newChannelID, 1148 visibility, "")
1149
1150 - def onClientKickFromServerEvent(self, schid, clientID, oldChannelID, 1151 newChannelID, visibility, kickerID, 1152 kickerName, kickerUniqueIdentifier, 1153 kickMessage):
1154 self.onClientMoveEvent(schid, clientID, oldChannelID, newChannelID, 1155 visibility, "")
1156
1157 - def onClientDisplayNameChanged(self, schid, clientID, displayName, uid):
1158 self.onUpdateClientEvent(schid, clientID, 0, "", "")
1159
1160 - def onTalkStatusChangeEvent(self, schid, status, isReceivedWhisper, clid):
1161 if schid != self.schid: 1162 return 1163 1164 obj = self.allclients[clid] 1165 talks = status == ts3defines.TalkStatus.STATUS_TALKING 1166 if obj.isTalking != talks: 1167 obj.isTalking = talks 1168 idx = self._createIndex(obj.rowOf(), 0, obj) 1169 self.dataChanged(idx, idx)
1170
1171 - def onServerGroupListEvent(self, schid, serverGroupID, name, atype, iconID, 1172 saveDB):
1173 if schid != self.schid: 1174 return 1175 1176 if iconID != 0: 1177 if iconID < 0: 1178 iconID = pow(2, 32) + iconID 1179 1180 self.sgicons[serverGroupID] = iconID
1181
1182 - def onChannelGroupListEvent(self, schid, channelGroupID, name, atype, 1183 iconID, saveDB):
1184 if schid != self.schid: 1185 return 1186 1187 if iconID != 0: 1188 if iconID < 0: 1189 iconID = pow(2, 32) + iconID 1190 1191 self.cgicons[channelGroupID] = iconID
1192
1193 - def _createIndex(self, row, column, obj):
1194 i = id(obj) 1195 self.objs[i] = obj 1196 return self.createIndex(row, column, i)
1197
1198 - def _indexObject(self, index):
1199 return self.objs[index.internalId()]
1200
1201 - def index(self, row, column, parent):
1202 if not parent.isValid(): 1203 assert row == 0 1204 return self._createIndex(0, column, self.root) 1205 else: 1206 parobj = self._indexObject(parent) 1207 return self._createIndex(row, column, parobj.child(row))
1208
1209 - def parent(self, index):
1210 obj = self._indexObject(index) 1211 1212 if obj == self.root: 1213 return QModelIndex() 1214 else: 1215 parent = obj.parentNode 1216 return self._createIndex(parent.rowOf(), 0, parent)
1217
1218 - def rowCount(self, parent):
1219 if not parent.isValid(): 1220 return 1 1221 else: 1222 return self._indexObject(parent).count()
1223
1224 - def columnCount(self, parent):
1225 return 1
1226
1227 - def data(self, index, role):
1228 obj = self._indexObject(index) 1229 1230 if role == Qt.DisplayRole: 1231 if type(obj) is Client: 1232 if obj.isRecording: 1233 return "*** %s *** [RECORDING]" % obj.displayName 1234 else: 1235 return obj.displayName 1236 1237 return obj.name 1238 elif role == ServerViewRoles.isspacer: 1239 return type(obj) is Channel and obj.isSpacer 1240 elif type(obj) is Channel and role == ServerViewRoles.spacertype: 1241 return obj.spacerType 1242 elif type(obj) is Channel and role == ServerViewRoles.spacercustomtext: 1243 return obj.spacerCustomtext 1244 elif type(obj) is Channel and role == ServerViewRoles.spaceralignment: 1245 return obj.spacerAlignment 1246 elif role == Qt.DecorationRole: 1247 if not (type(obj) is Channel and obj.isSpacer): 1248 return QIcon(self.iconpack.icon(obj.iconVariable())) 1249 elif role == ServerViewRoles.statusicons: 1250 ret = [] 1251 if type(obj) is Channel: 1252 if obj.isDefault: 1253 ret.append(QIcon(self.iconpack.icon("DEFAULT"))) 1254 if obj.codec == ts3defines.CodecType.CODEC_OPUS_MUSIC: 1255 ret.append(QIcon(self.iconpack.icon("MUSIC"))) 1256 if obj.neededTalkPower > 0: 1257 ret.append(QIcon(self.iconpack.icon("MODERATED"))) 1258 if obj.iconID != 0: 1259 ret.append(QIcon(self.icons.icon(obj.iconID))) 1260 elif type(obj) is Client: 1261 # badges 1262 # priority speaker 1263 if obj.isPrioritySpeaker: 1264 ret.append(QIcon(self.iconpack.icon("CAPTURE"))) 1265 # istalker 1266 if obj.isTalker: 1267 ret.append(QIcon(self.iconpack.icon("IS_TALKER"))) 1268 elif obj.talkPower < obj.parentNode.neededTalkPower: 1269 ret.append(QIcon(self.iconpack.icon("INPUT_MUTED"))) 1270 # channelgroup 1271 if obj.channelGroup in self.cgicons: 1272 ret.append(QIcon(self.icons.icon( 1273 self.cgicons[obj.channelGroup]))) 1274 # servergroups 1275 for sg in obj.serverGroups: 1276 if sg in self.sgicons: 1277 ret.append(QIcon(self.icons.icon(self.sgicons[sg]))) 1278 # clienticon 1279 if obj.iconID != 0: 1280 ret.append(QIcon(self.icons.icon(obj.iconID))) 1281 # talkrequest 1282 if obj.isRequestingTalkPower: 1283 ret.append(QIcon(self.iconpack.icon("REQUEST_TALK_POWER"))) 1284 # flag 1285 if obj.country != "": 1286 ret.append(QIcon(self.countries.flag(obj.country))) 1287 else: 1288 assert type(obj) is Server 1289 if obj.iconID != 0: 1290 ret.append(QIcon(self.icons.icon(obj.iconID))) 1291 1292 return ret 1293 elif role == Qt.FontRole: 1294 if type(obj) is Client and obj.isme: 1295 font = QApplication.font() 1296 font.setBold(True) 1297 return font 1298 elif role == Qt.ForegroundRole: 1299 if type(obj) is Client and obj.isRecording: 1300 return QColor(Qt.darkRed) 1301 1302 return None
1303
1304 1305 -class ServerviewDelegate(QStyledItemDelegate):
1306 """ 1307 Delegate to display Serverview items and query the properties and icons 1308 from the model to display and show them in one column. 1309 """ 1310
1311 - def _paintSpacer(self, painter, option, index):
1312 st = index.data(ServerViewRoles.spacertype) 1313 1314 if st != Qt.CustomDashLine: 1315 painter.setPen(st) 1316 painter.drawLine(option.rect.x(), 1317 option.rect.y() + option.rect.height() / 2, 1318 option.rect.x() + option.rect.width(), 1319 option.rect.y() + option.rect.height() / 2) 1320 else: 1321 align = index.data(ServerViewRoles.spaceralignment) 1322 ctext = index.data(ServerViewRoles.spacercustomtext) 1323 1324 if align != Qt.AlignJustify: 1325 painter.drawText(option.rect.x(), option.rect.y(), 1326 option.rect.width(), option.rect.height(), 1327 align, ctext) 1328 else: 1329 fm = QFontMetrics(QApplication.font()) 1330 w = l = fm.width(ctext) 1331 txt = ctext 1332 while l < option.rect.width(): 1333 txt += ctext 1334 l += w 1335 1336 painter.drawText(option.rect.x(), option.rect.y(), 1337 option.rect.width(), option.rect.height(), 1338 Qt.AlignLeft, txt)
1339
1340 - def paint(self, painter, option, index):
1341 if option.state & QStyle.State_Selected: 1342 painter.fillRect(option.rect, option.palette.highlight()) 1343 1344 if not index.isValid(): 1345 super().paint(painter, option, index) 1346 return 1347 1348 if index.data(ServerViewRoles.isspacer): 1349 painter.save() 1350 self._paintSpacer(painter, option, index) 1351 painter.restore() 1352 return 1353 1354 icon = index.data(Qt.DecorationRole) 1355 statusicons = index.data(ServerViewRoles.statusicons) 1356 font = index.data(Qt.FontRole) or QApplication.font() 1357 brush = index.data(Qt.ForegroundRole) or Qt.black 1358 1359 if icon: 1360 iconsize = icon.actualSize(option.decorationSize) 1361 icon.paint(painter, option.rect, Qt.AlignLeft) 1362 else: 1363 iconsize = option.decorationSize 1364 1365 headerRect = option.rect 1366 headerRect.setLeft(headerRect.left() + iconsize.width() + 5) 1367 1368 painter.save() 1369 1370 pen = painter.pen() 1371 pen.setBrush(brush) 1372 painter.setPen(pen) 1373 painter.setFont(font) 1374 1375 painter.drawText(headerRect, Qt.AlignLeft, index.data()) 1376 1377 nextx = 18 1378 if statusicons: 1379 for ico in reversed(statusicons): 1380 ico.paint(painter, option.rect.right() - nextx, 1381 option.rect.y(), iconsize.width(), iconsize.height()) 1382 nextx += 18 1383 1384 painter.restore()
1385
1386 1387 -class Serverview(QTreeView):
1388 """ 1389 A QTreeView widget to display the complete view on a TS3 Server. 1390 """ 1391
1392 - def __init__(self, parent=None, schid=None):
1393 """ 1394 Instantiates a new Serverview widget (including model and delegate). 1395 @param schid: the ID of the serverconnection 1396 @type schid: int 1397 @param parent: parent widget 1398 @type parent: QWidget 1399 """ 1400 super().__init__(parent) 1401 1402 self.svmodel = None 1403 1404 delegate = ServerviewDelegate(self) 1405 self.setItemDelegate(delegate) 1406 1407 self.header().hide() 1408 1409 if schid: 1410 try: 1411 self.setServerConnectionHandlerId(schid) 1412 except Exception as e: 1413 self.delete() 1414 raise e
1415
1416 - def setServerConnectionHandlerId(self, schid):
1417 if not self.svmodel: 1418 self.svmodel = ServerviewModel(schid, None, self) 1419 self.setModel(self.svmodel) 1420 else: 1421 self.svmode.setServerConnectionHandlerId(schid) 1422 1423 self.expandAll()
1424
1425 - def indexToObject(self, index):
1426 """ 1427 Returns the underlying object of a QModelIndex. 1428 @param index: the index of the model 1429 @type index: QModelIndex 1430 @return: the wrapped viewitem 1431 @rtype: Server or Channel or Client 1432 """ 1433 if self.svmodel: 1434 return self.svmodel._indexObject(index) 1435 else: 1436 return None
1437