Esempio n. 1
0
class ABCApp(object):
    def __init__(self,
                 params,
                 installdir,
                 autoload_discovery=True,
                 use_torrent_search=True,
                 use_channel_search=True):
        assert not isInIOThread(
        ), "isInIOThread() seems to not be working correctly"
        self._logger = logging.getLogger(self.__class__.__name__)

        self.params = params
        self.installdir = installdir

        self.state_dir = None
        self.error = None
        self.last_update = 0
        self.ready = False
        self.done = False
        self.frame = None
        self.upgrader = None

        self.said_start_playback = False
        self.decodeprogress = 0

        self.old_reputation = 0

        # DISPERSY will be set when available
        self.dispersy = None
        # BARTER_COMMUNITY will be set when both Dispersy and the EffortCommunity are available
        self.barter_community = None
        self.tunnel_community = None

        self.torrentfeed = None
        self.webUI = None
        self.utility = None

        # Stage 1 start
        session = self.InitStage1(installdir,
                                  autoload_discovery=autoload_discovery,
                                  use_torrent_search=use_torrent_search,
                                  use_channel_search=use_channel_search)

        self.splash = None
        try:
            bm = self.gui_image_manager.getImage(u'splash.png')
            self.splash = GaugeSplash(bm, "Loading...", 13)
            self.splash.Show()

            self._logger.info('Client Starting Up.')
            self._logger.info("Tribler is using %s as working directory",
                              self.installdir)

            # Stage 2: show the splash window and start the session

            self.splash.tick('Starting API')
            s = self.startAPI(session, self.splash.tick)

            self.utility = Utility(self.installdir, s.get_state_dir())

            if self.utility.read_config(u'saveas', u'downloadconfig'):
                DefaultDownloadStartupConfig.getInstance().set_dest_dir(
                    self.utility.read_config(u'saveas', u'downloadconfig'))

            self.utility.set_app(self)
            self.utility.set_session(s)
            self.guiUtility = GUIUtility.getInstance(self.utility, self.params,
                                                     self)
            GUIDBProducer.getInstance()

            self._logger.info('Tribler Version: %s Build: %s', version_id,
                              commit_id)

            version_info = self.utility.read_config('version_info')
            if version_info.get('version_id', None) != version_id:
                # First run of a different version
                version_info['first_run'] = int(time())
                version_info['version_id'] = version_id
                self.utility.write_config('version_info', version_info)

            self.splash.tick(
                'Starting session and upgrading database (it may take a while)'
            )
            s.start()
            self.dispersy = s.lm.dispersy

            self.splash.tick('Loading userdownloadchoice')
            from Tribler.Main.vwxGUI.UserDownloadChoice import UserDownloadChoice
            UserDownloadChoice.get_singleton().set_utility(self.utility)

            self.splash.tick('Initializing Family Filter')
            cat = Category.getInstance(session)

            state = self.utility.read_config('family_filter')
            if state in (1, 0):
                cat.set_family_filter(state == 1)
            else:
                self.utility.write_config('family_filter', 1)
                self.utility.flush_config()

                cat.set_family_filter(True)

            # Create global speed limits
            self.splash.tick('Setting up speed limits')

            # Counter to suppress some event from occurring
            self.ratestatecallbackcount = 0

            maxup = self.utility.read_config('maxuploadrate')
            maxdown = self.utility.read_config('maxdownloadrate')
            # set speed limits using LibtorrentMgr
            s.set_max_upload_speed(maxup)
            s.set_max_download_speed(maxdown)

            # Only allow updates to come in after we defined ratelimiter
            self.prevActiveDownloads = []
            s.set_download_states_callback(self.sesscb_states_callback)

            # Schedule task for checkpointing Session, to avoid hash checks after
            # crashes.
            startWorker(consumer=None,
                        workerFn=self.guiservthread_checkpoint_timer,
                        delay=SESSION_CHECKPOINT_INTERVAL)

            if not ALLOW_MULTIPLE:
                # Put it here so an error is shown in the startup-error popup
                # Start server for instance2instance communication
                Instance2InstanceServer(
                    self.utility.read_config('i2ilistenport'),
                    self.i2ithread_readlinecallback)

            self.splash.tick('GUIUtility register')
            self.guiUtility.register()

            self.frame = MainFrame(
                self, None, PLAYBACKMODE_INTERNAL
                in return_feasible_playback_modes(), self.splash.tick)
            self.frame.SetIcon(
                wx.Icon(
                    os.path.join(self.installdir, 'Tribler', 'Main', 'vwxGUI',
                                 'images', 'tribler.ico'), wx.BITMAP_TYPE_ICO))

            # Arno, 2011-06-15: VLC 1.1.10 pops up separate win, don't have two.
            self.frame.videoframe = None
            if PLAYBACKMODE_INTERNAL in return_feasible_playback_modes():
                vlcwrap = s.lm.videoplayer.get_vlcwrap()
                wx.CallLater(3000, vlcwrap._init_vlc)
                self.frame.videoframe = VideoDummyFrame(
                    self.frame.videoparentpanel, self.utility, vlcwrap)

            if sys.platform == 'win32':
                wx.CallAfter(self.frame.top_bg.Refresh)
                wx.CallAfter(self.frame.top_bg.Layout)
            else:
                self.frame.top_bg.Layout()

            # Arno, 2007-05-03: wxWidgets 2.8.3.0 and earlier have the MIME-type for .bmp
            # files set to 'image/x-bmp' whereas 'image/bmp' is the official one.
            try:
                bmphand = None
                hands = wx.Image.GetHandlers()
                for hand in hands:
                    # print "Handler",hand.GetExtension(),hand.GetType(),hand.GetMimeType()
                    if hand.GetMimeType() == 'image/x-bmp':
                        bmphand = hand
                        break
                # wx.Image.AddHandler()
                if bmphand is not None:
                    bmphand.SetMimeType('image/bmp')
            except:
                # wx < 2.7 don't like wx.Image.GetHandlers()
                print_exc()

            self.splash.Destroy()
            self.frame.Show(True)
            session.lm.threadpool.call_in_thread(
                0, self.guiservthread_free_space_check)

            self.torrentfeed = RssParser.getInstance()

            self.webUI = None
            if self.utility.read_config('use_webui'):
                try:
                    from Tribler.Main.webUI.webUI import WebUI
                    self.webUI = WebUI.getInstance(
                        self.guiUtility.library_manager,
                        self.guiUtility.torrentsearch_manager,
                        self.utility.read_config('webui_port'))
                    self.webUI.start()
                except Exception:
                    print_exc()

            self.emercoin_mgr = None
            try:
                from Tribler.Main.Emercoin.EmercoinMgr import EmercoinMgr
                self.emercoin_mgr = EmercoinMgr(self.utility)
            except Exception:
                print_exc()

            wx.CallAfter(self.PostInit2)

            # 08/02/10 Boudewijn: Working from home though console
            # doesn't allow me to press close.  The statement below
            # gracefully closes Tribler after 120 seconds.
            # wx.CallLater(120*1000, wx.GetApp().Exit)

            self.ready = True

        except Exception as e:
            if self.splash:
                self.splash.Destroy()

            self.onError(e)

    def InitStage1(self,
                   installdir,
                   autoload_discovery=True,
                   use_torrent_search=True,
                   use_channel_search=True):
        """ Stage 1 start: pre-start the session to handle upgrade.
        """

        self.gui_image_manager = GuiImageManager.getInstance(installdir)

        # Start Tribler Session
        defaultConfig = SessionStartupConfig()
        state_dir = defaultConfig.get_state_dir()

        # Switch to the state dir so relative paths can be used (IE, in LevelDB store paths)
        if not os.path.exists(state_dir):
            os.makedirs(state_dir)
        os.chdir(state_dir)

        cfgfilename = Session.get_default_config_filename(state_dir)

        self._logger.debug(u"Session config %s", cfgfilename)
        try:
            self.sconfig = SessionStartupConfig.load(cfgfilename)
        except:
            try:
                self.sconfig = convertSessionConfig(
                    os.path.join(state_dir, 'sessconfig.pickle'), cfgfilename)
                convertMainConfig(state_dir,
                                  os.path.join(state_dir, 'abc.conf'),
                                  os.path.join(state_dir, STATEDIR_GUICONFIG))
            except:
                self.sconfig = SessionStartupConfig()
                self.sconfig.set_state_dir(state_dir)

        self.sconfig.set_install_dir(self.installdir)

        # TODO(emilon): Do we still want to force limit this? With the new
        # torrent store it should be pretty fast even with more that that.

        # Arno, 2010-03-31: Hard upgrade to 50000 torrents collected
        self.sconfig.set_torrent_collecting_max_torrents(50000)

        dlcfgfilename = get_default_dscfg_filename(
            self.sconfig.get_state_dir())
        self._logger.debug("main: Download config %s", dlcfgfilename)
        try:
            defaultDLConfig = DefaultDownloadStartupConfig.load(dlcfgfilename)
        except:
            try:
                defaultDLConfig = convertDefaultDownloadConfig(
                    os.path.join(state_dir, 'dlconfig.pickle'), dlcfgfilename)
            except:
                defaultDLConfig = DefaultDownloadStartupConfig.getInstance()

        if not defaultDLConfig.get_dest_dir():
            defaultDLConfig.set_dest_dir(get_default_dest_dir())
        if not os.path.isdir(defaultDLConfig.get_dest_dir()):
            try:
                os.makedirs(defaultDLConfig.get_dest_dir())
            except:
                # Could not create directory, ask user to select a different location
                dlg = wx.DirDialog(
                    None,
                    "Could not find download directory, please select a new location to store your downloads",
                    style=wx.DEFAULT_DIALOG_STYLE)
                dlg.SetPath(get_default_dest_dir())
                if dlg.ShowModal() == wx.ID_OK:
                    new_dest_dir = dlg.GetPath()
                    defaultDLConfig.set_dest_dir(new_dest_dir)
                    defaultDLConfig.save(dlcfgfilename)
                    self.sconfig.save(cfgfilename)
                else:
                    # Quit
                    self.onError = lambda e: self._logger.error(
                        "tribler: quitting due to non-existing destination directory"
                    )
                    raise Exception()

        if not use_torrent_search:
            self.sconfig.set_enable_torrent_search(False)
        if not use_channel_search:
            self.sconfig.set_enable_channel_search(False)

        session = Session(self.sconfig, autoload_discovery=autoload_discovery)
        session.add_observer(self.show_upgrade_dialog, NTFY_UPGRADER,
                             [NTFY_STARTED])
        self.upgrader = session.prestart()

        while not self.upgrader.is_done:
            wx.SafeYield()
            sleep(0.1)

        return session

    @forceWxThread
    def show_upgrade_dialog(self, subject, changetype, objectID, *args):
        assert wx.Thread_IsMain()

        upgrade_dialog = TriblerUpgradeDialog(self.gui_image_manager,
                                              self.upgrader)
        failed = upgrade_dialog.ShowModal()
        upgrade_dialog.Destroy()
        if failed:
            wx.MessageDialog(
                None, "Failed to upgrade the on disk data.\n\n"
                "Tribler has backed up the old data and will now start from scratch.\n\n"
                "Get in contact with the Tribler team if you want to help debugging this issue.\n\n"
                "Error was: %s" % self.upgrader.current_status,
                "Data format upgrade failed",
                wx.OK | wx.CENTRE | wx.ICON_EXCLAMATION).ShowModal()

    def _frame_and_ready(self):
        return self.ready and self.frame and self.frame.ready

    def PostInit2(self):
        self.frame.Raise()
        self.startWithRightView()
        self.set_reputation()

        s = self.utility.session
        s.add_observer(self.sesscb_ntfy_reachable, NTFY_REACHABLE,
                       [NTFY_INSERT])
        s.add_observer(self.sesscb_ntfy_activities,
                       NTFY_ACTIVITIES, [NTFY_INSERT],
                       cache=10)
        s.add_observer(
            self.sesscb_ntfy_channelupdates,
            NTFY_CHANNELCAST,
            [NTFY_INSERT, NTFY_UPDATE, NTFY_CREATE, NTFY_STATE, NTFY_MODIFIED],
            cache=10)
        s.add_observer(self.sesscb_ntfy_channelupdates,
                       NTFY_VOTECAST, [NTFY_UPDATE],
                       cache=10)
        s.add_observer(self.sesscb_ntfy_myprefupdates, NTFY_MYPREFERENCES,
                       [NTFY_INSERT, NTFY_UPDATE, NTFY_DELETE])
        s.add_observer(self.sesscb_ntfy_torrentupdates,
                       NTFY_TORRENTS, [NTFY_UPDATE, NTFY_INSERT],
                       cache=10)
        s.add_observer(self.sesscb_ntfy_playlistupdates, NTFY_PLAYLISTS,
                       [NTFY_INSERT, NTFY_UPDATE])
        s.add_observer(self.sesscb_ntfy_commentupdates, NTFY_COMMENTS,
                       [NTFY_INSERT, NTFY_DELETE])
        s.add_observer(self.sesscb_ntfy_modificationupdates,
                       NTFY_MODIFICATIONS, [NTFY_INSERT])
        s.add_observer(self.sesscb_ntfy_moderationupdats, NTFY_MODERATIONS,
                       [NTFY_INSERT])
        s.add_observer(self.sesscb_ntfy_markingupdates, NTFY_MARKINGS,
                       [NTFY_INSERT])
        s.add_observer(self.sesscb_ntfy_torrentfinished, NTFY_TORRENTS,
                       [NTFY_FINISHED])
        s.add_observer(
            self.sesscb_ntfy_magnet, NTFY_TORRENTS,
            [NTFY_MAGNET_GOT_PEERS, NTFY_MAGNET_STARTED, NTFY_MAGNET_CLOSE])

        # TODO(emilon): Use the LogObserver I already implemented
        # self.dispersy.callback.attach_exception_handler(self.frame.exceptionHandler)

        startWorker(None,
                    self.loadSessionCheckpoint,
                    delay=5.0,
                    workerType="ThreadPool")

        # initialize the torrent feed thread
        channelcast = s.open_dbhandler(NTFY_CHANNELCAST)

        def db_thread():
            return channelcast.getMyChannelId()

        def wx_thread(delayedResult):
            my_channel = delayedResult.get()
            if my_channel:
                self.torrentfeed.register(self.utility.session, my_channel)
                self.torrentfeed.addCallback(
                    my_channel,
                    self.guiUtility.channelsearch_manager.createTorrentFromDef)

        startWorker(wx_thread, db_thread, delay=5.0)

    def startAPI(self, session, progress):
        @call_on_reactor_thread
        def define_communities(*args):
            assert isInIOThread()
            from Tribler.community.channel.community import ChannelCommunity
            from Tribler.community.channel.preview import PreviewChannelCommunity
            from Tribler.community.tunnel.tunnel_community import TunnelSettings
            from Tribler.community.tunnel.hidden_community import HiddenTunnelCommunity

            # make sure this is only called once
            session.remove_observer(define_communities)

            dispersy = session.get_dispersy_instance()

            self._logger.info("tribler: Preparing communities...")
            now = time()

            dispersy.attach_progress_handler(self.progressHandler)

            default_kwargs = {'tribler_session': session}
            # must be called on the Dispersy thread
            if session.get_barter_community_enabled():
                from Tribler.community.bartercast4.community import BarterCommunity
                dispersy.define_auto_load(BarterCommunity,
                                          session.dispersy_member,
                                          load=True)

            # load metadata community
            dispersy.define_auto_load(ChannelCommunity,
                                      session.dispersy_member,
                                      load=True,
                                      kargs=default_kwargs)
            dispersy.define_auto_load(PreviewChannelCommunity,
                                      session.dispersy_member,
                                      kargs=default_kwargs)

            keypair = dispersy.crypto.generate_key(u"curve25519")
            dispersy_member = dispersy.get_member(
                private_key=dispersy.crypto.key_to_bin(keypair), )
            settings = TunnelSettings(session.get_install_dir(),
                                      tribler_session=session)
            tunnel_kwargs = {'tribler_session': session, 'settings': settings}

            self.tunnel_community = dispersy.define_auto_load(
                HiddenTunnelCommunity,
                dispersy_member,
                load=True,
                kargs=tunnel_kwargs)[0]

            session.set_anon_proxy_settings(
                2, ("127.0.0.1",
                    session.get_tunnel_community_socks5_listen_ports()))

            diff = time() - now
            self._logger.info("tribler: communities are ready in %.2f seconds",
                              diff)

        session.add_observer(define_communities, NTFY_DISPERSY, [NTFY_STARTED])

        return session

    @forceWxThread
    def sesscb_ntfy_myprefupdates(self, subject, changeType, objectID, *args):
        if self._frame_and_ready():
            if changeType in [NTFY_INSERT, NTFY_UPDATE]:
                if changeType == NTFY_INSERT:
                    if self.frame.searchlist:
                        manager = self.frame.searchlist.GetManager()
                        manager.downloadStarted(objectID)

                    manager = self.frame.selectedchannellist.GetManager()
                    manager.downloadStarted(objectID)

                manager = self.frame.librarylist.GetManager()
                manager.downloadStarted(objectID)
            elif changeType == NTFY_DELETE:
                self.guiUtility.frame.librarylist.RemoveItem(objectID)

                if self.guiUtility.frame.librarylist.IsShownOnScreen() and \
                   self.guiUtility.frame.librarydetailspanel.torrent and \
                   self.guiUtility.frame.librarydetailspanel.torrent.infohash == objectID:
                    self.guiUtility.frame.librarylist.ResetBottomWindow()
                    self.guiUtility.frame.top_bg.ClearButtonHandlers()

                if self.guiUtility.frame.librarylist.list.IsEmpty():
                    self.guiUtility.frame.librarylist.SetData([])

    def progressHandler(self, title, message, maximum):
        from Tribler.Main.Dialogs.ThreadSafeProgressDialog import ThreadSafeProgressDialog
        return ThreadSafeProgressDialog(
            title, message, maximum, None, wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME
            | wx.PD_ESTIMATED_TIME | wx.PD_REMAINING_TIME | wx.PD_AUTO_HIDE)

    def set_reputation(self):
        def do_db():
            nr_connections = 0
            nr_channel_connections = 0
            if self.dispersy:
                for community in self.dispersy.get_communities():
                    from Tribler.community.search.community import SearchCommunity
                    from Tribler.community.allchannel.community import AllChannelCommunity

                    if isinstance(community, SearchCommunity):
                        nr_connections = community.get_nr_connections()
                    elif isinstance(community, AllChannelCommunity):
                        nr_channel_connections = community.get_nr_connections()

            return nr_connections, nr_channel_connections

        def do_wx(delayedResult):
            if not self.frame:
                return

            nr_connections, nr_channel_connections = delayedResult.get()

            # self.frame.SRstatusbar.set_reputation(myRep, total_down, total_up)

            # bitmap is 16px wide, -> but first and last pixel do not add anything.
            percentage = min(1.0, (nr_connections + 1) / 16.0)
            self.frame.SRstatusbar.SetConnections(percentage, nr_connections,
                                                  nr_channel_connections)

        """ set the reputation in the GUI"""
        if self._frame_and_ready():
            startWorker(do_wx, do_db, uId=u"tribler.set_reputation")
        startWorker(None,
                    self.set_reputation,
                    delay=5.0,
                    workerType="ThreadPool")

    def sesscb_states_callback(self, dslist):
        if not self.ready:
            return 5.0, []

        wantpeers = []
        self.ratestatecallbackcount += 1
        try:
            # Print stats on Console
            if self.ratestatecallbackcount % 5 == 0:
                for ds in dslist:
                    safename = repr(ds.get_download().get_def().get_name())
                    self._logger.debug("%s %s %.1f%% dl %.1f ul %.1f n %d",
                                       safename,
                                       dlstatus_strings[ds.get_status()],
                                       100.0 * ds.get_progress(),
                                       ds.get_current_speed(DOWNLOAD),
                                       ds.get_current_speed(UPLOAD),
                                       ds.get_num_peers())
                    if ds.get_status() == DLSTATUS_STOPPED_ON_ERROR:
                        self._logger.error("main: Error: %s",
                                           repr(ds.get_error()))
                        download = self.utility.session.lm.downloads.get(
                            ds.get_infohash())
                        if download:
                            download.stop()

                        # show error dialog
                        dlg = wx.MessageDialog(
                            self.frame, "Download stopped on error: %s" %
                            repr(ds.get_error()), "Download Error",
                            wx.OK | wx.ICON_ERROR)
                        dlg.ShowModal()
                        dlg.Destroy()

            # Pass DownloadStates to libaryView
            no_collected_list = [ds for ds in dslist]
            try:
                # Arno, 2012-07-17: Retrieving peerlist for the DownloadStates takes CPU
                # so only do it when needed for display.
                wantpeers.extend(
                    self.guiUtility.library_manager.download_state_callback(
                        no_collected_list))
            except:
                print_exc()

            # Check to see if a download has finished
            newActiveDownloads = []
            doCheckpoint = False
            seeding_download_list = []
            for ds in dslist:
                state = ds.get_status()
                download = ds.get_download()
                tdef = download.get_def()
                safename = tdef.get_name_as_unicode()

                if state == DLSTATUS_DOWNLOADING:
                    newActiveDownloads.append(safename)

                elif state == DLSTATUS_SEEDING:
                    seeding_download_list.append({
                        u'infohash':
                        tdef.get_infohash(),
                        u'download':
                        download,
                    })

                    if safename in self.prevActiveDownloads:
                        infohash = tdef.get_infohash()

                        self.utility.session.notifier.notify(
                            NTFY_TORRENTS, NTFY_FINISHED, infohash, safename)

                        doCheckpoint = True

                    elif download.get_hops(
                    ) == 0 and download.get_safe_seeding():
                        self._logger.info(
                            "Re-add torrent with default nr of hops to prevent naked seeding"
                        )
                        self.utility.session.remove_download(download)

                        # copy the old download_config and change the hop count
                        dscfg = download.copy()
                        dscfg.set_hops(
                            self.utility.read_config('default_number_hops'))

                        # TODO(emilon): That's a hack to work around the fact
                        # that removing a torrent is racy.
                        self.utility.session.lm.threadpool.call(
                            0.5, reactor.callInThread,
                            self.utility.session.start_download, tdef, dscfg)

            self.prevActiveDownloads = newActiveDownloads
            if doCheckpoint:
                self.utility.session.checkpoint()

            if self.utility.read_config(u'seeding_mode') == 'never':
                for data in seeding_download_list:
                    data[u'download'].stop()
                    from Tribler.Main.vwxGUI.UserDownloadChoice import UserDownloadChoice
                    UserDownloadChoice.get_singleton().set_download_state(
                        data[u'infohash'], "stop")

            # Adjust speeds and call TunnelCommunity.monitor_downloads once every 4 seconds
            adjustspeeds = False
            if self.ratestatecallbackcount % 4 == 0:
                adjustspeeds = True

            if adjustspeeds and self.tunnel_community:
                self.tunnel_community.monitor_downloads(dslist)

        except:
            print_exc()

        return 1.0, wantpeers

    def loadSessionCheckpoint(self):
        pstate_dir = self.utility.session.get_downloads_pstate_dir()

        filelist = os.listdir(pstate_dir)
        if any([filename.endswith('.pickle') for filename in filelist]):
            convertDownloadCheckpoints(pstate_dir)

        from Tribler.Main.vwxGUI.UserDownloadChoice import UserDownloadChoice
        user_download_choice = UserDownloadChoice.get_singleton()
        initialdlstatus_dict = {}
        for infohash, state in user_download_choice.get_download_states(
        ).iteritems():
            if state == 'stop':
                initialdlstatus_dict[infohash] = DLSTATUS_STOPPED

        self.utility.session.load_checkpoint(
            initialdlstatus_dict=initialdlstatus_dict)

    def guiservthread_free_space_check(self):
        if not (self and self.frame and self.frame.SRstatusbar):
            return

        free_space = get_free_space(
            DefaultDownloadStartupConfig.getInstance().get_dest_dir())
        self.frame.SRstatusbar.RefreshFreeSpace(free_space)

        storage_locations = defaultdict(list)
        for download in self.utility.session.get_downloads():
            if download.get_status() == DLSTATUS_DOWNLOADING:
                storage_locations[download.get_dest_dir()].append(download)

        show_message = False
        low_on_space = [
            path for path in storage_locations.keys() if 0 < get_free_space(
                path) < self.utility.read_config('free_space_threshold')
        ]
        for path in low_on_space:
            for download in storage_locations[path]:
                download.stop()
                show_message = True

        if show_message:
            wx.CallAfter(
                wx.MessageBox,
                "Tribler has detected low disk space. Related downloads have been stopped.",
                "Error")

        self.utility.session.lm.threadpool.call_in_thread(
            FREE_SPACE_CHECK_INTERVAL, self.guiservthread_free_space_check)

    def guiservthread_checkpoint_timer(self):
        """ Periodically checkpoint Session """
        if self.done:
            return
        try:
            self._logger.info("main: Checkpointing Session")
            self.utility.session.checkpoint()

            self.utility.session.lm.threadpool.call_in_thread(
                SESSION_CHECKPOINT_INTERVAL,
                self.guiservthread_checkpoint_timer)
        except:
            print_exc()

    @forceWxThread
    def sesscb_ntfy_activities(self, events):
        if self._frame_and_ready():
            for args in events:
                objectID = args[2]
                args = args[3:]

                self.frame.setActivity(objectID, *args)

    @forceWxThread
    def sesscb_ntfy_reachable(self, subject, changeType, objectID, msg):
        if self._frame_and_ready():
            self.frame.SRstatusbar.onReachable()

    @forceWxThread
    def sesscb_ntfy_channelupdates(self, events):
        if self._frame_and_ready():
            for args in events:
                subject = args[0]
                changeType = args[1]
                objectID = args[2]

                if self.frame.channellist:
                    if len(args) > 3:
                        myvote = args[3]
                    else:
                        myvote = False

                    manager = self.frame.channellist.GetManager()
                    manager.channelUpdated(objectID,
                                           subject == NTFY_VOTECAST,
                                           myvote=myvote)

                manager = self.frame.selectedchannellist.GetManager()
                manager.channelUpdated(objectID,
                                       stateChanged=changeType == NTFY_STATE,
                                       modified=changeType == NTFY_MODIFIED)

                if changeType == NTFY_CREATE:
                    if self.frame.channellist:
                        self.frame.channellist.SetMyChannelId(objectID)

                    self.torrentfeed.register(self.utility.session, objectID)
                    self.torrentfeed.addCallback(
                        objectID, self.guiUtility.channelsearch_manager.
                        createTorrentFromDef)

                self.frame.managechannel.channelUpdated(
                    objectID,
                    created=changeType == NTFY_CREATE,
                    modified=changeType == NTFY_MODIFIED)

    @forceWxThread
    def sesscb_ntfy_torrentupdates(self, events):
        if self._frame_and_ready():
            infohashes = [args[2] for args in events]

            if self.frame.searchlist:
                manager = self.frame.searchlist.GetManager()
                manager.torrentsUpdated(infohashes)

                manager = self.frame.selectedchannellist.GetManager()
                manager.torrentsUpdated(infohashes)

                manager = self.frame.playlist.GetManager()
                manager.torrentsUpdated(infohashes)

                manager = self.frame.librarylist.GetManager()
                manager.torrentsUpdated(infohashes)

            from Tribler.Main.Utility.GuiDBTuples import CollectedTorrent

            if self.frame.torrentdetailspanel.torrent and self.frame.torrentdetailspanel.torrent.infohash in infohashes:
                # If an updated torrent is being shown in the detailspanel, make sure the information gets refreshed.
                t = self.frame.torrentdetailspanel.torrent
                torrent = t.torrent if isinstance(t, CollectedTorrent) else t
                self.frame.torrentdetailspanel.setTorrent(torrent)

            if self.frame.librarydetailspanel.torrent and self.frame.librarydetailspanel.torrent.infohash in infohashes:
                t = self.frame.librarydetailspanel.torrent
                torrent = t.torrent if isinstance(t, CollectedTorrent) else t
                self.frame.librarydetailspanel.setTorrent(torrent)

    def sesscb_ntfy_torrentfinished(self, subject, changeType, objectID,
                                    *args):
        self.guiUtility.Notify(
            "Download Completed",
            "Torrent '%s' has finished downloading. Now seeding." % args[0],
            icon='seed')

        if self._frame_and_ready():
            infohash = objectID
            torrent = self.guiUtility.torrentsearch_manager.getTorrentByInfohash(
                infohash)
            self.guiUtility.library_manager.addDownloadState(torrent)

    def sesscb_ntfy_magnet(self, subject, changetype, objectID, *args):
        if changetype == NTFY_MAGNET_STARTED:
            self.guiUtility.library_manager.magnet_started(objectID)
        elif changetype == NTFY_MAGNET_GOT_PEERS:
            self.guiUtility.library_manager.magnet_got_peers(objectID, args[0])
        elif changetype == NTFY_MAGNET_CLOSE:
            self.guiUtility.library_manager.magnet_close(objectID)

    @forceWxThread
    def sesscb_ntfy_playlistupdates(self, subject, changeType, objectID,
                                    *args):
        if self._frame_and_ready():
            if changeType == NTFY_INSERT:
                self.frame.managechannel.playlistCreated(objectID)

                manager = self.frame.selectedchannellist.GetManager()
                manager.playlistCreated(objectID)

            else:
                self.frame.managechannel.playlistUpdated(
                    objectID, modified=changeType == NTFY_MODIFIED)

                if len(args) > 0:
                    infohash = args[0]
                else:
                    infohash = False
                manager = self.frame.selectedchannellist.GetManager()
                manager.playlistUpdated(objectID,
                                        infohash,
                                        modified=changeType == NTFY_MODIFIED)

                manager = self.frame.playlist.GetManager()
                manager.playlistUpdated(objectID,
                                        modified=changeType == NTFY_MODIFIED)

    @forceWxThread
    def sesscb_ntfy_commentupdates(self, subject, changeType, objectID, *args):
        if self._frame_and_ready():
            self.frame.selectedchannellist.OnCommentCreated(objectID)
            self.frame.playlist.OnCommentCreated(objectID)

    @forceWxThread
    def sesscb_ntfy_modificationupdates(self, subject, changeType, objectID,
                                        *args):
        if self._frame_and_ready():
            self.frame.selectedchannellist.OnModificationCreated(objectID)
            self.frame.playlist.OnModificationCreated(objectID)

    @forceWxThread
    def sesscb_ntfy_moderationupdats(self, subject, changeType, objectID,
                                     *args):
        if self._frame_and_ready():
            self.frame.selectedchannellist.OnModerationCreated(objectID)
            self.frame.playlist.OnModerationCreated(objectID)

    @forceWxThread
    def sesscb_ntfy_markingupdates(self, subject, changeType, objectID, *args):
        if self._frame_and_ready():
            self.frame.selectedchannellist.OnMarkingCreated(objectID)
            self.frame.playlist.OnModerationCreated(objectID)

    @forceWxThread
    def onError(self, e):
        print_exc()
        _, value, stack = sys.exc_info()
        backtrace = traceback.format_exception(type, value, stack)

        win = FeedbackWindow(
            "Unfortunately, Tribler ran into an internal error")
        win.CreateOutputWindow('')
        for line in backtrace:
            win.write(line)

        win.ShowModal()

    @forceWxThread
    def OnExit(self):
        bm = self.gui_image_manager.getImage(u'closescreen.png')
        self.closewindow = GaugeSplash(bm, "Closing...", 6)
        self.closewindow.Show()

        self._logger.info("main: ONEXIT")
        self.ready = False
        self.done = True

        # write all persistent data to disk
        self.closewindow.tick('Write all persistent data to disk')
        if self.torrentfeed:
            self.torrentfeed.shutdown()
            self.torrentfeed.delInstance()
        if self.webUI:
            self.webUI.stop()
            self.webUI.delInstance()

        if self.frame:
            self.frame.Destroy()
            self.frame = None

        # Don't checkpoint, interferes with current way of saving Preferences,
        # see Tribler/Main/Dialogs/abcoption.py
        if self.utility:
            # Niels: lets add a max waiting time for this session shutdown.
            session_shutdown_start = time()

            try:
                self._logger.info("ONEXIT cleaning database")
                self.closewindow.tick('Cleaning database')
                torrent_db = self.utility.session.open_dbhandler(NTFY_TORRENTS)
                torrent_db._db.clean_db(randint(0, 24) == 0, exiting=True)
            except:
                print_exc()

            self.closewindow.tick('Shutdown session')
            self.utility.session.shutdown(hacksessconfcheckpoint=False)

            # Arno, 2012-07-12: Shutdown should be quick
            # Niels, 2013-03-21: However, setting it too low will prevent checkpoints from being written to disk
            waittime = 60
            while not self.utility.session.has_shutdown():
                diff = time() - session_shutdown_start
                if diff > waittime:
                    self._logger.info(
                        "main: ONEXIT NOT Waiting for Session to shutdown, took too long"
                    )
                    break

                self._logger.info(
                    "ONEXIT Waiting for Session to shutdown, will wait for an additional %d seconds",
                    waittime - diff)
                sleep(3)
            self._logger.info("ONEXIT Session is shutdown")

        self.closewindow.tick('Deleting instances')
        self._logger.debug("ONEXIT deleting instances")

        Session.del_instance()
        GUIUtility.delInstance()
        GUIDBProducer.delInstance()
        DefaultDownloadStartupConfig.delInstance()
        GuiImageManager.delInstance()

        self.closewindow.tick('Exiting now')

        self.closewindow.Destroy()

        return 0

    def db_exception_handler(self, e):
        self._logger.debug("Database Exception handler called %s value %s #",
                           e, e.args)
        try:
            if e.args[1] == "DB object has been closed":
                return  # We caused this non-fatal error, don't show.
            if self.error is not None and self.error.args[1] == e.args[1]:
                return  # don't repeat same error
        except:
            self._logger.error("db_exception_handler error %s %s", e, type(e))
            print_exc()
            # print_stack()

        self.onError(e)

    def getConfigPath(self):
        return self.utility.getConfigPath()

    def startWithRightView(self):
        if self.params[0] != "":
            self.guiUtility.ShowPage('my_files')

    def i2ithread_readlinecallback(self, ic, cmd):
        """ Called by Instance2Instance thread """

        self._logger.info("main: Another instance called us with cmd %s", cmd)
        ic.close()

        if cmd.startswith('START '):
            param = cmd[len('START '):].strip().decode("utf-8")
            torrentfilename = None
            if param.startswith('http:'):
                # Retrieve from web
                f = tempfile.NamedTemporaryFile()
                n = urllib2.urlopen(param)
                data = n.read()
                f.write(data)
                f.close()
                n.close()
                torrentfilename = f.name
            else:
                torrentfilename = param

            # Switch to GUI thread
            # New for 5.0: Start in VOD mode
            def start_asked_download():
                if torrentfilename.startswith("magnet:"):
                    self.frame.startDownloadFromMagnet(torrentfilename)
                else:
                    self.frame.startDownload(torrentfilename)
                self.guiUtility.ShowPage('my_files')

            wx.CallAfter(start_asked_download)
Esempio n. 2
0
class GUIUtility(object):
    __single = None
    __single_lock = Lock()
    START_GAUGE_SPLASH_TICKS = 10
    CLOSE_GAUGE_SPLASH_TICKS = 6

    def __init__(self, utility=None, params=None, app=None):
        if GUIUtility.__single:
            raise RuntimeError("GUIUtility is singleton")
        GUIUtility.__single = self
        self.registered = False

        self._logger = logging.getLogger(self.__class__.__name__)

        # do other init
        self.utility = utility
        self.vwxGUI_path = os.path.join(self.utility.getPath(), LIBRARYNAME,
                                        'Main', 'vwxGUI')
        self.utility.guiUtility = self
        self.params = params
        self.frame = None
        self.startup_splash = None
        self.close_splash = None
        self.app = app
        self.close_splash = None
        self.startup_splash = None
        self.startup_completed = False
        self.close_completed = False

        # videoplayer
        self.videoplayer = None

        # current GUI page
        self.guiPage = 'home'
        # previous pages
        self.oldpage = []

        # firewall
        self.firewall_restart = False  # ie Tribler needs to restart for the port number to be updated

        # Recall improves by 20-25% by increasing the number of peers to query to 20 from 10 !
        self.max_remote_queries = 20  # max number of remote peers to query

        self.current_search_query = ''

        self.lists = []

        from Tribler.Main.vwxGUI.list_header import ListHeaderIcon

        self.listicon = ListHeaderIcon.getInstance()

        # Add listeners to listen if tribler is starting the initialisation and closing procedures.
        self.utility.session.add_observer(self.on_show_startup_splash,
                                          NTFY_STARTUP_TICK, [NTFY_CREATE])
        self.utility.session.add_observer(self.on_show_close_splash,
                                          NTFY_CLOSE_TICK, [NTFY_CREATE])

        # Add listeners to update ticks
        self.utility.session.add_observer(self.on_startup_tick,
                                          NTFY_STARTUP_TICK, [NTFY_INSERT])
        self.utility.session.add_observer(self.on_close_tick, NTFY_CLOSE_TICK,
                                          [NTFY_INSERT])

        # Add listeners to listen when to destroy the gauge splash
        self.utility.session.add_observer(self.destroy_startup_splash,
                                          NTFY_STARTUP_TICK, [NTFY_DELETE])
        self.utility.session.add_observer(self.destroy_close_splash,
                                          NTFY_CLOSE_TICK, [NTFY_DELETE])

    def getInstance(*args, **kw):
        if GUIUtility.__single is None:
            GUIUtility(*args, **kw)
        return GUIUtility.__single

    getInstance = staticmethod(getInstance)

    def hasInstance():
        return GUIUtility.__single is not None

    hasInstance = staticmethod(hasInstance)

    def delInstance():
        with GUIUtility.__single_lock:
            if GUIUtility.__single and GUIUtility.__single.registered:
                GUIUtility.__single.listicon.delInstance()
                GUIUtility.__single.library_manager = None
                GUIUtility.__single.channelsearch_manager.delInstance()
                GUIUtility.__single.torrentsearch_manager = None

            GUIUtility.__single = None

    delInstance = staticmethod(delInstance)

    def register(self):
        if not self.registered:
            self.registered = True

            self.torrentsearch_manager = TorrentManager(self)
            self.channelsearch_manager = ChannelManager.getInstance()
            self.library_manager = LibraryManager(self)

            self.torrentsearch_manager.connect(self.utility.session,
                                               self.library_manager,
                                               self.channelsearch_manager)
            self.channelsearch_manager.connect(self.utility.session,
                                               self.library_manager,
                                               self.torrentsearch_manager)
            self.library_manager.connect(self.utility.session,
                                         self.torrentsearch_manager,
                                         self.channelsearch_manager)
        else:
            raise RuntimeError('GuiUtility is already registered')

    def ShowPlayer(self):
        # TODO(emilon): Hack to work around video player tab not been properly
        # drawn when clicking on the stream button of a search result which
        # hasn't had it's torrent fetched.
        wx.SafeYield()
        self.showLibrary()
        wx.SafeYield()
        #EO Hack

        if self.frame.videoparentpanel:
            self.ShowPage('videoplayer')

    @forceWxThread
    def ShowPage(self, page, *args):
        if page == 'settings':
            from Tribler.Main.vwxGUI.settingsDialog import SettingsDialog
            dialog = SettingsDialog()

            dialog.Centre()
            dialog.ShowModal()
            dialog.Destroy()

        elif page != self.guiPage:
            self.frame.actlist.selectTab(page)

            self.frame.top_bg.ClearButtonHandlers()

            self.oldpage.append(self.guiPage)
            if len(self.oldpage) > 3:
                self.oldpage.pop(0)

            #self.frame.Freeze()

            if page not in [
                    'search_results', 'my_files', 'selectedchannel',
                    'playlist', 'channels'
            ]:
                self.frame.splitter.Show(False)

            if page == 'search_results':
                # Show list
                self.SetTopSplitterWindow(self.frame.searchlist)
                items = self.frame.searchlist.GetExpandedItems()
                if items:
                    self.frame.searchlist.Select(items[0][0], force=True)
                else:
                    self.frame.searchlist.ResetBottomWindow()
            elif self.guiPage == 'search_results':
                # Hide list
                self.frame.searchlist.Show(False)

            if page == 'channels':
                self.SetTopSplitterWindow(self.frame.channellist)
                items = self.frame.channellist.GetExpandedItems()
                if items:
                    self.frame.channellist.Select(items[0][0], force=True)
                else:
                    self.frame.channellist.ResetBottomWindow()

            elif self.guiPage == 'channels':
                self.frame.channellist.Show(False)

            if page == 'mychannel':
                # Show list
                self.frame.managechannel.SetChannelId(
                    self.channelsearch_manager.channelcast_db._channel_id)
                self.frame.managechannel.Show()

            elif self.guiPage == 'mychannel':
                self.frame.managechannel.Show(False)

            if page == 'managechannel':
                self.frame.managechannel.Show()

            elif self.guiPage == 'managechannel':
                self.frame.managechannel.Show(False)

            if page == 'selectedchannel':
                self.SetTopSplitterWindow(self.frame.selectedchannellist)
                items = self.frame.selectedchannellist.GetExpandedItems()
                if items:
                    self.frame.selectedchannellist.Select(items[0][0],
                                                          force=True)
                else:
                    self.frame.selectedchannellist.ResetBottomWindow()
                channelmenu = self.frame.actlist.GetItem(3)
                if channelmenu and channelmenu.expandedPanel:
                    channelmenu.expandedPanel.AddCurrentChannelLink()

            elif self.guiPage == 'selectedchannel':
                self.frame.selectedchannellist.Show(False)
                if not self.frame.splitter.IsSplit():
                    sashpos = getattr(self.frame.splitter_top_window,
                                      'sashpos', -185)
                    self.frame.splitter.SplitHorizontally(
                        self.frame.splitter_top_window,
                        self.frame.splitter_bottom_window, sashpos)

            if page == 'playlist':
                self.SetTopSplitterWindow(self.frame.playlist)
                items = self.frame.playlist.GetExpandedItems()
                if items:
                    self.frame.playlist.Select(items[0][0])
                else:
                    self.frame.playlist.ResetBottomWindow()
                channelmenu = self.frame.actlist.GetItem(3)
                if channelmenu and channelmenu.expandedPanel:
                    channelmenu.expandedPanel.AddCurrentPlaylistLink()

            elif self.guiPage == 'playlist':
                self.frame.playlist.Show(False)

            if page == 'my_files':
                # Show list
                self.SetTopSplitterWindow(self.frame.librarylist)

                # Open infohash
                if args:
                    self.frame.librarylist.GetManager().refresh_or_expand(
                        args[0])
                else:
                    items = self.frame.librarylist.GetExpandedItems()
                    if items:
                        self.frame.librarylist.Select(items[0][0], force=True)
                    else:
                        self.frame.librarylist.ResetBottomWindow()

            elif self.guiPage == 'my_files':
                # Hide list
                self.frame.librarylist.Show(False)

            if page == 'creditmining':
                self.frame.creditminingpanel.Show(True)

            elif self.guiPage == 'creditmining':
                self.frame.creditminingpanel.Show(False)

            if page == 'home':
                self.frame.home.ResetSearchBox()
                self.frame.home.Show()
            elif self.guiPage == 'home':
                self.frame.home.Show(False)

            if page == 'stats':
                self.frame.stats.Show()
            elif self.guiPage == 'stats':
                self.frame.stats.Show(False)

            if page == 'networkgraph':
                self.frame.networkgraph.Show()
            elif self.guiPage == 'networkgraph':
                self.frame.networkgraph.Show(False)

            if self.frame.videoparentpanel:
                if page == 'videoplayer':
                    self.frame.videoparentpanel.Show(True)
                elif self.guiPage == 'videoplayer':
                    self.frame.videoparentpanel.Show(False)

            self.guiPage = page
            self.frame.Layout()
            #self.frame.Thaw()

        # Set focus to page
        if page == 'search_results':
            self.frame.searchlist.Focus()

            if args:
                self.frame.searchlist.total_results = None
                self.frame.searchlist.SetKeywords(args[0])

        elif page == 'channels':
            self.frame.channellist.Focus()
        elif page == 'selectedchannel':
            self.frame.selectedchannellist.Focus()
        elif page == 'my_files':
            self.frame.librarylist.Focus()

    @forceWxThread
    def on_show_startup_splash(self, subject, changetype, objectID, *args):
        gui_image_manager = GuiImageManager.getInstance()
        bm = gui_image_manager.getImage(u'splash.png')
        self.startup_splash = GaugeSplash(bm, "Loading...",
                                          self.START_GAUGE_SPLASH_TICKS)

        # Check if the destroy message has already arrived. Loading the splash takes a while,
        # so the setup may have already been completed.
        if self.startup_completed:
            self.destroy_startup_splash(None, None, None, None)

    @forceWxThread
    def on_startup_tick(self, subject, changetype, objectID, *args):
        if self.startup_splash:
            self.startup_splash.tick(args[0])

    @forceWxThread
    def destroy_startup_splash(self, subject, changetype, objectID, *args):
        self.startup_completed = True
        if self.startup_splash:
            self.startup_splash.Destroy()

    @forceWxThread
    def on_show_close_splash(self, subject, changetype, objectID, *args):
        gui_image_manager = GuiImageManager.getInstance()
        bm = gui_image_manager.getImage(u'closescreen.png')
        self.close_splash = GaugeSplash(bm, "Closing...",
                                        self.CLOSE_GAUGE_SPLASH_TICKS)
        if self.close_completed:
            self.destroy_close_splash(None, None, None, None)

    @forceWxThread
    def on_close_tick(self, subject, changetype, objectID, *args):
        if self.close_splash:
            self.close_splash.tick(args[0])

    @forceWxThread
    def destroy_close_splash(self, subject, changetype, objectID, *args):
        if self.close_splash:
            self.close_splash.Destroy()

    def GetSelectedPage(self):
        if self.guiPage == 'home':
            return self.frame.home

        if self.guiPage == 'search_results':
            return self.frame.searchlist

        if self.guiPage == 'channels':
            return self.frame.channellist

        if self.guiPage == 'selectedchannel':
            return self.frame.selectedchannellist

        if self.guiPage == 'mychannel':
            return self.frame.managechannel

        if self.guiPage == 'managechannel':
            return self.frame.managechannel

        if self.guiPage == 'playlist':
            return self.frame.playlist

        if self.guiPage == 'my_files':
            return self.frame.librarylist

        if self.guiPage == 'creditmining':
            return self.frame.creditminingpanel

    def SetTopSplitterWindow(self, window=None, show=True):
        while self.frame.splitter_top.GetChildren():
            self.frame.splitter_top.Detach(0)

        if window:
            self.frame.splitter_top.Add(window, 1, wx.EXPAND)
            window.Show(show)
        self.frame.splitter.Show(show)
        self.frame.splitter_top.Layout()
        self.frame.splitter_top_window.Refresh()

    def SetBottomSplitterWindow(self, panel_type):
        #self.frame.splitter_bottom_window.Freeze()

        from Tribler.Main.vwxGUI.list_details import TorrentDetails, ChannelInfoPanel, LibraryDetails, ChannelDetails, PlaylistDetails, SearchInfoPanel, LibraryInfoPanel, SelectedchannelInfoPanel, PlaylistInfoPanel

        type_to_panel = {
            TorrentDetails.__name__: self.frame.torrentdetailspanel,
            LibraryDetails.__name__: self.frame.librarydetailspanel,
            ChannelDetails.__name__: self.frame.channeldetailspanel,
            PlaylistDetails.__name__: self.frame.playlistdetailspanel,
            SearchInfoPanel.__name__: self.frame.searchinfopanel,
            ChannelInfoPanel.__name__: self.frame.channelinfopanel,
            LibraryInfoPanel.__name__: self.frame.libraryinfopanel,
            PlaylistInfoPanel.__name__: self.frame.playlistinfopanel,
            SelectedchannelInfoPanel.__name__:
            self.frame.selectedchannelinfopanel
        }

        result = None
        for pt, pl in type_to_panel.iteritems():
            pl.Show(pt == panel_type.__name__)
            if pt == panel_type.__name__:
                result = pl
        if self.guiPage not in ['mychannel', 'home']:
            self.frame.splitter.Show(True)
        self.frame.splitter_bottom.Layout()
        #self.frame.splitter_bottom_window.Thaw()
        self.frame.splitter_bottom_window.Refresh()
        return result

    def SetColumnInfo(self, itemtype, columns, hide_defaults=[]):
        # Load hidden column info
        hide_columns = self.ReadGuiSetting("hide_columns", default={})
        hide_columns = hide_columns.get(itemtype.__name__, {})
        for index, column in enumerate(columns):
            if column['name'] in hide_columns:
                column['show'] = hide_columns[column['name']]
            else:
                column['show'] = not (index in hide_defaults)

        # Load column width info
        column_sizes = self.ReadGuiSetting("column_sizes", default={})
        column_sizes = column_sizes.get(itemtype.__name__, {})
        for index, column in enumerate(columns):
            if column['name'] in column_sizes:
                column['width'] = column_sizes[column['name']]

        return columns

    def ReadGuiSetting(self, setting_name, default=None, do_json=True):
        setting_value = self.utility.read_config(setting_name,
                                                 literal_eval=False)
        if do_json and setting_value:
            setting_value = json.loads(setting_value)
        elif not setting_value:
            setting_value = default
        return setting_value

    def WriteGuiSetting(self, setting_name, setting_value, do_json=True):
        self.utility.write_config(
            setting_name,
            json.dumps(setting_value) if do_json else setting_value)
        self.utility.flush_config()

    @forceWxThread
    def GoBack(self, scrollTo=None, topage=None):
        if topage:
            self.oldpage.pop()
        else:
            if len(self.oldpage) > 0:
                topage = self.oldpage.pop()
            else:
                return

        if topage == 'search_results':
            self.frame.actlist.selectTab('results')
        elif topage in ['channels', 'selectedchannel', 'mychannel']:
            self.frame.actlist.selectTab('channels')
        else:
            self.frame.actlist.selectTab(topage)

        self.ShowPage(topage)
        self.oldpage.pop()  # remove curpage from history

        if scrollTo:
            self.ScrollTo(scrollTo)

    def dosearch(self, input=None):
        if input is None:
            sf = self.frame.top_bg.searchField
            if sf is None:
                return

            input = sf.GetValue()

        if input:
            input = input.strip()
            if input == '':
                return
        else:
            return
        self.frame.top_bg.searchField.SetValue(input)

        if self.frame.startDownloadFromArg(input):
            self.frame.top_bg.searchField.Clear()
            self.ShowPage('my_files')
        else:
            keywords = split_into_keywords(input)
            keywords = [keyword for keyword in keywords if len(keyword) > 1]

            if len(keywords) == 0:
                self.Notify(
                    'Please enter a search term',
                    "Your search term '%s' was either to small or to general."
                    % input,
                    icon=wx.ART_INFORMATION)

            else:
                self.frame.top_bg.StartSearch()
                self.current_search_query = keywords
                self._logger.debug("GUIUtil: searchFiles: %s %s", keywords,
                                   time())

                #self.frame.searchlist.Freeze()

                self.torrentsearch_manager.setSearchKeywords(keywords)
                self.channelsearch_manager.setSearchKeywords(keywords)

                # We set oldkeywords to '', which will trigger a reset in SetKeywords (called from ShowPage).
                # This avoids calling reset twice.
                # Niels: 17-09-2012, unfortunately showpage calls show(true)
                # which results in the dirty items being refreshed.
                # We need to call Reset in order to prevent this from happening
                self.frame.searchlist.Reset()
                self.ShowPage('search_results', keywords)

                # We now have to call thaw, otherwise loading message will not be shown.
                #self.frame.searchlist.Thaw()

                # Peform local search
                self.torrentsearch_manager.set_gridmgr(
                    self.frame.searchlist.GetManager())
                self.channelsearch_manager.set_gridmgr(
                    self.frame.searchlist.GetManager())

                def db_thread():
                    self.torrentsearch_manager.refreshGrid()

                    nr_peers_connected = self.torrentsearch_manager.searchDispersy(
                    )
                    self.channelsearch_manager.searchDispersy()
                    return nr_peers_connected

                def wx_thread(delayedResult):
                    nr_peers_connected = delayedResult.get()

                    if self and self.frame and self.frame.searchlist:
                        self.frame.searchlist.SetMaxResults(
                            nr_peers_connected + 1, keywords)
                        self.frame.searchlist.NewResult()

                startWorker(wx_thread, db_thread, priority=1024)

    @forceWxThread
    def NewResult(self):
        self.frame.searchlist.NewResult()

    @forceWxThread
    def showChannelCategory(self, category, show=True):

        manager = self.frame.channellist.GetManager()
        manager.SetCategory(category, True)

        if show:
            self.ShowPage('channels')

    @forceWxThread
    def showLibrary(self, show=True):
        manager = self.frame.librarylist.GetManager()
        manager.do_or_schedule_refresh(True)

        if show:
            self.ShowPage('my_files')

    def showChannelFromId(self, channel_id):
        def db_callback():
            channel = self.channelsearch_manager.getChannel(channel_id)
            self.showChannel(channel)

        startWorker(None, db_callback, priority=GUI_PRI_DISPERSY)

    @forceWxThread
    def showChannel(self, channel):
        if channel:
            manager = self.frame.selectedchannellist.GetManager()
            manager.refresh_if_required(channel)

            self.ShowPage('selectedchannel')

            if isinstance(channel, RemoteChannel):
                self.showChannelFromId(channel.id)

    def showChannels(self):
        self.frame.actlist.selectTab('channels')
        self.ShowPage('channels')

    @forceWxThread
    def showChannelResults(self, data_channel):
        self.frame.actlist.selectTab('channels')

        def subscribe_latestupdate_sort(a, b):
            val = cmp(a.modified, b.modified)
            if val == 0:
                return cmp(a.name, b.name)
            return val

        data = data_channel.values()
        data.sort(subscribe_latestupdate_sort, reverse=True)

        manager = self.frame.channellist.GetManager()
        manager.SetCategory('searchresults')
        manager.refresh(data)

        self.ShowPage('channels')

    @forceWxThread
    def showManageChannel(self, channel):
        self.frame.managechannel.SetChannel(channel)
        self.ShowPage('managechannel')

    @forceWxThread
    def showPlaylist(self, data):
        self.frame.playlist.Set(data)
        self.ShowPage('playlist')

    def OnList(self, goto_end, event=None):
        lists = {
            'channels': self.frame.channellist,
            'selectedchannel': self.frame.selectedchannellist,
            'mychannel': self.frame.managechannel,
            'search_results': self.frame.searchlist,
            'my_files': self.frame.librarylist,
            'creditmining': self.frame.creditminingpanel
        }
        if self.guiPage in lists and lists[self.guiPage].HasFocus():
            lists[self.guiPage].ScrollToEnd(goto_end)
        elif event:
            event.Skip()

    def ScrollTo(self, id):
        lists = {
            'channels': self.frame.channellist,
            'selectedchannel': self.frame.selectedchannellist,
            'mychannel': self.frame.managechannel,
            'search_results': self.frame.searchlist,
            'my_files': self.frame.librarylist,
            'creditmining': self.frame.creditminingpanel
        }
        if self.guiPage in lists:
            lists[self.guiPage].ScrollToId(id)

    @forceWxThread
    def Notify(self, title, msg='', icon=wx.ART_INFORMATION):
        if not self or not self.frame or not self.frame.actlist:
            return
        if sys.platform == 'win32' and not self.frame.IsShownOnScreen():
            self.frame.tbicon.Notify(title, msg, icon)
        else:
            if isinstance(icon, basestring):
                icon = wx.ArtProvider.GetBitmap(icon, wx.ART_FRAME_ICON) or \
                    GuiImageManager.getInstance().getImage(u"notify_%s.png" % icon)
            self.frame.actlist.Notify(msg or title, icon)

    def ShouldGuiUpdate(self):
        # Avoid WxPyDeadObject exception
        if self.frame and self.frame.ready:
            return self.frame.GUIupdate
        return True

    def addList(self, l):
        if l not in self.lists:
            self.lists.append(l)

    def toggleFamilyFilter(self, newState=None, setCheck=False):
        if newState is None:
            newState = not self.getFamilyFilter()

        self.utility.session.lm.category.set_family_filter(newState)
        for l in self.lists:
            if getattr(l, 'GotFilter', False):
                l.GotFilter(None)

        if setCheck:
            self.frame.SRstatusbar.ff_checkbox.SetValue(newState)

        self.frame.home.aw_panel.refreshNow()

        if newState:
            self.utility.write_config('family_filter', 1)
        else:
            self.utility.write_config('family_filter', 0)
        self.utility.flush_config()

    def getFamilyFilter(self):
        catobj = self.utility.session.lm.category
        return catobj.family_filter_enabled()

    def set_firewall_restart(self, b):
        self.firewall_restart = b

    @forceWxThread
    def MarkAsFavorite(self, event, channel):
        if channel:
            if event:
                button = event.GetEventObject()
                button.Enable(False)
                if hasattr(button, 'selected'):
                    button.selected = False

            dlgname = 'MFdialog'
            if not self.ReadGuiSetting('show_%s' % dlgname, default=True):
                response = wx.ID_OK
            else:
                from Tribler.Main.Dialogs.ConfirmationDialog import ConfirmationDialog
                dlg = ConfirmationDialog(
                    None, dlgname,
                    "You are about to add \'%s\' to your list of favourite channels."
                    % channel.name,
                    "If you mark this channel as your favourite, you will be able to access its full content."
                )
                response = dlg.ShowModal()

            if response == wx.ID_OK:

                @forceDBThread
                def add_vote():
                    self.channelsearch_manager.favorite(channel.id)
                    wx.CallAfter(self.Notify,
                                 "Channel marked as favourite",
                                 "Marked channel '%s' as favourite" %
                                 channel.name,
                                 icon='favourite')
                    if event:
                        button.Enable(True)
                    self.RefreshChannel(channel.id)

                add_vote()
            elif event:
                button.Enable(True)

    @forceWxThread
    def RemoveFavorite(self, event, channel):
        if channel:
            if event:
                button = event.GetEventObject()
                button.Enable(False)
                if hasattr(button, 'selected'):
                    button.selected = False

            dlgname = 'RFdialog'
            if not self.ReadGuiSetting('show_%s' % dlgname, default=True):
                response = wx.ID_OK
            else:
                from Tribler.Main.Dialogs.ConfirmationDialog import ConfirmationDialog
                dlg = ConfirmationDialog(
                    None, dlgname,
                    "You are about to remove \'%s\' from your list of favourite channels."
                    % channel.name,
                    "If you remove this channel from your favourites, "
                    "you will no longer be able to access its full content.")
                response = dlg.ShowModal()

            if response == wx.ID_OK:

                @forceDBThread
                def remove_vote():
                    self.channelsearch_manager.remove_vote(channel.id)
                    wx.CallAfter(self.Notify,
                                 "Channel removed from favourites",
                                 "Removed channel '%s' from your favourites" %
                                 channel.name,
                                 icon='favourite')
                    if event:
                        button.Enable(True)
                    self.RefreshChannel(channel.id)

                remove_vote()
            elif event:
                button.Enable(True)

    @forceWxThread
    def MarkAsSpam(self, event, channel):
        if channel:
            if event:
                button = event.GetEventObject()
                button.Enable(False)
                if hasattr(button, 'selected'):
                    button.selected = False

            dlgname = 'MSdialog'
            if not self.ReadGuiSetting('show_%s' % dlgname, default=True):
                response = wx.ID_OK
            else:
                from Tribler.Main.Dialogs.ConfirmationDialog import ConfirmationDialog
                dlg = ConfirmationDialog(
                    None, dlgname,
                    "You are about to report channel \'%s\' as spam." %
                    channel.name, "")
                response = dlg.ShowModal()

            if response == wx.ID_OK:

                @forceDBThread
                def remove_vote():
                    self.channelsearch_manager.spam(channel.id)
                    wx.CallAfter(self.Notify, "Channel marked as spam",
                                 "Channel '%s' marked as spam" % channel.name)
                    if event:
                        button.Enable(True)
                    self.RefreshChannel(channel.id)

                remove_vote()
            elif event:
                button.Enable(True)

    @forceWxThread
    def RemoveSpam(self, event, channel):
        if channel:
            if event:
                button = event.GetEventObject()
                button.Enable(False)
                if hasattr(button, 'selected'):
                    button.selected = False

            dlgname = 'RSdialog'
            if not self.ReadGuiSetting('show_%s' % dlgname, default=True):
                response = wx.ID_OK
            else:
                from Tribler.Main.Dialogs.ConfirmationDialog import ConfirmationDialog
                dlg = ConfirmationDialog(
                    None, dlgname,
                    "You are about unmark channel \'%s\' as spam." %
                    channel.name, "")
                response = dlg.ShowModal()

            if response == wx.ID_OK:

                @forceDBThread
                def remove_vote():
                    self.channelsearch_manager.remove_vote(channel.id)
                    wx.CallAfter(
                        self.Notify, "Channel unmarked as spam",
                        "Channel '%s' unmarked as spam" % channel.name)
                    if event:
                        button.Enable(True)
                    self.RefreshChannel(channel.id)

                remove_vote()
            elif event:
                button.Enable(True)

    def RefreshChannel(self, channelid):
        if self.guiPage in ['search_results', 'selectedchannel', 'channels']:

            list = self.GetSelectedPage()
            if self.guiPage == 'search_results':
                list.GetManager().refresh_partial(channelids=[channelid])
            else:
                list.GetManager().refresh_partial((channelid, ))

            if self.guiPage == 'selectedchannel':
                wx.CallAfter(list.GetManager().reload, channelid)

    def SelectVideo(self, videofiles, selected_file=None):
        if len(videofiles) > 1:
            videofiles.sort()
            dialog = wx.SingleChoiceDialog(
                None,
                'Tribler currently only supports playing one file at a time.\nSelect the file you want to play.',
                'Which file do you want to play?', videofiles)
            if selected_file in videofiles:
                dialog.SetSelection(videofiles.index(selected_file))

            selected_file = dialog.GetStringSelection() if dialog.ShowModal(
            ) == wx.ID_OK else None
            dialog.Destroy()
            return selected_file
        elif len(videofiles) == 1:
            return videofiles[0]