Skip to content

Commit 615e675

Browse files
committed
[BUG] Progress bar works when reading USDZ files
[BUG] Find case sensitivity preference saves properly [BUG] Do not prompt to save when opening a new file [BUG] Windows support for usdcat and unzipping USDZ [ENH] Add line limit user preference for loading large files [ENH] Preferences and defaults maintenance, Advanced tab added to Preferences Version up to 0.7.0 Preferences and defaults no longer maintained in two separate places. Changing preferences no longer force reloads your current tab. Line limit increased from 10,000 to 50,000, with a user preference added to override this. Switch to zipfile module instead of unzip command for reading USDZ Signed-off-by: mds-dwa <[email protected]>
1 parent c81edfe commit 615e675

File tree

6 files changed

+263
-120
lines changed

6 files changed

+263
-120
lines changed

usdmanager/__init__.py

Lines changed: 80 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,6 @@ class UsdMngrWindow(QtWidgets.QMainWindow):
163163
- AddressBar file completer has problems occasionally.
164164
- Figure out why network printers aren't showing up. Linux or DWA
165165
issue? macOS and Windows are fine.
166-
- When reading in a USDZ file, the progress bar gets stuck.
167166
- Qt.py problems:
168167
169168
- PyQt5
@@ -208,7 +207,7 @@ def __init__(self, parent=None, **kwargs):
208207
# externally. The user's preferred programs are stored in
209208
# self.programs.
210209
self.defaultPrograms = {x: "" for x in USD_EXTS}
211-
self.defaultPrograms.update(self.app.appConfig.get("defaultPrograms", {}))
210+
self.defaultPrograms.update(self.app.DEFAULTS['defaultPrograms'])
212211
self.programs = self.defaultPrograms
213212
self.masterHighlighters = {}
214213

@@ -247,12 +246,14 @@ def setupUi(self):
247246
self.baseInstance = utils.loadUiWidget('main_window.ui', self)
248247

249248
# You now have access to the widgets defined in the ui file.
250-
self.defaultDocFont = QtGui.QFont()
251-
self.defaultDocFont.setStyleHint(QtGui.QFont.Courier)
252-
self.defaultDocFont.setFamily("Monospace")
253-
self.defaultDocFont.setPointSize(9)
254-
self.defaultDocFont.setBold(False)
255-
self.defaultDocFont.setItalic(False)
249+
# Update some app defaults that required the GUI to be created first.
250+
defaultDocFont = QtGui.QFont()
251+
defaultDocFont.setStyleHint(QtGui.QFont.Courier)
252+
defaultDocFont.setFamily("Monospace")
253+
defaultDocFont.setPointSize(9)
254+
defaultDocFont.setBold(False)
255+
defaultDocFont.setItalic(False)
256+
self.app.DEFAULTS['font'] = defaultDocFont
256257

257258
self.readSettings()
258259
self.compileLinkRegEx()
@@ -288,13 +289,13 @@ def setupUi(self):
288289
</style></head><body style="white-space:pre">{}</body></html>"""
289290

290291
searchPaths = QtGui.QIcon.themeSearchPaths()
291-
extraSearchPaths = [x for x in self.app.appConfig.get("themeSearchPaths", []) if x not in searchPaths]
292+
extraSearchPaths = [x for x in self.app.DEFAULTS['themeSearchPaths'] if x not in searchPaths]
292293
if extraSearchPaths:
293294
searchPaths = extraSearchPaths + searchPaths
294295
QtGui.QIcon.setThemeSearchPaths(searchPaths)
295296

296297
# Set the preferred theme name for some non-standard icons.
297-
QtGui.QIcon.setThemeName(self.app.appConfig.get("iconTheme", "crystal_project"))
298+
QtGui.QIcon.setThemeName(self.app.DEFAULTS['iconTheme'])
298299

299300
# Try to adhere to the freedesktop icon standards (https://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html).
300301
# Some icons are preferred from the crystal_project set, which sadly follows different naming standards.
@@ -633,27 +634,27 @@ def readSettings(self):
633634
""" Read in user config settings.
634635
"""
635636
logger.debug("Reading user settings from {}".format(self.config.fileName()))
636-
# Get basic preferences.
637-
# TODO: Read some of these from the same places as the preferences dialog so we don't have to maintain defaults in 2 places.
637+
default = self.app.DEFAULTS
638638
self.preferences = {
639-
'parseLinks': self.config.boolValue("parseLinks", True),
640-
'newTab': self.config.boolValue("newTab", False),
641-
'syntaxHighlighting': self.config.boolValue("syntaxHighlighting", True),
642-
'teletype': self.config.boolValue("teletype", True),
643-
'lineNumbers': self.config.boolValue("lineNumbers", True),
644-
'showAllMessages': self.config.boolValue("showAllMessages", True),
645-
'showHiddenFiles': self.config.boolValue("showHiddenFiles", False),
646-
'font': self.config.value("font", self.defaultDocFont),
647-
'fontSizeAdjust': int(self.config.value("fontSizeAdjust", 0)),
648-
'findMatchCase': self.config.boolValue("findMatchCase", self.checkBoxMatchCase.isChecked()),
649-
'includeVisible': self.config.boolValue("includeVisible", self.actionIncludePanel.isChecked()),
650-
'lastOpenWithStr': self.config.value("lastOpenWithStr", ""),
651-
'textEditor': self.config.value("textEditor", os.getenv("EDITOR", self.app.appConfig.get("textEditor", "nedit"))),
652-
'diffTool': self.config.value("diffTool", self.app.appConfig.get("diffTool", "xdiff")),
653-
'autoCompleteAddressBar': self.config.boolValue("autoCompleteAddressBar", True),
654-
'useSpaces': self.config.boolValue("useSpaces", True),
655-
'tabSpaces': int(self.config.value("tabSpaces", 4)),
656-
'theme': self.config.value("theme", None),
639+
'parseLinks': self.config.boolValue("parseLinks", default['parseLinks']),
640+
'newTab': self.config.boolValue("newTab", default['newTab']),
641+
'syntaxHighlighting': self.config.boolValue("syntaxHighlighting", default['syntaxHighlighting']),
642+
'teletype': self.config.boolValue("teletype", default['teletype']),
643+
'lineNumbers': self.config.boolValue("lineNumbers", default['lineNumbers']),
644+
'showAllMessages': self.config.boolValue("showAllMessages", default['showAllMessages']),
645+
'showHiddenFiles': self.config.boolValue("showHiddenFiles", default['showHiddenFiles']),
646+
'font': self.config.value("font", default['font']),
647+
'fontSizeAdjust': int(self.config.value("fontSizeAdjust", default['fontSizeAdjust'])),
648+
'findMatchCase': self.config.boolValue("findMatchCase", default['findMatchCase']),
649+
'includeVisible': self.config.boolValue("includeVisible", default['includeVisible']),
650+
'lastOpenWithStr': self.config.value("lastOpenWithStr", default['lastOpenWithStr']),
651+
'textEditor': self.config.value("textEditor", default['textEditor']),
652+
'diffTool': self.config.value("diffTool", default['diffTool']),
653+
'autoCompleteAddressBar': self.config.boolValue("autoCompleteAddressBar", default['autoCompleteAddressBar']),
654+
'useSpaces': self.config.boolValue("useSpaces", default['useSpaces']),
655+
'tabSpaces': int(self.config.value("tabSpaces", default['tabSpaces'])),
656+
'theme': self.config.value("theme", default['theme']),
657+
'lineLimit': int(self.config.value("lineLimit", default['lineLimit'])),
657658
}
658659

659660
# Read 'programs' settings object into self.programs.
@@ -736,6 +737,7 @@ def writeSettings(self):
736737
self.config.setValue("useSpaces", self.preferences['useSpaces'])
737738
self.config.setValue("tabSpaces", self.preferences['tabSpaces'])
738739
self.config.setValue("theme", self.preferences['theme'])
740+
self.config.setValue("lineLimit", self.preferences['lineLimit'])
739741

740742
# Write self.programs to settings object
741743
exts = self.programs.keys()
@@ -1918,11 +1920,14 @@ def editPreferences(self):
19181920
dlg = PreferencesDialog(self)
19191921
# Open dialog.
19201922
if dlg.exec_() == dlg.Accepted:
1921-
# Save new preferences.
1923+
# Users currently have to refresh to see these changes.
19221924
self.preferences['parseLinks'] = dlg.getPrefParseLinks()
1923-
self.preferences['newTab'] = dlg.getPrefNewTab()
19241925
self.preferences['syntaxHighlighting'] = dlg.getPrefSyntaxHighlighting()
19251926
self.preferences['teletype'] = dlg.getPrefTeletypeConversion()
1927+
self.preferences['theme'] = dlg.getPrefTheme()
1928+
1929+
# These changes do not require the user to refresh any tabs to see the change.
1930+
self.preferences['newTab'] = dlg.getPrefNewTab()
19261931
self.preferences['lineNumbers'] = dlg.getPrefLineNumbers()
19271932
self.preferences['showAllMessages'] = dlg.getPrefShowAllMessages()
19281933
self.preferences['showHiddenFiles'] = dlg.getPrefShowHiddenFiles()
@@ -1932,7 +1937,7 @@ def editPreferences(self):
19321937
self.preferences['font'] = dlg.getPrefFont()
19331938
self.preferences['useSpaces'] = dlg.getPrefUseSpaces()
19341939
self.preferences['tabSpaces'] = dlg.getPrefTabSpaces()
1935-
self.preferences['theme'] = dlg.getPrefTheme()
1940+
self.preferences['lineLimit'] = dlg.getPrefLineLimit()
19361941

19371942
# Update font and line number visibility in all tabs.
19381943
self.tabWidget.setFont(self.preferences['font'])
@@ -1964,9 +1969,6 @@ def editPreferences(self):
19641969
else:
19651970
self.addressBar.setCompleter(QtWidgets.QCompleter())
19661971

1967-
if not self.currTab.isDirty():
1968-
self.refreshTab()
1969-
19701972
self.writeSettings()
19711973

19721974
@Slot(int)
@@ -1977,7 +1979,7 @@ def updatePreference_findMatchCase(self, checked):
19771979
checked : `int`
19781980
State of checkbox.
19791981
"""
1980-
checked = checked & QtCore.Qt.Checked
1982+
checked = checked == QtCore.Qt.Checked
19811983
if checked != self.preferences['findMatchCase']:
19821984
self.preferences['findMatchCase'] = checked
19831985
for lang, h in self.masterHighlighters.iteritems():
@@ -2185,7 +2187,7 @@ def restoreTab(self, tab):
21852187
self.menuRecentlyClosedTabs.setEnabled(False)
21862188

21872189
# Update settings in the recently re-opened tab that may have changed.
2188-
if self.preferences['font'] != self.defaultDocFont:
2190+
if self.preferences['font'] != self.app.DEFAULTS['font']:
21892191
tab.textBrowser.setFont(self.preferences['font'])
21902192
tab.textEditor.setFont(self.preferences['font'])
21912193
tab.lineNumbers.setVisible(self.preferences['lineNumbers'])
@@ -2348,7 +2350,7 @@ def launchTextEditor(self):
23482350
def launchUsdView(self):
23492351
""" Launch the current file in usdview.
23502352
"""
2351-
app = self.app.appConfig.get("usdview", "usdview")
2353+
app = self.app.DEFAULTS['usdview']
23522354
path = self.currTab.getCurrentPath()
23532355
# Files with spaces have to be double-quoted on Windows for usdview.
23542356
if os.name == "nt":
@@ -2557,9 +2559,9 @@ def setSource(self, link, isNewFile=True, newTab=False, hScrollPos=0, vScrollPos
25572559
vScrollPos : `int`
25582560
Vertical scroll bar position.
25592561
"""
2560-
# Check if the current tab is dirty before doing anything.
2562+
# If we're staying in the current tab, check if the tab is dirty before doing anything.
25612563
# Perform save operations if necessary.
2562-
if not self.dirtySave():
2564+
if not newTab and not self.dirtySave():
25632565
return True
25642566

25652567
# Re-cast the QUrl so any query strings are evaluated properly.
@@ -2681,6 +2683,8 @@ def setSource(self, link, isNewFile=True, newTab=False, hScrollPos=0, vScrollPos
26812683
layer = utils.queryItemValue(link, "layer")
26822684
dest = utils.unzip(fileStr, layer, self.app.tmpDir)
26832685
self.restoreOverrideCursor()
2686+
self.statusbar.removeWidget(loadingProgressBar)
2687+
self.statusbar.removeWidget(loadingProgressLabel)
26842688
return self.setSource(QtCore.QUrl(dest))
26852689
else:
26862690
if ext == "usda":
@@ -2709,11 +2713,13 @@ def setSource(self, link, isNewFile=True, newTab=False, hScrollPos=0, vScrollPos
27092713

27102714
# TODO: Figure out a better way to handle streaming text for large files like Crate geometry.
27112715
# Large chunks of text (e.g. 2.2 billion characters) will cause Qt to segfault when creating a QString.
2712-
if length > LINE_LIMIT:
2713-
length = LINE_LIMIT
2716+
lineLimit = self.preferences['lineLimit']
2717+
if length > lineLimit:
2718+
length = lineLimit
27142719
truncated = True
27152720
fileText = fileText[:length]
2716-
warning = "Extremely large file! Capping display at {:,d} lines.".format(LINE_LIMIT)
2721+
warning = "Extremely large file! Capping display at {:,d} lines. You can edit this cap in the "\
2722+
"Advanced tab of Preferences.".format(lineLimit)
27172723

27182724
loadingProgressBar.setMaximum(length - 1)
27192725
if self.stopLoadingTab:
@@ -4274,13 +4280,40 @@ def run(self):
42744280
try:
42754281
logger.info("Loading app config from {}".format(appConfigPath))
42764282
with open(appConfigPath) as f:
4277-
self.appConfig = json.load(f)
4283+
appConfig = json.load(f)
42784284
except Exception as e:
42794285
logger.error("Failed to load app config from {}: {}".format(appConfigPath, e))
4280-
self.appConfig = {}
4286+
appConfig = {}
4287+
4288+
# Define app defaults that we use when the user preference doesn't exist and when resetting preferences in the
4289+
# Preferences dialog.
4290+
self.DEFAULTS = {
4291+
'autoCompleteAddressBar': True,
4292+
'defaultPrograms': appConfig.get("defaultPrograms", {}),
4293+
'diffTool': appConfig.get("diffTool", "xdiff"),
4294+
'findMatchCase': False,
4295+
'fontSizeAdjust': 0,
4296+
'iconTheme': appConfig.get("iconTheme", "crystal_project"),
4297+
'includeVisible': True,
4298+
'lastOpenWithStr': "",
4299+
'lineLimit': LINE_LIMIT,
4300+
'lineNumbers': True,
4301+
'newTab': False,
4302+
'parseLinks': True,
4303+
'showAllMessages': True,
4304+
'showHiddenFiles': False,
4305+
'syntaxHighlighting': True,
4306+
'tabSpaces': 4,
4307+
'teletype': True,
4308+
'textEditor': os.getenv("EDITOR", appConfig.get("textEditor", "nedit")),
4309+
'theme': None,
4310+
'themeSearchPaths': appConfig.get("themeSearchPaths", []),
4311+
'usdview': appConfig.get("usdview", "usdview"),
4312+
'useSpaces': True,
4313+
}
42814314

42824315
# Documentation URL.
4283-
self.appURL = self.appConfig.get("appURL", "https://github.com/dreamworksanimation/usdmanager")
4316+
self.appURL = appConfig.get("appURL", "https://github.com/dreamworksanimation/usdmanager")
42844317

42854318
# Create a main window.
42864319
window = self.newWindow()

usdmanager/constants.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@
5252

5353
# Truncate loading files with more lines than this.
5454
# Display can slow down and/or become unusable with too many lines.
55-
LINE_LIMIT = 10000
55+
# This number is less important than the total number of characters and can be overridden in Preferences.
56+
LINE_LIMIT = 50000
5657

5758
# Truncate loading files with more total chars than this.
5859
# QString crashes at ~2.1 billion chars, but display slows down way before that.

usdmanager/preferences_dialog.py

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from Qt.QtGui import QIcon, QRegExpValidator
2020
from Qt.QtWidgets import (QAbstractButton, QDialog, QDialogButtonBox, QFontDialog, QLineEdit, QMessageBox, QVBoxLayout)
2121

22+
from .constants import LINE_LIMIT
2223
from .utils import loadUiWidget
2324

2425

@@ -77,6 +78,7 @@ def setupUi(self, widget):
7778
self.lineEditTextEditor.setText(parent.preferences['textEditor'])
7879
self.lineEditDiffTool.setText(parent.preferences['diffTool'])
7980
self.themeWidget.setChecked(parent.preferences['theme'] == "dark")
81+
self.lineLimitSpinBox.setValue(parent.preferences['lineLimit'])
8082
self.updateFontLabel()
8183

8284
# ----- Programs tab -----
@@ -189,6 +191,15 @@ def getPrefAutoCompleteAddressBar(self):
189191
"""
190192
return self.checkBox_autoCompleteAddressBar.isChecked()
191193

194+
def getPrefLineLimit(self):
195+
"""
196+
:Returns:
197+
Number of lines to display before truncating a file.
198+
:Rtype:
199+
`int`
200+
"""
201+
return self.lineLimitSpinBox.value()
202+
192203
def getPrefSyntaxHighlighting(self):
193204
"""
194205
:Returns:
@@ -319,22 +330,23 @@ def restoreDefaults(self, btn):
319330
self.deleteItems(self.extLayout)
320331

321332
# Set other preferences in the GUI.
322-
window = self.parent().window()
323-
self.checkBox_parseLinks.setChecked(True)
324-
self.checkBox_newTab.setChecked(False)
325-
self.checkBox_syntaxHighlighting.setChecked(True)
326-
self.checkBox_teletypeConversion.setChecked(True)
327-
self.checkBox_lineNumbers.setChecked(True)
328-
self.checkBox_showAllMessages.setChecked(True)
329-
self.checkBox_showHiddenFiles.setChecked(False)
330-
self.checkBox_autoCompleteAddressBar.setChecked(True)
331-
self.lineEditTextEditor.setText(os.getenv("EDITOR", window.app.appConfig.get("textEditor", "nedit")))
332-
self.lineEditDiffTool.setText(window.app.appConfig.get("diffTool", "xdiff"))
333-
self.useSpacesCheckBox.setChecked(True)
334-
self.useSpacesSpinBox.setValue(4)
333+
default = self.parent().window().app.DEFAULTS
334+
self.checkBox_parseLinks.setChecked(default['parseLinks'])
335+
self.checkBox_newTab.setChecked(default['newTab'])
336+
self.checkBox_syntaxHighlighting.setChecked(default['syntaxHighlighting'])
337+
self.checkBox_teletypeConversion.setChecked(default['teletype'])
338+
self.checkBox_lineNumbers.setChecked(default['lineNumbers'])
339+
self.checkBox_showAllMessages.setChecked(default['showAllMessages'])
340+
self.checkBox_showHiddenFiles.setChecked(default['showHiddenFiles'])
341+
self.checkBox_autoCompleteAddressBar.setChecked(default['autoCompleteAddressBar'])
342+
self.lineEditTextEditor.setText(default['textEditor'])
343+
self.lineEditDiffTool.setText(default['diffTool'])
344+
self.useSpacesCheckBox.setChecked(default['useSpaces'])
345+
self.useSpacesSpinBox.setValue(default['tabSpaces'])
335346
self.themeWidget.setChecked(False)
336-
self.docFont = window.defaultDocFont
347+
self.docFont = default['font']
337348
self.updateFontLabel()
349+
self.lineLimitSpinBox.setValue(default['lineLimit'])
338350

339351
# Re-create file association fields with the default programs.
340352
self.populateProgsAndExts(self.parent().defaultPrograms)

0 commit comments

Comments
 (0)