def buildUI(self): self.process = Process(self.dataReady, self.onError, self.onOutput, self.isFinished, self) self.cmdField = PlainTextEdit({ 'lwm': QPlainTextEdit.NoWrap, 'sfh': 25, 'vsbp': SCROLLBAROFF, 'adr': True }) self.textWindow = PlainTextEdit({'rol': True}, self) self.cursor = self.cmdField.textCursor() self.copySelectedTextAction = ShortCut('Copy', 'Copy', 'Shift+Ctrl+c', self.copyText, self) self.cancelAction = ShortCut('Cancel', 'Cancel', 'Ctrl+c', self.killProcess, self) self.pasteTextAction = ShortCut('Paste', 'Paste', 'Shift+Ctrl+v', self.pasteText, self) self.textWindow.addActions( [self.cancelAction, self.copySelectedTextAction]) self.cmdField.addAction(self.pasteTextAction) self.cmdField.installEventFilter(self) self.cursorEnd() sysinfo = QSysInfo() myMachine = "CPU Architecture: {0}***{1}***{2}***{3}".format( sysinfo.currentCpuArchitecture(), sysinfo.prettyProductName(), sysinfo.kernelType(), sysinfo.kernelVersion()) self.statusBar = StatusBar(self) self.statusBar.showMessage(myMachine, 0) self.layout.addWidget(self.textWindow) self.layout.addWidget(self.cmdField) self.layout.addWidget(self.statusBar)
def get_os_name(self): global q_os_version_available try: os_name = QSysInfo.prettyProductName() if q_os_version_available: ver = QOperatingSystemVersion.current() if ver.name() != '': os_name += ' {} '.format(ver.name()) if ver.segmentCount() > 2: os_name += '{}.{}.{}'.format(ver.majorVersion(), ver.minorVersion(), ver.microVersion()) elif ver.segmentCount == 2: os_name += '{}.{}'.format(ver.majorVersion(), ver.minorVersion()) elif ver.segmentCount == 1: os_name += '{}'.format(ver.majorVersion()) kernel_name = QSysInfo.kernelType() + ' ' + QSysInfo.kernelVersion( ) if os_name != kernel_name: os_name += ' ({})'.format(kernel_name) return os_name except Exception as e: return platform.platform()
def setMessage(self, exType, exValue, exTrace): """Generate a message and append session data, error info and error traceback. """ from traceback import format_tb from novelwriter import __issuesurl__, __version__ from PyQt5.Qt import PYQT_VERSION_STR from PyQt5.QtCore import QT_VERSION_STR, QSysInfo self.msgHead.setText(( "<p>An unhandled error has been encountered.</p>" "<p>Please report this error by submitting an issue report on " "GitHub, providing a description and including the error " "message and traceback shown below.</p>" "<p>URL: <a href='{issueUrl}'>{issueUrl}</a></p>" ).format( issueUrl=__issuesurl__, )) try: kernelVersion = QSysInfo.kernelVersion() except Exception: kernelVersion = "Unknown" try: import lxml lxmlVersion = lxml.__version__ except Exception: lxmlVersion = "Unknown" try: import enchant enchantVersion = enchant.__version__ except Exception: enchantVersion = "Unknown" try: exTrace = "\n".join(format_tb(exTrace)) self.msgBody.setPlainText(( "Environment:\n" f"novelWriter Version: {__version__}\n" f"Host OS: {sys.platform} ({kernelVersion})\n" f"Python: {sys.version.split()[0]} ({sys.hexversion:#x})\n" f"Qt: {QT_VERSION_STR}, PyQt: {PYQT_VERSION_STR}\n" f"lxml: {lxmlVersion}\n" f"enchant: {enchantVersion}\n\n" f"{exType.__name__}:\n{str(exValue)}\n\n" f"Traceback:\n{exTrace}\n" )) except Exception: self.msgBody.setPlainText("Failed to generate error report ...") return
def setMessage(self, exType, exValue, exTrace): """Generate a message and append session data, error info and error traceback. """ import sys from traceback import format_tb from nw import __issuesurl__, __version__ from PyQt5.Qt import PYQT_VERSION_STR from PyQt5.QtCore import QT_VERSION_STR, QSysInfo self.msgHead.setText(( "<p>An unhandled error has been encountered.</p>" "<p>Please report this error by submitting an issue report on " "GitHub, providing a description and including the error " "message and traceback shown below.</p>" "<p>URL: <a href='{issueUrl}'>{issueUrl}</a></p>" ).format( issueUrl = __issuesurl__, )) try: kernelVersion = QSysInfo.kernelVersion() except Exception: kernelVersion = "Unknown" try: self.msgBody.setPlainText(( "Environment:\n" "novelWriter Version: {nwVersion}\n" "Host OS: {osType} ({osKernel})\n" "Python: {pyVersion} ({pyHexVer:#x})\n" "Qt: {qtVers}, PyQt: {pyqtVers}\n" "\n" "{exType}:\n{exMessage}\n" "\n" "Traceback:\n{exTrace}\n" ).format( nwVersion = __version__, osType = sys.platform, osKernel = kernelVersion, pyVersion = sys.version.split()[0], pyHexVer = sys.hexversion, qtVers = QT_VERSION_STR, pyqtVers = PYQT_VERSION_STR, exType = exType.__name__, exMessage = str(exValue), exTrace = "\n".join(format_tb(exTrace)), )) except Exception: self.msgBody.setPlainText("Failed to generate error report ...") return
def get_defaultstyle() -> str: style = STYLE_COOL if platform == "win32": version_str = QSysInfo.kernelVersion() if "." in version_str: dot_index = version_str.index(".") if dot_index != -1: version_str = version_str[: dot_index + 2] version_str.replace(".", "0") version_double, ok = QLocale().toDouble(version_str) if version_double >= 602: style = STYLE_THRESHOLD elif platform == "darwin": style = STYLE_VIENNA return style
def initConfig(self, confPath=None, dataPath=None): """Initialise the config class. The manual setting of confPath and dataPath is mainly intended for the test suite. """ logger.debug("Initialising Config ...") if confPath is None: confRoot = QStandardPaths.writableLocation( QStandardPaths.ConfigLocation) self.confPath = os.path.join(os.path.abspath(confRoot), self.appHandle) else: logger.info("Setting config from alternative path: %s" % confPath) self.confPath = confPath if dataPath is None: if self.verQtValue >= 50400: dataRoot = QStandardPaths.writableLocation( QStandardPaths.AppDataLocation) else: dataRoot = QStandardPaths.writableLocation( QStandardPaths.DataLocation) self.dataPath = os.path.join(os.path.abspath(dataRoot), self.appHandle) else: logger.info("Setting data path from alternative path: %s" % dataPath) self.dataPath = dataPath logger.verbose("Config path: %s" % self.confPath) logger.verbose("Data path: %s" % self.dataPath) self.confFile = self.appHandle + ".conf" self.lastPath = os.path.expanduser("~") self.appPath = getattr(sys, "_MEIPASS", os.path.abspath(os.path.dirname(__file__))) self.appRoot = os.path.abspath( os.path.join(self.appPath, os.path.pardir)) if os.path.isfile(self.appRoot): # novelWriter is packaged as a single file, so the app and # root paths are the same, and equal to the folder that # contains the single executable. self.appRoot = os.path.dirname(self.appRoot) self.appPath = self.appRoot # Assets self.assetPath = os.path.join(self.appPath, "assets") self.themeRoot = os.path.join(self.assetPath, "themes") self.dictPath = os.path.join(self.assetPath, "dict") self.iconPath = os.path.join(self.assetPath, "icons") self.appIcon = os.path.join(self.iconPath, "novelwriter.svg") # Internationalisation self.nwLangPath = os.path.join(self.appRoot, "i18n") logger.verbose("App path: %s" % self.appPath) logger.verbose("Last path: %s" % self.lastPath) # If config folder does not exist, create it. # This assumes that the os config folder itself exists. if not os.path.isdir(self.confPath): try: os.mkdir(self.confPath) except Exception as e: logger.error("Could not create folder: %s" % self.confPath) logException() self.hasError = True self.errData.append("Could not create folder: %s" % self.confPath) self.errData.append(str(e)) self.confPath = None # Check if config file exists if self.confPath is not None: if os.path.isfile(os.path.join(self.confPath, self.confFile)): # If it exists, load it self.loadConfig() else: # If it does not exist, save a copy of the default values self.saveConfig() # If data folder does not exist, make it. # This assumes that the os data folder itself exists. if self.dataPath is not None: if not os.path.isdir(self.dataPath): try: os.mkdir(self.dataPath) except Exception as e: logger.error("Could not create folder: %s" % self.dataPath) logException() self.hasError = True self.errData.append("Could not create folder: %s" % self.dataPath) self.errData.append(str(e)) self.dataPath = None # Host and Kernel if self.verQtValue >= 50600: self.hostName = QSysInfo.machineHostName() self.kernelVer = QSysInfo.kernelVersion() # Load recent projects cache self.loadRecentCache() # Check the availability of optional packages self._checkOptionalPackages() if self.spellTool is None: self.spellTool = nwConst.SP_INTERNAL if self.spellLanguage is None: self.spellLanguage = "en" # Check if local help files exist self.helpPath = os.path.join(self.assetPath, "help", "novelWriter.qhc") self.hasHelp = os.path.isfile(self.helpPath) self.hasHelp &= os.path.isfile( os.path.join(self.assetPath, "help", "novelWriter.qch")) logger.debug("Config initialisation complete") return True
def createPanel(self): labelReportTitle = QLabel( self.translate("bugReport", "Bug Report Title: ")) self.lineEditReportTitle = QLineEdit() labelTestedEnvironment = QLabel( self.translate("bugReport", "Tested Environment: ")) self.lineEditTestedEnvironment = QLineEdit( self.translate( "bugReport", """System Platform:{}_{} Python Version:{}.{}.{}-{} PyQt Version:{} QT Version:{}""" ).format( QSysInfo.prettyProductName() if QSysInfo.prettyProductName() == "unknown" else "{}-{}".format(QSysInfo.kernelType(), QSysInfo.kernelVersion()), QSysInfo.currentCpuArchitecture(), sys.version_info.major, sys.version_info.minor, sys.version_info.micro, sys.version_info.releaselevel, PYQT_VERSION_STR, QT_VERSION_STR)) radioBtnQuickReport = QRadioButton( self.translate("bugReport", "Quick Report")) radioBtnKnowHowFix = QRadioButton( self.translate("bugReport", "Know How Fix")) radioBtnKnowHowFix.setChecked(True) radioBtnFeatureRequest = QRadioButton( self.translate("bugReport", "Feature Request")) buttonOpenHowReportBugURL = QPushButton( self.translate( "bugReport", """Click Me! Read "HOW REPORT A BUG" before report a bug.""")) self.buttonGroupBugReport = QButtonGroup() self.buttonGroupBugReport.addButton(radioBtnQuickReport) self.buttonGroupBugReport.addButton(radioBtnKnowHowFix) self.buttonGroupBugReport.addButton(radioBtnFeatureRequest) self.buttonGroupBugReport.addButton(buttonOpenHowReportBugURL) hboxRadiobutton = QHBoxLayout() hboxRadiobutton.addWidget(radioBtnKnowHowFix) hboxRadiobutton.addWidget(radioBtnQuickReport) hboxRadiobutton.addWidget(radioBtnFeatureRequest) hboxRadiobutton.addWidget(buttonOpenHowReportBugURL) hboxRadiobutton.addStretch() labelStepsToReproduce = QLabel( self.translate("bugReport", "Steps To Reproduce: ")) self.textEditStepsToReproduce = QTextEdit() labelActualresults = QLabel( self.translate("bugReport", "Actual results: ")) self.textEditActualresults = QTextEdit() self.textEditActualresults.insertPlainText( self.translate( "bugReport", "if have Python's Traceback, Please Paste.\nif is V2Ray-core JSON Editor issue, please Paste the JSON File without server information." )) self.textEditActualresults.setAcceptDrops(True) labelExpectedresults = QLabel( self.translate("bugReport", "Expected results: ")) self.textEditExpectedresults = QTextEdit() labelFeatureRequest = QLabel( self.translate("bugReport", "Feature Request: ")) self.textEditFeatureRequest = QTextEdit() labelQuickReport = QLabel(self.translate("bugReport", "Quick Report: ")) self.textEditQuickReport = QTextEdit() labelHowFix = QLabel(self.translate("bugReport", "How Fix: ")) self.textEditHowFix = QTextEdit() gridBoxReport = QGridLayout() gridBoxReport.addWidget(labelReportTitle, 0, 0) gridBoxReport.addWidget(self.lineEditReportTitle, 0, 1) gridBoxReport.addWidget(labelTestedEnvironment, 1, 0) gridBoxReport.addWidget(self.lineEditTestedEnvironment, 1, 1) gridBoxReport.addLayout(hboxRadiobutton, 2, 0, 1, 2) gridBoxQuickReport = QGridLayout() gridBoxQuickReport.addWidget(labelQuickReport, 0, 0) gridBoxQuickReport.addWidget(self.textEditQuickReport, 0, 1) self.groupBoxQuickReport = QGroupBox("", self) self.groupBoxQuickReport.setLayout(gridBoxQuickReport) self.groupBoxQuickReport.hide() gridBoxKnowHowFix = QGridLayout() gridBoxKnowHowFix.addWidget(labelStepsToReproduce, 0, 0) gridBoxKnowHowFix.addWidget(self.textEditStepsToReproduce, 0, 1) gridBoxKnowHowFix.addWidget(labelActualresults, 1, 0) gridBoxKnowHowFix.addWidget(self.textEditActualresults, 1, 1) self.buttonInsertPiture = QPushButton( self.translate("bugReport", "Insert Picture From URL:")) self.lineEditInserPiture = QLineEdit() gridBoxKnowHowFix.addWidget(self.lineEditInserPiture, 2, 1) gridBoxKnowHowFix.addWidget(self.buttonInsertPiture, 2, 0) gridBoxKnowHowFix.addItem(QSpacerItem(50, 50), 3, 0, 1, 4) gridBoxKnowHowFix.addWidget(labelExpectedresults, 4, 0) gridBoxKnowHowFix.addWidget(self.textEditExpectedresults, 4, 1) gridBoxKnowHowFix.addWidget(labelHowFix, 5, 0) gridBoxKnowHowFix.addWidget(self.textEditHowFix, 5, 1) self.groupBoxKnowHowFix = QGroupBox() self.groupBoxKnowHowFix.setLayout(gridBoxKnowHowFix) gridBoxFeatureRequest = QGridLayout() gridBoxFeatureRequest.addWidget(labelFeatureRequest, 0, 0) gridBoxFeatureRequest.addWidget(self.textEditFeatureRequest, 0, 1) self.groupBoxFeatureRequest = QGroupBox("", self) self.groupBoxFeatureRequest.setLayout(gridBoxFeatureRequest) self.groupBoxFeatureRequest.hide() hboxButton = QHBoxLayout() self.buttonExportBugReportText = QPushButton( self.translate("bugReport", "Export Bug Report Text")) self.buttonExitButReport = QPushButton( self.translate("bugReport", "Exit")) hboxButton.addStretch() hboxButton.addWidget(self.buttonExportBugReportText) hboxButton.addWidget(self.buttonExitButReport) vboxBugReport = QVBoxLayout(self) vboxBugReport.addLayout(gridBoxReport) vboxBugReport.addWidget(self.groupBoxQuickReport) vboxBugReport.addWidget(self.groupBoxKnowHowFix) vboxBugReport.addWidget(self.groupBoxFeatureRequest) vboxBugReport.addLayout(hboxButton) vboxBugReport.addStretch() self.settextEidtReadonly(result=True) self.createSignals()
def createStatusBar(self): sysinfo = QSysInfo() myMachine = "current CPU Architecture: " + sysinfo.currentCpuArchitecture( ) + " *** " + sysinfo.prettyProductName( ) + " *** " + sysinfo.kernelType() + " " + sysinfo.kernelVersion() self.statusBar().showMessage(myMachine, 0)
def __init__(self): # Set Application Variables self.appName = "novelWriter" self.appHandle = self.appName.lower() # Debug Settings self.showGUI = True # Allow blocking the GUI (disabled for testing) self.debugInfo = False # True if log level is DEBUG or VERBOSE # Config Error Handling self.hasError = False # True if the config class encountered an error self.errData = [] # List of error messages # Set Paths self.cmdOpen = None # Path from command line for project to be opened on launch self.confPath = None # Folder where the config is saved self.confFile = None # The config file name self.dataPath = None # Folder where app data is stored self.lastPath = None # The last user-selected folder (browse dialogs) self.appPath = None # The full path to the novelwriter package folder self.appRoot = None # The full path to the novelwriter root folder self.appIcon = None # The full path to the novelwriter icon file self.assetPath = None # The full path to the nw/assets folder self.themeRoot = None # The full path to the nw/assets/themes folder self.dictPath = None # The full path to the nw/assets/dict folder self.iconPath = None # The full path to the nw/assets/icons folder self.helpPath = None # The full path to the novelwriter .qhc help file # Runtime Settings and Variables self.confChanged = False # True whenever the config has chenged, false after save self.hasHelp = False # True if the Qt help files are present in the assets folder ## General self.guiTheme = "default" self.guiSyntax = "default_light" self.guiIcons = "typicons_colour_light" self.guiDark = False # Load icons for dark backgrounds, if available self.guiLang = "en" # Hardcoded for now since the GUI is only in English self.guiFont = "" # Defaults to system defualt font self.guiFontSize = 11 self.guiScale = 1.0 # Set automatically by Theme class ## Sizes self.winGeometry = [1100, 650] self.treeColWidth = [120, 30, 50] self.projColWidth = [140, 55, 140] self.mainPanePos = [300, 800] self.docPanePos = [400, 400] self.viewPanePos = [500, 150] self.outlnPanePos = [500, 150] self.isFullScreen = False ## Features self.hideVScroll = False # Hide vertical scroll bars on main widgets self.hideHScroll = False # Hide horizontal scroll bars on main widgets ## Project self.autoSaveProj = 60 # Interval for auto-saving project in seconds self.autoSaveDoc = 30 # Interval for auto-saving document in seconds ## Text Editor self.textFont = None # Editor font self.textSize = 12 # Editor font size self.textFixedW = True # Keep editor text fixed width self.textWidth = 600 # Editor text width self.textMargin = 40 # Editor/viewer text margin self.tabWidth = 40 # Editor tabulator width self.focusWidth = 800 # Focus Mode text width self.hideFocusFooter = False # Hide document footer in Focus Mode self.showFullPath = True # Show full document path in editor header self.autoSelect = True # Auto-select word when applying format with no selection self.doJustify = False # Justify text self.showTabsNSpaces = False # Show tabs and spaces in edior self.showLineEndings = False # Show line endings in editor self.doReplace = True # Enable auto-replace as you type self.doReplaceSQuote = True # Smart single quotes self.doReplaceDQuote = True # Smart double quotes self.doReplaceDash = True # Replace multiple hyphens with dashes self.doReplaceDots = True # Replace three dots with ellipsis self.scrollPastEnd = True # Allow scrolling past end of document self.autoScroll = False # Typewriter-like scrolling self.autoScrollPos = 30 # Start point for typewriter-like scrolling self.wordCountTimer = 5.0 # Interval for word count update in seconds self.bigDocLimit = 800 # Size threshold for heavy editor features in kilobytes self.highlightQuotes = True # Highlight text in quotes self.highlightEmph = True # Add colour to text emphasis ## User-Selected Symbols self.fmtApostrophe = nwUnicode.U_RSQUO self.fmtSingleQuotes = [nwUnicode.U_LSQUO, nwUnicode.U_RSQUO] self.fmtDoubleQuotes = [nwUnicode.U_LDQUO, nwUnicode.U_RDQUO] ## Spell Checking self.spellTool = None self.spellLanguage = None ## Search Bar Switches self.searchCase = False self.searchWord = False self.searchRegEx = False self.searchLoop = False self.searchNextFile = False self.searchMatchCap = False ## Backup self.backupPath = "" self.backupOnClose = False self.askBeforeBackup = True ## State self.showRefPanel = True self.viewComments = True self.viewSynopsis = True # Check Qt5 Versions verQt = splitVersionNumber(QT_VERSION_STR) self.verQtString = QT_VERSION_STR self.verQtMajor = verQt[0] self.verQtMinor = verQt[1] self.verQtPatch = verQt[2] self.verQtValue = verQt[3] verQt = splitVersionNumber(PYQT_VERSION_STR) self.verPyQtString = PYQT_VERSION_STR self.verPyQtMajor = verQt[0] self.verPyQtMinor = verQt[1] self.verPyQtPatch = verQt[2] self.verPyQtValue = verQt[3] # Check Python Version self.verPyString = sys.version.split()[0] self.verPyMajor = sys.version_info[0] self.verPyMinor = sys.version_info[1] self.verPyPatch = sys.version_info[2] self.verPyHexVal = sys.hexversion # Check OS Type self.osType = sys.platform self.osLinux = False self.osWindows = False self.osDarwin = False self.osUnknown = False if self.osType.startswith("linux"): self.osLinux = True elif self.osType.startswith("darwin"): self.osDarwin = True elif self.osType.startswith("win32"): self.osWindows = True elif self.osType.startswith("cygwin"): self.osWindows = True else: self.osUnknown = True # Other System Info if self.verQtValue >= 50600: self.hostName = QSysInfo.machineHostName() self.kernelVer = QSysInfo.kernelVersion() else: self.hostName = "Unknown" self.kernelVer = "Unknown" # Packages self.hasEnchant = False # The pyenchant package self.hasAssistant = False # The Qt Assistant executable # Recent Cache self.recentProj = {} return