123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- import sys, parted
- from PyQt5.QtCore import *
- from PyQt5.QtGui import *
- from PyQt5.QtWidgets import *
- ####################################################################
- def main():
- app = QApplication(sys.argv)
- app.setApplicationName('Disk Partitioning Demo')
- w = PartioningDemo()
- w.show()
- sys.exit(app.exec_())
- ####################################################################
- class PartioningDemo(QWidget):
- def __init__(self, *args):
- QWidget.__init__(self, *args)
- self.device_list = parted.getAllDevices()
- device_list_widget = QWidget()
- device_list_layout = QHBoxLayout()
- self.device_selection_combobox = QComboBox()
- self.refresh_button = QPushButton("Refresh")
- self.refresh_button.pressed.connect(self.refresh_device_list)
- for disk in self.device_list:
- try:
- if parted.Disk(disk).type == "msdos":
- self.device_selection_combobox.addItem(
- "{} {} GB ({})".format(disk.model, format(disk.getSize(unit="GB"), '.2f'), disk.path),
- userData=disk.path)
- except parted.DiskLabelException:
- disk = parted.freshDisk(disk, 'msdos')
- # For discarding CD-ROM like devices
- try:
- disk.commit()
- except parted.IOException:
- pass
- else:
- disk = disk.device
- self.device_selection_combobox.addItem(
- "{} {} GB ({})".format(disk.model, format(disk.getSize(unit="GB"), '.2f'), disk.path),
- userData=disk.path)
- self.device_selection_combobox.currentIndexChanged.connect(self.selected_device_changed)
- if self.device_selection_combobox.currentData():
- self.current_device = parted.getDevice(self.device_selection_combobox.currentData())
- self.current_disk = parted.Disk(self.current_device)
- self.partition_list_widget = QListWidget()
- for part in self.current_disk.partitions:
- part_info = self.get_part_info(part, "GB")
- part_item = QListWidgetItem(
- "{}\t\t{} GB\t{}\t{}".format(part_info["path"], part_info["size"], part_info["file_system_type"],
- part_info["flags"]))
- part_item.setData(Qt.UserRole, part_info["order"])
- if part_info["type"] == parted.PARTITION_NORMAL:
- part_item.setIcon(QIcon("images/primary.xpm"))
- elif part_info["type"] == parted.PARTITION_EXTENDED:
- part_item.setIcon(QIcon("images/extended.xpm"))
- elif part_info["type"] == parted.PARTITION_LOGICAL:
- part_item.setIcon(QIcon("images/logical.xpm"))
- self.partition_list_widget.addItem(part_item)
- for free_space in self.current_disk.getFreeSpacePartitions():
- total_size = 0
- part_info = self.get_part_info(free_space, "GB")
- if float(part_info["size"]) > 0:
- if part_info["type"] == 5:
- extended_unallocated_item = QListWidgetItem(
- "{}\t{} GB".format("Unallocated", part_info["size"]))
- extended_unallocated_item.setIcon(QIcon("images/blank.xpm"))
- extended_unallocated_item.setData(Qt.UserRole, "unallocated")
- self.partition_list_widget.addItem(extended_unallocated_item)
- if part_info["type"] == parted.PARTITION_FREESPACE:
- total_size = total_size + float(part_info["size"])
- unallocated_item = QListWidgetItem("{}\t{} GB".format("Unallocated", total_size))
- unallocated_item.setIcon(QIcon("images/blank.xpm"))
- unallocated_item.setData(Qt.UserRole, "unallocated")
- self.partition_list_widget.addItem(unallocated_item)
- device_list_layout.addWidget(self.device_selection_combobox)
- device_list_layout.addWidget(self.refresh_button)
- device_list_widget.setLayout(device_list_layout)
- layout = QVBoxLayout()
- layout.addWidget(device_list_widget)
- layout.addWidget(self.partition_list_widget)
- partition_type_index_widget = QLabel()
- partition_type_index_widget.setPixmap(QPixmap("images/lejant.png"))
- partition_type_index_widget.setAlignment(Qt.AlignCenter)
- layout.addWidget(partition_type_index_widget)
- self.partition_list_widget.itemClicked.connect(self.on_part_select)
- self.partition_list_widget.itemDoubleClicked.connect(self.change_partition_format)
- op_widget = QWidget()
- op_buttons_layout = QHBoxLayout()
- self.new_part_btn = QPushButton("Add new partition")
- self.new_part_btn.pressed.connect(self.add_new_part)
- self.delete_part_btn = QPushButton("Delete partition")
- self.delete_part_btn.pressed.connect(self.delete_part)
- self.save_btn = QPushButton("Save")
- self.save_btn.pressed.connect(self.save)
- op_buttons_layout.addWidget(self.new_part_btn)
- op_buttons_layout.addWidget(self.delete_part_btn)
- op_buttons_layout.addWidget(self.save_btn)
- op_widget.setLayout(op_buttons_layout)
- layout.addWidget(op_widget)
- self.delete_part_btn.setEnabled(False)
- self.setLayout(layout)
- def save(self):
- self.current_disk.commit()
- def change_partition_format(self, selected_part):
- if selected_part.data(Qt.UserRole) != "unallocated":
- for part in self.current_disk.partitions:
- if part.number == selected_part.data(Qt.UserRole):
- print(part.path)
- def get_part_info(self, part, size_unit):
- part_info = {}
- part_info["path"] = part.path
- if size_unit == "GB":
- part_info["size"] = format(part.getSize(unit=size_unit), '.2f')
- else:
- part_info["size"] = part.getSize(unit=size_unit)
- part_info["file_system_type"] = "Unknown"
- if part.fileSystem:
- if part.fileSystem.type.startswith('linux-swap'):
- part_info["file_system_type"] = "swap"
- else:
- part_info["file_system_type"] = part.fileSystem.type
- try:
- part_info["flags"] = part.getFlagsAsString()
- except:
- pass
- part_info["order"] = part.number
- part_info["type"] = part.type
- return part_info
- def refresh_device_list(self):
- self.device_selection_combobox.clear()
- self.device_list = parted.getAllDevices()
- for disk in self.device_list:
- try:
- if parted.Disk(disk).type == "msdos":
- self.device_selection_combobox.addItem(
- "{} {} GB ({})".format(disk.model, format(disk.getSize(unit="GB"), '.2f'), disk.path),
- userData=disk.path)
- except parted.DiskLabelException:
- disk = parted.freshDisk(disk, 'msdos')
- # For discarding CD-ROM like devices
- try:
- disk.commit()
- except parted.IOException:
- pass
- else:
- disk = disk.device
- self.device_selection_combobox.addItem(
- "{} {} GB ({})".format(disk.model, format(disk.getSize(unit="GB"), '.2f'), disk.path),
- userData=disk.path)
- def selected_device_changed(self):
- if self.device_selection_combobox.currentData():
- self.current_device = parted.getDevice(self.device_selection_combobox.currentData())
- self.current_disk = parted.Disk(self.current_device)
- self.refresh_partition_list()
- def refresh_partition_list(self):
- self.partition_list_widget.clear()
- for part in self.current_disk.partitions:
- part_info = self.get_part_info(part, "GB")
- item = QListWidgetItem(
- "{}\t\t{} GB\t{}\t{}".format(part_info["path"], part_info["size"], part_info["file_system_type"],
- part_info["flags"]))
- item.setData(Qt.UserRole, part_info["order"])
- if part_info["type"] == parted.PARTITION_NORMAL:
- item.setIcon(QIcon("images/primary.xpm"))
- elif part_info["type"] == parted.PARTITION_EXTENDED:
- item.setIcon(QIcon("images/extended.xpm"))
- elif part_info["type"] == parted.PARTITION_LOGICAL:
- item.setIcon(QIcon("images/logical.xpm"))
- self.partition_list_widget.addItem(item)
- for free_part in self.current_disk.getFreeSpacePartitions():
- total_size = 0
- part_info = self.get_part_info(free_part, "GB")
- if float(part_info["size"]) > 0:
- if part_info["type"] == 5:
- extended_unallocated_item = QListWidgetItem("{}\t{} GB".format("Unallocated", part_info["size"]))
- extended_unallocated_item.setIcon(QIcon("images/blank.xpm"))
- extended_unallocated_item.setData(Qt.UserRole, "unallocated")
- self.partition_list_widget.addItem(extended_unallocated_item)
- if part_info["type"] == parted.PARTITION_FREESPACE:
- total_size = total_size + float(part_info["size"])
- unallocated_item = QListWidgetItem("{}\t{} GB".format("Unallocated", total_size))
- unallocated_item.setIcon(QIcon("images/blank.xpm"))
- unallocated_item.setData(Qt.UserRole, "unallocated")
- self.partition_list_widget.addItem(unallocated_item)
- def on_part_select(self, selected_part):
- if selected_part.data(Qt.UserRole) != "unallocated":
- self.delete_part_btn.setEnabled(True)
- else:
- self.delete_part_btn.setEnabled(False)
- def delete_part(self):
- part_order = self.partition_list_widget.currentItem().data(Qt.UserRole)
- for part in self.current_disk.partitions:
- if part.number == part_order:
- try:
- self.current_disk.deletePartition(part)
- self.refresh_partition_list()
- except parted.PartitionException:
- QMessageBox.warning(self, "Warning",
- "Extended partitions cannot be deleted before logical partitions.")
- self.partition_list_widget.setCurrentRow(self.partition_list_widget.count() - 2)
- def add_new_part(self):
- if self.get_max_unallocated_region():
- region = self.get_max_unallocated_region()
- number_of_primary_parts = len(self.current_disk.getPrimaryPartitions())
- number_of_extended_parts = ext_count = 1 if self.current_disk.getExtendedPartition() else 0
- parts_avail = self.current_disk.maxPrimaryPartitionCount - (
- number_of_primary_parts + number_of_extended_parts)
- if not parts_avail and not ext_count:
- QMessageBox.warning(self,
- "Warning",
- """You cannot create more than 4 primary partition.
- If you want more partitions,
- delete one of primary partition and create extended one. """
- )
- else:
- if parts_avail:
- if not number_of_extended_parts and parts_avail > 1:
- self.create_part(region, parted.PARTITION_NORMAL)
- self.refresh_partition_list()
- elif parts_avail == 1:
- self.create_part(region, parted.PARTITION_EXTENDED)
- self.refresh_partition_list()
- if number_of_extended_parts:
- ext_part = self.current_disk.getExtendedPartition()
- try:
- region = ext_part.geometry.intersect(region)
- except ArithmeticError:
- QMessageBox.critical(self, "Error",
- "There is no free space for new partition!"
- " Try increase size for extended partitions.")
- else:
- self.create_part(region, parted.PARTITION_LOGICAL)
- self.refresh_partition_list()
- else:
- QMessageBox.critical(self, "There is no free space for new partition!")
- def get_max_unallocated_region(self):
- max_size = -1
- region = None
- alignment = self.current_device.optimumAlignment
- for _region in self.current_disk.getFreeSpaceRegions():
- if _region.length > max_size and _region.length > alignment.grainSize:
- region = _region
- max_size = _region.length
- return region
- def create_part(self, region, part_type):
- if part_type == parted.PARTITION_NORMAL or part_type == parted.PARTITION_EXTENDED:
- for free_part in self.current_disk.getFreeSpacePartitions():
- part_info = self.get_part_info(free_part, "GB")
- if part_info["type"] == parted.PARTITION_FREESPACE:
- max_size = float(part_info["size"])
- elif part_type == part_type == parted.PARTITION_LOGICAL:
- for free_part in self.current_disk.getFreeSpacePartitions():
- part_info = self.get_part_info(free_part, "GB")
- if part_info["type"] == 5:
- max_size = float(part_info["size"])
- alignment = self.current_device.optimalAlignedConstraint
- constraint = self.current_device.getConstraint()
- data = {
- 'start': constraint.startAlign.alignUp(region, region.start),
- 'end': constraint.endAlign.alignDown(region, region.end),
- }
- selected_size, ok = QInputDialog().getDouble(self, 'Create partition', 'Size in GB:', min=0.001, value=1,
- max=max_size, decimals=3)
- if ok:
- data["end"] = int(data["start"]) + int(
- parted.sizeToSectors(float(selected_size), "GiB", self.current_device.sectorSize))
- try:
- geometry = parted.Geometry(device=self.current_device, start=int(data["start"]), end=int(data["end"]))
- partition = parted.Partition(
- disk=self.current_disk,
- type=part_type,
- geometry=geometry,
- )
- self.current_disk.addPartition(partition=partition, constraint=constraint)
- except (parted.PartitionException, parted.GeometryException, parted.CreateException) as e:
- # GeometryException raised when incorrect start/end values applied (e.g. start < end),
- # CreateException is raised when the partition doesn't fit on the disk, etc.
- # PartedException is raised on generic errors (e.g. start/end values out of range)
- raise RuntimeError(e.message)
- if __name__ == "__main__":
- main()
|