Esempio n. 1
0
class MainWindow(QWidget, Ui_Form):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.appname = "poliBeePsync"
        self.settings_fname = 'pbs-settings.ini'
        self.data_fname = 'pbs.data'
        self.setupUi(self)
        self.w = QWidget()

        self.about_text()
        self.timer = QTimer(self)

        # settings_path is a string containing the path to settings
        self.settings_path = None
        # settings is a dictionary of settings
        self.settings = None
        # load_settings() sets settings_path and settings
        self.load_settings()
        self.load_data()

        self.timer.timeout.connect(self.syncfiles)
        self.timer.start(1000 * 60 * int(self.settings['UpdateEvery']))

        self.loginthread = LoginThread(self.user)
        self.loginthread.signal_error.sig.connect(self.myStream_message)
        self.loginthread.signal_error.sig.connect(self.loginstatus)
        self.loginthread.signal_ok.sig.connect(self.myStream_message)
        self.loginthread.signal_ok.sig.connect(self.loginstatus)

        self.refreshcoursesthread = RefreshCoursesThread(self.user)
        self.refreshcoursesthread.dumpuser.sig.connect(self.dumpUser)
        self.refreshcoursesthread.newcourses.sig.connect(self.addtocoursesview)
        self.refreshcoursesthread.newcourses.sig.connect(self.syncnewcourses)
        self.refreshcoursesthread.refreshed.sig.connect(self.myStream_message)
        self.refreshcoursesthread.removable.sig.connect(self.rmfromcoursesview)

        self.downloadthread = DownloadThread(self.user,
                                             self.settings['RootFolder'])
        self.downloadthread.download_signal.connect(
            self.update_course_download)
        #self.downloadthread.download_signal.connect(self._resizeview)
        self.downloadthread.initial_sizes.connect(self.setinizialsizes)
        self.downloadthread.data_signal.connect(self.update_file_localtime)

        self.userCode.setText(str(self.user.username))
        self.userCode.textEdited.connect(self.setusercode)
        self.password.setText(self.user.password)
        self.password.textEdited.connect(self.setpassword)
        self.trylogin.clicked.connect(self.testlogin)

        self.courses_model = CoursesListModel(self.user.available_courses)
        self.coursesView.setModel(self.courses_model)
        self._resizeview()
        self.refreshCourses.clicked.connect(self.refreshcourses)

        self.courses_model.dataChanged.connect(self.dumpUser)
        self.syncNow.clicked.connect(self.syncfiles)
        #self.pushButton.clicked.connect(self.syncfiles)
        #self.pushButton.clicked.connect(self.inittextincourses)

        if self.settings['SyncNewCourses'] == str(True):
            self.sync_new = Qt.Checked
        else:
            self.sync_new = Qt.Unchecked

        self.rootfolder.setText(self.settings['RootFolder'])
        self.rootfolder.textChanged.connect(self.rootfolderslot)

        self.addSyncNewCourses.setCheckState(self.sync_new)
        self.addSyncNewCourses.stateChanged.connect(self.syncnewslot)

        self.timerMinutes.setValue(int(self.settings['UpdateEvery']))
        self.timerMinutes.valueChanged.connect(self.updateminuteslot)

        self.changeRootFolder.clicked.connect(self.chooserootdir)
        #self.version_label.setText("Current version: {}.".format(__version__))
        #self.pushButton_2.clicked.connect(self.checknewversion)

        self.trayIconMenu = QMenu()
        self.trayIcon = QSystemTrayIcon(self.icon, self.w)
        self.trayIcon.activated.connect(self._activate_traymenu)
        self.createTray()

    def _resizeview(self, **kwargs):
        self.coursesView.setColumnWidth(3, 160)
        self.coursesView.resizeColumnToContents(1)
        self.coursesView.setColumnWidth(0, 320)

    def inittextincourses(self):
        self.statusLabel.setText('Started syncing.')

    def checknewversion(self):
        rawdata = requests.get(
            'https://pypi.python.org/pypi/poliBeePsync/json')
        latest = json.loads(rawdata.text)['info']['version']
        self.version_label.setTextFormat(Qt.RichText)
        self.version_label.setOpenExternalLinks(True)
        self.version_label.setLocale(
            QLocale(QLocale.English, QLocale.UnitedStates))
        self.version_label.setScaledContents(True)
        self.version_label.setWordWrap(True)
        if latest != __version__:
            newtext = """<p>Current version: {}.<br>
Latest version: {}. </p>
<p>Visit <a href='http://www.davideolianas.com/polibeepsync/index.html#how-to\
-install-upgrade-remove'>here</a> to find out how to upgrade.
""".format(__version__, latest)
        else:
            newtext = "Current version: {} up-to-date.".format(__version__)
        self.version_label.setText(newtext)

    def _update_time(self, folder, file, path_list):
        print('inside ', folder.name)
        print('path_list: ', path_list)
        while len(path_list) > 0:
            namegoto = path_list.pop(0)
            print('namegoto: ', namegoto)
            # perché a volte è vuoto?
            if namegoto != "":
                fakefolder = Folder(namegoto, 'fake')
                print('contained folders: ', folder.folders)
                ind = folder.folders.index(fakefolder)
                goto = folder.folders[ind]
                self._update_time(goto, file, path_list)
        if file in folder.files:
            ind = folder.files.index(file)
            thisfile = folder.files[ind]
            thisfile.local_creation_time = file.local_creation_time
            self.dumpUser()

    def update_file_localtime(self, data, **kwargs):
        course, coursefile, path = data
        rootpath = os.path.join(self.settings['RootFolder'],
                                course.save_folder_name)
        if path.startswith(rootpath):
            partial = path[len(rootpath):]
        path_list = partial.split(os.path.sep)
        self._update_time(course.documents, coursefile, path_list)

    def update_course_download(self, course, **kwargs):
        logger.info('download size updated')
        if course in self.user.available_courses:
            updating = self.user.available_courses[course.name]
            updating.downloaded_size = course.downloaded_size
            row = self.courses_model.courses.index(updating)
            where = self.courses_model.index(row, 3)
            self.courses_model.dataChanged.emit(where, where)
            self.dumpUser()

    def setinizialsizes(self, course, **kwargs):
        if course in self.user.available_courses:
            updating = self.user.available_courses[course.name]
            updating.downloaded_size = course.downloaded_size
            updating.total_file_size = course.total_file_size
            row = self.courses_model.courses.index(updating)
            where = self.courses_model.index(row, 3)
            self.courses_model.dataChanged.emit(where, where)
            self.dumpUser()

    def syncnewcourses(self, newlist):
        if self.settings['SyncNewCourses'] == 'True':
            for elem in newlist:
                elem.sync = True

    def load_settings(self):
        for path in [
                user_config_dir(self.appname),
                user_data_dir(self.appname)
        ]:
            try:
                os.makedirs(path, exist_ok=True)
            except OSError:
                logger.critical('OSError while calling os.makedirs.',
                                exc_info=True)
                self.myStream_message("I couldn't create {}.\nStart"
                                      " poliBeePsync with --debug "
                                      "error to get more details.")
        self.settings_path = os.path.join(user_config_dir(self.appname),
                                          self.settings_fname)
        defaults = {
            'UpdateEvery': '60',
            'RootFolder': os.path.join(os.path.expanduser('~'), self.appname),
            'SyncNewCourses': 'False'
        }
        self.settings = filesettings.settingsFromFile(self.settings_path,
                                                      defaults)

    def load_data(self):
        try:
            with open(
                    os.path.join(user_data_dir(self.appname), self.data_fname),
                    'rb') as f:
                self.user = pickle.load(f)
                self.myStream_message("Data has been loaded successfully.")

        except FileNotFoundError:
            logger.error('Settings file not found.', exc_info=True)
            self.user = User('', '')
            self.myStream_message("I couldn't find data in the"
                                  " predefined directory. Ignore this"
                                  "message if you're using poliBeePsync"
                                  " for the first time.")

    def loginstatus(self, status):
        self.login_attempt.setText(status)

    # @Slot(int)
    # def notifynew(self, state):
    # if state == 2:
    # self.settings['NotifyNewCourses'] = 'True'
    # else:
    # self.settings['NotifyNewCourses'] = 'False'
    #    filesettings.settingsToFile(self.settings, self.settings_path)

    @Slot(int)
    def syncnewslot(self, state):
        if state == 2:
            self.settings['SyncNewCourses'] = 'True'
        else:
            self.settings['SyncNewCourses'] = 'False'
        filesettings.settingsToFile(self.settings, self.settings_path)

    @Slot(int)
    def updateminuteslot(self, minutes):
        self.settings['UpdateEvery'] = str(minutes)
        filesettings.settingsToFile(self.settings, self.settings_path)
        self.timer.start(1000 * 60 * int(self.settings['UpdateEvery']))

    @Slot(str)
    def rootfolderslot(self, path):
        self.settings['RootFolder'] = path
        filesettings.settingsToFile(self.settings, self.settings_path)

    def chooserootdir(self):
        currentdir = self.settings['RootFolder']
        flags = QFileDialog.DontResolveSymlinks | QFileDialog.ShowDirsOnly
        newroot = QFileDialog.getExistingDirectory(None, "Open Directory",
                                                   currentdir, flags)
        if newroot != "" and str(newroot) != currentdir:
            self.settings['RootFolder'] = str(newroot)
            filesettings.settingsToFile(self.settings, self.settings_path)
            self.rootfolder.setText(newroot)
            # we delete the already present downloadthread and recreate it
            # because otherwise it uses the old download folder. I don't know
            # if there's a cleaner approach
            del self.downloadthread
            self.downloadthread = DownloadThread(self.user,
                                                 self.settings['RootFolder'])
            self.downloadthread.dumpuser.sig.connect(self.dumpUser)
            self.downloadthread.course_finished.sig.connect(
                self.myStream_message)
            self.downloadthread.signal_error.sig.connect(self.myStream_message)

    def setusercode(self):
        newcode = self.userCode.text()
        self.user.username = newcode
        try:
            self.dumpUser()
            if len(newcode) == 8:
                self.myStream_message(
                    "User code changed to {}.".format(newcode))
        except OSError:
            self.myStream_message("I couldn't save data to disk. Run"
                                  " poliBeePsync with option --debug"
                                  " error to get more details.")
            logger.error(
                'OSError raised while trying to write the User'
                'instance to disk.',
                exc_info=True)

    def setpassword(self):
        newpass = self.password.text()
        self.user.password = newpass
        try:
            self.dumpUser()
            self.myStream_message("Password changed.")
        except OSError:
            self.myStream_message("I couldn't save data to disk. Run"
                                  " poliBeePsync with option --debug"
                                  " error to get more details.")
            logger.error(
                'OSError raised while trying to write the User'
                'instance to disk.',
                exc_info=True)

    def testlogin(self):
        if not self.loginthread.isRunning():
            self.loginthread.exiting = False
            self.loginthread.start()
            self.login_attempt.setStyleSheet("color: rgba(0, 0, 0, 255);")
            self.login_attempt.setText("Logging in, please wait.")

    def addtocoursesview(self, addlist):
        for elem in addlist:
            self.courses_model.insertRows(0, 1, elem)

    def rmfromcoursesview(self, removelist):
        for elem in removelist:
            index = self.courses_model.courses.index(elem)
            self.courses_model.removeRows(index, 1)

    def dumpUser(self):
        # we don't use the message...
        with open(os.path.join(user_data_dir(self.appname), self.data_fname),
                  'wb') as f:
            pickle.dump(self.user, f)

    def refreshcourses(self):
        self.statusLabel.setText(
            'Searching for online updates...this may take a'
            ' while.')
        if not self.loginthread.isRunning():
            self.loginthread.exiting = False
            self.loginthread.signal_ok.sig.connect(self.do_refreshcourses)
            self.loginthread.start()

    def do_refreshcourses(self):
        self.loginthread.signal_ok.sig.disconnect(self.do_refreshcourses)
        if not self.refreshcoursesthread.isRunning():
            self.refreshcoursesthread.exiting = False
            self.refreshcoursesthread.start()

    @Slot()
    def syncfiles(self):
        self.refreshcoursesthread.finished.connect(self.do_syncfiles)
        self.refreshcourses()

    def do_syncfiles(self):
        self.refreshcoursesthread.finished.disconnect(self.do_syncfiles)
        self.inittextincourses()
        self.downloadthread.start()

    @Slot(str)
    def myStream_message(self, message):
        self.status.moveCursor(QTextCursor.End)
        self.status.insertPlainText(message + "\n\n")

    def createTray(self):
        restoreAction = QAction("&Restore", self, triggered=self.showNormal)
        quitAction = QAction("&Quit", self, triggered=qApp.quit)
        self.trayIconMenu.addAction(restoreAction)
        self.trayIconMenu.addAction(quitAction)
        self.trayIcon.setContextMenu(self.trayIconMenu)
        self.trayIcon.show()

    def _activate_traymenu(self, reason):
        if reason == QSystemTrayIcon.ActivationReason.Trigger:
            self.showNormal()
        else:
            self.trayIconMenu.activateWindow()
            self.trayIconMenu.popup(QCursor.pos())

    def closeEvent(self, event):
        self.hide()
        event.ignore()

    def about_text(self):
        self.label_3 = QLabel()
        self.label_3.setTextFormat(Qt.RichText)
        self.label_3.setOpenExternalLinks(True)
        self.label_3.setLocale(QLocale(QLocale.English, QLocale.UnitedStates))
        self.label_3.setScaledContents(True)
        self.label_3.setWordWrap(True)
        text = """
<html>
<head/>
<body>
  <p>poliBeePsync is a program written by Davide Olianas,
released under GNU GPLv3+.</p>
  <p>Feel free to contact me at <a
  href=\"mailto:[email protected]\">[email protected]</a> for
  suggestions and bug reports.</p>
  <p>More information is available on the
  <a href=\"http://www.davideolianas.com/polibeepsync\">
  <span style=\" text-decoration: underline; color:#0000ff;\">
  official website</span></a>.
  </p>
</body>
</html>
"""

        if pysideVersion == '1.2.2':
            self.label_3.setText(
                QApplication.translate("Form", text, None,
                                       QApplication.UnicodeUTF8))
        else:
            self.label_3.setText(QApplication.translate("Form", text, None))