예제 #1
0
class Application(Gtk.Application):
    def __init__(self, *args, **kwargs):
        super().__init__(*args,
                         application_id="easy-ebook-viewer",
                         flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE,
                         **kwargs)
        self.window = None
        self.file_path = None
        GLib.set_application_name('Easy eBook Viewer')
        GLib.set_prgname('easy-ebook-viewer')
        GLib.setenv('PULSE_PROP_application.icon_name', 'easy-ebook-viewer',
                    True)

    def do_startup(self):
        Gtk.Application.do_startup(self)

        action = Gio.SimpleAction.new("about", None)
        action.connect("activate", self.on_about)
        self.add_action(action)

        action = Gio.SimpleAction.new("quit", None)
        action.connect("activate", self.on_quit)
        self.add_action(action)

    def do_activate(self):
        GObject.threads_init()
        gettext.install('easy-ebook-viewer',
                        '/usr/share/easy-ebook-viewer/locale')
        # We only allow a single window and raise any existing ones
        if not self.window:
            # Windows are associated with the application
            # when the last one is closed the application shuts down
            self.window = MainWindow(file_path=self.file_path)
            self.window.connect("delete-event", self.on_quit)
            self.window.set_wmclass("easy-ebook-viewer", "easy-ebook-viewer")
        self.window.show_all()
        if not self.window.book_loaded:
            self.window.header_bar_component.hide_jumping_navigation()
        Gtk.main()

    def do_command_line(self, command_line):
        # If book came from arguments ie. was oppened using "Open with..." method etc.
        if len(sys.argv) > 1:
            # Check if that file really exists
            if os.path.exists(sys.argv[1]):
                self.file_path = sys.argv[1]
        self.activate()
        return 0

    def on_about(self, action, param):
        dialog = about_dialog.AboutDialog()
        dialog.show_all()

    def on_quit(self, action, param):
        Gdk.threads_leave()
        Gtk.main_quit()
        self.quit()
예제 #2
0
class Application(Gtk.Application):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, application_id="easy-ebook-viewer",
                         flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE,
                         **kwargs)
        self.window = None
        self.file_path = None
        GLib.set_application_name('Easy eBook Viewer')
        GLib.set_prgname('easy-ebook-viewer')
        GLib.setenv('PULSE_PROP_application.icon_name', 'easy-ebook-viewer', True)

    def do_startup(self):
        Gtk.Application.do_startup(self)

        action = Gio.SimpleAction.new("about", None)
        action.connect("activate", self.on_about)
        self.add_action(action)

        action = Gio.SimpleAction.new("quit", None)
        action.connect("activate", self.on_quit)
        self.add_action(action)

    def do_activate(self):
        GObject.threads_init()
        gettext.install('easy-ebook-viewer', '/usr/share/easy-ebook-viewer/locale')
        # We only allow a single window and raise any existing ones
        if not self.window:
            # Windows are associated with the application
            # when the last one is closed the application shuts down
            self.window = MainWindow(file_path=self.file_path)
            self.window.connect("delete-event", self.on_quit)
            self.window.set_wmclass("easy-ebook-viewer", "easy-ebook-viewer")
        self.window.show_all()
        if not self.window.book_loaded:
            self.window.header_bar_component.hide_jumping_navigation()
        Gtk.main()

    def do_command_line(self, command_line):
        # If book came from arguments ie. was oppened using "Open with..." method etc.
        if len(sys.argv) > 1:
            # Check if that file really exists
            if os.path.exists(sys.argv[1]):
                self.file_path = sys.argv[1]
        self.activate()
        return 0

    def on_about(self, action, param):
        dialog = about_dialog.AboutDialog()
        dialog.show_all()

    def on_quit(self, action, param):
        Gdk.threads_leave()
        Gtk.main_quit()
        self.quit()
class Main():
    def __init__(self):
        if os.geteuid() != 0:
            root_popup = Popup('Error', 'You need to be root to run the VPN.')
            root_popup.set_icon_from_file("surfshark_linux_client.png")
            root_popup.connect('destroy', Gtk.main_quit)
            Gtk.main()
            return None

        self.debug_on = True  # set to False to disable debug infos

        # TODO : Make a loader
        # TODO : Find a way to have a popup to run the app as root

        self.folder_path = os.path.abspath(os.path.dirname(__file__)) + "/"

        self.servers = self.get_servers()
        self.unhash_pass = ""
        self.config_files = {}
        self.vpn_command = False
        self.thread = False
        self.ip = ""

        with open(self.folder_path + "config.json", "r") as file:
            self.config = json.load(file)

        style = Gtk.CssProvider()
        #Theme
        if (self.config['theme'] == 'light'):
            style.load_from_path(self.folder_path +
                                 "style/style_lightmode.css")
        else:
            style.load_from_path(self.folder_path + "style/style_darkmode.css")

        Gtk.StyleContext.add_provider_for_screen(
            Gdk.Screen.get_default(), style,
            Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)

        if (self.config['password_needed'] or not self.config['registered']):
            self.log_window = LogWindow(self)
            self.log_window.connect("destroy", Gtk.main_quit)
            self.log_window.show_all()

        self.main_window = MainWindow(self)
        self.main_window.connect("destroy", self.soft_quit_g)

        if (not self.config['password_needed'] and self.config['registered']):
            screen = self.main_window.get_display()
            monitor_size = screen.get_monitor_at_window(
                Gdk.get_default_root_window()).get_geometry()
            self.main_window.move(
                monitor_size.width / 2 - self.main_window.get_size().width / 2,
                monitor_size.height / 2 -
                self.main_window.get_size().height / 2)
            self.main_window.credentials_username.set_text(
                self.config['vpn_username'])
            self.main_window.credentials_password.set_text(
                self.config['vpn_password'])
            self.main_window.show_all()

        try:
            Gtk.main()
        except (KeyboardInterrupt, SystemExit):
            self.soft_quit()

    def log_action(self, widget):
        hashed_password = self.hash_pass(self.log_window.password.get_text())
        if self.config['registered']:
            if (hashed_password == self.config['password']):
                self.log_in()
            else:
                self.log_window.password.get_style_context().add_class('error')
                self.debug("Wrong password")
        else:
            if (widget == self.log_window.log_without_pass_button):
                self.config['registered'] = True
                self.config['password_needed'] = False

                self.save_config()
                self.log_in()
            else:
                if (self.log_window.password.get_text() ==
                        self.log_window.confirm_password.get_text()):
                    self.config['registered'] = True
                    self.config['password_needed'] = True
                    self.config['password'] = hashed_password

                    self.save_config()
                    self.debug("Account created")

                    self.log_in()
                else:
                    self.log_window.password.get_style_context().add_class(
                        'error')
                    self.log_window.confirm_password.get_style_context(
                    ).add_class('error')

    def get_servers(self):
        with urllib.request.urlopen(
                "https://api.surfshark.com/v3/server/clusters/all") as url:
            return json.loads(url.read().decode())

    def log_in(self):
        if (self.config['password_needed']):
            self.unhash_pass = self.log_window.password.get_text()

        old_pos = self.log_window.get_position()
        self.log_window.hide()
        self.main_window.move(old_pos.root_x, old_pos.root_y)
        self.main_window.show_all()

        if ('vpn_password' in self.config and 'vpn_username' in self.config
                and self.config['vpn_username'] != ""
                and self.config['vpn_password'] != ""):
            if (self.config['password_needed']):
                try:
                    vpn_username = self.sym_decrypt(
                        self.config['vpn_username'])
                    vpn_password = self.sym_decrypt(
                        self.config['vpn_password'])
                except:
                    vpn_username = ""
                    vpn_password = ""
            else:
                vpn_username = self.config['vpn_username']
                vpn_password = self.config['vpn_password']
            self.main_window.credentials_username.set_text(vpn_username)
            self.main_window.credentials_password.set_text(vpn_password)

        self.debug("Logged In")

    def save_config(self):
        with open(self.folder_path + "config.json", "w") as file:
            file.write(json.dumps(self.config))
            self.debug("Config saved")

    def debug(self, message, type=None):
        if (self.debug_on):
            type = "INFO" if not type else type
            print(type + ": " + message)

    def save_credentials(self, widget):
        vpn_username = self.main_window.credentials_username.get_text()
        vpn_password = self.main_window.credentials_password.get_text()
        error_count = 0
        if (vpn_username == ""):
            self.main_window.credentials_username.get_style_context(
            ).add_class('error')
            error_count += 1
        if (vpn_password == ""):
            self.main_window.credentials_password.get_style_context(
            ).add_class('error')
            error_count += 1
        if (error_count > 0): return

        if (self.config['password_needed']):
            vpn_username = self.sym_encrypt(vpn_username)
            vpn_password = self.sym_encrypt(vpn_password)

        self.config['vpn_username'] = vpn_username
        self.config['vpn_password'] = vpn_password
        self.save_config()

        creds_updated = threading.Thread(target=self.credential_updated)
        creds_updated.start()

    def sym_encrypt(self, raw):
        private_key = hashlib.sha256(self.unhash_pass.encode("utf-8")).digest()
        raw = pad(raw.encode('utf-8'), AES.block_size)
        iv = Random.new().read(AES.block_size)
        cipher = AES.new(private_key, AES.MODE_CBC, iv)
        return base64.b64encode(iv + cipher.encrypt(raw)).decode('utf8')

    def sym_decrypt(self, encrypted_text):
        private_key = hashlib.sha256(self.unhash_pass.encode("utf-8")).digest()
        encrypted_text = base64.b64decode(encrypted_text)
        iv = encrypted_text[:16]
        cipher = AES.new(private_key, AES.MODE_CBC, iv)
        return unpad(cipher.decrypt(encrypted_text[16:]),
                     AES.block_size).decode('utf8')

    def hash_pass(self, password):
        return base64.b64encode(
            hashlib.sha3_512(
                password.encode("utf-8")).hexdigest().encode()).decode()

    def change_protocol(self, switch, is_tcp):
        if (is_tcp):
            self.config["connection_protocol"] = "tcp"
            self.main_window.udp_label.set_markup("UDP")
            self.main_window.tcp_label.set_markup("<b>TCP</b>")
        else:
            self.config["connection_protocol"] = "udp"
            self.main_window.udp_label.set_markup("<b>UDP</b>")
            self.main_window.tcp_label.set_markup("TCP")
        self.save_config()

    def change_password_need(self, button):
        button.set_sensitive(False)
        if (self.config['password_needed']):
            self.config['password_needed'] = False
            self.config['vpn_username'] = self.sym_decrypt(
                self.config['vpn_username'])
            self.config['vpn_password'] = self.sym_decrypt(
                self.config['vpn_password'])
            button.set_label("Enable Pass")
        else:
            self.config['password_needed'] = True
            self.config['vpn_username'] = self.sym_encrypt(
                self.config['vpn_username'])
            self.config['vpn_password'] = self.sym_encrypt(
                self.config['vpn_password'])
            button.set_label("Disable Pass")

        self.save_config()
        button.set_sensitive(True)

    def update_password(self, button):
        self.main_window.new_password.get_style_context().remove_class("error")
        self.main_window.confirm_new_password.get_style_context().remove_class(
            "error")

        if (self.main_window.new_password.get_text() ==
                self.main_window.confirm_new_password.get_text()):
            self.config['password'] = self.hash_pass(
                self.main_window.new_password.get_text())
            if (self.config['password_needed']):
                vpn_user = self.sym_decrypt(self.config['vpn_username'])
                vpn_pass = self.sym_decrypt(self.config['vpn_password'])
            else:
                vpn_user = self.config['vpn_username']
                vpn_pass = self.config['vpn_password']
                self.config['password_needed'] = True
                self.main_window.disable_pass_button.set_label("Disable Pass")
                self.main_window.enable_password_container.set_sensitive(True)

            self.unhash_pass = self.main_window.new_password.get_text()
            self.config['vpn_username'] = self.sym_encrypt(vpn_user)
            self.config['vpn_password'] = self.sym_encrypt(vpn_pass)
            self.main_window.new_password.set_text("")
            self.main_window.confirm_new_password.set_text("")

            self.save_config()

            password_updated_t = threading.Thread(target=self.password_updated)
            password_updated_t.start()
        else:
            self.main_window.new_password.get_style_context().add_class(
                "error")
            self.main_window.confirm_new_password.get_style_context(
            ).add_class("error")

    def password_updated(self):
        self.main_window.updated_password_label.set_label("Password updated")
        time.sleep(2)
        self.main_window.updated_password_label.set_label("")

    def credential_updated(self):
        self.main_window.updated_vpn_credential_label.set_label(
            "Credentials updated")
        time.sleep(2)
        self.main_window.updated_vpn_credential_label.set_label("")

    def change_theme(self, switch, current_theme):
        style = Gtk.CssProvider()

        if (current_theme):
            style.load_from_path(self.folder_path + "style/style_darkmode.css")
            self.config['theme'] = "dark"
            self.main_window.light_label.set_markup("Light")
            self.main_window.dark_label.set_markup("<b>Dark</b>")
        else:
            style.load_from_path(self.folder_path +
                                 "style/style_lightmode.css")
            self.config['theme'] = "light"

            self.main_window.light_label.set_markup("<b>Light</b>")
            self.main_window.dark_label.set_markup("Dark")

        Gtk.StyleContext.add_provider_for_screen(
            Gdk.Screen.get_default(), style,
            Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
        self.save_config()

    def change_killswitch(self, switch, killswitch_status):
        if (killswitch_status):
            self.config["killswitch"] = "on"
            self.main_window.killswitch_off_label.set_markup("OFF")
            self.main_window.killswitch_on_label.set_markup("<b>ON</b>")
            if (self.vpn_command):
                self.enable_killswitch()
            success_popup = Popup(
                "Killswitch activated",
                "The killswitch is now activated!\nThe current version of the killswitch can leak on port 1194"
            )
        else:
            self.config["killswitch"] = "off"
            self.main_window.killswitch_off_label.set_markup("<b>OFF</b>")
            self.main_window.killswitch_on_label.set_markup("ON")
            self.disable_killswitch()
            success_popup = Popup(
                "Killswitch deactivated",
                "The killswitch is now inactive!\nYour network traffic is now possible to leak"
            )
        self.save_config()

    def credential_updated(self):
        self.main_window.updated_vpn_credential_label.set_label(
            "Credentials updated")
        time.sleep(2)
        self.main_window.updated_vpn_credential_label.set_label("")

    def switch_server(self, button):
        text = self.main_window.selected_label.get_text()
        if (text == "Nothing"): return False

        self.main_window.connected_to_label.set_label("Connecting...")
        self.main_window.switch_server_btn.set_sensitive(False)
        openvpn_config_file = self.config_files[text] + "_" + self.config[
            'connection_protocol'] + ".ovpn"

        if (self.vpn_command):
            self.vpn_command.terminate()
            self.thread.join()
            self.main_window.ip_label.set_label("")

        with open(self.folder_path + '.tmp_creds_file', 'a+') as cred_file:
            if (self.config['password_needed']):
                cred_file.write(
                    self.sym_decrypt(self.config['vpn_username']) + "\n" +
                    self.sym_decrypt(self.config['vpn_password']))
            else:
                cred_file.write(self.config['vpn_username'] + "\n" +
                                self.config['vpn_password'])
        subprocess.call([
            "cp", self.folder_path + "vpn_config_files/" + openvpn_config_file,
            self.folder_path + ".tmp_cfg_file"
        ])

        try:
            i = 0
            with open(self.folder_path + '.tmp_cfg_file', 'r+') as cfg_file:
                file = cfg_file.readlines()
                for line in file:
                    if ("auth-user-pass" in line): break
                    i += 1
        except:
            self.debug("VPN config file is missing", "ERROR")
            self.main_window.connected_to_label.set_label("ERROR")
            self.main_window.switch_server_btn.set_sensitive(True)

            Popup(
                'Error',
                "Couldn't find config file for this server, try to update the config files.",
                self.main_window)

            return False

        with open(self.folder_path + '.tmp_cfg_file', 'w') as cfg_file:
            file[i] = "auth-user-pass " + self.folder_path + ".tmp_creds_file"
            cfg_file.writelines(file)

        #disable the killswitch to connect to a new server
        if (self.config["killswitch"] == "on"):
            self.disable_killswitch()

        self.vpn_command = subprocess.Popen(
            ["openvpn", self.folder_path + ".tmp_cfg_file"],
            stdout=subprocess.PIPE)

        self.thread = threading.Thread(target=self.command_log)
        self.thread.start()

    def command_log(self):
        time.sleep(.1)
        subprocess.call(["rm", self.folder_path + ".tmp_cfg_file"])
        subprocess.call(["rm", self.folder_path + ".tmp_creds_file"])

        for line in iter(self.vpn_command.stdout.readline, b''):
            line = line.decode()[:-1]
            if ("Exiting due to fatal error" in line):
                self.debug('Fatal error, VPN disconnected', type="ERROR")
                self.ip = ""
                self.main_window.vpn_exited()
            elif ("AUTH_FAILED" in line):
                self.debug('Wrong credentials', type="ERROR")
                self.ip = ""
                self.main_window.vpn_connection_failed()
            elif ("Initialization Sequence Completed" in line):
                self.debug('Connected to VPN')
                p = subprocess.Popen(["curl", "ipecho.net/plain"],
                                     stdout=subprocess.PIPE)
                ip, err = p.communicate()
                self.ip = ip.decode()
                self.main_window.confirm_connection()
                if (self.config["killswitch"] == "on"):
                    self.enable_killswitch()

            with open(
                    self.folder_path + "logs/openvpn-logs-" +
                    str(date.today()) + ".txt", 'a') as logfile:
                logfile.write(line + "\n")

    def disconnect(self, button):
        self.debug('DISCONNECTED')
        self.vpn_command.terminate()
        self.thread.join()
        self.disable_killswitch()

        self.ip = ""
        self.main_window.connected_to_label.set_label("Disconnected")
        self.main_window.ip_label.set_label("")
        self.main_window.disconnect_btn.set_sensitive(False)
        self.main_window.switch_server_btn.set_sensitive(True)

    def check_updates(self, button):
        subprocess.call([
            "wget",
            "https://account.surfshark.com/api/v1/server/configurations", "-O",
            self.folder_path + "vpn_config_files/conf.zip"
        ])
        p = subprocess.Popen(
            ["md5sum", self.folder_path + "vpn_config_files/conf.zip"],
            stdout=subprocess.PIPE)
        checksum, err = p.communicate()
        checksum = checksum.decode().split("  ")[0]

        if (checksum != self.config['config_md5']):
            subprocess.call("rm " + self.folder_path +
                            "vpn_config_files/*.ovpn",
                            shell=True,
                            stdout=subprocess.DEVNULL,
                            stderr=subprocess.DEVNULL)
            subprocess.call([
                "unzip", self.folder_path + "vpn_config_files/conf.zip", "-d",
                self.folder_path + "vpn_config_files"
            ],
                            stdout=subprocess.DEVNULL,
                            stderr=subprocess.DEVNULL)
            self.config['config_md5'] = checksum
            self.save_config()
            self.main_window.updates_info.set_label(
                "The config files were updated !")
        else:
            self.main_window.updates_info.set_label("Everything is okay !")

        subprocess.call(["rm", self.folder_path + "vpn_config_files/conf.zip"],
                        stdout=subprocess.DEVNULL,
                        stderr=subprocess.DEVNULL)

    def soft_quit(self):
        if self.vpn_command and self.thread:
            self.vpn_command.terminate()
            self.thread.join()
        self.disable_killswitch()
        self.create_tray()
        Gtk.main_quit()

    def soft_quit_g(self, window):
        self.soft_quit()

    def enable_killswitch(self):
        #enable killswitch
        enable_killswitch_command = "sudo ./enablekillswitch.sh"

        command = enable_killswitch_command.split()
        subprocess.run(command)

    def disable_killswitch(self):
        #restore old iptable rules
        restore_iptables_command = "sudo ./restoreiptables.sh"
        command = restore_iptables_command.split()
        subprocess.run(command)

    def create_tray(self):
        #TODO
        pass
예제 #4
0
class PSSOptimisation(object):
    def __init__(self):
        QtCore.QCoreApplication.setApplicationName("PSSOptimisation")
        QtCore.QCoreApplication.setApplicationVersion(str(VERSION))
        QtCore.QCoreApplication.setOrganizationName("Hubert Grzeskowiak")

        self.settings = QtCore.QSettings()
        self.main_window = MainWindow(True)
        self.grades_model = GradesModel(self.main_window)
        self.proxy_model = GradesModelProxy()
        self.proxy_model.setSourceModel(self.grades_model)

        self.initUI()
        # tray = QtGui.QSystemTrayIcon(self.main_window)
        # tray.setIcon(QtGui.QIcon("icons/Accessories-calculator.svg"))
        # tray.connect(tray,
        #     SIGNAL("activated(QSystemTrayIcon::ActivationReason)"),
        #     self.trayClicked)
        # tray.show()
        # self.tray = tray

    # def trayClicked(self, reason):
    #     print reason
    #     if reason == QtGui.QSystemTrayIcon.DoubleClick:
    #         self.main_window.setVisible(not self.main_window.isVisible())

    def initUI(self):
        self.connectUI()
        self.main_window.show()

    def __columnsChanged(self):
        """This should be called whenever the columns filter is changed.
        It's a workaround for a bug in the headerview not updating the columns
        properly on filter change.
        """
        header = self.main_window.grades_table.horizontalHeader()
        header.resizeSections(QtGui.QHeaderView.ResizeToContents)
        header.resizeSection(0, header.sectionSize(0) - 1)
        header.resizeSection(0, header.sectionSize(0) + 1)

    def connectUI(self):
        self.main_window.grades_table.setModel(self.proxy_model)
        delegate = CheckBoxDelegate()
        self.main_window.grades_table.setItemDelegate(delegate)

        self.main_window.connect(self.main_window.action_download,
                                 SIGNAL("triggered()"), self.openLoginDialog)
        self.main_window.connect(self.main_window.action_donate,
                                 SIGNAL("triggered()"),
                                 self.openDonationDialog)
        self.main_window.connect(self.main_window.action_open,
                                 SIGNAL("triggered()"), self.openFileDialog)
        self.main_window.connect(self.grades_model, SIGNAL("dataChanged()"),
                                 self.updateStats)
        self.main_window.connect(
            self.grades_model, SIGNAL("dataChanged()"),
            self.main_window.grades_table.resizeColumnsToContents)
        self.main_window.connect(self.grades_model, SIGNAL("modelReset()"),
                                 self.updateStats)
        self.main_window.connect(
            self.grades_model, SIGNAL("modelReset()"),
            self.main_window.grades_table.resizeColumnsToContents)

        # workaround for buggy headerview
        self.main_window.connect(self.proxy_model,
                                 SIGNAL("columnsVisibilityChanged()"),
                                 self.__columnsChanged)

        # create actions for toggling table columns
        header = self.main_window.grades_table.horizontalHeader()
        self.header_actions = QtGui.QActionGroup(header)
        self.header_actions.setExclusive(False)
        for nr, (name, visible) in enumerate(
                zip(self.grades_model.header_data,
                    self.proxy_model.col_visibility)):
            action = self.header_actions.addAction(name)
            action.setCheckable(True)
            action.setChecked(visible)
            action.connect(action,
                           SIGNAL("triggered()"),
                           lambda nr=nr: self.proxy_model.toggleColumn(nr))

        # add that menu as context menu for the header
        header.addActions(self.header_actions.actions())
        header.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)

        # and put it into the main window's menus
        self.main_window.menu_table_columns.clear()
        for action in self.header_actions.actions():
            self.main_window.menu_table_columns.addAction(action)

        # automatically download new grades (depends on settings)
        if self.settings.value("updateOnStart", False).toBool():
            QtCore.QTimer.singleShot(200, self.tryAutoDownloadFromPSSO)

    def openLoginDialog(self):
        self.login_dialog = LoginDialog(self.main_window)
        self.login_dialog.exec_()
        if self.login_dialog.result():
            username = str(self.login_dialog.username_line.text())
            password = str(self.login_dialog.password_line.text())
            if username and password:
                remember = self.login_dialog.remember_checkbox.isChecked()
                self.handleLoginData(username, password, remember)

    def handleLoginData(self, username, password, remember=False):
        """Try to load the grades by using the provided login credentials.
        On failure, an error popup is displayed. On success a loading progress
        bar and after that a table is shown.
        If "remember" is True, then the login data is saved.
        """
        self.main_window.setDisabled(True)
        QtGui.QApplication.processEvents()
        QtGui.QApplication.processEvents()
        progress = 0
        try:
            iterator = self.grades_model.getFromPSSOIterator(
                username, password)
            for step in iterator:
                self.main_window.showProgress(progress, step)
                # getFromPSSOIterator defines 8 steps, but 1st is at 0
                progress += 100.0 / 7
                QtGui.QApplication.processEvents()
        except (ConnectionError, ServiceUnavailableError, ParsingError) as e:
            QtGui.QMessageBox.critical(self.main_window, e.title, e.message)
            return
        except LoginError as e:
            self.clearLoginData(username)
            QtGui.QMessageBox.critical(self.main_window, e.title, e.message)
            return
        finally:
            self.main_window.setEnabled(True)
            self.main_window.showProgress(-1)

        self.main_window.showTable()
        self.main_window.setEnabled(True)

        if remember:
            self.saveLoginData(username, password)

    def saveLoginData(self, username, password):
        assert username and password
        self.settings.setValue("username", username)
        keyring.set_password("PSSO", username, password)

    def getLoginData(self):
        """Try to retrieve previously saved username and password. Returns
        a tuple of two empty strings on failure.
        """
        username = str(self.settings.value("username").toString() or "")
        try:
            password = keyring.get_password("PSSO", username) or ""
        except IOError:
            return "", ""
        return username, password

    def clearLoginData(self, username=None):
        """Remove username and password settings. If a username is given,
        its password will be removed. If there is a saved username,
        it will also get removed alongside with the corresponding password.
        """
        username2 = str(self.settings.value("username").toString() or "")
        try:
            keyring.delete_password("PSSO", username)
        except:
            pass
        try:
            keyring.delete_password("PSSO", username2)
        except:
            pass
        self.settings.remove("username")

    def tryAutoDownloadFromPSSO(self):
        u, p = self.getLoginData()
        if u and p:
            self.handleLoginData(u, p)

    def openFileDialog(self):
        ofd = OpenFileDialog(self.main_window)
        accepted = ofd.exec_()
        if accepted and ofd.html_file:
            try:
                html = codecs.open(ofd.html_file, encoding='utf-8')
            except IOError:
                QtGui.QMessageBox.warning(
                    self.main_window, "File not found",
                    "Sorry, but there seems to be no file called {}.".format(
                        ofd.html_file))
                return
            self.grades_model.getFromHTML(html)
            self.main_window.showTable()

    def openDonationDialog(self):
        dp = DonationDialog(self.main_window)
        dp.exec_()

    def updateStats(self):
        self.main_window.num_of_grades.setText(
            str(self.grades_model.getNumOfGrades()))
        self.main_window.num_of_credits.setText(
            str(self.grades_model.getNumOfCredits()))
        self.main_window.average_grade.setText(
            str(self.grades_model.getAverageGrade()))
예제 #5
0
#!/usr/bin/env python3

# Easy eBook Viewer by Michal Daniel

# Easy eBook Viewer is free software; you can redistribute it and/or modify it under the terms
# of the GNU General Public Licence as published by the Free Software Foundation.

# Easy eBook Viewer is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.  See the GNU General Public Licence for more details.

# You should have received a copy of the GNU General Public Licence along with
# Easy eBook Viewer; if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
# Fifth Floor, Boston, MA 02110-1301, USA.

import gi

gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from main_window import MainWindow

# Let the fun begin...
if __name__ == "__main__":
    win = MainWindow()
    win.connect("delete-event", Gtk.main_quit)
    win.show_all()
    if not win.book_loaded:
        win.header_bar_component.hide_jumping_navigation()
    Gtk.main()
예제 #6
0
class PSSOptimisation(object):
    def __init__(self):
        QtCore.QCoreApplication.setApplicationName("PSSOptimisation")
        QtCore.QCoreApplication.setApplicationVersion(str(VERSION))
        QtCore.QCoreApplication.setOrganizationName("Hubert Grzeskowiak")

        self.settings = QtCore.QSettings()
        self.main_window = MainWindow(True)
        self.grades_model = GradesModel(self.main_window)
        self.proxy_model = GradesModelProxy()
        self.proxy_model.setSourceModel(self.grades_model)

        self.initUI()
        # tray = QtGui.QSystemTrayIcon(self.main_window)
        # tray.setIcon(QtGui.QIcon("icons/Accessories-calculator.svg"))
        # tray.connect(tray,
        #     SIGNAL("activated(QSystemTrayIcon::ActivationReason)"),
        #     self.trayClicked)
        # tray.show()
        # self.tray = tray

    # def trayClicked(self, reason):
    #     print reason
    #     if reason == QtGui.QSystemTrayIcon.DoubleClick:
    #         self.main_window.setVisible(not self.main_window.isVisible())

    def initUI(self):
        self.connectUI()
        self.main_window.show()

    def __columnsChanged(self):
        """This should be called whenever the columns filter is changed.
        It's a workaround for a bug in the headerview not updating the columns
        properly on filter change.
        """
        header = self.main_window.grades_table.horizontalHeader()
        header.resizeSections(QtGui.QHeaderView.ResizeToContents)
        header.resizeSection(0, header.sectionSize(0)-1)
        header.resizeSection(0, header.sectionSize(0)+1)

    def connectUI(self):
        self.main_window.grades_table.setModel(self.proxy_model)
        delegate = CheckBoxDelegate()
        self.main_window.grades_table.setItemDelegate(delegate)

        self.main_window.connect(self.main_window.action_download,
            SIGNAL("triggered()"), self.openLoginDialog)
        self.main_window.connect(self.main_window.action_donate,
            SIGNAL("triggered()"), self.openDonationDialog)
        self.main_window.connect(self.main_window.action_open,
            SIGNAL("triggered()"), self.openFileDialog)
        self.main_window.connect(self.grades_model,
            SIGNAL("dataChanged()"), self.updateStats)
        self.main_window.connect(self.grades_model,
            SIGNAL("dataChanged()"),
            self.main_window.grades_table.resizeColumnsToContents)
        self.main_window.connect(self.grades_model,
            SIGNAL("modelReset()"), self.updateStats)
        self.main_window.connect(self.grades_model,
            SIGNAL("modelReset()"),
            self.main_window.grades_table.resizeColumnsToContents)
        
        # workaround for buggy headerview
        self.main_window.connect(self.proxy_model,
            SIGNAL("columnsVisibilityChanged()"),
            self.__columnsChanged)
        
        # create actions for toggling table columns
        header = self.main_window.grades_table.horizontalHeader()
        self.header_actions = QtGui.QActionGroup(header)
        self.header_actions.setExclusive(False)
        for nr, (name, visible) in enumerate(zip(
                self.grades_model.header_data,
                self.proxy_model.col_visibility)):
            action = self.header_actions.addAction(name)
            action.setCheckable(True)
            action.setChecked(visible)
            action.connect(action, SIGNAL("triggered()"),
                lambda nr=nr: self.proxy_model.toggleColumn(nr))

        # add that menu as context menu for the header
        header.addActions(self.header_actions.actions())
        header.setContextMenuPolicy(
            QtCore.Qt.ActionsContextMenu)

        # and put it into the main window's menus
        self.main_window.menu_table_columns.clear()
        for action in self.header_actions.actions():
            self.main_window.menu_table_columns.addAction(action)

        # automatically download new grades (depends on settings)
        if self.settings.value("updateOnStart", False).toBool():
            QtCore.QTimer.singleShot(200, self.tryAutoDownloadFromPSSO)
    
    def openLoginDialog(self):
        self.login_dialog = LoginDialog(self.main_window)
        self.login_dialog.exec_()
        if self.login_dialog.result():
            username = str(self.login_dialog.username_line.text())
            password = str(self.login_dialog.password_line.text())
            if username and password:
                remember = self.login_dialog.remember_checkbox.isChecked()
                self.handleLoginData(username, password, remember)

    def handleLoginData(self, username, password, remember=False):
        """Try to load the grades by using the provided login credentials.
        On failure, an error popup is displayed. On success a loading progress
        bar and after that a table is shown.
        If "remember" is True, then the login data is saved.
        """
        self.main_window.setDisabled(True)
        QtGui.QApplication.processEvents()
        QtGui.QApplication.processEvents()
        progress = 0
        try:
            iterator = self.grades_model.getFromPSSOIterator(username, password)
            for step in iterator:
                self.main_window.showProgress(progress, step)
                # getFromPSSOIterator defines 8 steps, but 1st is at 0
                progress += 100.0/7
                QtGui.QApplication.processEvents()
        except (ConnectionError, ServiceUnavailableError, ParsingError) as e:
            QtGui.QMessageBox.critical(self.main_window,
                e.title, e.message)
            return
        except LoginError as e:
            self.clearLoginData(username)
            QtGui.QMessageBox.critical(self.main_window,
                e.title, e.message)
            return
        finally:
            self.main_window.setEnabled(True)
            self.main_window.showProgress(-1)
        
        self.main_window.showTable()
        self.main_window.setEnabled(True)

        if remember:
            self.saveLoginData(username, password)

    def saveLoginData(self, username, password):
        assert username and password
        self.settings.setValue("username", username)
        keyring.set_password("PSSO", username, password)

    def getLoginData(self):
        """Try to retrieve previously saved username and password. Returns
        a tuple of two empty strings on failure.
        """
        username = str(self.settings.value("username").toString() or "")
        try:
            password = keyring.get_password("PSSO", username) or ""
        except IOError:
            return "", ""
        return username, password

    def clearLoginData(self, username=None):
        """Remove username and password settings. If a username is given,
        its password will be removed. If there is a saved username,
        it will also get removed alongside with the corresponding password.
        """
        username2 = str(self.settings.value("username").toString() or "")
        try:
            keyring.delete_password("PSSO", username)
        except:
            pass
        try:
            keyring.delete_password("PSSO", username2)
        except:
            pass
        self.settings.remove("username")

    def tryAutoDownloadFromPSSO(self):
        u, p = self.getLoginData()
        if u and p:
            self.handleLoginData(u, p)

    def openFileDialog(self):
        ofd = OpenFileDialog(self.main_window)
        accepted = ofd.exec_()
        if accepted and ofd.html_file:
            try:
                html = codecs.open(ofd.html_file, encoding='utf-8')
            except IOError:
                QtGui.QMessageBox.warning(self.main_window, "File not found",
                    "Sorry, but there seems to be no file called {}.".format(
                        ofd.html_file))
                return
            self.grades_model.getFromHTML(html)
            self.main_window.showTable()

    def openDonationDialog(self):
        dp = DonationDialog(self.main_window)
        dp.exec_()

    def updateStats(self):
        self.main_window.num_of_grades.setText(str(
            self.grades_model.getNumOfGrades()))
        self.main_window.num_of_credits.setText(str(
            self.grades_model.getNumOfCredits()))
        self.main_window.average_grade.setText(str(
            self.grades_model.getAverageGrade()))
예제 #7
0
 def do_activate(self):
     win = MainWindow(app=self)
     # win.connect("delete-event", Gtk.main_quit)
     win.connect("key_press_event", win.press_key_event)
     win.set_resizable(False)
     win.show_all()