Files
Portable-Computing-Toolkit-…/portable_computing_toolkit_installer/ui/installer_wizard.py
2019-04-19 20:12:57 -04:00

141 lines
6.8 KiB
Python

import platform
import subprocess
import tarfile
import tempfile
import webbrowser
import zipfile
from pathlib import Path
from queue import Queue
import json
import psutil
from PyQt5 import QtCore, QtGui, QtNetwork, QtWebEngineWidgets, QtWidgets
from PyQt5.uic import loadUi
try:
import win32api
import win32file
except ImportError:
if platform.system()=='Windows':
raise #require win32api and win32file on windows
from validators.location_validator import Location_Validator
class InstallerWizard(QtWidgets.QWizard):
def __init__(self, parent=None):
super().__init__(parent)
loadUi(Path(__file__).parent / "installer_wizard.ui", baseinstance=self)
#self.button(QtWidgets.QWizard.NextButton).clicked.connect(self._requirements_check)
self.intro_page.validatePage = self._requirements_check
self.currentIdChanged.connect
self.intro.anchorClicked.connect(self._link_clicked)
self.license.anchorClicked.connect(self._link_clicked)
self.install_location.setValidator(Location_Validator(parent=self))
self.location_page.registerField("Location*", self.install_location)
self.browse_button.clicked.connect(self._select_location)
#Change button text on license page
self.license_page.setButtonText(QtWidgets.QWizard.NextButton, "Agree")
self.license_page.setButtonText(QtWidgets.QWizard.CancelButton, "Decline")
self._network_manager = QtNetwork.QNetworkAccessManager(parent=QtWidgets.QApplication.instance())
self._thread_pool = QtCore.QThreadPool.globalInstance()
self._project_page=Path("https://github.com/norweeg/portable-computing-tookit-installer/")
def _link_clicked(self, address):
"""Receives anchorClicked signal from the intro or license text browsers and opens the address in an external web browser.
Opens an error message window if it is not able to do so.
Arguments:
address {QtCore.QUrl} -- The URL from the received signal
"""
try:
webbrowser.open(address.toString())
except webbrowser.Error as e:
self._display_error("Unable to launch external browser",e)
except Exception as e:
self._display_error("An error has occurred",e)
def _select_location(self):
"""Opens a QFileDialog in either the root of the most-recently connected non-network, non-cd drive or the user's home directory,
if no suitable external device is found
"""
if platform.system() == 'Windows':
drives = [drive for drive in win32api.GetLogicalDriveStrings().split('\0') if drive]
dialog_location = drives.pop()
while (win32file.GetDriveType(dialog_location) == win32file.DRIVE_CDROM
or win32file.GetDriveType(dialog_location) == win32file.DRIVE_REMOTE):
dialog_location = drives.pop()
if dialog_location == "C:\\":
dialog_location = Path.home()
break
else:
dialog_location = Path.home()
selected_location = QtWidgets.QFileDialog.getExistingDirectory(parent=self,caption="Select Location",
directory=str(dialog_location),
options=(QtWidgets.QFileDialog.ShowDirsOnly))
self.install_location.clear()
self.install_location.insert(selected_location)
def _requirements_check(self):
"""Checks that the minimum system requirements are met. Opens error message and resets the wizard if they are not.
Minimum Requirements: Windows 7 or greater (64-bit)
"""
process_dialog = QtWidgets.QProgressDialog("Checking System Requirements...", str(), 0, 100, self, QtCore.Qt.Dialog)
process_dialog.setAutoClose(True)
process_dialog.setAutoReset(True)
process_dialog.setCancelButton(None)
if self._network_manager.networkAccessible():
request = QtNetwork.QNetworkRequest(QtCore.QUrl("https://www.gnu.org/licenses/gpl-3.0-standalone.html"))
request.setAttribute(QtNetwork.QNetworkRequest.CacheLoadControlAttribute, QtNetwork.QNetworkRequest.AlwaysNetwork)
reply = self._network_manager.get(request)
reply.downloadProgress.connect(lambda received, total: process_dialog.setMaximum(total))
reply.downloadProgress.connect(lambda received, total: process_dialog.setValue(received))
process_dialog.show()
else:
self._display_error("This installer requires an active internet connection, but you do not appear to be online")
return False
if not platform.system() == 'Windows' or int(platform.release()) < 7:
reply.abort()
process_dialog.cancel()
self._display_error("This toolkit requires Microsoft Windows 7 or newer")
return False
elif not platform.machine() == 'AMD64':
reply.abort()
process_dialog.cancel()
self._display_error("Parts of this toolkit require a 64-bit processor. Please open an issue on Github if you absolutely need it all 32-bit")\
.buttonClicked.connect(lambda: webbrowser.open_new_tab(self._project_page/"issues"))
return False
else:
while reply.isRunning():
QtWidgets.QApplication.instance().processEvents()
QtCore.QThread.currentThread().msleep(100)
if not reply.error():
self.license.setHtml(bytearray(reply.readAll()).decode("utf-8"))
return True
else:
self._display_error(f"Encountered a {QtCore.QMetaEnum.valueToKey(reply.error())} while testing Internet connectivity")
return False
def _display_error(self, message, exception = None):
message=QtWidgets.QMessageBox(icon=QtWidgets.QMessageBox.Critical,parent=self,text=message)
if exception:
message.setDetailedText(str(exception))
message.show()
return message
def _load_tools(self):
request = QtNetwork.QNetworkRequest(QtCore.QUrl(f"{self._project_page}/blob/master/resources/supported_tools.json"))
reply = self._network_manager.get(request)
while reply.isRunning():
QtWidgets.QApplication.instance().processEvents()
QtCore.QThread.currentThread().msleep(100)
if not reply.error():
try:
self.tools = json.loads(reply.readAll())
except json.JSONDecodeError as e:
self._display_error(f"Unable to decode {request.url.toString()}", e)
else:
self._display_error(f"Encountered a {QtCore.QMetaEnum.valueToKey(reply.error())} error while loading supported tools")
return False