def exec_newpost_window(self, action, tweet): from NewpostWindow import NewpostWindow try: self.wecase_new = NewpostWindow(action, tweet) self.wecase_new.userClicked.connect(self.userClicked) self.wecase_new.tagClicked.connect(self.tagClicked) self.wecase_new.show() except APIError as e: self.errorWindow.raiseException.emit(e)
def exec_newpost_window(self, action, tweet): from NewpostWindow import NewpostWindow try: self.wecase_new = NewpostWindow(action, tweet) self.wecase_new.userClicked.connect(self.userClicked) self.wecase_new.tagClicked.connect(self.tagClicked) self.wecase_new.show() except APIError as e: self._handle_api_error(e)
def reply(self, idstr, cidstr): wecase_new = NewpostWindow(action="reply", id=int(idstr), cid=int(cidstr)) wecase_new.client = self.client wecase_new.exec_()
def comment(self, idstr): wecase_new = NewpostWindow(action="comment", id=int(idstr)) wecase_new.client = self.client wecase_new.exec_()
def repost(self, idstr, text): wecase_new = NewpostWindow(action="retweet", id=int(idstr), text=text) wecase_new.client = self.client wecase_new.exec_()
def postTweet(self): self.wecase_new = NewpostWindow() self.wecase_new.userClicked.connect(self.userClicked) self.wecase_new.tagClicked.connect(self.tagClicked) self.wecase_new.show()
def postTweet(self): wecase_new = NewpostWindow() wecase_new.client = self.client wecase_new.exec_()
class WeCaseWindow(QtGui.QMainWindow): client = None uid = None timelineLoaded = QtCore.pyqtSignal(int) imageLoaded = QtCore.pyqtSignal(str) tabBadgeChanged = QtCore.pyqtSignal(int, int) tabAvatarFetched = QtCore.pyqtSignal(str) def __init__(self, parent=None): super(WeCaseWindow, self).__init__(parent) self.errorWindow = APIErrorWindow(self) self.setAttribute(QtCore.Qt.WA_QuitOnClose, True) self._iconPixmap = {} self.setupUi(self) self._setupSysTray() self.tweetViews = [self.homeView, self.mentionsView, self.commentsView, self.commentsMentionsTab] self.info = WeRuntimeInfo() self.client = const.client self.loadConfig() self.init_account() self.setupModels() self.IMG_AVATAR = -2 self.IMG_THUMB = -1 self.notify = Notify(timeout=self.notify_timeout) self.applyConfig() self.download_lock = [] self._last_reminds_count = 0 self._setupUserTab(self.uid(), False, True) def _setupTab(self, view): tab = QtGui.QWidget() layout = QtGui.QGridLayout(tab) layout.addWidget(view) view.setParent(tab) return tab def _setupCommonTab(self, timeline, view, switch=True, protect=False): self._prepareTimeline(timeline) view.setModel(timeline) view.userClicked.connect(self.userClicked) view.tagClicked.connect(self.tagClicked) tab = self._setupTab(view) self.tabWidget.addTab(tab, "") if switch: self.tabWidget.setCurrentWidget(tab) if protect: self.tabWidget.tabBar().setProtectTab(tab, True) return tab def _getSameTab(self, attr, value): for i in range(self.tabWidget.count()): try: tab = self.tabWidget.widget(i).layout().itemAt(0).widget() _value = getattr(tab.model(), attr)() if _value == value: return i except AttributeError: pass return False def _setupUserTab(self, uid, switch=True, myself=False): index = self._getSameTab("uid", uid) if index: if switch: self.tabWidget.setCurrentIndex(index) return view = TweetListWidget() _timeline = TweetUserModel(self.client.statuses.user_timeline, uid, view) timeline = TweetFilterModel(_timeline) timeline.setModel(_timeline) tab = self._setupCommonTab(timeline, view, switch, myself) def setAvatar(f): self._setTabIcon(tab, WObjectCache().open(QtGui.QPixmap, f)) fetcher = AsyncFetcher("".join((path.cache_path, str(self.info["uid"])))) fetcher.addTask(self.client.users.show.get(uid=uid)["profile_image_url"], setAvatar) def _setupTopicTab(self, topic, switch=True): index = self._getSameTab("topic", topic) if index: if switch: self.tabWidget.setCurrentIndex(index) return view = TweetListWidget() timeline = TweetTopicModel(self.client.search.topics, topic, view) tab = self._setupCommonTab(timeline, view, switch, protect=False) self._setTabIcon(tab, WObjectCache().open( QtGui.QPixmap, ":/IMG/img/topic.jpg" )) def userClicked(self, userItem, openAtBackend): try: self._setupUserTab(userItem.id, switch=(not openAtBackend)) except APIError as e: self.errorWindow.raiseException.emit(e) def tagClicked(self, str, openAtBackend): try: self._setupTopicTab(str, switch=(not openAtBackend)) except APIError as e: self.errorWindow.raiseException.emit(e) def setupUi(self, mainWindow): mainWindow.setWindowIcon(QtGui.QIcon(":/IMG/img/WeCase.svg")) mainWindow.setDocumentMode(False) mainWindow.setDockOptions(QtGui.QMainWindow.AllowTabbedDocks | QtGui.QMainWindow.AnimatedDocks) self.centralwidget = QtGui.QWidget(mainWindow) self.verticalLayout = QtGui.QVBoxLayout(self.centralwidget) self.tabWidget = QtGui.QTabWidget(self.centralwidget) self.tabWidget.setTabBar(WTabBar(self.tabWidget)) self.tabWidget.setTabPosition(QtGui.QTabWidget.West) self.tabWidget.setTabShape(QtGui.QTabWidget.Rounded) self.tabWidget.setDocumentMode(False) self.tabWidget.setMovable(True) self.tabWidget.tabCloseRequested.connect(self.closeTab) self.homeView = TweetListWidget() self.homeView.userClicked.connect(self.userClicked) self.homeView.tagClicked.connect(self.tagClicked) self.homeTab = self._setupTab(self.homeView) self.tabWidget.addTab(self.homeTab, "") self.tabWidget.tabBar().setProtectTab(self.homeTab, True) self.mentionsView = TweetListWidget() self.mentionsView.userClicked.connect(self.userClicked) self.mentionsView.tagClicked.connect(self.tagClicked) self.mentionsTab = self._setupTab(self.mentionsView) self.tabWidget.addTab(self.mentionsTab, "") self.tabWidget.tabBar().setProtectTab(self.mentionsTab, True) self.commentsView = TweetListWidget() self.commentsView.userClicked.connect(self.userClicked) self.commentsView.tagClicked.connect(self.tagClicked) self.commentsTab = self._setupTab(self.commentsView) self.tabWidget.addTab(self.commentsTab, "") self.tabWidget.tabBar().setProtectTab(self.commentsTab, True) self.commentsMentionsView = TweetListWidget() self.commentsMentionsView.userClicked.connect(self.userClicked) self.commentsMentionsView.tagClicked.connect(self.tagClicked) self.commentsMentionsTab = self._setupTab(self.commentsMentionsView) self.tabWidget.addTab(self.commentsMentionsTab, "") self.tabWidget.tabBar().setProtectTab(self.commentsMentionsTab, True) self.verticalLayout.addWidget(self.tabWidget) self.widget = QtGui.QWidget(self.centralwidget) self.verticalLayout.addWidget(self.widget) mainWindow.setCentralWidget(self.centralwidget) self.aboutAction = QtGui.QAction(mainWindow) self.refreshAction = QtGui.QAction(mainWindow) self.logoutAction = QtGui.QAction(mainWindow) self.exitAction = QtGui.QAction(mainWindow) self.settingsAction = QtGui.QAction(mainWindow) self.aboutAction.setIcon(QtGui.QIcon(QtGui.QPixmap("./IMG/img/where_s_my_weibo.svg"))) self.exitAction.setIcon(QtGui.QIcon(QtGui.QPixmap(":/IMG/img/application-exit.svg"))) self.settingsAction.setIcon(QtGui.QIcon(QtGui.QPixmap(":/IMG/img/preferences-other.png"))) self.refreshAction.setIcon(QtGui.QIcon(QtGui.QPixmap(":/IMG/img/refresh.png"))) self.menubar = QtGui.QMenuBar(mainWindow) self.menubar.setEnabled(True) self.menubar.setDefaultUp(False) mainWindow.setMenuBar(self.menubar) self.mainMenu = QtGui.QMenu(self.menubar) self.helpMenu = QtGui.QMenu(self.menubar) self.optionsMenu = QtGui.QMenu(self.menubar) self.mainMenu.addAction(self.refreshAction) self.mainMenu.addSeparator() self.mainMenu.addAction(self.logoutAction) self.mainMenu.addAction(self.exitAction) self.helpMenu.addAction(self.aboutAction) self.optionsMenu.addAction(self.settingsAction) self.menubar.addAction(self.mainMenu.menuAction()) self.menubar.addAction(self.optionsMenu.menuAction()) self.menubar.addAction(self.helpMenu.menuAction()) self.exitAction.triggered.connect(mainWindow.close) self.aboutAction.triggered.connect(mainWindow.showAbout) self.settingsAction.triggered.connect(mainWindow.showSettings) self.logoutAction.triggered.connect(mainWindow.logout) self.refreshAction.triggered.connect(mainWindow.refresh) self.pushButton_refresh = QtGui.QPushButton(self.widget) self.pushButton_new = QtGui.QPushButton(self.widget) self.pushButton_refresh.clicked.connect(mainWindow.refresh) self.pushButton_new.clicked.connect(mainWindow.postTweet) self.timelineLoaded.connect(self.moveToTop) self.tabBadgeChanged.connect(self.drawNotifyBadge) self.refreshAction.setShortcut(QtGui.QKeySequence("F5")) self.pushButton_refresh.setIcon(QtGui.QIcon(":/IMG/img/refresh.png")) self.pushButton_new.setIcon(QtGui.QIcon(":/IMG/img/new.png")) if self.isGlobalMenu(): self._setupToolBar() else: self._setupButtonWidget() self._setTabIcon(self.homeTab, QtGui.QPixmap(":/IMG/img/sina.png")) self._setTabIcon(self.mentionsTab, QtGui.QPixmap(":/IMG/img/mentions.png")) self._setTabIcon(self.commentsTab, QtGui.QPixmap(":/IMG/img/comments2.png")) self._setTabIcon(self.commentsMentionsTab, QtGui.QPixmap(":/IMG/img/mentions_comments.svg")) self.retranslateUi(mainWindow) def isGlobalMenu(self): if os.environ.get("TOOLBAR") == "1": return True elif os.environ.get("TOOLBAR") == "0": return False elif os.environ.get('DESKTOP_SESSION') in ["ubuntu", "ubuntu-2d"]: if not os.environ.get("UBUNTU_MENUPROXY"): return False elif os.environ.get("APPMENU_DISPLAY_BOTH"): return False else: return True elif os.environ.get("DESKTOP_SESSION") == "kde-plasma" and platform.linux_distribution()[0] == "Ubuntu": return True elif platform.system() == "Darwin": return True return False def _setupToolBar(self): self.toolBar = QtGui.QToolBar() self.toolBar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) empty = QtGui.QWidget() empty.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) self.toolBar.addWidget(empty) self.toolBar.addAction(self.refreshAction) newAction = self.toolBar.addAction(QtGui.QIcon(":/IMG/img/new.png"), "New") newAction.triggered.connect(self.pushButton_new.clicked) self.addToolBar(self.toolBar) def _setupButtonWidget(self): self.buttonWidget = QtGui.QWidget(self) self.buttonLayout = QtGui.QHBoxLayout(self.buttonWidget) self.horizontalSpacer = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) self.buttonLayout.addSpacerItem(self.horizontalSpacer) self.buttonLayout.addWidget(self.pushButton_refresh) self.buttonLayout.addWidget(self.pushButton_new) def resizeEvent(self, event): # This is a hack!!! if self.isGlobalMenu(): return self.buttonWidget.resize(self.menubar.sizeHint().width(), self.menubar.sizeHint().height() + 12) self.buttonWidget.move(self.width() - self.buttonWidget.width(), self.menubar.geometry().topRight().y() - 5) def retranslateUi(self, frm_MainWindow): frm_MainWindow.setWindowTitle(self.tr("WeCase")) self.mainMenu.setTitle(self.tr("&WeCase")) self.helpMenu.setTitle(self.tr("&Help")) self.optionsMenu.setTitle(self.tr("&Options")) self.aboutAction.setText(self.tr("&About...")) self.refreshAction.setText(self.tr("Refresh")) self.logoutAction.setText(self.tr("&Log out")) self.exitAction.setText(self.tr("&Exit")) self.settingsAction.setText(self.tr("&Settings")) def _setupSysTray(self): self.systray = QtGui.QSystemTrayIcon() self.systray.activated.connect(self.clickedSystray) self.systray.setToolTip("WeCase") self.systray.setIcon(QtGui.QIcon(":/IMG/img/WeCase.svg")) self.systray.show() self.visibleAction = QtGui.QAction(self) self.visibleAction.setText(self.tr("&Hide")) self.visibleAction.triggered.connect(self._switchVisibility) self.sysMenu = QtGui.QMenu(self) self.sysMenu.addAction(self.visibleAction) self.sysMenu.addAction(self.logoutAction) self.sysMenu.addAction(self.exitAction) self.systray.setContextMenu(self.sysMenu) def clickedSystray(self, reason): if reason == QtGui.QSystemTrayIcon.Trigger: self._switchVisibility() elif reason == QtGui.QSystemTrayIcon.Context: pass def _switchVisibility(self): if self.isVisible(): self.hide() self.visibleAction.setText(self.tr("&Show")) else: self.show() self.visibleAction.setText(self.tr("&Hide")) def _setTabIcon(self, tab, icon): pixmap = icon.transformed(QtGui.QTransform().rotate(90)) icon = QtGui.QIcon(pixmap) self._iconPixmap[icon.cacheKey()] = pixmap self.tabWidget.setTabIcon(self.tabWidget.indexOf(tab), icon) self.tabWidget.setIconSize(QtCore.QSize(24, 24)) def _prepareTimeline(self, timeline): try: timeline.setUsersBlacklist(self.usersBlacklist) timeline.setTweetsKeywordsBlacklist(self.tweetKeywordsBlacklist) timeline.setWordWarKeywords(self.wordWarKeywords) timeline.setBlockWordwars(self.blockWordwars) except AttributeError: pass timeline.load() def closeTab(self, index): widget = self.tabWidget.widget(index) self.tabWidget.removeTab(index) widget.deleteLater() def init_account(self): self.uid() def loadConfig(self): self.config = WConfigParser(path.myself_path + "WMetaConfig", path.config_path, "main") self.notify_interval = self.config.notify_interval self.notify_timeout = self.config.notify_timeout self.usersBlacklist = self.config.usersBlacklist self.tweetKeywordsBlacklist = self.config.tweetsKeywordsBlacklist self.remindMentions = self.config.remind_mentions self.remindComments = self.config.remind_comments self.wordWarKeywords = self.config.wordWarKeywords self.blockWordwars = self.config.blockWordwars self.maxRetweets = self.config.maxRetweets self.maxTweetsPerUser = self.config.maxTweetsPerUser self.mainWindow_geometry = self.config.mainwindow_geometry def applyConfig(self): try: self.timer.stop() except AttributeError: pass self.timer = WTimer(self.show_notify, self.notify_interval) self.timer.start() self.notify.timeout = self.notify_timeout setGeometry(self, self.mainWindow_geometry) def setupModels(self): self._all_timeline = TweetCommonModel(self.client.statuses.home_timeline, self) self.all_timeline = TweetFilterModel(self._all_timeline) self.all_timeline.setModel(self._all_timeline) self._prepareTimeline(self.all_timeline) # extra rules self.all_timeline.setMaxRetweets(self.maxRetweets) self.all_timeline.setMaxTweetsPerUser(self.maxTweetsPerUser) self.homeView.setModel(self.all_timeline) self._mentions = TweetCommonModel(self.client.statuses.mentions, self) self.mentions = TweetFilterModel(self._mentions) self.mentions.setModel(self._mentions) self._prepareTimeline(self.mentions) self.mentionsView.setModel(self.mentions) self._comment_to_me = TweetCommentModel(self.client.comments.to_me, self) self.comment_to_me = TweetFilterModel(self._comment_to_me) self.comment_to_me.setModel(self._comment_to_me) self._prepareTimeline(self.comment_to_me) self.commentsView.setModel(self.comment_to_me) self._comment_mentions = TweetCommentModel(self.client.comments.mentions, self) self.comment_mentions = TweetFilterModel(self._comment_mentions) self.comment_mentions.setModel(self._comment_mentions) self._prepareTimeline(self.comment_mentions) self.commentsMentionsView.setModel(self.comment_mentions) @async def reset_remind(self): typ = "" if self.currentTweetView() == self.homeView: self.tabBadgeChanged.emit(self.tabWidget.currentIndex(), 0) elif self.currentTweetView() == self.mentionsView: typ = "mention_status" self.tabBadgeChanged.emit(self.tabWidget.currentIndex(), 0) elif self.currentTweetView() == self.commentsView: typ = "cmt" self.tabBadgeChanged.emit(self.tabWidget.currentIndex(), 0) elif self.currentTweetView() == self.commentsMentionsView: typ = "mention_cmt" self.tabBadgeChanged.emit(self.tabWidget.currentIndex(), 0) if typ: self.client.remind.set_count.post(type=typ) def get_remind(self, uid): """this function is used to get unread_count from Weibo API. uid is necessary.""" reminds = self.client.remind.unread_count.get(uid=uid) return reminds def uid(self): """How can I get my uid? here it is""" if not self.info.get("uid"): self.info["uid"] = self.client.account.get_uid.get().uid return self.info["uid"] def show_notify(self): # This function is run in another thread by WTimer. # Do not modify UI directly. Send signal and react it in a slot only. # We use SIGNAL self.tabTextChanged and SLOT self.setTabText() # to display unread count reminds = self.get_remind(self.uid()) msg = self.tr("You have:") + "\n" reminds_count = 0 if reminds['status'] != 0: # Note: do NOT send notify here, or users will crazy. self.tabBadgeChanged.emit(self.tabWidget.indexOf(self.homeTab), reminds['status']) if reminds['mention_status'] and self.remindMentions: msg += self.tr("%d unread @ME") % reminds['mention_status'] + "\n" self.tabBadgeChanged.emit(self.tabWidget.indexOf(self.mentionsTab), reminds['mention_status']) reminds_count += 1 else: self.tabBadgeChanged.emit(self.tabWidget.indexOf(self.mentionsTab), 0) if reminds['cmt'] and self.remindComments: msg += self.tr("%d unread comment(s)") % reminds['cmt'] + "\n" self.tabBadgeChanged.emit(self.tabWidget.indexOf(self.commentsTab), reminds['cmt']) reminds_count += 1 else: self.tabBadgeChanged.emit(self.tabWidget.indexOf(self.commentsTab), 0) if reminds["mention_cmt"] and self.remindMentions: msg += self.tr("%d unread @ME comment(s)") % reminds["mention_cmt"] + "\n" self.tabBadgeChanged.emit(self.tabWidget.indexOf(self.commentsMentionsTab), reminds["mention_cmt"]) reminds_count += 1 else: self.tabBadgeChanged.emit(self.tabWidget.indexOf(self.commentsMentionsTab), 0) if reminds_count and reminds_count != self._last_reminds_count: self.notify.showMessage(self.tr("WeCase"), msg) self._last_reminds_count = reminds_count def drawNotifyBadge(self, index, count): tabIcon = self.tabWidget.tabIcon(index) _tabPixmap = self._iconPixmap[tabIcon.cacheKey()] tabPixmap = _tabPixmap.transformed(QtGui.QTransform().rotate(-90)) icon = NotifyBadgeDrawer().draw(tabPixmap, str(count)) icon = icon.transformed(QtGui.QTransform().rotate(90)) icon = QtGui.QIcon(icon) self._iconPixmap[icon.cacheKey()] = _tabPixmap self.tabWidget.setTabIcon(index, icon) def moveToTop(self): self.currentTweetView().moveToTop() def showSettings(self): wecase_settings = WeSettingsWindow() if wecase_settings.exec_(): self.loadConfig() self.applyConfig() def showAbout(self): wecase_about = AboutWindow() wecase_about.exec_() def logout(self): self.close() # This is a model dialog, if we exec it before we close MainWindow # MainWindow won't close from LoginWindow import LoginWindow wecase_login = LoginWindow(allow_auto_login=False) wecase_login.exec_() def postTweet(self): self.wecase_new = NewpostWindow() self.wecase_new.userClicked.connect(self.userClicked) self.wecase_new.tagClicked.connect(self.tagClicked) self.wecase_new.show() def refresh(self): tweetView = self.currentTweetView() tweetView.model().timelineLoaded.connect(self.moveToTop) tweetView.refresh() self.reset_remind() def currentTweetView(self): # The most tricky part of MainWindow. return self.tabWidget.currentWidget().layout().itemAt(0).widget() def saveConfig(self): self.config.mainwindow_geometry = getGeometry(self) self.config.save() def closeEvent(self, event): self.systray.hide() self.hide() self.saveConfig() self.timer.stop(True) # Reset uid when the thread exited. self.info["uid"] = None logging.info("Die")
class SingleTweetWidget(QtGui.QFrame): userClicked = QtCore.pyqtSignal(UserItem, bool) tagClicked = QtCore.pyqtSignal(str, bool) deleteReturn = QtCore.pyqtSignal(bool) MENTIONS_RE = re.compile('(@[-a-zA-Z0-9_\u4e00-\u9fa5]+)') SINA_URL_RE = re.compile(r"(http://t.cn/[a-zA-Z0-9]{5,7})") HASHTAG_RE = re.compile("(#.*?#)") def __init__(self, tweet=None, without=(), parent=None): super(SingleTweetWidget, self).__init__(parent) self.errorWindow = APIErrorWindow(self) self.tweet = tweet self.client = const.client self.without = without self.setObjectName("SingleTweetWidget") try: self.setupUi() except RuntimeError: return self.fetcher = AsyncFetcher("".join((cache_path, str(WeRuntimeInfo()["uid"])))) self.download_lock = False self.__favorite_queue = [] def setupUi(self): self.horizontalLayout = QtGui.QHBoxLayout(self) self.horizontalLayout.setMargin(0) self.horizontalLayout.setObjectName("horizontalLayout") self.verticalLayout_2 = QtGui.QVBoxLayout() self.verticalLayout_2.setObjectName("verticalLayout_2") reason = self.tweet.author.verify_reason if self.tweet.author.verify_type == "personal": self.avatar = WAvatarLabel(WAvatarLabel.PERSONAL_VERIFY, reason) elif self.tweet.author.verify_type == "organization": self.avatar = WAvatarLabel(WAvatarLabel.ORGANIZATION_VERIFY, reason) else: self.avatar = WAvatarLabel(WAvatarLabel.NO_VERIFY) self.avatar.setObjectName("avatar") self.avatar.setPixmap(self.tweet.author.avatar) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.avatar.sizePolicy().hasHeightForWidth()) self.avatar.setSizePolicy(sizePolicy) self.avatar.setAlignment(QtCore.Qt.AlignTop) self.avatar.clicked.connect(self._userClicked) self.verticalLayout_2.addWidget(self.avatar) self.time = SimpleLabel() self.time.setObjectName("time") sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.time.sizePolicy().hasHeightForWidth()) self.time.setSizePolicy(sizePolicy) self.verticalLayout_2.addWidget(self.time) self.verticalLayout_2.setAlignment(QtCore.Qt.AlignTop) self.horizontalLayout.addLayout(self.verticalLayout_2) self.verticalLayout = QtGui.QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") self.username = QtGui.QLabel(self) self.username.setObjectName("username") self.username.setAlignment(QtCore.Qt.AlignTop) self.username.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse) self.verticalLayout.addWidget(self.username) self.tweetText = WTweetLabel(self) self.tweetText.setObjectName("tweetText") self.tweetText.setAlignment(QtCore.Qt.AlignTop) self.tweetText.userClicked.connect(self._userTextClicked) self.tweetText.tagClicked.connect(self._tagClicked) self.verticalLayout.addWidget(self.tweetText) if self.tweet.thumbnail_pic and ("image" not in self.without): self.imageWidget = self._createImageLabel(self.tweet.thumbnail_pic) self.verticalLayout.addWidget(self.imageWidget) if self.tweet.original and ("original" not in self.without): self.originalLabel = self._createOriginalLabel() self.verticalLayout.addWidget(self.originalLabel) self.horizontalLayout.setStretch(1, 1) self.horizontalLayout.setStretch(2, 10) self.counterHorizontalLayout = QtGui.QHBoxLayout() self.counterHorizontalLayout.setObjectName("counterhorizontalLayout") self.horizontalSpacer = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) self.counterHorizontalLayout.addItem(self.horizontalSpacer) if WeRuntimeInfo().get("uid") == self.tweet.author.id: self.delete = self._createDeleteLabel() self.counterHorizontalLayout.addWidget(self.delete) if not (self.tweet.type == TweetItem.COMMENT): self.client = QtGui.QLabel() self.client.setText(self.tr("From: %s") % self.tweet.source) self.client.linkActivated.connect(lambda link: openLink(link)) self.counterHorizontalLayout.addWidget(self.client) self.retweet = self._createRetweetLabel() self.counterHorizontalLayout.addWidget(self.retweet) self.comment = self._createCommentLabel() self.counterHorizontalLayout.addWidget(self.comment) self.favorite = self._createFavoriteLabel() self.counterHorizontalLayout.addWidget(self.favorite) self.counterHorizontalLayout.setAlignment(QtCore.Qt.AlignTop) elif self.tweet.type == TweetItem.COMMENT: self.reply = self._createReplyLabel() self.counterHorizontalLayout.addWidget(self.reply) self.verticalLayout.addLayout(self.counterHorizontalLayout) self.horizontalLayout.addLayout(self.verticalLayout) self.setStyleSheet(""" QFrame#SingleTweetWidget { border-bottom: 2px solid palette(highlight); border-radius: 0px; padding: 2px; } """) self.username.setText(" " + self.tweet.author.name) text = QtCore.Qt.escape(self.tweet.text) text = self._create_mentions(text) text = self._create_html_url(text) text = self._create_hashtag(text) text = self._create_smiles(text) self.tweetText.setHtml(text) self.timer = QtCore.QTimer(self) self.timer.timeout.connect(self._update_time) self._update_time() def _setup_timer(self): self.timer.stop() passedSeconds = self.tweet.passedSeconds if passedSeconds < 60: self.timer.start(1 * 1000) elif passedSeconds < 3600: self.timer.start(60 * 1000) elif passedSeconds < 86400: self.timer.start(60 * 60 * 1000) else: self.timer.start(60 * 60 * 24 * 1000) def _update_time(self): try: if not self.time.visibleRegion() and self.timer.isActive(): # Skip update only when timer is active, insure # at least run once time. return if not self.time.toolTip(): self.time.setToolTip(self.tweet.timestamp) if self.tweet.type != TweetItem.COMMENT: self.time.setText("<a href='%s'>%s</a>" % (self.tweet.url, self.tweet.time)) else: self.time.setText("<a href='%s'>%s</a>" % (self.tweet.original.url, self.tweet.time)) self._setup_timer() except: # Sometimes, user closed the window and the window # has been garbage collected already, but # the timer is still running. It will cause a runtime error pass def _createOriginalLabel(self): widget = QtGui.QWidget(self) widget.setObjectName("originalWidget") widgetLayout = QtGui.QVBoxLayout(widget) widgetLayout.setSpacing(0) widgetLayout.setMargin(0) widgetLayout.setAlignment(QtCore.Qt.AlignCenter | QtCore.Qt.AlignTop) frame = QtGui.QFrame() frame.setObjectName("originalFrame") widgetLayout.addWidget(frame) layout = QtGui.QVBoxLayout(frame) layout.setObjectName("originalLayout") layout.setAlignment(QtCore.Qt.AlignTop) textLabel = WTweetLabel() textLabel.setAlignment(QtCore.Qt.AlignCenter | QtCore.Qt.AlignTop) textLabel.userClicked.connect(self._userTextClicked) textLabel.tagClicked.connect(self._tagClicked) self.textLabel = textLabel # Hack: save a reference originalItem = self.tweet.original text = QtCore.Qt.escape(originalItem.text) text = self._create_mentions(text) text = self._create_html_url(text) text = self._create_hashtag(text) text = self._create_smiles(text) try: authorName = self._create_mentions("@" + originalItem.author.name) textLabel.setHtml("%s: %s" % (authorName, text)) except: # originalItem.text == This tweet deleted by author textLabel.setHtml(text) layout.addWidget(textLabel) if originalItem.thumbnail_pic: layout.addWidget(self._createImageLabel(originalItem.thumbnail_pic)) counterHorizontalLayout = QtGui.QHBoxLayout() counterHorizontalLayout.setObjectName("counterhorizontalLayout") horizontalSpacer = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) counterHorizontalLayout.addItem(horizontalSpacer) retweet = WIconLabel(widget) retweet.setObjectName("retweet") retweet.setText(str(originalItem.retweets_count)) retweet.setIcon(":/IMG/img/retweets.png") retweet.clicked.connect(self._original_retweet) counterHorizontalLayout.addWidget(retweet) comment = WIconLabel(widget) comment.setObjectName("comment") comment.setIcon(":/IMG/img/comments.png") comment.setText(str(originalItem.comments_count)) comment.clicked.connect(self._original_comment) counterHorizontalLayout.addWidget(comment) counterHorizontalLayout.setSpacing(6) layout.setMargin(8) layout.setSpacing(0) layout.addLayout(counterHorizontalLayout) frame.setStyleSheet(""" QFrame#originalFrame { border: 2px solid palette(highlight); border-radius: 4px; padding: 2px; } """) return widget def _createImageLabel(self, thumbnail_pic): widget = QtGui.QWidget(self) widget.setObjectName("imageLabel") widgetLayout = QtGui.QVBoxLayout(widget) widgetLayout.setSpacing(0) widgetLayout.setMargin(0) widgetLayout.setAlignment(QtCore.Qt.AlignCenter) frame = QtGui.QFrame() frame.setObjectName("imageFrame") widgetLayout.addWidget(frame) layout = QtGui.QVBoxLayout(frame) layout.setObjectName("imageLayout") self.imageLabel = WSwitchLabel(widget) self.imageLabel.setImagesUrls(thumbnail_pic) self.imageLabel.clicked.connect(self._showFullImage) layout.addWidget(self.imageLabel) widgetLayout.addWidget(frame) return widget def _createFavoriteLabel(self): favorite = WIconLabel(self) favorite.setIcon(":/IMG/img/no_favorites.png") favorite.clicked.connect(self._favorite) return favorite def _createRetweetLabel(self): retweet = WIconLabel(self) retweet.setObjectName("retweet") retweet.setText(str(self.tweet.retweets_count)) retweet.setIcon(":/IMG/img/retweets.png") retweet.clicked.connect(self._retweet) return retweet def _createCommentLabel(self): comment = WIconLabel(self) comment.setObjectName("comment") comment.setIcon(":/IMG/img/comments.png") comment.setText(str(self.tweet.comments_count)) comment.clicked.connect(self._comment) return comment def _createReplyLabel(self): reply = WIconLabel(self) reply.setObjectName("reply") reply.setIcon(":/IMG/img/retweets.png") reply.clicked.connect(self._reply) return reply def _createDeleteLabel(self): delete = WIconLabel(self) delete.setObjectName("delete") delete.setIcon(":/IMG/img/deletes.png") delete.clicked.connect(self._delete) return delete def fetch_open_original_pic(self, thumbnail_pic): """Fetch and open original pic from thumbnail pic url. Pictures will stored in cache directory. If we already have a same name in cache directory, just open it. If we don't, then download it first.""" def open_pic(localfile): start(localfile) self.download_lock = False self.imageLabel.setBusy(False) if self.download_lock: return self.download_lock = True self.imageLabel.setBusy(True) original_pic = thumbnail_pic.replace("thumbnail", "large") # A simple trick ... ^_^ self.fetcher.addTask(original_pic, open_pic) def _showFullImage(self): self.fetch_open_original_pic(self.imageLabel.url()) def _favorite(self): needWorker = False if not self.__favorite_queue: state = not self.tweet.isFavorite() needWorker = True elif not self.__favorite_queue[-1]: state = True else: state = False self.__favorite_queue.append(state) if state: self.favorite.setIcon(":/IMG/img/favorites.png") else: self.favorite.setIcon(":/IMG/img/no_favorites.png") if needWorker: self.__favorite_worker() @async def __favorite_worker(self): while self.__favorite_queue: state = self.__favorite_queue[0] try: self.tweet.setFavorite(state) sleep(0.5) self.__favorite_queue.pop(0) except APIError as e: if e.error_code == 20101: self.tweet.setFavoriteForce(True) elif e.error_code == 20704: self.tweet.setFavoriteForce(True) self._e = e self.__favorite_queue = [] self.errorWindow.raiseException.emit(self._e) return def _retweet(self, tweet=None): if not tweet: tweet = self.tweet self.exec_newpost_window("retweet", tweet) def _comment(self, tweet=None): if not tweet: tweet = self.tweet if tweet.type == TweetItem.COMMENT: self._reply(tweet) return self.exec_newpost_window("comment", tweet) def _reply(self, tweet=None): if not tweet: tweet = self.tweet self.exec_newpost_window("reply", tweet) def _delete(self): @async def do_delete(): try: self.tweet.delete() self.deleteReturn.emit(True) except APIError as e: self.errorWindow.raiseException.emit(e) self.deleteReturn.emit(False) questionDialog = QtGui.QMessageBox.question choice = questionDialog(self, self.tr("Delete?"), self.tr("You can't undo your deletion."), QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) if choice == QtGui.QMessageBox.No: return self.deleteReturn.connect(lambda state: state and self.remove()) do_delete() def remove(self): # not really remove myself. self.timer.stop() self.hide() def _original_retweet(self): self._retweet(self.tweet.original) def _original_comment(self): self._comment(self.tweet.original) def _create_html_url(self, text): return self.SINA_URL_RE.sub(r"""<a href='\1'>\1</a>""", text) def _create_smiles(self, text): faceModel = FaceModel() for face in faceModel.all_faces(): new_text = text.replace("[%s]" % face.name, '<img src="%s" />' % face.path) if new_text != text: self._create_animation(face.path) text = new_text return text def _create_mentions(self, text): return self.MENTIONS_RE.sub(r"""<a href='mentions:///\1'>\1</a>""", text) def _create_hashtag(self, text): return self.HASHTAG_RE.sub(r"""<a href='hashtag:///\1'>\1</a>""", text) def _create_animation(self, path): movie = WObjectCache().open(WMovie, path) movie.frameChanged.connect(self.drawAnimate, QtCore.Qt.UniqueConnection) movie.start() def drawAnimate(self): sender = self.sender() if (not isinstance(sender, WMovie)) or (not self.tweetText.visibleRegion()): return movie = sender self._addSingleFrame(movie, self.tweetText) if self.tweet.original and ("original" not in self.without): self._addSingleFrame(movie, self.textLabel) def _addSingleFrame(self, movie, textBrowser): document = textBrowser.document() document.addResource(QtGui.QTextDocument.ImageResource, QtCore.QUrl(movie.fileName()), movie.currentPixmap()) # Cause a force refresh textBrowser.update() def exec_newpost_window(self, action, tweet): from NewpostWindow import NewpostWindow try: self.wecase_new = NewpostWindow(action, tweet) self.wecase_new.userClicked.connect(self.userClicked) self.wecase_new.tagClicked.connect(self.tagClicked) self.wecase_new.show() except APIError as e: self.errorWindow.raiseException.emit(e) def _userClicked(self, button): openAtBackend = False if button == QtCore.Qt.MiddleButton: openAtBackend = True self.userClicked.emit(self.tweet.author, openAtBackend) @async def _userTextClicked(self, user, button): openAtBackend = False if button == QtCore.Qt.MiddleButton: openAtBackend = True try: self.__userItem = UserItem({"name": user}) except APIError as e: self.errorWindow.raiseException.emit(e) return self.userClicked.emit(self.__userItem, openAtBackend) def _tagClicked(self, tag, button): openAtBackend = False if button == QtCore.Qt.MiddleButton: openAtBackend = True self.tagClicked.emit(tag, openAtBackend)
class SingleTweetWidget(QtGui.QFrame): userClicked = QtCore.pyqtSignal(UserItem, bool) tagClicked = QtCore.pyqtSignal(str, bool) deleteReturn = QtCore.pyqtSignal(bool) MENTIONS_RE = re.compile('(@[-a-zA-Z0-9_\u4e00-\u9fa5]+)') SINA_URL_RE = re.compile(r"(http://t.cn/[a-zA-Z0-9]{5,7})") HASHTAG_RE = re.compile("(#.*?#)") def __init__(self, tweet=None, without=(), parent=None): super(SingleTweetWidget, self).__init__(parent) self.errorWindow = APIErrorWindow(self) self.tweet = tweet self.client = const.client self.without = without self.setObjectName("SingleTweetWidget") try: self.setupUi() except RuntimeError: return self.fetcher = AsyncFetcher("".join( (cache_path, str(WeRuntimeInfo()["uid"])))) self.download_lock = False self.__favorite_queue = [] def setupUi(self): self.horizontalLayout = QtGui.QHBoxLayout(self) self.horizontalLayout.setMargin(0) self.horizontalLayout.setObjectName("horizontalLayout") self.verticalLayout_2 = QtGui.QVBoxLayout() self.verticalLayout_2.setObjectName("verticalLayout_2") reason = self.tweet.author.verify_reason if self.tweet.author.verify_type == "personal": self.avatar = WAvatarLabel(WAvatarLabel.PERSONAL_VERIFY, reason) elif self.tweet.author.verify_type == "organization": self.avatar = WAvatarLabel(WAvatarLabel.ORGANIZATION_VERIFY, reason) else: self.avatar = WAvatarLabel(WAvatarLabel.NO_VERIFY) self.avatar.setObjectName("avatar") self.avatar.setPixmap(self.tweet.author.avatar) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.avatar.sizePolicy().hasHeightForWidth()) self.avatar.setSizePolicy(sizePolicy) self.avatar.setAlignment(QtCore.Qt.AlignTop) self.avatar.clicked.connect(self._userClicked) self.verticalLayout_2.addWidget(self.avatar) self.time = SimpleLabel() self.time.setObjectName("time") sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.time.sizePolicy().hasHeightForWidth()) self.time.setSizePolicy(sizePolicy) self.verticalLayout_2.addWidget(self.time) self.verticalLayout_2.setAlignment(QtCore.Qt.AlignTop) self.horizontalLayout.addLayout(self.verticalLayout_2) self.verticalLayout = QtGui.QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") self.username = QtGui.QLabel(self) self.username.setObjectName("username") self.username.setAlignment(QtCore.Qt.AlignTop) self.username.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse) self.verticalLayout.addWidget(self.username) self.tweetText = WTweetLabel(self) self.tweetText.setObjectName("tweetText") self.tweetText.setAlignment(QtCore.Qt.AlignTop) self.tweetText.userClicked.connect(self._userTextClicked) self.tweetText.tagClicked.connect(self._tagClicked) self.verticalLayout.addWidget(self.tweetText) if self.tweet.thumbnail_pic and ("image" not in self.without): self.imageWidget = self._createImageLabel(self.tweet.thumbnail_pic) self.verticalLayout.addWidget(self.imageWidget) if self.tweet.original and ("original" not in self.without): self.originalLabel = self._createOriginalLabel() self.verticalLayout.addWidget(self.originalLabel) self.horizontalLayout.setStretch(1, 1) self.horizontalLayout.setStretch(2, 10) self.counterHorizontalLayout = QtGui.QHBoxLayout() self.counterHorizontalLayout.setObjectName("counterhorizontalLayout") self.horizontalSpacer = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) self.counterHorizontalLayout.addItem(self.horizontalSpacer) if WeRuntimeInfo().get("uid") == self.tweet.author.id: self.delete = self._createDeleteLabel() self.counterHorizontalLayout.addWidget(self.delete) if not (self.tweet.type == TweetItem.COMMENT): self.client = QtGui.QLabel() self.client.setText(self.tr("From: %s") % self.tweet.source) self.client.linkActivated.connect(lambda link: openLink(link)) self.counterHorizontalLayout.addWidget(self.client) self.retweet = self._createRetweetLabel() self.counterHorizontalLayout.addWidget(self.retweet) self.comment = self._createCommentLabel() self.counterHorizontalLayout.addWidget(self.comment) self.favorite = self._createFavoriteLabel() self.counterHorizontalLayout.addWidget(self.favorite) self.counterHorizontalLayout.setAlignment(QtCore.Qt.AlignTop) elif self.tweet.type == TweetItem.COMMENT: self.reply = self._createReplyLabel() self.counterHorizontalLayout.addWidget(self.reply) self.verticalLayout.addLayout(self.counterHorizontalLayout) self.horizontalLayout.addLayout(self.verticalLayout) self.setStyleSheet(""" QFrame#SingleTweetWidget { border-bottom: 2px solid palette(highlight); border-radius: 0px; padding: 2px; } """) self.username.setText(" " + self.tweet.author.name) text = QtCore.Qt.escape(self.tweet.text) text = self._create_mentions(text) text = self._create_html_url(text) text = self._create_hashtag(text) text = self._create_smiles(text) self.tweetText.setHtml(text) self.timer = QtCore.QTimer(self) self.timer.timeout.connect(self._update_time) self._update_time() def _setup_timer(self): self.timer.stop() passedSeconds = self.tweet.passedSeconds if passedSeconds < 60: self.timer.start(1 * 1000) elif passedSeconds < 3600: self.timer.start(60 * 1000) elif passedSeconds < 86400: self.timer.start(60 * 60 * 1000) else: self.timer.start(60 * 60 * 24 * 1000) def _update_time(self): try: if not self.time.visibleRegion() and self.timer.isActive(): # Skip update only when timer is active, insure # at least run once time. return if not self.time.toolTip(): self.time.setToolTip(self.tweet.timestamp) if self.tweet.type != TweetItem.COMMENT: self.time.setText("<a href='%s'>%s</a>" % (self.tweet.url, self.tweet.time)) else: self.time.setText("<a href='%s'>%s</a>" % (self.tweet.original.url, self.tweet.time)) self._setup_timer() except: # Sometimes, user closed the window and the window # has been garbage collected already, but # the timer is still running. It will cause a runtime error pass def _createOriginalLabel(self): widget = QtGui.QWidget(self) widget.setObjectName("originalWidget") widgetLayout = QtGui.QVBoxLayout(widget) widgetLayout.setSpacing(0) widgetLayout.setMargin(0) widgetLayout.setAlignment(QtCore.Qt.AlignCenter | QtCore.Qt.AlignTop) frame = QtGui.QFrame() frame.setObjectName("originalFrame") widgetLayout.addWidget(frame) layout = QtGui.QVBoxLayout(frame) layout.setObjectName("originalLayout") layout.setAlignment(QtCore.Qt.AlignTop) textLabel = WTweetLabel() textLabel.setAlignment(QtCore.Qt.AlignCenter | QtCore.Qt.AlignTop) textLabel.userClicked.connect(self._userTextClicked) textLabel.tagClicked.connect(self._tagClicked) self.textLabel = textLabel # Hack: save a reference originalItem = self.tweet.original text = QtCore.Qt.escape(originalItem.text) text = self._create_mentions(text) text = self._create_html_url(text) text = self._create_hashtag(text) text = self._create_smiles(text) try: authorName = self._create_mentions("@" + originalItem.author.name) textLabel.setHtml("%s: %s" % (authorName, text)) except: # originalItem.text == This tweet deleted by author textLabel.setHtml(text) layout.addWidget(textLabel) if originalItem.thumbnail_pic: layout.addWidget(self._createImageLabel( originalItem.thumbnail_pic)) counterHorizontalLayout = QtGui.QHBoxLayout() counterHorizontalLayout.setObjectName("counterhorizontalLayout") horizontalSpacer = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) counterHorizontalLayout.addItem(horizontalSpacer) retweet = WIconLabel(widget) retweet.setObjectName("retweet") retweet.setText(str(originalItem.retweets_count)) retweet.setIcon(":/IMG/img/retweets.png") retweet.clicked.connect(self._original_retweet) counterHorizontalLayout.addWidget(retweet) comment = WIconLabel(widget) comment.setObjectName("comment") comment.setIcon(":/IMG/img/comments.png") comment.setText(str(originalItem.comments_count)) comment.clicked.connect(self._original_comment) counterHorizontalLayout.addWidget(comment) counterHorizontalLayout.setSpacing(6) layout.setMargin(8) layout.setSpacing(0) layout.addLayout(counterHorizontalLayout) frame.setStyleSheet(""" QFrame#originalFrame { border: 2px solid palette(highlight); border-radius: 4px; padding: 2px; } """) return widget def _createImageLabel(self, thumbnail_pic): widget = QtGui.QWidget(self) widget.setObjectName("imageLabel") widgetLayout = QtGui.QVBoxLayout(widget) widgetLayout.setSpacing(0) widgetLayout.setMargin(0) widgetLayout.setAlignment(QtCore.Qt.AlignCenter) frame = QtGui.QFrame() frame.setObjectName("imageFrame") widgetLayout.addWidget(frame) layout = QtGui.QVBoxLayout(frame) layout.setObjectName("imageLayout") self.imageLabel = WSwitchLabel(widget) self.imageLabel.setImagesUrls(thumbnail_pic) self.imageLabel.clicked.connect(self._showFullImage) layout.addWidget(self.imageLabel) widgetLayout.addWidget(frame) return widget def _createFavoriteLabel(self): favorite = WIconLabel(self) favorite.setIcon(":/IMG/img/no_favorites.png") favorite.clicked.connect(self._favorite) return favorite def _createRetweetLabel(self): retweet = WIconLabel(self) retweet.setObjectName("retweet") retweet.setText(str(self.tweet.retweets_count)) retweet.setIcon(":/IMG/img/retweets.png") retweet.clicked.connect(self._retweet) return retweet def _createCommentLabel(self): comment = WIconLabel(self) comment.setObjectName("comment") comment.setIcon(":/IMG/img/comments.png") comment.setText(str(self.tweet.comments_count)) comment.clicked.connect(self._comment) return comment def _createReplyLabel(self): reply = WIconLabel(self) reply.setObjectName("reply") reply.setIcon(":/IMG/img/retweets.png") reply.clicked.connect(self._reply) return reply def _createDeleteLabel(self): delete = WIconLabel(self) delete.setObjectName("delete") delete.setIcon(":/IMG/img/deletes.png") delete.clicked.connect(self._delete) return delete def fetch_open_original_pic(self, thumbnail_pic): """Fetch and open original pic from thumbnail pic url. Pictures will stored in cache directory. If we already have a same name in cache directory, just open it. If we don't, then download it first.""" def open_pic(localfile): start(localfile) self.download_lock = False self.imageLabel.setBusy(False) if self.download_lock: return self.download_lock = True self.imageLabel.setBusy(True) original_pic = thumbnail_pic.replace("thumbnail", "large") # A simple trick ... ^_^ self.fetcher.addTask(original_pic, open_pic) def _showFullImage(self): self.fetch_open_original_pic(self.imageLabel.url()) def _favorite(self): needWorker = False if not self.__favorite_queue: state = not self.tweet.isFavorite() needWorker = True elif not self.__favorite_queue[-1]: state = True else: state = False self.__favorite_queue.append(state) if state: self.favorite.setIcon(":/IMG/img/favorites.png") else: self.favorite.setIcon(":/IMG/img/no_favorites.png") if needWorker: self.__favorite_worker() @async def __favorite_worker(self): while self.__favorite_queue: state = self.__favorite_queue[0] try: self.tweet.setFavorite(state) sleep(0.5) self.__favorite_queue.pop(0) except APIError as e: if e.error_code == 20101: self.tweet.setFavoriteForce(True) elif e.error_code == 20704: self.tweet.setFavoriteForce(True) self._e = e self.__favorite_queue = [] self.errorWindow.raiseException.emit(self._e) return def _retweet(self, tweet=None): if not tweet: tweet = self.tweet self.exec_newpost_window("retweet", tweet) def _comment(self, tweet=None): if not tweet: tweet = self.tweet if tweet.type == TweetItem.COMMENT: self._reply(tweet) return self.exec_newpost_window("comment", tweet) def _reply(self, tweet=None): if not tweet: tweet = self.tweet self.exec_newpost_window("reply", tweet) def _delete(self): @async def do_delete(): try: self.tweet.delete() self.deleteReturn.emit(True) except APIError as e: self.errorWindow.raiseException.emit(e) self.deleteReturn.emit(False) questionDialog = QtGui.QMessageBox.question choice = questionDialog(self, self.tr("Delete?"), self.tr("You can't undo your deletion."), QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) if choice == QtGui.QMessageBox.No: return self.deleteReturn.connect(lambda state: state and self.remove()) do_delete() def remove(self): # not really remove myself. self.timer.stop() self.hide() def _original_retweet(self): self._retweet(self.tweet.original) def _original_comment(self): self._comment(self.tweet.original) def _create_html_url(self, text): return self.SINA_URL_RE.sub(r"""<a href='\1'>\1</a>""", text) def _create_smiles(self, text): faceModel = FaceModel() for face in faceModel.all_faces(): new_text = text.replace("[%s]" % face.name, '<img src="%s" />' % face.path) if new_text != text: self._create_animation(face.path) text = new_text return text def _create_mentions(self, text): return self.MENTIONS_RE.sub(r"""<a href='mentions:///\1'>\1</a>""", text) def _create_hashtag(self, text): return self.HASHTAG_RE.sub(r"""<a href='hashtag:///\1'>\1</a>""", text) def _create_animation(self, path): movie = WObjectCache().open(WMovie, path) try: movie.frameChanged.connect(self.drawAnimate, QtCore.Qt.UniqueConnection) except TypeError: # connected already pass movie.start() def drawAnimate(self): sender = self.sender() if (not isinstance(sender, WMovie)) or (not self.tweetText.visibleRegion()): return movie = sender self._addSingleFrame(movie, self.tweetText) if self.tweet.original and ("original" not in self.without): self._addSingleFrame(movie, self.textLabel) def _addSingleFrame(self, movie, textBrowser): document = textBrowser.document() document.addResource(QtGui.QTextDocument.ImageResource, QtCore.QUrl(movie.fileName()), movie.currentPixmap()) # Cause a force refresh textBrowser.update() def exec_newpost_window(self, action, tweet): from NewpostWindow import NewpostWindow try: self.wecase_new = NewpostWindow(action, tweet) self.wecase_new.userClicked.connect(self.userClicked) self.wecase_new.tagClicked.connect(self.tagClicked) self.wecase_new.show() except APIError as e: self.errorWindow.raiseException.emit(e) def _userClicked(self, button): openAtBackend = False if button == QtCore.Qt.MiddleButton: openAtBackend = True self.userClicked.emit(self.tweet.author, openAtBackend) @async def _userTextClicked(self, user, button): openAtBackend = False if button == QtCore.Qt.MiddleButton: openAtBackend = True try: self.__userItem = UserItem({"name": user}) except APIError as e: self.errorWindow.raiseException.emit(e) return self.userClicked.emit(self.__userItem, openAtBackend) def _tagClicked(self, tag, button): openAtBackend = False if button == QtCore.Qt.MiddleButton: openAtBackend = True self.tagClicked.emit(tag, openAtBackend)
class SingleTweetWidget(QtGui.QFrame): imageLoaded = QtCore.pyqtSignal() userClicked = QtCore.pyqtSignal(UserItem, bool) tagClicked = QtCore.pyqtSignal(str, bool) commonSignal = QtCore.pyqtSignal(object) def __init__(self, tweet=None, without=[], parent=None): super(SingleTweetWidget, self).__init__(parent) self.commonSignal.connect(self.commonProcessor) self._gif_list = {} self.tweet = tweet self.client = const.client self.without = without self.setObjectName("SingleTweetWidget") self.setupUi() self.download_lock = False self.__favorite_queue = [] def setupUi(self): self.horizontalLayout = QtGui.QHBoxLayout(self) self.horizontalLayout.setMargin(0) self.horizontalLayout.setObjectName("horizontalLayout") self.verticalLayout_2 = QtGui.QVBoxLayout() self.verticalLayout_2.setObjectName("verticalLayout_2") reason = self.tweet.author.verify_reason if self.tweet.author.verify_type == "personal": self.avatar = WAvatarLabel(WAvatarLabel.PERSONAL_VERIFY, reason) elif self.tweet.author.verify_type == "organization": self.avatar = WAvatarLabel(WAvatarLabel.ORGANIZATION_VERIFY, reason) else: self.avatar = WAvatarLabel(WAvatarLabel.NO_VERIFY) self.avatar.setObjectName("avatar") self.avatar.setPixmap(self.tweet.author.avatar) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.avatar.sizePolicy().hasHeightForWidth()) self.avatar.setSizePolicy(sizePolicy) self.avatar.setAlignment(QtCore.Qt.AlignTop) self.avatar.clicked.connect(self._userClicked) self.verticalLayout_2.addWidget(self.avatar) self.time = QtGui.QLabel(self) self.time.setObjectName("time") sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.time.sizePolicy().hasHeightForWidth()) self.time.setSizePolicy(sizePolicy) self.time.setOpenExternalLinks(True) self.verticalLayout_2.addWidget(self.time) self.verticalLayout_2.setAlignment(QtCore.Qt.AlignTop) self.horizontalLayout.addLayout(self.verticalLayout_2) self.verticalLayout = QtGui.QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") self.username = QtGui.QLabel(self) self.username.setObjectName("username") self.username.setAlignment(QtCore.Qt.AlignTop) self.username.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse) self.verticalLayout.addWidget(self.username) self.tweetText = WTweetLabel(self) self.tweetText.setObjectName("tweetText") self.tweetText.setAlignment(QtCore.Qt.AlignTop) self.tweetText.userClicked.connect(self._userTextClicked) self.tweetText.tagClicked.connect(self._tagClicked) self.verticalLayout.addWidget(self.tweetText) if self.tweet.thumbnail_pic and (not "image" in self.without): self.imageWidget = self._createImageLabel(self.tweet.thumbnail_pic) self.verticalLayout.addWidget(self.imageWidget) if self.tweet.original and (not "original" in self.without): self.originalLabel = self._createOriginalLabel() self.verticalLayout.addWidget(self.originalLabel) self.horizontalLayout.setStretch(1, 1) self.horizontalLayout.setStretch(2, 10) self.counterHorizontalLayout = QtGui.QHBoxLayout() self.counterHorizontalLayout.setObjectName("counterhorizontalLayout") self.horizontalSpacer = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) self.counterHorizontalLayout.addItem(self.horizontalSpacer) if WeRuntimeInfo().get("uid") == self.tweet.author.id: self.delete = self._createDeleteLabel() self.counterHorizontalLayout.addWidget(self.delete) if not (self.tweet.type == TweetItem.COMMENT): self.retweet = self._createRetweetLabel() self.counterHorizontalLayout.addWidget(self.retweet) self.comment = self._createCommentLabel() self.counterHorizontalLayout.addWidget(self.comment) self.favorite = self._createFavoriteLabel() self.counterHorizontalLayout.addWidget(self.favorite) self.counterHorizontalLayout.setAlignment(QtCore.Qt.AlignTop) elif self.tweet.type == TweetItem.COMMENT: self.reply = self._createReplyLabel() self.counterHorizontalLayout.addWidget(self.reply) self.verticalLayout.addLayout(self.counterHorizontalLayout) self.horizontalLayout.addLayout(self.verticalLayout) self.setStyleSheet(""" QFrame#SingleTweetWidget { border-bottom: 2px solid palette(highlight); border-radius: 0px; padding: 2px; } """) self.username.setText(" " + self.tweet.author.name) text = QtCore.Qt.escape(self.tweet.text) text = self._create_mentions(text) text = self._create_html_url(text) text = self._create_hashtag(text) text = self._create_smiles(text) self.tweetText.setHtml(text) self.timer = QtCore.QTimer(self) self.timer.timeout.connect(self._update_time) self._update_time() def _setup_timer(self): self.timer.stop() passedSeconds = self.tweet.passedSeconds if passedSeconds < 60: self.timer.start(1 * 1000) elif passedSeconds < 3600: self.timer.start(60 * 1000) elif passedSeconds < 86400: self.timer.start(60 * 60 * 1000) else: self.timer.start(60 * 60 * 24 * 1000) def _update_time(self): try: if self.tweet.type != TweetItem.COMMENT: self.time.setText("<a href='%s'>%s</a>" % (self.tweet.url, self.tweet.time)) else: self.time.setText("<a href='%s'>%s</a>" % (self.tweet.original.url, self.tweet.time)) self._setup_timer() except: # Sometimes, user closed the window and the window # has been garbage collected already, but # the timer is still running. It will cause a runtime error pass def _createOriginalLabel(self): widget = QtGui.QWidget(self) widget.setObjectName("originalWidget") widgetLayout = QtGui.QVBoxLayout(widget) widgetLayout.setSpacing(0) widgetLayout.setMargin(0) widgetLayout.setAlignment(QtCore.Qt.AlignCenter | QtCore.Qt.AlignTop) frame = QtGui.QFrame() frame.setObjectName("originalFrame") widgetLayout.addWidget(frame) layout = QtGui.QVBoxLayout(frame) layout.setObjectName("originalLayout") layout.setAlignment(QtCore.Qt.AlignTop) textLabel = WTweetLabel() textLabel.setAlignment(QtCore.Qt.AlignCenter | QtCore.Qt.AlignTop) textLabel.userClicked.connect(self._userTextClicked) textLabel.tagClicked.connect(self._tagClicked) self.textLabel = textLabel # Hack: save a reference originalItem = self.tweet.original text = QtCore.Qt.escape(originalItem.text) text = self._create_mentions(text) text = self._create_html_url(text) text = self._create_hashtag(text) text = self._create_smiles(text) try: authorName = self._create_mentions("@" + originalItem.author.name) textLabel.setHtml("%s: %s" % (authorName, text)) except: # originalItem.text == This tweet deleted by author textLabel.setHtml(text) layout.addWidget(textLabel) if originalItem.thumbnail_pic: layout.addWidget(self._createImageLabel(originalItem.thumbnail_pic)) counterHorizontalLayout = QtGui.QHBoxLayout() counterHorizontalLayout.setObjectName("counterhorizontalLayout") horizontalSpacer = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) counterHorizontalLayout.addItem(horizontalSpacer) retweet = WIconLabel(widget) retweet.setObjectName("retweet") retweet.setText(str(originalItem.retweets_count)) retweet.setIcon(const.myself_path + "/icon/retweets.png") retweet.clicked.connect(self._original_retweet) counterHorizontalLayout.addWidget(retweet) comment = WIconLabel(widget) comment.setObjectName("comment") comment.setIcon(const.myself_path + "/icon/comments.png") comment.setText(str(originalItem.comments_count)) comment.clicked.connect(self._original_comment) counterHorizontalLayout.addWidget(comment) counterHorizontalLayout.setSpacing(6) layout.setMargin(8) layout.setSpacing(0) layout.addLayout(counterHorizontalLayout) frame.setStyleSheet(""" QFrame#originalFrame { border: 2px solid palette(highlight); border-radius: 4px; padding: 2px; } """) return widget def _createImageLabel(self, thumbnail_pic): widget = QtGui.QWidget(self) widget.setObjectName("imageLabel") widgetLayout = QtGui.QVBoxLayout(widget) widgetLayout.setSpacing(0) widgetLayout.setMargin(0) widgetLayout.setAlignment(QtCore.Qt.AlignCenter) frame = QtGui.QFrame() frame.setObjectName("imageFrame") widgetLayout.addWidget(frame) layout = QtGui.QVBoxLayout(frame) layout.setObjectName("imageLayout") self.imageLabel = WAsyncLabel(widget) self.imageLabel.setPixmap(thumbnail_pic) self.imageLabel.setAlignment(QtCore.Qt.AlignCenter) self.imageLabel.clicked.connect(self._showFullImage) layout.addWidget(self.imageLabel) widgetLayout.addWidget(frame) return widget def _createFavoriteLabel(self): favorite = WIconLabel(self) favorite.setIcon(const.myself_path + "/icon/no_favorites.png") favorite.clicked.connect(self._favorite) return favorite def _createRetweetLabel(self): retweet = WIconLabel(self) retweet.setObjectName("retweet") retweet.setText(str(self.tweet.retweets_count)) retweet.setIcon(const.myself_path + "/icon/retweets.png") retweet.clicked.connect(self._retweet) return retweet def _createCommentLabel(self): comment = WIconLabel(self) comment.setObjectName("comment") comment.setIcon(const.myself_path + "/icon/comments.png") comment.setText(str(self.tweet.comments_count)) comment.clicked.connect(self._comment) return comment def _createReplyLabel(self): reply = WIconLabel(self) reply.setObjectName("reply") reply.setIcon(const.myself_path + "/icon/retweets.png") reply.clicked.connect(self._reply) return reply def _createDeleteLabel(self): delete = WIconLabel(self) delete.setObjectName("delete") delete.setIcon(const.myself_path + "/icon/deletes.png") delete.clicked.connect(self._delete) return delete @async def fetch_open_original_pic(self, thumbnail_pic): """Fetch and open original pic from thumbnail pic url. Pictures will stored in cache directory. If we already have a same name in cache directory, just open it. If we don't, then download it first.""" if self.download_lock: return self.download_lock = True self.commonSignal.emit(lambda: self.imageLabel.setBusy(True)) original_pic = thumbnail_pic.replace("thumbnail", "large") # A simple trick ... ^_^ localfile = cache_path + original_pic.split("/")[-1] if not os.path.exists(localfile): while True: try: urllib.request.urlretrieve(original_pic, localfile) break except (BadStatusLine, URLError, ContentTooShortError): continue self.download_lock = False self.commonSignal.emit(lambda: self.imageLabel.setBusy(False)) start(localfile) self.imageLoaded.emit() def _showFullImage(self): self.fetch_open_original_pic(self.imageLabel.url()) def commonProcessor(self, object): object() def _favorite(self): needWorker = False if not self.__favorite_queue: state = not self.tweet.isFavorite() needWorker = True elif not self.__favorite_queue[-1]: state = True else: state = False self.__favorite_queue.append(state) if state: self.favorite.setIcon(const.icon("favorites.png")) else: self.favorite.setIcon(const.icon("no_favorites.png")) if needWorker: self.__favorite_worker() @async def __favorite_worker(self): while self.__favorite_queue: state = self.__favorite_queue[0] try: self.tweet.setFavorite(state) sleep(0.5) self.__favorite_queue.pop(0) except APIError as e: if e.error_code == 20101: self.tweet.setFavoriteForce(True) elif e.error_code == 20704: self.tweet.setFavoriteForce(True) self._e = e self.__favorite_queue = [] self.commonSignal.emit(lambda: self._handle_api_error(self._e)) return def _retweet(self, tweet=None): if not tweet: tweet = self.tweet self.exec_newpost_window("retweet", tweet) def _comment(self, tweet=None): if not tweet: tweet = self.tweet if tweet.type == TweetItem.COMMENT: self._reply(tweet) return self.exec_newpost_window("comment", tweet) def _reply(self, tweet=None): if not tweet: tweet = self.tweet self.exec_newpost_window("reply", tweet) def _delete(self): questionDialog = QtGui.QMessageBox.question choice = questionDialog(self, self.tr("Delete?"), self.tr("You can't undo your deletion."), QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) if choice == QtGui.QMessageBox.No: return try: self.tweet.delete() except APIError as e: self._handle_api_error(e) self.timer.stop() self.hide() def _original_retweet(self): self._retweet(self.tweet.original) def _original_comment(self): self._comment(self.tweet.original) def _create_html_url(self, text): COMMON_URL_RE = ( r"(?:(http|https)://" r"(?:(?:(?:(?:(?:[a-zA-Z\d](?:(?:[a-zA-Z\d]|-)*[a-zA-Z\d])?)\." r")*(?:[a-zA-Z](?:(?:[a-zA-Z\d]|-)*[a-zA-Z\d])?))|(?:(?:\d+)(?:\.(?:\d+)" r"){3}))(?::(?:\d+))?)(?:/(?:(?:(?:(?:[a-zA-Z\d$\-_.+!*'(),]|(?:%[a-fA-F" r"\d]{2}))|[;:@&=])*)(?:/(?:(?:(?:[a-zA-Z\d$\-_.+!*'(),]|(?:%[a-fA-F\d]{" r"2}))|[;:@&=])*))*)(?:\?(?:(?:(?:[a-zA-Z\d$\-_.+!*'(),]|(?:%[a-fA-F\d]{" r"2}))|[;:@&=])*))?)?)|(?:ftp://(?:(?:(?:(?:(?:[a-zA-Z\d$\-_.+!*'(),]|(?" r":%[a-fA-F\d]{2}))|[;?&=])*)(?::(?:(?:(?:[a-zA-Z\d$\-_.+!*'(),]|(?:%[a-" r"fA-F\d]{2}))|[;?&=])*))?@)?(?:(?:(?:(?:(?:[a-zA-Z\d](?:(?:[a-zA-Z\d]|-" r")*[a-zA-Z\d])?)\.)*(?:[a-zA-Z](?:(?:[a-zA-Z\d]|-)*[a-zA-Z\d])?))|(?:(?" r":\d+)(?:\.(?:\d+)){3}))(?::(?:\d+))?))(?:/(?:(?:(?:(?:[a-zA-Z\d$\-_.+!" r"*'(),]|(?:%[a-fA-F\d]{2}))|[?:@&=])*)(?:/(?:(?:(?:[a-zA-Z\d$\-_.+!*'()" r",]|(?:%[a-fA-F\d]{2}))|[?:@&=])*))*)(?:;type=[AIDaid])?)?)" ) SINA_URL_RE = r"(http://t.cn/\w{6,7})" regex = re.compile("((%s)|(%s))" % (SINA_URL_RE, COMMON_URL_RE)) new_text = regex.sub(r"""<a href='\1'>\1</a>""", text) return new_text def _create_smiles(self, text): faceModel = FaceModel() faceModel.init() for name, path in faceModel.dic().items(): new_text = text.replace("[%s]" % name, '<img src="%s" />' % path) if new_text != text: self._create_animation(path) text = new_text return text def _create_mentions(self, text): MENTIONS_RE = re.compile('(@[-a-zA-Z0-9_\u4e00-\u9fa5]+)') regex = re.compile(MENTIONS_RE) new_text = regex.sub(r"""<a href='mentions:///\1'>\1</a>""", text) return new_text def _create_hashtag(self, text): HASHTAG_RE = re.compile("([#]+[a-zA-Z0-9_\u4e00-\u9fa5\s]+[#])") regex = re.compile(HASHTAG_RE) new_text = regex.sub(r"""<a href='hashtag:///\1'>\1</a>""", text) return new_text def _create_animation(self, path): if path in self._gif_list.values(): # We added it already. return movie = WObjectCache().open(QtGui.QMovie, path) self._gif_list[movie] = path movie.frameChanged.connect(self.drawAnimate) movie.start() def drawAnimate(self): sender = self.sender() if isinstance(sender, QtGui.QMovie): movie = sender else: return self._addSingleFrame(movie, self.tweetText) if self.tweet.type == TweetItem.RETWEET: try: self._addSingleFrame(movie, self.textLabel) except AttributeError: pass def _addSingleFrame(self, movie, textBrowser): document = textBrowser.document() document.addResource(QtGui.QTextDocument.ImageResource, QtCore.QUrl(self._gif_list[movie]), movie.currentPixmap()) # Cause a force refresh textBrowser.setLineWrapColumnOrWidth(textBrowser.lineWrapColumnOrWidth()) def exec_newpost_window(self, action, tweet): from NewpostWindow import NewpostWindow try: self.wecase_new = NewpostWindow(action, tweet) self.wecase_new.userClicked.connect(self.userClicked) self.wecase_new.tagClicked.connect(self.tagClicked) self.wecase_new.show() except APIError as e: self._handle_api_error(e) def _handle_api_error(self, exception): if exception.error_code == 20101: QtGui.QMessageBox.information(self, self.tr("Error"), self.tr("This tweet have been deleted.")) elif exception.error_code == 20704: QtGui.QMessageBox.information(self, self.tr("Error"), self.tr("This tweet have been collected already..")) else: QtGui.QMessageBox.warning(self, self.tr("Unknown Error"), str(exception)) def _userClicked(self, button): openAtBackend = False if button == QtCore.Qt.MiddleButton: openAtBackend = True self.userClicked.emit(self.tweet.author, openAtBackend) def _userTextClicked(self, user, button): openAtBackend = False if button == QtCore.Qt.MiddleButton: openAtBackend = True self.userClicked.emit(UserItem({"name": user}), openAtBackend) def _tagClicked(self, tag, button): openAtBackend = False if button == QtCore.Qt.MiddleButton: openAtBackend = True self.tagClicked.emit(tag, openAtBackend)
class WeCaseWindow(QtGui.QMainWindow): client = None uid = None timelineLoaded = QtCore.pyqtSignal(int) imageLoaded = QtCore.pyqtSignal(str) tabBadgeChanged = QtCore.pyqtSignal(int, int) tabAvatarFetched = QtCore.pyqtSignal(str) def __init__(self, username, parent=None): super(WeCaseWindow, self).__init__(parent) self.username = username LoginInfo().add_account(self.username) self.errorWindow = APIErrorWindow(self) self.setAttribute(QtCore.Qt.WA_QuitOnClose, True) self._iconPixmap = {} self.setupUi(self) self._setupSysTray() self.tweetViews = [ self.homeView, self.mentionsView, self.commentsView, self.commentsMentionsTab ] self.info = WeRuntimeInfo() self.client = const.client self.loadConfig() self.init_account() self.setupModels() self.IMG_AVATAR = -2 self.IMG_THUMB = -1 self.notify = Notify(timeout=self.notify_timeout) self.applyConfig() self.download_lock = [] self._last_reminds_count = 0 self._setupUserTab(self.uid(), False, True) def _setupTab(self, view): tab = QtGui.QWidget() layout = QtGui.QGridLayout(tab) layout.addWidget(view) view.setParent(tab) return tab def _setupCommonTab(self, timeline, view, switch=True, protect=False): self._prepareTimeline(timeline) view.setModel(timeline) view.userClicked.connect(self.userClicked) view.tagClicked.connect(self.tagClicked) tab = self._setupTab(view) self.tabWidget.addTab(tab, "") if switch: self.tabWidget.setCurrentWidget(tab) if protect: self.tabWidget.tabBar().setProtectTab(tab, True) return tab def _getSameTab(self, attr, value): for i in range(self.tabWidget.count()): try: tab = self.tabWidget.widget(i).layout().itemAt(0).widget() _value = getattr(tab.model(), attr)() if _value == value: return i except AttributeError: pass return False def _setupUserTab(self, uid, switch=True, myself=False): index = self._getSameTab("uid", uid) if index: if switch: self.tabWidget.setCurrentIndex(index) return view = TweetListWidget() _timeline = TweetUserModel(self.client.api("statuses/user_timeline"), uid, view) timeline = TweetFilterModel(_timeline) timeline.setModel(_timeline) tab = self._setupCommonTab(timeline, view, switch, myself) def setAvatar(f): self._setTabIcon(tab, WObjectCache().open(QtGui.QPixmap, f)) fetcher = AsyncFetcher("".join( (path.cache_path, str(self.info["uid"])))) fetcher.addTask( self.client.api("users/show").get(uid=uid)["profile_image_url"], setAvatar) def _setupTopicTab(self, topic, switch=True): index = self._getSameTab("topic", topic) if index: if switch: self.tabWidget.setCurrentIndex(index) return view = TweetListWidget() timeline = TweetTopicModel(self.client.api("search/topics"), topic, view) tab = self._setupCommonTab(timeline, view, switch, protect=False) self._setTabIcon( tab, WObjectCache().open(QtGui.QPixmap, ":/IMG/img/topic.jpg")) def userClicked(self, userItem, openAtBackend): try: self._setupUserTab(userItem.id, switch=(not openAtBackend)) except APIError as e: self.errorWindow.raiseException.emit(e) def tagClicked(self, str, openAtBackend): try: self._setupTopicTab(str, switch=(not openAtBackend)) except APIError as e: self.errorWindow.raiseException.emit(e) def setupUi(self, mainWindow): mainWindow.setWindowIcon(QtGui.QIcon(":/IMG/img/WeCase.svg")) mainWindow.setDocumentMode(False) mainWindow.setDockOptions(QtGui.QMainWindow.AllowTabbedDocks | QtGui.QMainWindow.AnimatedDocks) self.centralwidget = QtGui.QWidget(mainWindow) self.verticalLayout = QtGui.QVBoxLayout(self.centralwidget) self.tabWidget = QtGui.QTabWidget(self.centralwidget) self.tabWidget.setTabBar(WTabBar(self.tabWidget)) self.tabWidget.setTabPosition(QtGui.QTabWidget.West) self.tabWidget.setTabShape(QtGui.QTabWidget.Rounded) self.tabWidget.setDocumentMode(False) self.tabWidget.setMovable(True) self.tabWidget.tabCloseRequested.connect(self.closeTab) self.homeView = TweetListWidget() self.homeView.userClicked.connect(self.userClicked) self.homeView.tagClicked.connect(self.tagClicked) self.homeTab = self._setupTab(self.homeView) self.tabWidget.addTab(self.homeTab, "") self.tabWidget.tabBar().setProtectTab(self.homeTab, True) self.mentionsView = TweetListWidget() self.mentionsView.userClicked.connect(self.userClicked) self.mentionsView.tagClicked.connect(self.tagClicked) self.mentionsTab = self._setupTab(self.mentionsView) self.tabWidget.addTab(self.mentionsTab, "") self.tabWidget.tabBar().setProtectTab(self.mentionsTab, True) self.commentsView = TweetListWidget() self.commentsView.userClicked.connect(self.userClicked) self.commentsView.tagClicked.connect(self.tagClicked) self.commentsTab = self._setupTab(self.commentsView) self.tabWidget.addTab(self.commentsTab, "") self.tabWidget.tabBar().setProtectTab(self.commentsTab, True) self.commentsMentionsView = TweetListWidget() self.commentsMentionsView.userClicked.connect(self.userClicked) self.commentsMentionsView.tagClicked.connect(self.tagClicked) self.commentsMentionsTab = self._setupTab(self.commentsMentionsView) self.tabWidget.addTab(self.commentsMentionsTab, "") self.tabWidget.tabBar().setProtectTab(self.commentsMentionsTab, True) self.verticalLayout.addWidget(self.tabWidget) self.widget = QtGui.QWidget(self.centralwidget) self.verticalLayout.addWidget(self.widget) mainWindow.setCentralWidget(self.centralwidget) self.aboutAction = QtGui.QAction(mainWindow) self.refreshAction = QtGui.QAction(mainWindow) self.logoutAction = QtGui.QAction(mainWindow) self.exitAction = QtGui.QAction(mainWindow) self.settingsAction = QtGui.QAction(mainWindow) self.aboutAction.setIcon( QtGui.QIcon(QtGui.QPixmap("./IMG/img/where_s_my_weibo.svg"))) self.exitAction.setIcon( QtGui.QIcon(QtGui.QPixmap(":/IMG/img/application-exit.svg"))) self.settingsAction.setIcon( QtGui.QIcon(QtGui.QPixmap(":/IMG/img/preferences-other.png"))) self.refreshAction.setIcon( QtGui.QIcon(QtGui.QPixmap(":/IMG/img/refresh.png"))) self.menubar = QtGui.QMenuBar(mainWindow) self.menubar.setEnabled(True) self.menubar.setDefaultUp(False) mainWindow.setMenuBar(self.menubar) self.mainMenu = QtGui.QMenu(self.menubar) self.helpMenu = QtGui.QMenu(self.menubar) self.optionsMenu = QtGui.QMenu(self.menubar) self.mainMenu.addAction(self.refreshAction) self.mainMenu.addSeparator() self.mainMenu.addAction(self.logoutAction) self.mainMenu.addAction(self.exitAction) self.helpMenu.addAction(self.aboutAction) self.optionsMenu.addAction(self.settingsAction) self.menubar.addAction(self.mainMenu.menuAction()) self.menubar.addAction(self.optionsMenu.menuAction()) self.menubar.addAction(self.helpMenu.menuAction()) self.exitAction.triggered.connect(mainWindow.close) self.aboutAction.triggered.connect(mainWindow.showAbout) self.settingsAction.triggered.connect(mainWindow.showSettings) self.logoutAction.triggered.connect(mainWindow.logout) self.refreshAction.triggered.connect(mainWindow.refresh) self.pushButton_refresh = QtGui.QPushButton(self.widget) self.pushButton_new = QtGui.QPushButton(self.widget) self.pushButton_refresh.clicked.connect(mainWindow.refresh) self.pushButton_new.clicked.connect(mainWindow.postTweet) self.timelineLoaded.connect(self.moveToTop) self.tabBadgeChanged.connect(self.drawNotifyBadge) self.refreshAction.setShortcut(QtGui.QKeySequence("F5")) self.pushButton_refresh.setIcon(QtGui.QIcon(":/IMG/img/refresh.png")) self.pushButton_new.setIcon(QtGui.QIcon(":/IMG/img/new.png")) if self.isGlobalMenu(): self._setupToolBar() else: self._setupButtonWidget() self._setTabIcon(self.homeTab, QtGui.QPixmap(":/IMG/img/sina.png")) self._setTabIcon(self.mentionsTab, QtGui.QPixmap(":/IMG/img/mentions.png")) self._setTabIcon(self.commentsTab, QtGui.QPixmap(":/IMG/img/comments2.png")) self._setTabIcon(self.commentsMentionsTab, QtGui.QPixmap(":/IMG/img/mentions_comments.svg")) self.retranslateUi(mainWindow) def isGlobalMenu(self): if os.environ.get("TOOLBAR") == "1": return True elif os.environ.get("TOOLBAR") == "0": return False elif os.environ.get('DESKTOP_SESSION') in ["ubuntu", "ubuntu-2d"]: if not os.environ.get("UBUNTU_MENUPROXY"): return False elif os.environ.get("APPMENU_DISPLAY_BOTH"): return False else: return True elif os.environ.get( "DESKTOP_SESSION" ) == "kde-plasma" and platform.linux_distribution()[0] == "Ubuntu": return True elif platform.system() == "Darwin": return True return False def _setupToolBar(self): self.toolBar = QtGui.QToolBar() self.toolBar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) empty = QtGui.QWidget() empty.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) self.toolBar.addWidget(empty) self.toolBar.addAction(self.refreshAction) newAction = self.toolBar.addAction(QtGui.QIcon(":/IMG/img/new.png"), "New") newAction.triggered.connect(self.pushButton_new.clicked) self.addToolBar(self.toolBar) def _setupButtonWidget(self): self.buttonWidget = QtGui.QWidget(self) self.buttonLayout = QtGui.QHBoxLayout(self.buttonWidget) self.horizontalSpacer = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) self.buttonLayout.addSpacerItem(self.horizontalSpacer) self.buttonLayout.addWidget(self.pushButton_refresh) self.buttonLayout.addWidget(self.pushButton_new) def resizeEvent(self, event): # This is a hack!!! if self.isGlobalMenu(): return self.buttonWidget.resize(self.menubar.sizeHint().width(), self.menubar.sizeHint().height() + 12) self.buttonWidget.move(self.width() - self.buttonWidget.width(), self.menubar.geometry().topRight().y() - 5) def retranslateUi(self, frm_MainWindow): frm_MainWindow.setWindowTitle(self.tr("WeCase")) self.mainMenu.setTitle(self.tr("&WeCase")) self.helpMenu.setTitle(self.tr("&Help")) self.optionsMenu.setTitle(self.tr("&Options")) self.aboutAction.setText(self.tr("&About...")) self.refreshAction.setText(self.tr("Refresh")) self.logoutAction.setText(self.tr("&Log out")) self.exitAction.setText(self.tr("&Exit")) self.settingsAction.setText(self.tr("&Settings")) def _setupSysTray(self): self.systray = QtGui.QSystemTrayIcon() self.systray.activated.connect(self.clickedSystray) self.systray.setToolTip("WeCase") self.systray.setIcon(QtGui.QIcon(":/IMG/img/WeCase.svg")) self.systray.show() self.visibleAction = QtGui.QAction(self) self.visibleAction.setText(self.tr("&Hide")) self.visibleAction.triggered.connect(self._switchVisibility) self.sysMenu = QtGui.QMenu(self) self.sysMenu.addAction(self.visibleAction) self.sysMenu.addAction(self.logoutAction) self.sysMenu.addAction(self.exitAction) self.systray.setContextMenu(self.sysMenu) def clickedSystray(self, reason): if reason == QtGui.QSystemTrayIcon.Trigger: self._switchVisibility() elif reason == QtGui.QSystemTrayIcon.Context: pass def _switchVisibility(self): if self.isVisible(): self.hide() self.visibleAction.setText(self.tr("&Show")) else: self.show() self.visibleAction.setText(self.tr("&Hide")) def _setTabIcon(self, tab, icon): pixmap = icon.transformed(QtGui.QTransform().rotate(90)) icon = QtGui.QIcon(pixmap) self._iconPixmap[icon.cacheKey()] = pixmap self.tabWidget.setTabIcon(self.tabWidget.indexOf(tab), icon) self.tabWidget.setIconSize(QtCore.QSize(24, 24)) def _prepareTimeline(self, timeline): try: timeline.setUsersBlacklist(self.usersBlacklist) timeline.setTweetsKeywordsBlacklist(self.tweetKeywordsBlacklist) timeline.setWordWarKeywords(self.wordWarKeywords) timeline.setBlockWordwars(self.blockWordwars) timeline.setKeywordsAsRegexs(self.keywordsAsRegex) except AttributeError: pass timeline.load() def closeTab(self, index): widget = self.tabWidget.widget(index) self.tabWidget.removeTab(index) widget.deleteLater() def init_account(self): self.uid() def loadConfig(self): self.config = WConfigParser(path.myself_path + "WMetaConfig", path.config_path, "main") self.notify_interval = self.config.notify_interval self.notify_timeout = self.config.notify_timeout self.usersBlacklist = self.config.usersBlacklist self.tweetKeywordsBlacklist = self.config.tweetsKeywordsBlacklist self.remindMentions = self.config.remind_mentions self.remindComments = self.config.remind_comments self.wordWarKeywords = self.config.wordWarKeywords self.blockWordwars = self.config.blockWordwars self.maxRetweets = self.config.maxRetweets self.maxTweetsPerUser = self.config.maxTweetsPerUser self.mainWindow_geometry = self.config.mainwindow_geometry self.keywordsAsRegex = self.config.keywordsAsRegex def applyConfig(self): try: self.timer.stop() except AttributeError: pass self.timer = WTimer(self.show_notify, self.notify_interval) self.timer.start() self.notify.timeout = self.notify_timeout setGeometry(self, self.mainWindow_geometry) def setupModels(self): self._all_timeline = TweetCommonModel( self.client.api("statuses/home_timeline"), self) self.all_timeline = TweetFilterModel(self._all_timeline) self.all_timeline.setModel(self._all_timeline) self._prepareTimeline(self.all_timeline) # extra rules self.all_timeline.setMaxRetweets(self.maxRetweets) self.all_timeline.setMaxTweetsPerUser(self.maxTweetsPerUser) self.homeView.setModel(self.all_timeline) self._mentions = TweetCommonModel(self.client.api("statuses/mentions"), self) self.mentions = TweetFilterModel(self._mentions) self.mentions.setModel(self._mentions) self._prepareTimeline(self.mentions) self.mentionsView.setModel(self.mentions) self._comment_to_me = TweetCommentModel( self.client.api("comments/to_me"), self) self.comment_to_me = TweetFilterModel(self._comment_to_me) self.comment_to_me.setModel(self._comment_to_me) self._prepareTimeline(self.comment_to_me) self.commentsView.setModel(self.comment_to_me) self._comment_mentions = TweetCommentModel( self.client.api("comments/mentions"), self) self.comment_mentions = TweetFilterModel(self._comment_mentions) self.comment_mentions.setModel(self._comment_mentions) self._prepareTimeline(self.comment_mentions) self.commentsMentionsView.setModel(self.comment_mentions) @async def reset_remind(self): typ = "" if self.currentTweetView() == self.homeView: self.tabBadgeChanged.emit(self.tabWidget.currentIndex(), 0) elif self.currentTweetView() == self.mentionsView: typ = "mention_status" self.tabBadgeChanged.emit(self.tabWidget.currentIndex(), 0) elif self.currentTweetView() == self.commentsView: typ = "cmt" self.tabBadgeChanged.emit(self.tabWidget.currentIndex(), 0) elif self.currentTweetView() == self.commentsMentionsView: typ = "mention_cmt" self.tabBadgeChanged.emit(self.tabWidget.currentIndex(), 0) if typ: self.client.api("remind/set_count").post(type=typ) def get_remind(self, uid): """this function is used to get unread_count from Weibo API. uid is necessary.""" reminds = self.client.api("remind/unread_count").get(uid=uid) return reminds def uid(self): """How can I get my uid? here it is""" if not self.info.get("uid"): self.info["uid"] = self.client.api("account/get_uid").get().uid return self.info["uid"] def show_notify(self): # This function is run in another thread by WTimer. # Do not modify UI directly. Send signal and react it in a slot only. # We use SIGNAL self.tabTextChanged and SLOT self.setTabText() # to display unread count reminds = self.get_remind(self.uid()) msg = self.tr("You have:") + "\n" reminds_count = 0 if reminds['status'] != 0: # Note: do NOT send notify here, or users will crazy. self.tabBadgeChanged.emit(self.tabWidget.indexOf(self.homeTab), reminds['status']) if reminds['mention_status'] and self.remindMentions: msg += self.tr("%d unread @ME") % reminds['mention_status'] + "\n" self.tabBadgeChanged.emit(self.tabWidget.indexOf(self.mentionsTab), reminds['mention_status']) reminds_count += 1 else: self.tabBadgeChanged.emit(self.tabWidget.indexOf(self.mentionsTab), 0) if reminds['cmt'] and self.remindComments: msg += self.tr("%d unread comment(s)") % reminds['cmt'] + "\n" self.tabBadgeChanged.emit(self.tabWidget.indexOf(self.commentsTab), reminds['cmt']) reminds_count += 1 else: self.tabBadgeChanged.emit(self.tabWidget.indexOf(self.commentsTab), 0) if reminds["mention_cmt"] and self.remindMentions: msg += self.tr( "%d unread @ME comment(s)") % reminds["mention_cmt"] + "\n" self.tabBadgeChanged.emit( self.tabWidget.indexOf(self.commentsMentionsTab), reminds["mention_cmt"]) reminds_count += 1 else: self.tabBadgeChanged.emit( self.tabWidget.indexOf(self.commentsMentionsTab), 0) if reminds_count and reminds_count != self._last_reminds_count: self.notify.showMessage(self.tr("WeCase"), msg) self._last_reminds_count = reminds_count def drawNotifyBadge(self, index, count): tabIcon = self.tabWidget.tabIcon(index) _tabPixmap = self._iconPixmap[tabIcon.cacheKey()] tabPixmap = _tabPixmap.transformed(QtGui.QTransform().rotate(-90)) icon = NotifyBadgeDrawer().draw(tabPixmap, str(count)) icon = icon.transformed(QtGui.QTransform().rotate(90)) icon = QtGui.QIcon(icon) self._iconPixmap[icon.cacheKey()] = _tabPixmap self.tabWidget.setTabIcon(index, icon) def moveToTop(self): self.currentTweetView().moveToTop() def showSettings(self): wecase_settings = WeSettingsWindow() if wecase_settings.exec_(): self.loadConfig() self.applyConfig() def showAbout(self): wecase_about = AboutWindow() wecase_about.exec_() def logout(self): self.close() # This is a model dialog, if we exec it before we close MainWindow # MainWindow won't close from LoginWindow import LoginWindow wecase_login = LoginWindow(allow_auto_login=False) wecase_login.exec_() def postTweet(self): self.wecase_new = NewpostWindow() self.wecase_new.userClicked.connect(self.userClicked) self.wecase_new.tagClicked.connect(self.tagClicked) self.wecase_new.show() def refresh(self): tweetView = self.currentTweetView() tweetView.model().timelineLoaded.connect(self.moveToTop) tweetView.refresh() self.reset_remind() def currentTweetView(self): # The most tricky part of MainWindow. return self.tabWidget.currentWidget().layout().itemAt(0).widget() def saveConfig(self): self.config.mainwindow_geometry = getGeometry(self) self.config.save() def closeEvent(self, event): self.systray.hide() self.hide() self.saveConfig() self.timer.stop(True) # Reset uid when the thread exited. self.info["uid"] = None LoginInfo().remove_account(self.username) logging.info("Die")
def postTweet(self): wecase_new = NewpostWindow() wecase_new.exec_()