Last active
April 22, 2022 11:41
-
-
Save lukestanley/30804df1e9ab316c295198665b9d9112 to your computer and use it in GitHub Desktop.
PySide2 VirtualBox VM launcher script for Linux
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
A PySide2 script to open selected VirtualBox VMs. | |
It shows a list of VirtualBox VM's and that is filtered on every key press (without pressing enter). | |
When enter is pressed, we launch or switch to the first matching VirtualBox machine. | |
Later the filter query is cleared ready for the next interaction. | |
VirtualBox's own GUI doesn't clear it's search box after using it. | |
This annoyed me so I wrote this for my Linux box. | |
It depends on PySide2, wmctrl, and VBoxManage. | |
""" | |
from PySide2.QtWidgets import ( | |
QApplication, | |
QWidget, | |
QVBoxLayout, | |
QLineEdit, | |
QListWidget, | |
QListWidgetItem, | |
) | |
from PySide2.QtCore import Qt, Signal, Slot, QThread, QEvent | |
from PySide2 import QtGui | |
from subprocess import check_output | |
from time import time, sleep | |
from threading import Thread, Timer | |
from os import system | |
highlight = "✨ " | |
def run(command): | |
return check_output(command, shell=True).decode("utf-8") | |
def text_list_to_dict(shell_string): | |
"""VBoxManage list returns each VM name in doublequote marks, followed by a space, and a UUID in curly brackets. | |
This method returns a dictionary with the VM names as the key, and the VM UUID as the value: | |
"windows system" {09b51c63-07c7-4d4a-90c3-839236e760dd} | |
"ubuntu" {8021745f-ed7f-4b5c-84a3-dee6f9b14ce8} | |
"android" {7de65689-2d0d-470f-8bf5-c8890588f100} | |
""" | |
vm_list = shell_string.split("\n") | |
vm_dict = {} | |
for vm in vm_list: | |
if "{" not in vm: | |
continue | |
vm_name = vm.split("{")[0].split('"')[1] | |
vm_uuid = vm.split("{")[1].split("}")[0] | |
vm_dict[vm_name] = vm_uuid | |
return vm_dict | |
running_vms = text_list_to_dict(run("VBoxManage list runningvms")) | |
all_vms = text_list_to_dict(run("VBoxManage list vms")) | |
last_clear = time() | |
wordlist = list(all_vms.keys()) | |
wordlist.sort() | |
def start_or_resume_vm(name): | |
""" | |
Given the name of the VM (e.g: "Ubuntu"), we start or resume the VM if it is not already running. | |
If the VM is already running, we focus its window. | |
""" | |
if name in running_vms: | |
system("wmctrl -a '" + name + "' &") | |
else: | |
system("VBoxManage startvm " + all_vms[name] + " &") | |
class MainWindow(QWidget): | |
def __init__(self): | |
QWidget.__init__(self) | |
self.setWindowTitle("VirtualBox selector") | |
self.setWindowIcon(QtGui.QIcon("/home/user/Pictures/virtualbox.png")) | |
self.setGeometry(100, 100, 400, 600) | |
self.filter = QLineEdit() | |
self.filter.setPlaceholderText("Filter") | |
self.filter.textChanged.connect(self.filter_changed) | |
self.filter.returnPressed.connect(self.filter_return) | |
self.list = QListWidget() | |
self.list.itemClicked.connect(self.list_item_clicked) | |
self.list.setStyleSheet("QListView { font-size: 30px; }") | |
layout = QVBoxLayout() | |
layout.addWidget(self.filter) | |
layout.addWidget(self.list) | |
self.setLayout(layout) | |
self.setFocusProxy(self.filter) | |
self.list.setFocusProxy(self.filter) | |
self.filter_changed("") | |
self.thread = UpdateThread() | |
self.thread.update.connect(self.update_wordlist) | |
self.thread.start() | |
def changeEvent(self, event): | |
# Detect focus changes on the window | |
if (event.type() == QEvent.ActivationChange) and (not self.isActiveWindow()): | |
self.filter.clear() | |
@Slot(str) | |
def filter_changed(self, text): | |
self.list.clear() | |
for word in wordlist: | |
if (text.lower() in word.lower()) and (word in running_vms): | |
self.list.addItem(highlight + word) | |
wordlist.sort() | |
for word in wordlist: | |
if (text.lower() in word.lower()) and (word not in running_vms): | |
self.list.addItem(word) | |
@Slot(QListWidgetItem) | |
def list_item_clicked(self, item): | |
print(item.text()) | |
start_or_resume_vm(item.text().replace(highlight, "")) | |
self.filter.clear() | |
@Slot() | |
def filter_return(self): | |
filter = self.filter.text() | |
items = [word for word in wordlist if filter.lower() in word.lower()] | |
print(items[0]) | |
start_or_resume_vm(items[0]) | |
self.filter.clear() | |
@Slot() | |
def update_wordlist(self): | |
global wordlist | |
global running_vms | |
global last_clear | |
running_vms = text_list_to_dict(run("VBoxManage list runningvms")) | |
all_vms = text_list_to_dict(run("VBoxManage list vms")) | |
wordlist = list(all_vms.keys()) | |
wordlist.sort() | |
self.filter_changed(self.filter.text()) | |
class UpdateThread(QThread): | |
update = Signal() | |
def run(self): | |
while True: | |
self.update.emit() | |
sleep(2) | |
app = QApplication() | |
window = MainWindow() | |
window.show() | |
app.exec_() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment