Skip to content

Instantly share code, notes, and snippets.

@lukestanley
Last active April 22, 2022 11:41
Show Gist options
  • Save lukestanley/30804df1e9ab316c295198665b9d9112 to your computer and use it in GitHub Desktop.
Save lukestanley/30804df1e9ab316c295198665b9d9112 to your computer and use it in GitHub Desktop.
PySide2 VirtualBox VM launcher script for Linux
"""
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