Beispiel #1
0
class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(QMainWindow, self).__init__()
        # Set up the user interface from Designer.
        self.setupUi(self)

        self.setAccessibleName("Hive Desktop")
        self.redrawLock = Lock()
        self.updateLock = Lock()

        self.optionsDialog = dialogs.Options(self)
        self.aboutDialog = dialogs.About(
            self,
            copyright='holger80',
            programName='Hive Desktop',
            version=VERSION,
            website='https://github.com/holgern/hivedesktop',
            websiteLabel='Github',
            comments=
            '"Welcome to Hive desktop!\n This is the first release for testing qt5.\n Please vote for holger80 as witness, if you like this :).',
            licenseName='GPL-3.0',
            # licenseUrl=helpers.joinpath_to_cwd('LICENSE').as_uri(),
            authors=('holger80', ),
            # dependencies=[l.strip() for l in requirements.readlines()],
        )
        self.mdrenderer = MDRenderer(str(helpers.joinpath_to_cwd('themes')))

        # tmpfile = helpers.mktemp(prefix='hivedesktop', suffix='.html')

        self.post = {"body": "##test", "authorperm": "@test/test"}
        self.thread = threads.MDThread(self)

        # self.webview.url = tmpfile.as_uri()

        self.feedListWidget.currentRowChanged.connect(
            self.change_displayed_post, Qt.QueuedConnection)

        self.timer = QTimer()
        self.timer.timeout.connect(self.refresh_account_thread)

        self.timer2 = QTimer()
        self.timer2.timeout.connect(self.update_account_hist_thread)

        self.timer3 = QTimer()
        self.timer3.timeout.connect(self.update_account_feed_thread)

        self.cache_path = QStandardPaths.writableLocation(
            QStandardPaths.CacheLocation)
        self.db_type = "shelve"
        self.feed = []
        self.post = None
        # Get settings
        settings = QSettings()
        # Get checkbox state with speciying type of checkbox:
        # type=bool is a replacement of toBool() in PyQt5
        check_state = settings.value(SETTINGS_TRAY, True, type=bool)
        hist_info_check_state = settings.value(SETTINGS_HIST_INFO,
                                               True,
                                               type=bool)
        account_state = settings.value(SETTINGS_ACCOUNT, "", type=str)
        # Set state
        self.accountHistNotificationCheckBox.setChecked(hist_info_check_state)
        self.autoRefreshCheckBox.setChecked(check_state)
        if check_state:
            self.timer.start(5000)
            self.timer2.start(15000)
            self.timer3.start(60000)
        self.accountLineEdit.setText(account_state)
        # connect the slot to the signal by clicking the checkbox to save the state settings
        self.autoRefreshCheckBox.clicked.connect(self.save_check_box_settings)
        self.accountHistNotificationCheckBox.clicked.connect(
            self.save_check_box_settings)
        self.accountLineEdit.editingFinished.connect(
            self.save_account_settings)
        self.actionAbout.triggered.connect(self.about)
        self.actionOptions.triggered.connect(self.options)
        self.threadpool = QThreadPool()

        self.minimizeAction = QAction("Mi&nimize", self, triggered=self.hide)
        self.maximizeAction = QAction("Ma&ximize",
                                      self,
                                      triggered=self.showMaximized)
        self.restoreAction = QAction("&Restore",
                                     self,
                                     triggered=self.showNormal)

        menu = QMenu()
        menu.addAction(self.minimizeAction)
        menu.addAction(self.maximizeAction)
        menu.addAction(self.restoreAction)
        menu.addSeparator()
        # aboutAction = menu.addAction("about")
        # aboutAction.triggered.connect(self.about)
        exitAction = menu.addAction("Exit")
        exitAction.triggered.connect(self.closeApp)

        self.tray = QSystemTrayIcon(QIcon(':/icons/icon.ico'))

        self.tray.setContextMenu(menu)

        self.tray.setToolTip("Hive Desktop!")
        self.tray.setObjectName("Hive Desktop")
        self.setWindowTitle("Hive Desktop")
        self.tray.show()

        splash_pix = QPixmap(':/icons/splash.png')
        splash = QSplashScreen(splash_pix, Qt.WindowStaysOnTopHint)
        splash.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
        splash.setEnabled(False)

        splash.show()
        splash.showMessage("<h1><font color='green'>starting...</font></h1>",
                           Qt.AlignTop | Qt.AlignCenter, Qt.black)

        account = account_state
        nodelist = NodeList()
        nodelist.update_nodes()
        self.stm = Steem(node=nodelist.get_nodes(hive=True))
        if account != "":
            try:
                self.hist_account = Account(account, steem_instance=self.stm)
            except:
                self.hist_account = None
        else:
            self.hist_account = None
        if self.hasFocus is not None:
            self.init_new_account()
        # self.button.clicked.connect(lambda: self.text.setText(_get_quote(self.hist_account, self.stm)))
        self.refreshPushButton.clicked.connect(self.refresh_account_thread)
        self.refreshPushButton.clicked.connect(self.update_account_hist_thread)
        self.accountLineEdit.editingFinished.connect(self.update_account_info)
        splash.deleteLater()
        self.tray.showMessage("Ready", "Account history loaded!")

    def triggeredPreview(self):
        self.authorLabel.setText(self.post["author"])
        self.titleLabel.setText(self.post["title"])
        self.thread.start()

    @pyqtSlot(str)
    def cbMDThread(self, html):
        self.webview.setHtml(html)

    def closeEvent(self, event):
        if self.tray.isVisible():
            QMessageBox.information(
                self, "Hive Desktop",
                "The program will keep running in the system tray. To "
                "terminate the program, choose <b>Quit</b> in the "
                "context menu of the system tray entry.")
            self.hide()
            event.ignore()

    def closeApp(self):
        self.store_account_hist()
        sys.exit()

    def about(self):
        self.aboutDialog.exec_()

    def options(self):
        self.optionsDialog.exec_()

    # Slot checkbox to save the settings
    def save_check_box_settings(self):
        settings = QSettings()
        settings.setValue(SETTINGS_HIST_INFO,
                          self.accountHistNotificationCheckBox.isChecked())
        settings.setValue(SETTINGS_TRAY, self.autoRefreshCheckBox.isChecked())
        if self.autoRefreshCheckBox.isChecked():
            self.timer.start(5000)
            self.timer2.start(15000)
            self.timer3.start(60000)
        else:
            self.timer.stop()
            self.timer2.stop()
            self.timer3.stop()
        settings.sync()

    # Slot checkbox to save the settings
    def save_account_settings(self):
        settings = QSettings()
        settings.setValue(SETTINGS_ACCOUNT, self.accountLineEdit.text())
        settings.sync()

    def update_account_info(self):
        if self.hist_account is None or self.hist_account[
                "name"] != self.accountLineEdit.text():
            self.store_account_hist()
            try:
                self.hist_account = Account(self.accountLineEdit.text(),
                                            steem_instance=self.stm)
            except:
                self.hist_account = None
            self.init_new_account()

    def init_new_account(self):
        if self.hist_account is not None:
            self.db = database.Database(self.db_type, self.cache_path,
                                        self.hist_account["name"])
        self.refresh_account()
        self.init_account_hist()
        self.update_account_hist()
        self.update_account_feed()
        self.store_account_hist()

    def refresh_account_thread(self):
        worker = Worker(self.refresh_account)
        self.threadpool.start(worker)

    def refresh_account(self):
        if self.hist_account is None:
            return
        self.hist_account.refresh()
        self.accountInfoGroupBox.setTitle(
            "%s (%.3f)" % (self.hist_account["name"], self.hist_account.rep))
        with self.redrawLock:
            self.votePowerProgressBar.setValue(int(self.hist_account.vp))
            if self.hist_account.vp == 100:
                self.votePowerProgressBar.setFormat("%.2f %%" %
                                                    (self.hist_account.vp))
            else:
                self.votePowerProgressBar.setFormat(
                    "%.2f %%, full in %s" %
                    (self.hist_account.vp,
                     self.hist_account.get_recharge_time_str()))
        down_vp = self.hist_account.get_downvoting_power()
        with self.redrawLock:
            self.downvotePowerProgressBar.setValue(int(down_vp))
            if down_vp == 100:
                self.downvotePowerProgressBar.setFormat("%.2f %%" % (down_vp))
            else:
                self.downvotePowerProgressBar.setFormat(
                    "%.2f %%, full in %s" %
                    (down_vp,
                     self.hist_account.get_recharge_time(
                         starting_voting_power=down_vp)))

        self.votePowerLabel.setText("Vote Power, a 100%% vote is %.3f $" %
                                    (self.hist_account.get_voting_value_SBD()))
        self.downvotePowerLabel.setText("DownVote Power")
        self.STEEMLabel.setText(str(self.hist_account["balance"]))
        self.SBDLabel.setText(str(self.hist_account["sbd_balance"]))
        self.SPLabel.setText(
            "%.3f HP" %
            self.stm.vests_to_sp(self.hist_account["vesting_shares"]))
        try:
            rc_manabar = self.hist_account.get_rc_manabar()
            with self.redrawLock:
                self.RCProgressBar.setValue(int(rc_manabar["current_pct"]))
                self.RCProgressBar.setFormat(
                    "%.2f %%, full in %s" %
                    (rc_manabar["current_pct"],
                     self.hist_account.get_manabar_recharge_time_str(
                         rc_manabar)))

            rc = self.hist_account.get_rc()
            estimated_rc = int(rc["max_rc"]) * rc_manabar["current_pct"] / 100
            rc_calc = RC(steem_instance=self.stm)
            self.RCLabel.setText(
                "RC (%.0f G RC of %.0f G RC)" %
                (estimated_rc / 10**9, int(rc["max_rc"]) / 10**9))
            ret = "--- Approx Costs ---\n"
            ret += "comment - %.2f G RC - enough RC for %d comments\n" % (
                rc_calc.comment() / 10**9, int(
                    estimated_rc / rc_calc.comment()))
            ret += "vote - %.2f G RC - enough RC for %d votes\n" % (
                rc_calc.vote() / 10**9, int(estimated_rc / rc_calc.vote()))
            ret += "transfer - %.2f G RC - enough RC for %d transfers\n" % (
                rc_calc.transfer() / 10**9,
                int(estimated_rc / rc_calc.transfer()))
            ret += "custom_json - %.2f G RC - enough RC for %d custom_json\n" % (
                rc_calc.custom_json() / 10**9,
                int(estimated_rc / rc_calc.custom_json()))
            self.text.setText(ret)
        except:
            rc_manabar = None

    def store_account_hist(self):
        if self.hist_account is None:
            return
        self.db.store_account_hist(self.account_history,
                                   self.append_hist_info["trx_ids"])

    def init_account_hist(self):
        if self.hist_account is None:
            return
        b = Blockchain(steem_instance=self.stm)
        latest_block_num = b.get_current_block_num()
        start_block_num = latest_block_num - (20 * 60 * 24)
        self.account_history = []
        self.account_hist_info = {"start_block": 0, "trx_ids": []}
        self.append_hist_info = {"start_block": 0, "trx_ids": []}

        if self.db.has_account_hist():
            self.account_history, trx_ids = self.db.load_account_hist()
            if self.account_history[0]["block"] <= start_block_num:
                self.append_hist_info["start_block"] = self.account_history[
                    -1]["block"]
                self.append_hist_info["trx_ids"] = trx_ids
                return

        self.lastUpvotesListWidget.clear()
        self.lastCurationListWidget.clear()
        self.lastAuthorListWidget.clear()
        self.accountHistListWidget.clear()
        start_block = 0
        trx_ids = []
        for op in self.hist_account.history(start=start_block_num):
            if op["block"] < start_block:
                continue
            elif op["block"] == start_block:
                if op["trx_id"] in trx_ids:
                    continue
                else:
                    trx_ids.append(op["trx_id"])
            else:
                trx_ids = [op["trx_id"]]
            start_block = op["block"]
            self.account_history.append(op)
        self.append_hist_info["start_block"] = start_block
        self.append_hist_info["trx_ids"] = trx_ids

    def append_account_hist(self):
        if self.hist_account is None:
            return
        start_block = self.append_hist_info["start_block"]
        trx_ids = self.append_hist_info["trx_ids"]
        for op in self.hist_account.history(start=start_block - 20,
                                            use_block_num=True):
            if op["block"] < start_block:
                continue
            elif op["block"] == start_block:
                if op["trx_id"] in trx_ids:
                    continue
                else:
                    trx_ids.append(op["trx_id"])
            else:
                trx_ids = [op["trx_id"]]

            start_block = op["block"]
            # print("Write %d" % op["index"])
            self.account_history.append(op)
        self.append_hist_info["start_block"] = start_block
        self.append_hist_info["trx_ids"] = trx_ids

    def update_account_hist_thread(self):
        worker = Worker(self.update_account_hist)
        self.threadpool.start(worker)

    def update_account_feed_thread(self):
        worker = Worker(self.update_account_feed)
        self.threadpool.start(worker)

    @pyqtSlot(int)
    def change_displayed_post(self, row):
        if row < 0:
            return
        if len(self.feed) == 0:
            return
        if len(self.feed) <= row:
            return
        #index = self.feedListWidget.currentIndex()
        #row = index.row()
        self.post = self.feed[row]
        with self.updateLock:
            self.triggeredPreview()

    def update_account_feed(self):
        if self.hist_account is None:
            return
        updated_feed = self.hist_account.get_account_posts()
        if len(self.feed) == 0:
            self.feed = updated_feed
        else:
            for post in updated_feed[::-1]:
                found = False
                for p in self.feed:
                    if post["authorperm"] == p["authorperm"]:
                        found = True
                if not found:
                    self.feed.insert(0, post)
                    self.tray.showMessage(post["author"], post["title"])
        with self.updateLock:
            if self.post is None:
                self.post = self.feed[0]
                self.triggeredPreview()

            self.feedListWidget.currentRowChanged.disconnect(
                self.change_displayed_post)
            self.feedListWidget.clear()
            for post in self.feed[::-1]:
                post_text = "%s - %s" % (post["author"], post["title"])
                post_item = QListWidgetItem()
                post_item.setText(post_text)
                post_item.setToolTip(post["author"])
                self.feedListWidget.insertItem(0, post_item)
            self.feedListWidget.currentRowChanged.connect(
                self.change_displayed_post, Qt.QueuedConnection)

    def update_account_hist(self):
        if self.hist_account is None:
            return
        votes = []
        daily_curation = 0
        daily_author_SP = 0
        daily_author_SBD = 0
        daily_author_STEEM = 0
        self.append_account_hist()

        new_op_found = False

        start_block = self.account_hist_info["start_block"]
        if start_block == 0:
            first_call = True
        else:
            first_call = False
        trx_ids = self.account_hist_info["trx_ids"]

        for op in self.account_history:
            if op["block"] < start_block:
                # last_block = op["block"]
                continue
            elif op["block"] == start_block:
                if op["trx_id"] in trx_ids:
                    continue
                else:
                    trx_ids.append(op["trx_id"])
            else:
                trx_ids = [op["trx_id"]]
            start_block = op["block"]
            new_op_found = True
            op_timedelta = formatTimedelta(
                addTzInfo(datetime.utcnow()) -
                formatTimeString(op["timestamp"]))
            op_local_time = formatTimeString(op["timestamp"]).astimezone(
                tz.tzlocal())
            # print("Read %d" % op["index"])
            if op["type"] == "vote":
                if op["voter"] == self.hist_account["name"]:
                    continue
                if op["weight"] >= 0:

                    self.lastUpvotesListWidget.insertItem(
                        0, "%s - %s (%.2f %%) upvote %s" %
                        (op_timedelta, op["voter"], op["weight"] / 100,
                         op["permlink"]))
                    hist_item = "%s - %s - %s (%.2f %%) upvote %s" % (
                        op_local_time, op["type"], op["voter"],
                        op["weight"] / 100, op["permlink"])
                    tray_item = "%s - %s (%.2f %%) upvote %s" % (
                        op["type"], op["voter"], op["weight"] / 100,
                        op["permlink"])
                else:
                    hist_item = "%s - %s - %s (%.2f %%) downvote %s" % (
                        op_local_time, op["type"], op["voter"],
                        op["weight"] / 100, op["permlink"])
                    tray_item = "%s - %s (%.2f %%) downvote %s" % (
                        op["type"], op["voter"], op["weight"] / 100,
                        op["permlink"])

                self.accountHistListWidget.insertItem(0, hist_item)
            elif op["type"] == "curation_reward":
                curation_reward = self.stm.vests_to_sp(
                    Amount(op["reward"], steem_instance=self.stm))
                self.lastCurationListWidget.insertItem(
                    0, "%s - %.3f HP for %s" %
                    (op_timedelta, curation_reward,
                     construct_authorperm(op["comment_author"],
                                          op["comment_permlink"])))
                hist_item = "%s - %s - %.3f HP for %s" % (
                    op_local_time, op["type"], curation_reward,
                    construct_authorperm(op["comment_author"],
                                         op["comment_permlink"]))
                tray_item = "%s - %.3f HP for %s" % (
                    op["type"], curation_reward,
                    construct_authorperm(op["comment_author"],
                                         op["comment_permlink"]))
                self.accountHistListWidget.insertItem(0, hist_item)
            elif op["type"] == "author_reward":
                sbd_payout = (Amount(op["sbd_payout"],
                                     steem_instance=self.stm))
                steem_payout = (Amount(op["steem_payout"],
                                       steem_instance=self.stm))
                sp_payout = self.stm.vests_to_sp(
                    Amount(op["vesting_payout"], steem_instance=self.stm))
                self.lastAuthorListWidget.insertItem(
                    0, "%s - %s %s %.3f HP for %s" %
                    (op_timedelta, str(sbd_payout), str(steem_payout),
                     sp_payout, op["permlink"]))
                hist_item = "%s - %s - %s %s %.3f SP for %s" % (
                    op_local_time, op["type"], str(sbd_payout),
                    str(steem_payout), sp_payout, op["permlink"])
                tray_item = "%s - %s %s %.3f SP for %s" % (
                    op["type"], str(sbd_payout), str(steem_payout), sp_payout,
                    op["permlink"])
                self.accountHistListWidget.insertItem(0, hist_item)
            elif op["type"] == "custom_json":
                hist_item = "%s - %s - %s" % (op_local_time, op["type"],
                                              op["id"])
                tray_item = "%s - %s" % (op["type"], op["id"])
                self.accountHistListWidget.insertItem(0, hist_item)
            elif op["type"] == "transfer":
                hist_item = "%s - %s - %s from %s" % (
                    op_local_time, op["type"],
                    str(Amount(op["amount"],
                               steem_instance=self.stm)), op["from"])
                tray_item = "%s - %s from %s" % (
                    op["type"],
                    str(Amount(op["amount"],
                               steem_instance=self.stm)), op["from"])
                self.accountHistListWidget.insertItem(0, hist_item)
            elif op["type"] == "comment":
                comment_type = "post"
                if op["parent_author"] != "":
                    hist_item = "%s - comment on %s - %s from %s" % (
                        op_local_time,
                        construct_authorperm(
                            op["parent_author"],
                            op["parent_permlink"]), op["title"], op["author"])
                    tray_item = "comment from %s: %s on %s" % (
                        op["author"], op["body"][:100], op["title"])
                else:
                    hist_item = "%s - post - %s from %s" % (
                        op_local_time, op["title"], op["author"])
                    tray_item = "post from %s: %s" % (op["author"],
                                                      op["title"])
                self.accountHistListWidget.insertItem(0, hist_item)
            else:
                hist_item = "%s - %s" % (op_local_time, op["type"])
                tray_item = "%s" % (op["type"])
                self.accountHistListWidget.insertItem(0, hist_item)

            if self.accountHistNotificationCheckBox.isChecked(
            ) and not first_call:
                self.tray.showMessage(self.hist_account["name"], tray_item)

        if new_op_found:
            self.account_hist_info["start_block"] = start_block
            self.account_hist_info["trx_ids"] = trx_ids

            for op in self.account_history:
                if op["type"] == "vote":
                    if op["voter"] == self.hist_account["name"]:
                        continue
                    votes.append(op)
                elif op["type"] == "curation_reward":
                    curation_reward = self.stm.vests_to_sp(
                        Amount(op["reward"], steem_instance=self.stm))
                    daily_curation += curation_reward
                elif op["type"] == "author_reward":
                    sbd_payout = (Amount(op["sbd_payout"],
                                         steem_instance=self.stm))
                    steem_payout = (Amount(op["steem_payout"],
                                           steem_instance=self.stm))
                    sp_payout = self.stm.vests_to_sp(
                        Amount(op["vesting_payout"], steem_instance=self.stm))
                    daily_author_SP += sp_payout
                    daily_author_STEEM += float(steem_payout)
                    daily_author_SBD += float(sbd_payout)

            reward_text = "Curation reward (last 24 h): %.3f HP\n" % daily_curation
            reward_text += "Author reward (last 24 h):\n"
            reward_text += "%.3f HP - %.3f HIVE - %.3f HBD" % (
                daily_author_SP, (daily_author_STEEM), (daily_author_SBD))
            self.text2.setText(reward_text)
Beispiel #2
0
class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(QMainWindow, self).__init__()
        # Set up the user interface from Designer.
        self.setupUi(self)
        
        self.setAccessibleName("Hive Desktop")
        self.redrawLock = Lock()
        self.updateLock = Lock()
        
        self.optionsDialog = dialogs.Options(self)
        self.aboutDialog = dialogs.About(self,
            copyright='holger80',
            programName='Hive Desktop',
            version=VERSION,
            website='https://github.com/holgern/hivedesktop',
            websiteLabel='Github',
            comments='"Welcome to Hive desktop!\n This is the first release for testing qt5.\n Please vote for holger80 as witness, if you like this :).',
            licenseName='GPL-3.0',
            # licenseUrl=helpers.joinpath_to_cwd('LICENSE').as_uri(),
            authors=('holger80',),
            # dependencies=[l.strip() for l in requirements.readlines()],
        )		
        self.mdrenderer = MDRenderer(str(helpers.joinpath_to_cwd('themes')))

        # tmpfile = helpers.mktemp(prefix='hivedesktop', suffix='.html')
        
        self.post = {"body": "##test", "authorperm": "@test/test"}
        self.thread = threads.MDThread(self)
        
        
        # self.webview.url = tmpfile.as_uri()
        
        
        self.feedListWidget.currentRowChanged.connect(self.change_displayed_post, Qt.QueuedConnection)
        
        self.timer = QTimer()
        self.timer.timeout.connect(self.refresh_account_thread)
        
        self.timer2 = QTimer()
        self.timer2.timeout.connect(self.update_account_hist_thread)

        self.timer3 = QTimer()
        self.timer3.timeout.connect(self.update_account_feed_thread)
        
        self.cache_path = QStandardPaths.writableLocation(QStandardPaths.CacheLocation)
        self.db_type = "shelve"
        self.db_type = "sqlite"
        self.feed = []
        self.post = None
        # Get settings
        settings = QSettings()
        # Get checkbox state with speciying type of checkbox:
        # type=bool is a replacement of toBool() in PyQt5
        check_state = settings.value(SETTINGS_TRAY, True, type=bool)
        hist_info_check_state = settings.value(SETTINGS_HIST_INFO, True, type=bool)
        account_state = settings.value(SETTINGS_ACCOUNT, "", type=str)
        self.resize(settings.value(SETTINGS_SIZE, QSize(1053, 800)))
        self.move(settings.value(SETTINGS_POS, QPoint(50, 50)))
        
        #self.accountHistTableWidget.setColumnCount(5)
        #self.accountHistTableWidget.setHorizontalHeaderLabels(["type", "1", "2", "3", "timestamp"])
        
        self.update_account_refreshtime = 5000
        
        # Set state
        self.accountHistNotificationCheckBox.setChecked(hist_info_check_state)
        self.autoRefreshCheckBox.setChecked(check_state)
        if check_state:
            self.timer.start(self.update_account_refreshtime)
            self.timer2.start(15000)
            self.timer3.start(60000)
        self.accountLineEdit.setText(account_state)
        # connect the slot to the signal by clicking the checkbox to save the state settings
        self.autoRefreshCheckBox.clicked.connect(self.save_check_box_settings)   
        self.accountHistNotificationCheckBox.clicked.connect(self.save_check_box_settings)  
        self.accountLineEdit.editingFinished.connect(self.save_account_settings)
        self.actionAbout.triggered.connect(self.about)
        self.actionOptions.triggered.connect(self.options)
        self.threadpool = QThreadPool()
        
        self.minimizeAction = QAction("Mi&nimize", self, triggered=self.hide)
        self.maximizeAction = QAction("Ma&ximize", self,
                triggered=self.showMaximized)
        self.restoreAction = QAction("&Restore", self,
                triggered=self.showNormal)        
        
        menu = QMenu()
        menu.addAction(self.minimizeAction)
        menu.addAction(self.maximizeAction)
        menu.addAction(self.restoreAction)
        menu.addSeparator()        
        # aboutAction = menu.addAction("about")
        # aboutAction.triggered.connect(self.about)
        exitAction = menu.addAction("Exit")
        exitAction.triggered.connect(self.closeApp)
        
        self.tray = QSystemTrayIcon(QIcon(':/icons/icon.ico'))
        
        self.tray.setContextMenu(menu)
        self.tray.activated.connect(self.trayAction)
        
        self.tray.setToolTip("Hive Desktop!")
        self.tray.setObjectName("Hive Desktop")
        self.setWindowTitle("Hive Desktop")
        self.tray.show()
        
        splash_pix = QPixmap(':/icons/splash.png')
        splash = QSplashScreen(splash_pix, Qt.WindowStaysOnTopHint)
        splash.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
        splash.setEnabled(False)
        
        #splash.show()
        #splash.showMessage("<h1><font color='green'>starting...</font></h1>", Qt.AlignTop | Qt.AlignCenter, Qt.black)        
        
        account = account_state
        nodelist = NodeList()
        nodelist.update_nodes()
        self.stm = Steem(node=nodelist.get_nodes(hive=True))
        set_shared_blockchain_instance(self.stm)
        if account != "":
            try:
                self.hist_account = Account(account, steem_instance=self.stm)
            except:
                self.hist_account = None
        else:
            self.hist_account = None
            
        self.refreshPushButton.clicked.connect(self.refresh_account)
        self.refreshPushButton.clicked.connect(self.update_account_hist_thread)
        self.accountLineEdit.editingFinished.connect(self.update_account_info)        
        if self.hasFocus is not None:
            self.init_new_account()
            self.init_new_blocks()
        splash.deleteLater()

    def triggeredPreview(self):
        self.authorLabel.setText(self.post["author"])
        self.titleLabel.setText(self.post["title"])
        self.auhorpermLineEdit.setText(construct_authorperm(self.post["author"], self.post["permlink"]))
        self.thread.start()

    @pyqtSlot(str)
    def cbMDThread(self, html):
        self.webview.setHtml(html)

    @pyqtSlot(QSystemTrayIcon.ActivationReason)
    def trayAction(self, reason):
        if reason == QSystemTrayIcon.Trigger:
            if self.isHidden():
                self.showNormal()
            else:
                self.hide()

    def closeEvent(self, event):
        if self.tray.isVisible():
            #QMessageBox.information(self, "Hive Desktop",
            #        "The program will keep running in the system tray. To "
            #        "terminate the program, choose <b>Quit</b> in the "
            #        "context menu of the system tray entry.")
            self.tray.showMessage("Hive Desktop",
                    "The program will keep running in the system tray. To "
                    "terminate the program, choose Quit in the "
                    "context menu of the system tray entry.")           
            self.hide()
            event.ignore()

    def hide(self):
        self.update_account_refreshtime = 60000
        self.timer.start(self.update_account_refreshtime)
        QMainWindow.hide(self)
        

    def showNormal(self):
        self.update_account_refreshtime = 5000
        self.timer.start(self.update_account_refreshtime)
        QMainWindow.showNormal(self)

    def showMaximized(self):
        self.update_account_refreshtime = 5000
        self.timer.start(self.update_account_refreshtime)
        QMainWindow.showMaximized(self)

    def closeApp(self):
        settings = QSettings()
        settings.setValue(SETTINGS_SIZE, self.size())
        settings.setValue(SETTINGS_POS, self.pos())
        settings.sync()
        sys.exit()

    def about(self):
        self.aboutDialog.exec_()

    def options(self):
        self.optionsDialog.exec_()

    # Slot checkbox to save the settings
    def save_check_box_settings(self):
        settings = QSettings()
        settings.setValue(SETTINGS_HIST_INFO, self.accountHistNotificationCheckBox.isChecked())
        settings.setValue(SETTINGS_TRAY, self.autoRefreshCheckBox.isChecked())
        if self.autoRefreshCheckBox.isChecked():
            self.timer.start(self.update_account_refreshtime)
            self.timer2.start(15000)
            self.timer3.start(60000)
        else:
            self.timer.stop()
            self.timer2.stop()
            self.timer3.stop()
        settings.sync()

    # Slot checkbox to save the settings
    def save_account_settings(self):
        settings = QSettings()
        settings.setValue(SETTINGS_ACCOUNT, self.accountLineEdit.text())
        settings.sync()

    def update_account_info(self):
        if self.hist_account is None or self.hist_account["name"] != self.accountLineEdit.text():
            try:
                self.hist_account = Account(self.accountLineEdit.text(), steem_instance=self.stm)
            except:
                self.hist_account = None
            self.init_new_account()

    def init_new_account(self):
        self.refresh_account()
        self.update_account_feed()
        if self.hist_account is not None:
            self.db = database.Database(self.db_type, self.cache_path, self.hist_account["name"])
            self.loadingProgressBar.setMaximum(self.db.get_account().virtual_op_count())
            self.iah_thread = threads.IAHThread(self, self.db)
            self.timer.stop()
            self.timer2.stop()
            self.refreshPushButton.setEnabled(False)
            self.accountLineEdit.setEnabled(False)            
            self.iah_thread.start()
        else:
            self.init_account_hist()
            self.update_account_hist()

    def init_new_blocks(self):
        blockchain = Blockchain()
        self.block_db = database_blocks.DatabaseBlocks(self.db_type, self.cache_path)
        self.loadingBlocksProgressBar.setMaximum(self.block_db.block_history)
        self.blocks_thread = threads.BlocksThread(self, self.block_db, blockchain)
        self.blocks_thread.start()

    @pyqtSlot(str)
    def cIAHThread(self, dummy):
        self.init_account_hist()
        self.update_account_hist()
        self.refreshPushButton.setEnabled(True)
        self.accountLineEdit.setEnabled(True)
        self.tray.showMessage("Ready", "Account history loaded!")     
        self.timer.start(self.update_account_refreshtime)
        self.timer2.start(15000)

    @pyqtSlot(str)
    def cBlocksThread(self, dummy):
        self.init_blocks()
        self.update_blocks()
           
        #self.timer.start(self.update_account_refreshtime)
        #self.timer2.start(15000)

    def refresh_account_thread(self):
        worker = Worker(self.refresh_account)
        self.threadpool.start(worker)        

    def refresh_account(self):
        if self.hist_account is None:
            return
        self.hist_account.refresh()
        self.accountInfoGroupBox.setTitle("%s (%.3f)" % (self.hist_account["name"], self.hist_account.rep))
        with self.redrawLock:
            self.votePowerProgressBar.setValue(int(self.hist_account.vp))
            if self.hist_account.vp == 100:
                self.votePowerProgressBar.setFormat("%.2f %%" % (self.hist_account.vp))
            else:
                self.votePowerProgressBar.setFormat("%.2f %%, full in %s" % (self.hist_account.vp, self.hist_account.get_recharge_time_str()))
        down_vp = self.hist_account.get_downvoting_power()
        with self.redrawLock:
            self.downvotePowerProgressBar.setValue(int(down_vp))
            if down_vp == 100:
                self.downvotePowerProgressBar.setFormat("%.2f %%" % (down_vp))
            else:
                self.downvotePowerProgressBar.setFormat("%.2f %%, full in %s" % (down_vp, self.hist_account.get_recharge_time(starting_voting_power=down_vp)))
        
        self.votePowerLabel.setText("Vote Power, a 100%% vote is %.3f $" % (self.hist_account.get_voting_value_SBD()))
        self.downvotePowerLabel.setText("DownVote Power")
        self.STEEMLabel.setText(str(self.hist_account["balance"]))
        self.SBDLabel.setText(str(self.hist_account["sbd_balance"]))
        self.SPLabel.setText("%.3f HP" % self.stm.vests_to_sp(self.hist_account["vesting_shares"]))
        try:
            rc_manabar = self.hist_account.get_rc_manabar()
            with self.redrawLock:
                self.RCProgressBar.setValue(int(rc_manabar["current_pct"]))
                self.RCProgressBar.setFormat("%.2f %%, full in %s" % (rc_manabar["current_pct"], self.hist_account.get_manabar_recharge_time_str(rc_manabar)))
        
            rc = self.hist_account.get_rc()
            estimated_rc = int(rc["max_rc"]) * rc_manabar["current_pct"] / 100
            rc_calc = RC(steem_instance=self.stm)
            self.RCLabel.setText("RC (%.0f G RC of %.0f G RC)" % (estimated_rc / 10**9, int(rc["max_rc"]) / 10**9))
            ret = "--- Approx Costs ---\n"
            ret += "comment - %.2f G RC - enough RC for %d comments\n" % (rc_calc.comment() / 10**9, int(estimated_rc / rc_calc.comment()))
            ret += "vote - %.2f G RC - enough RC for %d votes\n" % (rc_calc.vote() / 10**9, int(estimated_rc / rc_calc.vote()))
            ret += "transfer - %.2f G RC - enough RC for %d transfers\n" % (rc_calc.transfer() / 10**9, int(estimated_rc / rc_calc.transfer()))
            ret += "custom_json - %.2f G RC - enough RC for %d custom_json\n" % (rc_calc.custom_json() / 10**9, int(estimated_rc / rc_calc.custom_json()))
            self.text.setText(ret)
        except:
            rc_manabar = None

    @pyqtSlot(int)
    def set_loading_progress(self, val):
        with self.redrawLock:
            self.loadingProgressBar.setValue(val)

    @pyqtSlot(int)
    def set_loading_blocks_progress(self, val):
        with self.redrawLock:
            self.loadingBlocksProgressBar.setValue(val)

    def init_blocks(self):
        self.blockCountLabel.setText("%d entries" % self.block_db.get_block_count())

    def init_account_hist(self):
        if self.hist_account is None:
            return
        self.account_hist_info = {"start_block": 0}
        self.append_hist_info = {"start_block": 0}

        #if not self.db.has_account_hist():
        #    ops = self.db.get_acc_hist()
        #    self.db.store_account_hist(ops)
        #else:
        #    start_op = self.db.get_last_op()
        #    ops = self.db.read_missing_account_history_data(start_op)
        #    self.db.store_account_hist(ops)
        self.accountHistoryLabel.setText("%d entries" % self.db.get_count())
        self.accountHistTableWidget.clear()

    def append_blocks(self):
        start_op = self.block_db.get_last_op()
        data = self.block_db.read_missing_block_data(start_op)
        # print("finished")
        if len(data) > 0:
            # print(op["timestamp"])
            self.block_db.append_blocks(data)
            self.blockCountLabel.setText("%d entries" % self.block_db.get_block_count())        

    def append_account_hist(self):
        if self.hist_account is None:
            return
        
        # print("loading db")
        
        start_op = self.db.get_last_op()
        data = self.db.read_missing_account_history_data(start_op)
        # print("finished")
        if len(data) > 0:
            # print(op["timestamp"])
            self.db.append_account_hist(data)
            self.accountHistoryLabel.setText("%d entries" % self.db.get_count())

    def update_account_hist_thread(self):
        worker = Worker(self.update_account_hist)
        self.threadpool.start(worker)

    def update_account_feed_thread(self):
        worker = Worker(self.update_account_feed)
        self.threadpool.start(worker)

    @pyqtSlot(int)
    def change_displayed_post(self, row):
        if row < 0:
            return
        if len(self.feed) == 0:
            return
        if len(self.feed) <= row:
            return
        #index = self.feedListWidget.currentIndex()
        #row = index.row()
        self.post = self.feed[row]
        with self.updateLock:
            self.triggeredPreview()
        replies = Comment(self.post).get_all_replies()
        is_shown = [0] * len(replies)
        max_depth = 0
        for i in range(len(replies)):
            if replies[i].depth > max_depth:
                max_depth = replies[i].depth
        self.commentsTreeWidget.clear()
        
        def create_item(reply):
            item = QTreeWidgetItem()
            item.setText(0, reply["author"])
            item.setText(1, reply["body"])
            item.setToolTip(1, reply["body"])
            return item

      
        for i in range(len(replies)):
            if is_shown[i] == 1:
                continue
            reply = replies[i]
            if reply.depth == 1:
                is_shown[i] = 1
                item = create_item(reply)
                
                for j in range(len(replies)):
                    rr = replies[j]
                    if is_shown[j] == 1:
                        continue
                    if rr.depth == 2:
                        is_shown[j] = 1
                        if rr.parent_author == reply["author"] and rr.parent_permlink == reply["permlink"]:
                            rr_item = create_item(rr)
                            if max_depth >= 3:
                                for k in range(len(replies)):
                                    rrr = replies[k]
                                    if is_shown[k] == 1:
                                        continue
                                    if rrr.depth == 3:
                                        is_shown[k] = 1
                                        if rrr.parent_author == rr["author"] and rrr.parent_permlink == rr["permlink"]:
                                            rrr_item = create_item(rrr)
                                            if max_depth >= 4:
                                                for l in range(len(replies)):
                                                    rrrr = replies[l]
                                                    if is_shown[l] == 1:
                                                        continue
                                                    if rrrr.depth == 4:
                                                        is_shown[l] = 1
                                                        if rrrr.parent_author == rrr["author"] and rrrr.parent_permlink == rrr["permlink"]:
                                                            rrrr_item = create_item(rrrr)
                                                            rrr_item.addChild(rrrr_item)
                                        
                                        rr_item.addChild(rrr_item)
                            item.addChild(rr_item)
                
                self.commentsTreeWidget.addTopLevelItem(item)

    def update_account_feed(self):
        if self.hist_account is None:
            return
        try:
            updated_feed = self.hist_account.get_account_posts()
        except:
            print("could not update feed")
            return
        if len(self.feed) == 0:
            self.feed = updated_feed
        else:
            for post in updated_feed[::-1]:
                found = False
                for p in self.feed:
                    if post["authorperm"] == p["authorperm"]:
                        found = True
                if not found:
                    self.feed.insert(0, post)
                    if (addTzInfo(datetime.utcnow()) - post["created"]).total_seconds() / 60 < 5:
                        self.tray.showMessage(post["author"], post["title"])
        with self.updateLock:
            if self.post is None:
                self.post = self.feed[0]
                self.triggeredPreview()

            self.feedListWidget.currentRowChanged.disconnect(self.change_displayed_post)
            self.feedListWidget.clear()
            for post in self.feed[::-1]:
                post_text = "%s - %s" % (post["author"], post["title"])
                post_item = QListWidgetItem()
                post_item.setText(post_text)
                post_item.setToolTip(post["author"])
                self.feedListWidget.insertItem(0, post_item)   
            self.feedListWidget.currentRowChanged.connect(self.change_displayed_post, Qt.QueuedConnection)

    def update_blocks(self):
        return

    def update_account_hist(self):
        if self.hist_account is None:
            return        
        votes = []
        daily_curation = 0
        daily_author_SP = 0
        daily_author_SBD = 0
        daily_author_STEEM = 0
        self.append_account_hist()
        
        new_op_found = False
        
        start_block = self.account_hist_info["start_block"]
        if start_block == 0:
            first_call = True
            b = Blockchain()
            start_block = b.get_current_block_num() - 20 * 60 * 24 * 7
            self.account_hist_info["start_block"] = start_block
        else:
            first_call = False

        ops = self.db.load_account_hist(start_block)
     
        for op in ops:
            if op["block"] < start_block:
                # last_block = op["block"]
                continue

            start_block = op["block"]
            new_op_found = True
            tray_item = None
            op_timedelta = formatTimedelta(addTzInfo(datetime.utcnow()) - addTzInfo(op["timestamp"]))
            op_local_time = addTzInfo(op["timestamp"]).astimezone(tz.tzlocal())
            # print("Read %d" % op["index"])
            self.accountHistTableWidget.insertRow(0)
            self.accountHistTableWidget.setItem(0, 4, QTableWidgetItem(str(op_local_time)))
            if op["type"] == "vote":
                
                
                if op["voter"] == self.hist_account["name"]:
                    self.accountHistTableWidget.setItem(0, 0, QTableWidgetItem("Vote"))
                    self.accountHistTableWidget.setItem(0, 1, QTableWidgetItem(op["author"]))
                    
                elif op["weight"] >= 0:
                    self.accountHistTableWidget.setItem(0, 0, QTableWidgetItem("Vote Post"))
                    self.accountHistTableWidget.setItem(0, 1, QTableWidgetItem(op["voter"]))
                    tray_item = "%s - %s (%.2f %%) vote %s" % (op["type"], op["voter"], op["weight"] / 100, op["permlink"])
                else:
                    self.accountHistTableWidget.setItem(0, 0, QTableWidgetItem("Dowvote Post"))
                    self.accountHistTableWidget.setItem(0, 1, QTableWidgetItem(op["voter"]))
                    # hist_item.setToolTip(0, op["permlink"])
                    tray_item = "%s - %s (%.2f %%) downvote %s" % (op["type"], op["voter"], op["weight"] / 100, op["permlink"])
                
                
                self.accountHistTableWidget.setItem(0, 2, QTableWidgetItem("%.2f %%" % (op["weight"] / 100)))
                
            elif op["type"] == "curation_reward":
                self.accountHistTableWidget.setItem(0, 0, QTableWidgetItem(op["type"]))
                
                curation_reward = self.stm.vests_to_sp(Amount(op["reward"], steem_instance=self.stm))
                self.accountHistTableWidget.setItem(0, 1, QTableWidgetItem("%.3f HP" % curation_reward))
                self.accountHistTableWidget.setItem(0, 2, QTableWidgetItem(construct_authorperm(op["comment_author"], op["comment_permlink"])))
                hist_item = "%s - %s - %.3f HP for %s" % (op_local_time, op["type"], curation_reward, construct_authorperm(op["comment_author"], op["comment_permlink"]))
                tray_item = "%s - %.3f HP for %s" % (op["type"], curation_reward, construct_authorperm(op["comment_author"], op["comment_permlink"]))
            elif op["type"] == "author_reward":
                self.accountHistTableWidget.setItem(0, 0, QTableWidgetItem(op["type"]))
                sbd_payout = (Amount(op["sbd_payout"], steem_instance=self.stm))
                steem_payout = (Amount(op["steem_payout"], steem_instance=self.stm))
                sp_payout = self.stm.vests_to_sp(Amount(op["vesting_payout"], steem_instance=self.stm))
                
                hist_item = "%s - %s - %s %s %.3f SP for %s" % (op_local_time, op["type"], str(sbd_payout), str(steem_payout), sp_payout, op["permlink"])
                tray_item = "%s - %s %s %.3f SP for %s" % (op["type"], str(sbd_payout), str(steem_payout), sp_payout, op["permlink"])
            elif op["type"] == "custom_json":
                self.accountHistTableWidget.setItem(0, 0, QTableWidgetItem(op["type"]))
                self.accountHistTableWidget.setItem(0, 1, QTableWidgetItem(op["json_id"]))
                json_data = QTableWidgetItem(op["json"])
                json_data.setToolTip(op["json"])
                self.accountHistTableWidget.setItem(0, 2, json_data)
                hist_item = "%s - %s - %s" % (op_local_time, op["type"], op["id"])
                tray_item = "%s - %s" % (op["type"], op["json_id"])
            elif op["type"] == "transfer":
                self.accountHistTableWidget.setItem(0, 0, QTableWidgetItem(op["type"]))
                hist_item = "%s - %s - %s from %s" % (op_local_time, op["type"], str(Amount(op["amount"], steem_instance=self.stm)), op["from"])
                tray_item = "%s - %s from %s" % (op["type"], str(Amount(op["amount"], steem_instance=self.stm)), op["from"])
            elif op["type"] == "comment":
                
                
                if op["parent_author"] != "":
                    comment_type = "comment"
                    hist_item = "%s - comment on %s - %s from %s" % (op_local_time, construct_authorperm(op["parent_author"], op["parent_permlink"]), op["title"], op["author"])
                    tray_item = "comment from %s: %s on %s" % (op["author"], op["body"][:100], op["title"])
                    self.accountHistTableWidget.setItem(0, 0, QTableWidgetItem(op["type"]))
                    self.accountHistTableWidget.setItem(0, 1, QTableWidgetItem(op["author"]))
                    body = QTableWidgetItem(op["body"])
                    body.setToolTip(op["body"])
                    self.accountHistTableWidget.setItem(0, 2, body)
                else:
                    comment_type = "post"
                    hist_item = "%s - post - %s from %s" % (op_local_time, op["title"], op["author"])
                    tray_item = "post from %s: %s" % (op["author"], op["title"])
                self.accountHistTableWidget.setItem(0, 0, QTableWidgetItem(comment_type))
            elif op["type"] == "producer_reward":
                self.accountHistTableWidget.setItem(0, 0, QTableWidgetItem(op["type"]))
                self.accountHistTableWidget.setItem(0, 1, QTableWidgetItem(" %.3f HP" % float(self.stm.vests_to_sp(Amount(op["vesting_shares"])))))
                hist_item = "%s - %s" % (op_local_time, op["type"])
                tray_item = "%s - %.3f HP" % (op["type"], float(self.stm.vests_to_sp(Amount(op["vesting_shares"]))))               
            else:
                self.accountHistTableWidget.setItem(0, 0, QTableWidgetItem(op["type"]))
                hist_item = "%s - %s" % (op_local_time, op["type"])
                tray_item = "%s" % (op["type"])
            
            if self.accountHistNotificationCheckBox.isChecked() and not first_call and tray_item is not None:
                self.tray.showMessage(self.hist_account["name"], tray_item)

        if new_op_found:
            self.account_hist_info["start_block"] = start_block
            
            for op in ops:
                if op["type"] == "vote":
                    if op["voter"] == self.hist_account["name"]:
                        continue
                    votes.append(op)
                elif op["type"] == "curation_reward":
                    curation_reward = self.stm.vests_to_sp(Amount(op["reward"], steem_instance=self.stm))
                    daily_curation += curation_reward
                elif op["type"] == "author_reward":
                    sbd_payout = (Amount(op["sbd_payout"], steem_instance=self.stm))
                    steem_payout = (Amount(op["steem_payout"], steem_instance=self.stm))
                    sp_payout = self.stm.vests_to_sp(Amount(op["vesting_payout"], steem_instance=self.stm))
                    daily_author_SP += sp_payout
                    daily_author_STEEM += float(steem_payout)
                    daily_author_SBD += float(sbd_payout)        
        
            
            reward_text = "Curation reward (last 24 h): %.3f HP\n" % daily_curation
            reward_text += "Author reward (last 24 h):\n"
            reward_text += "%.3f HP - %.3f HIVE - %.3f HBD" % (daily_author_SP, (daily_author_STEEM), (daily_author_SBD))
            self.text2.setText(reward_text)