1#!/usr/bin/env python 2 3############################################################################# 4## 5## Copyright (C) 2017 Hans-Peter Jansen <hpj@urpla.net> 6## Copyright (C) 2016 The Qt Company Ltd. 7## Copyright (C) 2016 Ivan Komissarov 8## 9## This file is part of the examples of the Qt Toolkit. 10## 11## $QT_BEGIN_LICENSE:BSD$ 12## Commercial License Usage 13## Licensees holding valid commercial Qt licenses may use this file in 14## accordance with the commercial license agreement provided with the 15## Software or, alternatively, in accordance with the terms contained in 16## a written agreement between you and The Qt Company. For licensing terms 17## and conditions see https:#www.qt.io/terms-conditions. For further 18## information use the contact form at https:#www.qt.io/contact-us. 19## 20## BSD License Usage 21## Alternatively, you may use self file under the terms of the BSD license 22## as follows: 23## 24## "Redistribution and use in source and binary forms, with or without 25## modification, are permitted provided that the following conditions are 26## met: 27## * Redistributions of source code must retain the above copyright 28## notice, self list of conditions and the following disclaimer. 29## * Redistributions in binary form must reproduce the above copyright 30## notice, self list of conditions and the following disclaimer in 31## the documentation and/or other materials provided with the 32## distribution. 33## * Neither the name of The Qt Company Ltd nor the names of its 34## contributors may be used to endorse or promote products derived 35## from self software without specific prior written permission. 36## 37## 38## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 39## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 40## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 41## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 42## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 43## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 44## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 45## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 46## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 47## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 48## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 49## 50## $QT_END_LICENSE$ 51## 52############################################################################# 53 54 55import math 56 57from PyQt5.QtCore import QAbstractTableModel, QByteArray, QDir, QStorageInfo, Qt 58from PyQt5.QtWidgets import QAbstractItemView, QApplication, QTreeView 59 60 61def sizeToString(size): 62 if size <= 0: 63 return "0 b" 64 decimals = 2 65 units = ["b", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] 66 power = int(math.log(size, 1024)) 67 try: 68 unit = units[power] 69 except IndexError: 70 unit = units[-1] 71 power = len(units) - 1 72 if power == 0: 73 decimals = 0 74 normsize = size / math.pow(1024, power) 75 #: this should expand to "1.23 GB" 76 return "%0.*f %s" % (decimals, normsize, unit) 77 78 79class StorageModel(QAbstractTableModel): 80 ColumnRootPath, ColumnName, ColumnDevice, ColumnFileSystemName, \ 81 ColumnTotal, ColumnFree, ColumnAvailable, ColumnIsReady, \ 82 ColumnIsReadOnly, ColumnIsValid, ColumnCount = range(11) 83 84 columnFuncMap = { 85 ColumnRootPath: lambda volume: QDir.toNativeSeparators(volume.rootPath()), 86 ColumnName: lambda volume: volume.name(), 87 ColumnDevice: lambda volume: volume.device(), 88 ColumnFileSystemName: lambda volume: volume.fileSystemType(), 89 ColumnTotal: lambda volume: sizeToString(volume.bytesTotal()), 90 ColumnFree: lambda volume: sizeToString(volume.bytesFree()), 91 ColumnAvailable: lambda volume: sizeToString(volume.bytesAvailable()), 92 ColumnIsReady: lambda volume: volume.isReady(), 93 ColumnIsReadOnly: lambda volume: volume.isReadOnly(), 94 ColumnIsValid: lambda volume: volume.isValid(), 95 } 96 97 columnNameMap = { 98 ColumnRootPath: "Root path", 99 ColumnName: "Volume Name", 100 ColumnDevice: "Device", 101 ColumnFileSystemName: "File system", 102 ColumnTotal: "Total", 103 ColumnFree: "Free", 104 ColumnAvailable: "Available", 105 ColumnIsReady: "Ready", 106 ColumnIsReadOnly: "Read-only", 107 ColumnIsValid: "Valid", 108 } 109 110 def __init__(self, parent = None): 111 super(StorageModel, self).__init__(parent) 112 self.volumes = QStorageInfo.mountedVolumes() 113 114 def columnCount(self, parent = None): 115 return self.ColumnCount 116 117 def rowCount(self, parent): 118 if parent.isValid(): 119 return 0 120 return len(self.volumes) 121 122 def data(self, index, role): 123 if not index.isValid(): 124 return None 125 if role == Qt.DisplayRole: 126 volume = self.volumes[index.row()] 127 func = self.columnFuncMap.get(index.column()) 128 if func is not None: 129 return func(volume) 130 131 elif role == Qt.ToolTipRole: 132 volume = self.volumes[index.row()] 133 tooltip = [] 134 for column in range(self.ColumnCount): 135 label = self.columnNameMap.get(column) 136 value = self.columnFuncMap[column](volume) 137 if isinstance(value, QByteArray): 138 value = str(bytes(value).decode('utf-8')) 139 tooltip.append("{0}: {1}".format(label, value)) 140 return "\n".join(tooltip) 141 142 def headerData(self, section, orientation, role): 143 if orientation != Qt.Horizontal: 144 return None 145 if role != Qt.DisplayRole: 146 return None 147 return self.columnNameMap.get(section) 148 149 150def main(args): 151 app = QApplication (args) 152 view = QTreeView() 153 view.setModel(StorageModel(view)) 154 view.resize(640, 480) 155 view.setSelectionBehavior(QAbstractItemView.SelectRows) 156 for column in range(view.model().columnCount()): 157 view.resizeColumnToContents(column) 158 view.show() 159 return app.exec_() 160 161 162if __name__ == '__main__': 163 import sys 164 main(sys.argv) 165