faff to make __main__ not throw a warning

This commit is contained in:
Roberto Alsina 2020-02-02 11:37:38 -03:00
parent cde1180525
commit ce37e2a48f
3 changed files with 423 additions and 421 deletions

View File

@ -1 +1 @@
from .__main__ import main from .main import main

View File

@ -1,420 +1,2 @@
import os from .main import main
import shlex
import subprocess
import sys
from copy import deepcopy
from PySide2.QtCore import QFile, QObject
from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import QApplication, QGraphicsScene, QLabel
from .monitor_item import MonitorItem
def gen_xrandr_from_data(data):
"""Takes monitor data and generates a xrandr command line."""
cli = ["xrandr"]
for name, mon in data.items():
cli.append(f"--output {name}")
if not mon["enabled"]:
cli.append("--off")
else:
cli.append(f'--pos {int(mon["pos_x"])}x{int(mon["pos_y"])}')
cli.append(f'--mode {mon["current_mode"]}')
mod_x, mod_y = [int(n) for n in mon["current_mode"].split("x")]
if mon["orientation"] in (1, 3):
mod_x, mod_y = mod_y, mod_x
cli.append(f'--scale {mon["res_x"]/mod_x}x{mon["res_y"]/mod_y}')
cli.append(
f"--rotate {['normal', 'left', 'inverted', 'right'][mon['orientation']]}"
)
if mon["primary"]:
cli.append("--primary")
return " ".join(cli)
def parse_monitor(line):
parts = line.split()
name = parts[0]
primary = "primary" in parts
if "+" in line: # Is enabled
enabled = True
res_x, res_y = [p for p in parts if "x" in p][0].split("+")[0].split("x")
pos_x, pos_y = [p for p in parts if "x" in p][0].split("+")[1:]
w_in_mm, h_in_mm = [p.split("mm")[0] for p in parts if p.endswith("mm")]
else:
enabled = False
res_x = res_y = pos_x = pos_y = w_in_mm = h_in_mm = 0
left_side = line.split(" (normal left inverted ")[0]
orientation = 0
if "left" in left_side:
orientation = 1
elif "inverted" in left_side:
orientation = 2
elif "right" in left_side:
orientation = 3
return (
name,
primary,
int(res_x),
int(res_y),
int(w_in_mm),
int(h_in_mm),
int(pos_x),
int(pos_y),
enabled,
orientation,
)
def is_replica_of(a, b):
"""Return True if monitor a is a replica of b.
Replica means same resolution and position.
"""
return (
a["pos_x"] == b["pos_x"]
and a["pos_y"] == b["pos_y"]
and a["res_x"] == b["res_x"]
and a["res_y"] == b["res_y"]
)
class Window(QObject):
def __init__(self, ui):
super().__init__()
self.ui = ui
ui.show()
self.ui.setWindowTitle("Display Configuration")
self.ui.screenCombo.currentTextChanged.connect(self.monitor_selected)
self.ui.replicaOf.currentTextChanged.connect(self.replica_changed)
self.ui.orientationCombo.currentIndexChanged.connect(self.orientation_changed)
self.xrandr_info = {}
self.get_xrandr_info()
self.orig_xrandr_info = deepcopy(self.xrandr_info)
self.fill_ui()
self.ui.horizontalScale.valueChanged.connect(self.scale_changed)
self.ui.verticalScale.valueChanged.connect(self.scale_changed)
self.ui.modes.currentTextChanged.connect(self.mode_changed)
self.ui.applyButton.clicked.connect(self.do_apply)
self.ui.okButton.clicked.connect(self.do_ok)
self.ui.resetButton.clicked.connect(self.do_reset)
self.ui.cancelButton.clicked.connect(self.ui.reject)
self.ui.scaleModeCombo.currentTextChanged.connect(self.scale_mode_changed)
self.ui.primary.stateChanged.connect(self.primary_changed)
self.ui.enabled.stateChanged.connect(self.enabled_changed)
self.pos_label = QLabel(self.ui.sceneView)
self.pos_label.move(5, 5)
def enabled_changed(self):
mon = self.ui.screenCombo.currentText()
enabled = self.ui.enabled.isChecked()
print(f"Setting {mon} enabled status to {enabled}")
monitor = self.xrandr_info[mon]
monitor["enabled"] = enabled
monitor["item"].update_visuals(monitor)
def primary_changed(self):
mon = self.ui.screenCombo.currentText()
primary = self.ui.primary.isChecked()
# Update visuals on all monitos
for name, monitor in self.xrandr_info.items():
if name == mon:
monitor["primary"] = primary
else:
if primary: # There can only be one primary
monitor["primary"] = False
monitor["item"].update_visuals(monitor)
def scale_mode_changed(self):
mon = self.ui.screenCombo.currentText()
scale_mode = self.ui.scaleModeCombo.currentText()
print(f"Set {mon} scale mode to {scale_mode}")
if scale_mode == "Manual":
self.ui.horizontalScale.setEnabled(True)
self.ui.verticalScale.setEnabled(True)
try:
self.ui.horizontalScale.valueChanged.disconnect(
self.ui.verticalScale.setValue
)
except RuntimeError: # Not connected
pass
elif scale_mode == "Disabled (1x1)":
self.ui.verticalScale.setEnabled(False)
self.ui.horizontalScale.setEnabled(False)
self.ui.horizontalScale.setValue(1000)
self.ui.verticalScale.setValue(1000)
try:
self.ui.horizontalScale.valueChanged.disconnect(
self.ui.verticalScale.setValue
)
except RuntimeError: # Not connected
pass
elif scale_mode == "Automatic: physical dimensions":
# Calculate scale factors so that the logical pixels will be the same
# size as in the primary window
if self.ui.primary.isChecked():
print("Has no effect on primary display.")
return
# Find the primary monitor
primary = [k for k in self.xrandr_info if self.xrandr_info[k]["primary"]]
if not primary:
print("Oops, no primary!")
return
primary = self.xrandr_info[primary[0]]
monitor = self.xrandr_info[mon]
prim_density_x = primary["res_x"] / primary["w_in_mm"]
prim_density_y = primary["res_y"] / primary["h_in_mm"]
dens_x = monitor["res_x"] / monitor["w_in_mm"]
dens_y = monitor["res_y"] / monitor["h_in_mm"]
try:
self.ui.horizontalScale.valueChanged.disconnect(
self.ui.verticalScale.setValue
)
except RuntimeError: # Not connected
pass
self.ui.horizontalScale.setEnabled(False)
self.ui.verticalScale.setEnabled(False)
self.ui.horizontalScale.setValue(prim_density_x / dens_x * 1000)
self.ui.verticalScale.setValue(prim_density_y / dens_y * 1000)
elif scale_mode == "Manual, same in both dimensions":
self.ui.horizontalScale.setEnabled(True)
self.ui.verticalScale.setEnabled(False)
self.ui.horizontalScale.valueChanged.connect(self.ui.verticalScale.setValue)
self.ui.verticalScale.setValue(self.ui.horizontalScale.value())
def replica_changed(self):
mon = self.ui.screenCombo.currentText()
replicate = self.ui.replicaOf.currentText()
print(f"Making {mon} a replica of {replicate}")
if replicate in ("None", "", None):
print("TODO: make things non-replicas")
return
mon = self.xrandr_info[mon]
replicate = self.xrandr_info[replicate]
# Making a replica implies:
# Set the same position
mon["pos_x"] = replicate["pos_x"]
mon["pos_y"] = replicate["pos_y"]
# Set the same mode if possible
if replicate["current_mode"] in mon["modes"]:
mon["current_mode"] = replicate["current_mode"]
else:
# Keep the current mode, and change scaling so it
# has the same effective size as the desired mode
mod_x, mod_y = [int(x) for x in mon["current_mode"].split("x")]
target_x, target_y = [replicate[x] for x in ["res_x", "res_y"]]
scale_x = 1000 * target_x / mod_x
scale_y = 1000 * target_y / mod_y
breakpoint()
print(target_x, target_y, mod_x, mod_y)
print(scale_x, scale_y)
self.ui.horizontalScale.setValue(scale_x)
self.ui.verticalScale.setValue(scale_y)
mon["item"].update_visuals(mon)
def do_reset(self):
for n in self.xrandr_info:
self.xrandr_info[n].update(self.orig_xrandr_info[n])
self.fill_ui()
def do_ok(self):
self.do_apply()
self.ui.accept()
def do_apply(self):
cli = gen_xrandr_from_data(self.xrandr_info)
print(cli)
subprocess.check_call(shlex.split(cli))
def fill_ui(self):
"""Load data from xrandr and setup the whole thing."""
self.scene = QGraphicsScene(self)
self.ui.sceneView.setScene(self.scene)
self.ui.screenCombo.clear()
for name, monitor in self.xrandr_info.items():
self.ui.screenCombo.addItem(name)
mon_item = MonitorItem(data=monitor, window=self, name=name,)
# mon_item.setPos(monitor["pos_x"], monitor["pos_y"])
self.scene.addItem(mon_item)
monitor["item"] = mon_item
self.adjust_view()
self.scale_changed() # Trigger scale labels update
def orientation_changed(self):
mon = self.ui.screenCombo.currentText()
orientation = self.ui.orientationCombo.currentIndex()
self.xrandr_info[mon]["orientation"] = orientation
self.mode_changed()
def mode_changed(self):
mon = self.ui.screenCombo.currentText()
mode = self.ui.modes.currentText()
if not mode:
return
print(f"Changing {mon} to {mode}")
self.xrandr_info[mon]["current_mode"] = mode
mode_x, mode_y = mode.split("x")
# use resolution via scaling
if self.xrandr_info[mon]["orientation"] in (0, 2):
self.xrandr_info[mon]["res_x"] = int(
int(mode_x) * self.ui.horizontalScale.value() / 1000
)
self.xrandr_info[mon]["res_y"] = int(
int(mode_y) * self.ui.verticalScale.value() / 1000
)
else:
self.xrandr_info[mon]["res_x"] = int(
int(mode_y) * self.ui.horizontalScale.value() / 1000
)
self.xrandr_info[mon]["res_y"] = int(
int(mode_x) * self.ui.verticalScale.value() / 1000
)
self.xrandr_info[mon]["item"].update_visuals(self.xrandr_info[mon])
def show_pos(self, x, y):
self.pos_label.setText(f"{x},{y}")
self.pos_label.resize(self.pos_label.sizeHint())
def monitor_moved(self):
"Update xrandr_info with new monitor positions"
for _, mon in self.xrandr_info.items():
item = mon["item"]
mon["pos_x"] = item.x()
mon["pos_y"] = item.y()
self.update_replica_of_data()
for _, mon in self.xrandr_info.items():
mon["item"].update_visuals(mon)
self.adjust_view()
def adjust_view(self):
self.ui.sceneView.resetTransform()
self.ui.sceneView.ensureVisible(self.scene.sceneRect(), 100, 100)
scale_factor = 0.8 * min(
self.ui.sceneView.width() / self.scene.sceneRect().width(),
self.ui.sceneView.height() / self.scene.sceneRect().height(),
)
self.ui.sceneView.scale(scale_factor, scale_factor)
def get_xrandr_info(self):
data = subprocess.check_output(["xrandr"]).decode("utf-8").splitlines()
name = None
for line in data:
if line and line[0] not in "S \t": # Output line
(
name,
primary,
res_x,
res_y,
w_in_mm,
h_in_mm,
pos_x,
pos_y,
enabled,
orientation,
) = parse_monitor(line)
self.xrandr_info[name] = dict(
primary=primary,
res_x=res_x,
res_y=res_y,
w_in_mm=w_in_mm,
h_in_mm=h_in_mm,
pos_x=pos_x,
pos_y=pos_y,
modes=[],
current_mode=None,
enabled=enabled,
replica_of=[],
orientation=orientation,
)
elif line[0] == " ": # A mode
mode_name = line.strip().split()[0]
self.xrandr_info[name]["modes"].append(mode_name)
if "*" in line:
print(f"Current mode for {name}: {mode_name}")
self.xrandr_info[name]["current_mode"] = mode_name
self.update_replica_of_data()
def update_replica_of_data(self):
for a in self.xrandr_info:
self.xrandr_info[a]["replica_of"] = []
for b in self.xrandr_info:
if a != b and is_replica_of(self.xrandr_info[a], self.xrandr_info[b]):
self.xrandr_info[a]["replica_of"].append(b)
def monitor_selected(self, name):
if not name:
return
# needed so we don't flip through all modes as they are added
self.ui.modes.blockSignals(True)
# Show modes
self.ui.modes.clear()
for mode in self.xrandr_info[name]["modes"]:
self.ui.modes.addItem(mode)
if (
self.xrandr_info[name]["current_mode"] is None
): # Happens with turned off monitors
self.xrandr_info[name]["enabled"] = False
h_scale = v_scale = 1
else:
self.ui.modes.setCurrentText(self.xrandr_info[name]["current_mode"])
mod_x, mod_y = [
int(x) for x in self.xrandr_info[name]["current_mode"].split("x")
]
if self.xrandr_info[name]["orientation"] in (0, 2):
h_scale = self.xrandr_info[name]["res_x"] / mod_x
v_scale = self.xrandr_info[name]["res_y"] / mod_y
else:
h_scale = self.xrandr_info[name]["res_y"] / mod_x
v_scale = self.xrandr_info[name]["res_x"] / mod_y
self.ui.horizontalScale.setValue(h_scale * 1000)
self.ui.verticalScale.setValue(v_scale * 1000)
self.ui.primary.setChecked(self.xrandr_info[name]["primary"])
self.ui.enabled.setChecked(self.xrandr_info[name]["enabled"])
self.ui.orientationCombo.setCurrentIndex(self.xrandr_info[name]["orientation"])
self.ui.replicaOf.clear()
self.ui.replicaOf.addItem("None")
for mon in self.xrandr_info:
if mon != name:
self.ui.replicaOf.addItem(mon)
if mon in self.xrandr_info[name]["replica_of"]:
self.ui.replicaOf.setCurrentText(mon)
self.ui.modes.blockSignals(False)
def scale_changed(self):
self.ui.horizontalScaleLabel.setText(
f"{int(self.ui.horizontalScale.value()/10)}%"
)
self.ui.verticalScaleLabel.setText(f"{int(self.ui.verticalScale.value()/10)}%")
self.mode_changed() # Not really, but it's the same thing
def main():
app = QApplication(sys.argv)
ui_file = QFile(os.path.join(os.path.dirname(__file__), "main.ui"))
ui_file.open(QFile.ReadOnly)
loader = QUiLoader()
window = Window(loader.load(ui_file))
sys.exit(app.exec_())
if __name__ == "__main__":
main() main()

420
xrandroll/main.py Normal file
View File

@ -0,0 +1,420 @@
import os
import shlex
import subprocess
import sys
from copy import deepcopy
from PySide2.QtCore import QFile, QObject
from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import QApplication, QGraphicsScene, QLabel
from .monitor_item import MonitorItem
def gen_xrandr_from_data(data):
"""Takes monitor data and generates a xrandr command line."""
cli = ["xrandr"]
for name, mon in data.items():
cli.append(f"--output {name}")
if not mon["enabled"]:
cli.append("--off")
else:
cli.append(f'--pos {int(mon["pos_x"])}x{int(mon["pos_y"])}')
cli.append(f'--mode {mon["current_mode"]}')
mod_x, mod_y = [int(n) for n in mon["current_mode"].split("x")]
if mon["orientation"] in (1, 3):
mod_x, mod_y = mod_y, mod_x
cli.append(f'--scale {mon["res_x"]/mod_x}x{mon["res_y"]/mod_y}')
cli.append(
f"--rotate {['normal', 'left', 'inverted', 'right'][mon['orientation']]}"
)
if mon["primary"]:
cli.append("--primary")
return " ".join(cli)
def parse_monitor(line):
parts = line.split()
name = parts[0]
primary = "primary" in parts
if "+" in line: # Is enabled
enabled = True
res_x, res_y = [p for p in parts if "x" in p][0].split("+")[0].split("x")
pos_x, pos_y = [p for p in parts if "x" in p][0].split("+")[1:]
w_in_mm, h_in_mm = [p.split("mm")[0] for p in parts if p.endswith("mm")]
else:
enabled = False
res_x = res_y = pos_x = pos_y = w_in_mm = h_in_mm = 0
left_side = line.split(" (normal left inverted ")[0]
orientation = 0
if "left" in left_side:
orientation = 1
elif "inverted" in left_side:
orientation = 2
elif "right" in left_side:
orientation = 3
return (
name,
primary,
int(res_x),
int(res_y),
int(w_in_mm),
int(h_in_mm),
int(pos_x),
int(pos_y),
enabled,
orientation,
)
def is_replica_of(a, b):
"""Return True if monitor a is a replica of b.
Replica means same resolution and position.
"""
return (
a["pos_x"] == b["pos_x"]
and a["pos_y"] == b["pos_y"]
and a["res_x"] == b["res_x"]
and a["res_y"] == b["res_y"]
)
class Window(QObject):
def __init__(self, ui):
super().__init__()
self.ui = ui
ui.show()
self.ui.setWindowTitle("Display Configuration")
self.ui.screenCombo.currentTextChanged.connect(self.monitor_selected)
self.ui.replicaOf.currentTextChanged.connect(self.replica_changed)
self.ui.orientationCombo.currentIndexChanged.connect(self.orientation_changed)
self.xrandr_info = {}
self.get_xrandr_info()
self.orig_xrandr_info = deepcopy(self.xrandr_info)
self.fill_ui()
self.ui.horizontalScale.valueChanged.connect(self.scale_changed)
self.ui.verticalScale.valueChanged.connect(self.scale_changed)
self.ui.modes.currentTextChanged.connect(self.mode_changed)
self.ui.applyButton.clicked.connect(self.do_apply)
self.ui.okButton.clicked.connect(self.do_ok)
self.ui.resetButton.clicked.connect(self.do_reset)
self.ui.cancelButton.clicked.connect(self.ui.reject)
self.ui.scaleModeCombo.currentTextChanged.connect(self.scale_mode_changed)
self.ui.primary.stateChanged.connect(self.primary_changed)
self.ui.enabled.stateChanged.connect(self.enabled_changed)
self.pos_label = QLabel(self.ui.sceneView)
self.pos_label.move(5, 5)
def enabled_changed(self):
mon = self.ui.screenCombo.currentText()
enabled = self.ui.enabled.isChecked()
print(f"Setting {mon} enabled status to {enabled}")
monitor = self.xrandr_info[mon]
monitor["enabled"] = enabled
monitor["item"].update_visuals(monitor)
def primary_changed(self):
mon = self.ui.screenCombo.currentText()
primary = self.ui.primary.isChecked()
# Update visuals on all monitos
for name, monitor in self.xrandr_info.items():
if name == mon:
monitor["primary"] = primary
else:
if primary: # There can only be one primary
monitor["primary"] = False
monitor["item"].update_visuals(monitor)
def scale_mode_changed(self):
mon = self.ui.screenCombo.currentText()
scale_mode = self.ui.scaleModeCombo.currentText()
print(f"Set {mon} scale mode to {scale_mode}")
if scale_mode == "Manual":
self.ui.horizontalScale.setEnabled(True)
self.ui.verticalScale.setEnabled(True)
try:
self.ui.horizontalScale.valueChanged.disconnect(
self.ui.verticalScale.setValue
)
except RuntimeError: # Not connected
pass
elif scale_mode == "Disabled (1x1)":
self.ui.verticalScale.setEnabled(False)
self.ui.horizontalScale.setEnabled(False)
self.ui.horizontalScale.setValue(1000)
self.ui.verticalScale.setValue(1000)
try:
self.ui.horizontalScale.valueChanged.disconnect(
self.ui.verticalScale.setValue
)
except RuntimeError: # Not connected
pass
elif scale_mode == "Automatic: physical dimensions":
# Calculate scale factors so that the logical pixels will be the same
# size as in the primary window
if self.ui.primary.isChecked():
print("Has no effect on primary display.")
return
# Find the primary monitor
primary = [k for k in self.xrandr_info if self.xrandr_info[k]["primary"]]
if not primary:
print("Oops, no primary!")
return
primary = self.xrandr_info[primary[0]]
monitor = self.xrandr_info[mon]
prim_density_x = primary["res_x"] / primary["w_in_mm"]
prim_density_y = primary["res_y"] / primary["h_in_mm"]
dens_x = monitor["res_x"] / monitor["w_in_mm"]
dens_y = monitor["res_y"] / monitor["h_in_mm"]
try:
self.ui.horizontalScale.valueChanged.disconnect(
self.ui.verticalScale.setValue
)
except RuntimeError: # Not connected
pass
self.ui.horizontalScale.setEnabled(False)
self.ui.verticalScale.setEnabled(False)
self.ui.horizontalScale.setValue(prim_density_x / dens_x * 1000)
self.ui.verticalScale.setValue(prim_density_y / dens_y * 1000)
elif scale_mode == "Manual, same in both dimensions":
self.ui.horizontalScale.setEnabled(True)
self.ui.verticalScale.setEnabled(False)
self.ui.horizontalScale.valueChanged.connect(self.ui.verticalScale.setValue)
self.ui.verticalScale.setValue(self.ui.horizontalScale.value())
def replica_changed(self):
mon = self.ui.screenCombo.currentText()
replicate = self.ui.replicaOf.currentText()
print(f"Making {mon} a replica of {replicate}")
if replicate in ("None", "", None):
print("TODO: make things non-replicas")
return
mon = self.xrandr_info[mon]
replicate = self.xrandr_info[replicate]
# Making a replica implies:
# Set the same position
mon["pos_x"] = replicate["pos_x"]
mon["pos_y"] = replicate["pos_y"]
# Set the same mode if possible
if replicate["current_mode"] in mon["modes"]:
mon["current_mode"] = replicate["current_mode"]
else:
# Keep the current mode, and change scaling so it
# has the same effective size as the desired mode
mod_x, mod_y = [int(x) for x in mon["current_mode"].split("x")]
target_x, target_y = [replicate[x] for x in ["res_x", "res_y"]]
scale_x = 1000 * target_x / mod_x
scale_y = 1000 * target_y / mod_y
breakpoint()
print(target_x, target_y, mod_x, mod_y)
print(scale_x, scale_y)
self.ui.horizontalScale.setValue(scale_x)
self.ui.verticalScale.setValue(scale_y)
mon["item"].update_visuals(mon)
def do_reset(self):
for n in self.xrandr_info:
self.xrandr_info[n].update(self.orig_xrandr_info[n])
self.fill_ui()
def do_ok(self):
self.do_apply()
self.ui.accept()
def do_apply(self):
cli = gen_xrandr_from_data(self.xrandr_info)
print(cli)
subprocess.check_call(shlex.split(cli))
def fill_ui(self):
"""Load data from xrandr and setup the whole thing."""
self.scene = QGraphicsScene(self)
self.ui.sceneView.setScene(self.scene)
self.ui.screenCombo.clear()
for name, monitor in self.xrandr_info.items():
self.ui.screenCombo.addItem(name)
mon_item = MonitorItem(data=monitor, window=self, name=name,)
# mon_item.setPos(monitor["pos_x"], monitor["pos_y"])
self.scene.addItem(mon_item)
monitor["item"] = mon_item
self.adjust_view()
self.scale_changed() # Trigger scale labels update
def orientation_changed(self):
mon = self.ui.screenCombo.currentText()
orientation = self.ui.orientationCombo.currentIndex()
self.xrandr_info[mon]["orientation"] = orientation
self.mode_changed()
def mode_changed(self):
mon = self.ui.screenCombo.currentText()
mode = self.ui.modes.currentText()
if not mode:
return
print(f"Changing {mon} to {mode}")
self.xrandr_info[mon]["current_mode"] = mode
mode_x, mode_y = mode.split("x")
# use resolution via scaling
if self.xrandr_info[mon]["orientation"] in (0, 2):
self.xrandr_info[mon]["res_x"] = int(
int(mode_x) * self.ui.horizontalScale.value() / 1000
)
self.xrandr_info[mon]["res_y"] = int(
int(mode_y) * self.ui.verticalScale.value() / 1000
)
else:
self.xrandr_info[mon]["res_x"] = int(
int(mode_y) * self.ui.horizontalScale.value() / 1000
)
self.xrandr_info[mon]["res_y"] = int(
int(mode_x) * self.ui.verticalScale.value() / 1000
)
self.xrandr_info[mon]["item"].update_visuals(self.xrandr_info[mon])
def show_pos(self, x, y):
self.pos_label.setText(f"{x},{y}")
self.pos_label.resize(self.pos_label.sizeHint())
def monitor_moved(self):
"Update xrandr_info with new monitor positions"
for _, mon in self.xrandr_info.items():
item = mon["item"]
mon["pos_x"] = item.x()
mon["pos_y"] = item.y()
self.update_replica_of_data()
for _, mon in self.xrandr_info.items():
mon["item"].update_visuals(mon)
self.adjust_view()
def adjust_view(self):
self.ui.sceneView.resetTransform()
self.ui.sceneView.ensureVisible(self.scene.sceneRect(), 100, 100)
scale_factor = 0.8 * min(
self.ui.sceneView.width() / self.scene.sceneRect().width(),
self.ui.sceneView.height() / self.scene.sceneRect().height(),
)
self.ui.sceneView.scale(scale_factor, scale_factor)
def get_xrandr_info(self):
data = subprocess.check_output(["xrandr"]).decode("utf-8").splitlines()
name = None
for line in data:
if line and line[0] not in "S \t": # Output line
(
name,
primary,
res_x,
res_y,
w_in_mm,
h_in_mm,
pos_x,
pos_y,
enabled,
orientation,
) = parse_monitor(line)
self.xrandr_info[name] = dict(
primary=primary,
res_x=res_x,
res_y=res_y,
w_in_mm=w_in_mm,
h_in_mm=h_in_mm,
pos_x=pos_x,
pos_y=pos_y,
modes=[],
current_mode=None,
enabled=enabled,
replica_of=[],
orientation=orientation,
)
elif line[0] == " ": # A mode
mode_name = line.strip().split()[0]
self.xrandr_info[name]["modes"].append(mode_name)
if "*" in line:
print(f"Current mode for {name}: {mode_name}")
self.xrandr_info[name]["current_mode"] = mode_name
self.update_replica_of_data()
def update_replica_of_data(self):
for a in self.xrandr_info:
self.xrandr_info[a]["replica_of"] = []
for b in self.xrandr_info:
if a != b and is_replica_of(self.xrandr_info[a], self.xrandr_info[b]):
self.xrandr_info[a]["replica_of"].append(b)
def monitor_selected(self, name):
if not name:
return
# needed so we don't flip through all modes as they are added
self.ui.modes.blockSignals(True)
# Show modes
self.ui.modes.clear()
for mode in self.xrandr_info[name]["modes"]:
self.ui.modes.addItem(mode)
if (
self.xrandr_info[name]["current_mode"] is None
): # Happens with turned off monitors
self.xrandr_info[name]["enabled"] = False
h_scale = v_scale = 1
else:
self.ui.modes.setCurrentText(self.xrandr_info[name]["current_mode"])
mod_x, mod_y = [
int(x) for x in self.xrandr_info[name]["current_mode"].split("x")
]
if self.xrandr_info[name]["orientation"] in (0, 2):
h_scale = self.xrandr_info[name]["res_x"] / mod_x
v_scale = self.xrandr_info[name]["res_y"] / mod_y
else:
h_scale = self.xrandr_info[name]["res_y"] / mod_x
v_scale = self.xrandr_info[name]["res_x"] / mod_y
self.ui.horizontalScale.setValue(h_scale * 1000)
self.ui.verticalScale.setValue(v_scale * 1000)
self.ui.primary.setChecked(self.xrandr_info[name]["primary"])
self.ui.enabled.setChecked(self.xrandr_info[name]["enabled"])
self.ui.orientationCombo.setCurrentIndex(self.xrandr_info[name]["orientation"])
self.ui.replicaOf.clear()
self.ui.replicaOf.addItem("None")
for mon in self.xrandr_info:
if mon != name:
self.ui.replicaOf.addItem(mon)
if mon in self.xrandr_info[name]["replica_of"]:
self.ui.replicaOf.setCurrentText(mon)
self.ui.modes.blockSignals(False)
def scale_changed(self):
self.ui.horizontalScaleLabel.setText(
f"{int(self.ui.horizontalScale.value()/10)}%"
)
self.ui.verticalScaleLabel.setText(f"{int(self.ui.verticalScale.value()/10)}%")
self.mode_changed() # Not really, but it's the same thing
def main():
app = QApplication(sys.argv)
ui_file = QFile(os.path.join(os.path.dirname(__file__), "main.ui"))
ui_file.open(QFile.ReadOnly)
loader = QUiLoader()
window = Window(loader.load(ui_file))
sys.exit(app.exec_())
if __name__ == "__main__":
main()