Skip to content

Commit 90e2ef9

Browse files
Allow GUI to only mount as a drive letter on Windows (#450)
* Mount as drive letter in GUI * Remove browse button on Windows * Cleanup code * Use Qt Creator UI for button to select mount or drive letter
1 parent cb5ce49 commit 90e2ef9

File tree

2 files changed

+47
-11
lines changed

2 files changed

+47
-11
lines changed

gui/mountPrimaryWindow.py

+46-7
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
from sys import platform
2626
import os
2727
from shutil import which
28+
import ctypes
29+
import string
2830

2931
# Import QT libraries
3032
from PySide6.QtCore import Qt, QSettings
@@ -69,13 +71,18 @@ def __init__(self):
6971
# https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#file-and-directory-names
7072
# Disallow the following [<,>,.,",|,?,*] - note, we still need directory characters to declare a path
7173
self.lineEdit_mountPoint.setValidator(QtGui.QRegularExpressionValidator(r'^[^<>."|?\0*]*$',self))
74+
self.button_browse.setText('Drive Letter')
75+
self.button_browse.setToolTip('Select an unused drive letter for mounting')
76+
self.button_browse.clicked.connect(self.chooseDriveLetter)
7277
else:
7378
# Allow anything BUT Nul
7479
# Note: Different versions of Python don't like the embedded null character, send in the raw string instead
7580
self.lineEdit_mountPoint.setValidator(QtGui.QRegularExpressionValidator(r'^[^\0]*$',self))
81+
self.button_browse.setText('Browse')
82+
self.button_browse.setToolTip('Browse to a pre-existing directory to mount')
83+
self.button_browse.clicked.connect(self.getFileDirInput)
7684

7785
# Set up the signals for all the interactive entities
78-
self.button_browse.clicked.connect(self.getFileDirInput)
7986
self.button_config.clicked.connect(self.showSettingsWidget)
8087
self.button_mount.clicked.connect(self.mountBucket)
8188
self.button_unmount.clicked.connect(self.unmountBucket)
@@ -84,11 +91,9 @@ def __init__(self):
8491
self.lineEdit_mountPoint.editingFinished.connect(self.updateMountPointInSettings)
8592
self.dropDown_bucketSelect.currentIndexChanged.connect(self.modifyPipeline)
8693
if platform == 'win32':
87-
self.lineEdit_mountPoint.setToolTip('Designate a new location to mount the bucket, do not create the directory')
88-
self.button_browse.setToolTip("Browse to a new location but don't create a new directory")
94+
self.lineEdit_mountPoint.setToolTip('Designate a drive letter to mount to')
8995
else:
9096
self.lineEdit_mountPoint.setToolTip('Designate a location to mount the bucket - the directory must already exist')
91-
self.button_browse.setToolTip('Browse to a pre-existing directory')
9297

9398
def checkConfigDirectory(self):
9499
workingDir = self.getWorkingDir()
@@ -162,12 +167,16 @@ def mountBucket(self):
162167
except ValueError as e:
163168
self.addOutputText(f"Invalid mount path: {str(e)}")
164169
return
165-
directory = os.path.join(directory, mountDirSuffix)
166170
# get config path
167171
configPath = os.path.join(self.getWorkingDir(), 'config.yaml')
168172

169173
# on Windows, the mount directory should not exist (yet)
170174
if platform == 'win32':
175+
drive, tail = os.path.splitdrive(directory)
176+
# Only append the cloudfuse suffix if not mounting to a drive letter
177+
if not(drive and (tail == '' or tail in ['\\', '/'])):
178+
directory = os.path.join(directory, mountDirSuffix)
179+
171180
if os.path.exists(directory):
172181
self.addOutputText(f"Directory {directory} already exists! Aborting new mount.")
173182
self.errorMessageBox(f"Error: Cloudfuse needs to create the directory {directory}, but it already exists!")
@@ -226,7 +235,10 @@ def unmountBucket(self):
226235
directory = str(self.lineEdit_mountPoint.text())
227236
commandParts = []
228237
# TODO: properly handle unmount. This is relying on the line_edit not being changed by the user.
229-
directory = os.path.join(directory, mountDirSuffix)
238+
drive, tail = os.path.splitdrive(directory)
239+
# Only append the cloudfuse suffix if not mounting to a drive letter
240+
if not(drive and (tail == '' or tail in ['\\', '/'])):
241+
directory = os.path.join(directory, mountDirSuffix)
230242
commandParts = [cloudfuseCli, 'unmount', directory]
231243
if platform != 'win32':
232244
commandParts.append('--lazy')
@@ -267,7 +279,7 @@ def runCommand(self, commandParts):
267279
# run command
268280
try:
269281
process = subprocess.run(
270-
commandParts,
282+
commandParts,
271283
capture_output=True,
272284
creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)
273285
stdOut = process.stdout.decode().strip()
@@ -291,3 +303,30 @@ def errorMessageBox(self, messageString, titleString='Error'):
291303
msg.setText(messageString)
292304
# Show the message box
293305
msg.exec()
306+
307+
def chooseDriveLetter(self):
308+
unused_letters = get_unused_driver_letters()
309+
if not unused_letters:
310+
QtWidgets.QMessageBox.warning(self, 'No Drive Letters', 'No unused drive letters available.')
311+
return
312+
313+
drive, ok = QtWidgets.QInputDialog.getItem(
314+
self,
315+
'Select Drive Letter',
316+
'Available drive letters:',
317+
unused_letters,
318+
0,
319+
False
320+
)
321+
if ok and drive:
322+
self.lineEdit_mountPoint.setText(f"{drive}")
323+
self.updateMountPointInSettings()
324+
325+
def get_unused_driver_letters():
326+
bitmask = ctypes.windll.kernel32.GetLogicalDrives()
327+
unused = []
328+
for letter in string.ascii_uppercase:
329+
if not ((bitmask & 1) or (letter == 'A' or letter == 'B')):
330+
unused.append(letter + ':')
331+
bitmask >>=1
332+
return unused

gui/mountPrimaryWindow.ui

+1-4
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,12 @@
5252
<item>
5353
<widget class="QLineEdit" name="lineEdit_mountPoint">
5454
<property name="toolTip">
55-
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Designate a location to mount the bucket - the directory must exist on Linux, but not on Windows&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
55+
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Designate a location to mount the bucket&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
5656
</property>
5757
</widget>
5858
</item>
5959
<item>
6060
<widget class="QPushButton" name="button_browse">
61-
<property name="toolTip">
62-
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Choose a location to mount the bucket - the directory must exist on Linux, but not on Windows&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
63-
</property>
6461
<property name="text">
6562
<string>Browse</string>
6663
</property>

0 commit comments

Comments
 (0)