1
0
Fork 0
mirror of https://github.com/kbumsik/VirtScreen.git synced 2025-03-09 15:40:18 +00:00

Python pacakge structure and installing it as python packages

This commit is contained in:
Bumsik Kim 2018-05-20 00:42:58 -04:00
parent 9001fee975
commit f87702f638
19 changed files with 264 additions and 31 deletions

View file

@ -0,0 +1,294 @@
import QtQuick 2.10
import QtQuick.Controls 2.3
import QtQuick.Controls.Material 2.3
import QtQuick.Layouts 1.3
import QtQuick.Window 2.2
import VirtScreen.Backend 1.0
ApplicationWindow {
id: window
visible: false
flags: Qt.FramelessWindowHint
title: "Basic layouts"
property int theme_color: settings.theme_color
Material.theme: Material.Light
Material.primary: theme_color
Material.accent: theme_color
// Material.background: Material.Grey
width: 380
height: 540
property int margin: 10
property int popupWidth: width - 26
// hide screen when loosing focus
property bool autoClose: true
property bool ignoreCloseOnce: false
onAutoCloseChanged: {
// When setting auto close disabled and then enabled again, we need to
// ignore focus change once. Otherwise the window always is closed one time
// even when the mouse is clicked in the window.
if (!autoClose) {
ignoreCloseOnce = true;
}
}
onActiveFocusItemChanged: {
if (autoClose && !ignoreCloseOnce && !activeFocusItem && !sysTrayIcon.clicked) {
this.hide();
}
if (ignoreCloseOnce && autoClose) {
ignoreCloseOnce = false;
}
}
menuBar: ToolBar {
id: toolbar
font.weight: Font.Medium
font.pointSize: 11 //parent.font.pointSize + 1
RowLayout {
anchors.fill: parent
anchors.leftMargin: margin + 10
Label {
id: vncStateLabel
color: "white"
text: vncStateText.text
}
ToolButton {
id: menuButton
anchors.right: parent.right
text: qsTr("⋮")
contentItem: Text {
text: parent.text
font: parent.font
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
onClicked: menu.open()
Menu {
id: menu
y: toolbar.height
MenuItem {
text: qsTr("&Preference")
onTriggered: {
preferenceLoader.active = true;
}
}
MenuItem {
text: qsTr("&About")
onTriggered: {
aboutDialog.open();
}
}
MenuItem {
text: qsTr("&Quit")
onTriggered: quitAction.onTriggered()
}
}
}
}
}
header: TabBar {
id: tabBar
position: TabBar.Footer
// Material.primary: Material.Teal
currentIndex: 0
TabButton {
text: qsTr("Display")
}
TabButton {
text: qsTr("VNC")
}
}
footer: ProgressBar {
z: 1
indeterminate: backend.vncState == Backend.WAITING
value: backend.vncState == Backend.CONNECTED ? 1 : 0
}
Popup {
id: busyDialog
modal: true
closePolicy: Popup.NoAutoClose
x: (parent.width - width) / 2
y: parent.height / 2 - height
BusyIndicator {
anchors.fill: parent
running: true
}
background: Rectangle {
color: "transparent"
implicitWidth: 100
implicitHeight: 100
// border.color: "#444"
}
}
Dialog {
id: aboutDialog
focus: true
x: (parent.width - width) / 2
y: (parent.width - height) / 2 //(window.height) / 2
width: popupWidth
ColumnLayout {
anchors.fill: parent
Text {
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
font { weight: Font.Bold; pointSize: 15 }
text: "VirtScreen"
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
text: "Make your iPad/tablet/computer<br/>as a secondary monitor.<br/>"
}
Text {
text: "- <a href='https://github.com/kbumsik/VirtScreen'>Project Website</a>"
onLinkActivated: Qt.openUrlExternally(link)
}
Text {
text: "- <a href='https://github.com/kbumsik/VirtScreen/issues'>Issues & Bug Report</a>"
onLinkActivated: Qt.openUrlExternally(link)
}
Text {
font { pointSize: 10 }
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
lineHeight: 0.7
text: "<br/>Copyright © 2018 Bumsik Kim <a href='https://kbumsik.io/'>Homepage</a><br/>"
onLinkActivated: Qt.openUrlExternally(link)
}
Text {
font { pointSize: 9 }
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
text: "This program comes with absolutely no warranty.<br/>" +
"See the <a href='https://github.com/kbumsik/VirtScreen/blob/master/LICENSE'>" +
"GNU General Public License, version 3</a> for details."
onLinkActivated: Qt.openUrlExternally(link)
}
}
}
Dialog {
id: passwordDialog
title: "New password"
focus: true
modal: true
standardButtons: Dialog.Ok | Dialog.Cancel
x: (parent.width - width) / 2
y: (parent.width - height) / 2 //(window.height) / 2
width: popupWidth
ColumnLayout {
anchors.fill: parent
TextField {
id: passwordFIeld
focus: true
anchors.left: parent.left
anchors.right: parent.right
placeholderText: "New Password";
echoMode: TextInput.Password;
}
Keys.onPressed: {
event.accepted = true;
if (event.key == Qt.Key_Return || event.key == Qt.Key_Enter) {
passwordDialog.accept();
}
}
}
onAccepted: {
backend.createVNCPassword(passwordFIeld.text);
passwordFIeld.text = "";
}
onRejected: passwordFIeld.text = ""
}
Dialog {
id: errorDialog
title: "Error"
focus: true
modal: true
standardButtons: Dialog.Ok
x: (parent.width - width) / 2
y: (parent.width - height) / 2 //(window.height) / 2
width: popupWidth
height: 310
ColumnLayout {
anchors.fill: parent
ScrollView {
anchors.fill: parent
TextArea {
// readOnly: true
selectByMouse: true
Layout.fillWidth: true
// wrapMode: Text.WordWrap
text: errorText.text
onTextChanged: {
if (text) {
busyDialog.close();
errorDialog.open();
}
}
}
ScrollBar.vertical: ScrollBar {
// parent: ipListView.parent
anchors.top: parent.top
anchors.left: parent.right
anchors.bottom: parent.bottom
policy: ScrollBar.AlwaysOn
}
ScrollBar.horizontal: ScrollBar {
// parent: ipListView.parent
anchors.top: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
policy: ScrollBar.AlwaysOn
}
}
}
}
Loader {
id: preferenceLoader
active: false
source: "preferenceDialog.qml"
onLoaded: {
item.onClosed.connect(function() {
preferenceLoader.active = false;
});
}
}
SwipeView {
anchors.top: tabBar.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: margin
clip: true
currentIndex: tabBar.currentIndex
// in the same "qml" folder
DisplayPage {}
VncPage {}
}
}

View file

@ -0,0 +1,138 @@
import QtQuick 2.10
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import VirtScreen.Backend 1.0
ColumnLayout {
GroupBox {
title: "Virtual Display"
Layout.fillWidth: true
enabled: backend.virtScreenCreated ? false : true
ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
RowLayout {
Label { text: "Width"; Layout.fillWidth: true }
SpinBox {
value: settings.virt.width
from: 640
to: 1920
stepSize: 1
editable: true
onValueModified: {
settings.virt.width = value;
}
textFromValue: function(value, locale) { return value; }
}
}
RowLayout {
Label { text: "Height"; Layout.fillWidth: true }
SpinBox {
value: settings.virt.height
from: 360
to: 1080
stepSize : 1
editable: true
onValueModified: {
settings.virt.height = value;
}
textFromValue: function(value, locale) { return value; }
}
}
RowLayout {
Label { text: "Portrait Mode"; Layout.fillWidth: true }
Switch {
checked: settings.virt.portrait
onCheckedChanged: {
settings.virt.portrait = checked;
}
}
}
RowLayout {
Label { text: "HiDPI (2x resolution)"; Layout.fillWidth: true }
Switch {
checked: settings.virt.hidpi
onCheckedChanged: {
settings.virt.hidpi = checked;
}
}
}
RowLayout {
anchors.left: parent.left
anchors.right: parent.right
Label { id: deviceLabel; text: "Device"; }
ComboBox {
id: deviceComboBox
anchors.left: deviceLabel.right
anchors.right: parent.right
anchors.leftMargin: 100
textRole: "name"
model: backend.screens
currentIndex: backend.virtScreenIndex
onActivated: function(index) {
backend.virtScreenIndex = index
}
delegate: ItemDelegate {
width: deviceComboBox.width
text: modelData.name
font.weight: deviceComboBox.currentIndex === index ? Font.DemiBold : Font.Normal
highlighted: ListView.isCurrentItem
enabled: modelData.connected ? false : true
}
}
}
}
}
ColumnLayout {
Layout.margins: margin / 2
Button {
id: virtScreenButton
Layout.fillWidth: true
text: virtScreenAction.text
highlighted: true
enabled: virtScreenAction.enabled
onClicked: {
busyDialog.open();
virtScreenAction.onTriggered();
connectOnce(backend.onVirtScreenCreatedChanged, function(created) {
busyDialog.close();
});
}
}
Button {
id: displaySettingButton
Layout.fillWidth: true
text: "Open Display Setting"
enabled: backend.virtScreenCreated ? true : false
onClicked: {
busyDialog.open();
window.autoClose = false;
if (backend.vncState != Backend.OFF) {
console.log("vnc is running");
var restoreVNC = true;
if (autostart) {
autostart = false;
var restoreAutoStart = true;
}
}
connectOnce(backend.onDisplaySettingClosed, function() {
window.autoClose = true;
busyDialog.close();
if (restoreAutoStart) {
autostart = true;
}
if (restoreVNC) {
backend.startVNC(settings.vnc.port);
}
});
backend.stopVNC();
backend.openDisplaySetting();
}
}
}
RowLayout {
// Empty layout
Layout.fillHeight: true
}
}

105
virtscreen/qml/VncPage.qml Normal file
View file

@ -0,0 +1,105 @@
import QtQuick 2.10
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import VirtScreen.Backend 1.0
ColumnLayout {
GroupBox {
title: "VNC Server"
Layout.fillWidth: true
enabled: backend.vncState == Backend.OFF ? true : false
ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
RowLayout {
Label { text: "Port"; Layout.fillWidth: true }
SpinBox {
value: settings.vnc.port
from: 1
to: 65535
stepSize: 1
editable: true
onValueModified: {
settings.vnc.port = value;
}
textFromValue: function(value, locale) { return value; }
}
}
RowLayout {
anchors.left: parent.left
anchors.right: parent.right
Label { text: "Password"; Layout.fillWidth: true }
Button {
text: "Delete"
font.capitalization: Font.MixedCase
highlighted: false
enabled: backend.vncUsePassword
onClicked: backend.deleteVNCPassword()
}
Button {
text: "New"
font.capitalization: Font.MixedCase
highlighted: true
enabled: !backend.vncUsePassword
onClicked: passwordDialog.open()
}
}
}
}
RowLayout {
Layout.fillWidth: true
Layout.margins: margin / 2
Button {
id: vncButton
Layout.fillWidth: true
text: vncAction.text
highlighted: true
enabled: vncAction.enabled
onClicked: vncAction.onTriggered()
}
CheckBox {
checked: autostart
onToggled: {
autostart = checked;
if ((checked == true) && (backend.vncState == Backend.OFF) &&
backend.virtScreenCreated) {
backend.startVNC(settings.vnc.port);
}
}
}
Label { text: "Auto"; }
}
GroupBox {
title: "Available IP addresses"
Layout.fillWidth: true
implicitHeight: 150
ColumnLayout {
anchors.fill: parent
ListView {
id: ipListView
anchors.fill: parent
clip: true
ScrollBar.vertical: ScrollBar {
parent: ipListView.parent
anchors.top: ipListView.top
anchors.right: ipListView.right
anchors.bottom: ipListView.bottom
policy: ScrollBar.AlwaysOn
}
model: backend.ipAddresses
delegate: TextEdit {
text: modelData
readOnly: true
selectByMouse: true
anchors.horizontalCenter: parent.horizontalCenter
font.pointSize: 12
}
}
}
}
RowLayout {
// Empty layout
Layout.fillHeight: true
}
}

214
virtscreen/qml/main.qml Normal file
View file

@ -0,0 +1,214 @@
import QtQuick 2.10
import Qt.labs.platform 1.0
import VirtScreen.DisplayProperty 1.0
import VirtScreen.Backend 1.0
Item {
property alias window: mainLoader.item
property var settings: JSON.parse(backend.settings)
property bool autostart: settings.vnc.autostart
function switchVNC () {
if ((backend.vncState == Backend.OFF) && backend.virtScreenCreated) {
backend.startVNC(settings.vnc.port);
}
}
onAutostartChanged: {
if (autostart) {
backend.onVirtScreenCreatedChanged.connect(switchVNC);
backend.onVncStateChanged.connect(switchVNC);
} else {
backend.onVirtScreenCreatedChanged.disconnect(switchVNC);
backend.onVncStateChanged.disconnect(switchVNC);
}
}
// virtscreen.py backend.
Backend {
id: backend
onVncStateChanged: {
if (backend.vncState == Backend.ERROR) {
autostart = false;
}
}
}
// Timer object and function
Timer {
id: timer
function setTimeout(cb, delayTime) {
timer.interval = delayTime;
timer.repeat = false;
timer.triggered.connect(cb);
timer.triggered.connect(function() {
timer.triggered.disconnect(cb);
});
timer.start();
}
}
// One-shot signal connect
function connectOnce (signal, slot) {
var f = function() {
slot.apply(this, arguments);
signal.disconnect(f);
}
signal.connect(f);
}
Loader {
id: mainLoader
active: false
source: "AppWindow.qml"
onStatusChanged: {
console.log("Loader Status Changed.", status);
if (status == Loader.Null) {
gc();
// This cause memory leak at this moment.
// backend.clearCache();
}
}
onLoaded: {
window.onVisibleChanged.connect(function(visible) {
if (!visible) {
console.log("Unloading ApplicationWindow...");
mainLoader.active = false;
}
});
// Move window to the corner of the primary display
var primary = backend.primary;
var width = primary.width;
var height = primary.height;
var cursor_x = backend.cursor_x - primary.x_offset;
var cursor_y = backend.cursor_y - primary.y_offset;
var x_mid = width / 2;
var y_mid = height / 2;
var x = width - window.width; //(cursor_x > x_mid)? width - window.width : 0;
var y = (cursor_y > y_mid)? height - window.height : 0;
x += primary.x_offset;
y += primary.y_offset;
window.x = x;
window.y = y;
window.show();
window.raise();
window.requestActivate();
timer.setTimeout (function() {
sysTrayIcon.clicked = false;
}, 200);
}
}
// Sytray Icon
SystemTrayIcon {
id: sysTrayIcon
iconSource: backend.vncState == Backend.CONNECTED ? "../icon/icon_tablet_on.png" :
backend.virtScreenCreated ? "../icon/icon_tablet_off.png" :
"../icon/icon.png"
visible: true
property bool clicked: false
onMessageClicked: console.log("Message clicked")
Component.onCompleted: {
// without delay, the message appears in a wierd place
timer.setTimeout (function() {
showMessage("VirtScreen is running",
"The program will keep running in the system tray.\n" +
"To terminate the program, choose \"Quit\" in the \n" +
"context menu of the system tray entry.");
}, 1500);
}
onActivated: function(reason) {
if (reason == SystemTrayIcon.Context) {
return;
}
sysTrayIcon.clicked = true;
mainLoader.active = true;
}
menu: Menu {
MenuItem {
id: vncStateText
text: !backend.virtScreenCreated ? "Enable Virtual Screen first" :
backend.vncState == Backend.OFF ? "Turn on VNC Server in the VNC tab" :
backend.vncState == Backend.ERROR ? "Error occurred" :
backend.vncState == Backend.WAITING ? "VNC Server is waiting for a client..." :
backend.vncState == Backend.CONNECTED ? "Connected" :
"Server state error!"
}
MenuItem {
id: errorText
visible: (text)
text: ""
Component.onCompleted : {
backend.onError.connect(function(errMsg) {
errorText.text = ""; // To trigger onTextChanged signal
errorText.text = errMsg;
});
}
}
MenuItem {
separator: true
}
MenuItem {
id: virtScreenAction
text: backend.virtScreenCreated ? "Disable Virtual Screen" : "Enable Virtual Screen"
enabled: autostart ? true :
backend.vncState == Backend.OFF ? true : false
onTriggered: {
// Give a very short delay to show busyDialog.
timer.setTimeout (function() {
if (!backend.virtScreenCreated) {
backend.createVirtScreen(settings.virt.width, settings.virt.height,
settings.virt.portrait, settings.virt.hidpi);
} else {
// If auto start enabled, stop VNC first then
if (autostart && (backend.vncState != Backend.OFF)) {
autostart = false;
connectOnce(backend.onVncStateChanged, function() {
console.log("autoOff called here", backend.vncState);
if (backend.vncState == Backend.OFF) {
console.log("Yes. Delete it");
backend.deleteVirtScreen();
autostart = true;
}
});
backend.stopVNC();
} else {
backend.deleteVirtScreen();
}
}
}, 200);
}
}
MenuItem {
id: vncAction
text: autostart ? "Auto start enabled" :
backend.vncState == Backend.OFF ? "Start VNC Server" : "Stop VNC Server"
enabled: autostart ? false :
backend.virtScreenCreated ? true : false
onTriggered: backend.vncState == Backend.OFF ? backend.startVNC(settings.vnc.port) : backend.stopVNC()
}
MenuItem {
separator: true
}
MenuItem {
text: "Open VirtScreen"
onTriggered: sysTrayIcon.onActivated(SystemTrayIcon.Trigger)
}
MenuItem {
id: quitAction
text: qsTr("&Quit")
onTriggered: {
settings.vnc.autostart = autostart;
backend.settings = JSON.stringify(settings, null, 4);
backend.quitProgram();
}
}
}
}
}

View file

@ -0,0 +1,58 @@
import QtQuick 2.10
import QtQuick.Controls 2.3
import QtQuick.Controls.Material 2.3
import QtQuick.Layouts 1.3
Dialog {
id: preferenceDialog
title: "Preference"
focus: true
modal: true
visible: true
standardButtons: Dialog.Ok
x: (window.width - width) / 2
y: (window.width - height) / 2
width: popupWidth
ColumnLayout {
anchors.fill: parent
RowLayout {
anchors.left: parent.left
anchors.right: parent.right
Label { id: themeColorLabel; text: "Theme Color"; }
ComboBox {
id: themeColorComboBox
anchors.left: themeColorLabel.right
anchors.right: parent.right
anchors.leftMargin: 50
Material.background: currentIndex
Material.foreground: "white"
textRole: "name"
model: [{"value": Material.Red, "name": "Red"}, {"value": Material.Pink, "name": "Pink"},
{"value": Material.Purple, "name": "Purple"},{"value": Material.DeepPurple, "name": "DeepPurple"},
{"value": Material.Indigo, "name": "Indigo"}, {"value": Material.Blue, "name": "Blue"},
{"value": Material.LightBlue, "name": "LightBlue"}, {"value": Material.Cyan, "name": "Cyan"},
{"value": Material.Teal, "name": "Teal"}, {"value": Material.Green, "name": "Green"},
{"value": Material.LightGreen, "name": "LightGreen"}, {"value": Material.Lime, "name": "Lime"},
{"value": Material.Yellow, "name": "Yellow"}, {"value": Material.Amber, "name": "Amber"},
{"value": Material.Orange, "name": "Orange"}, {"value": Material.DeepOrange, "name": "DeepOrange"},
{"value": Material.Brown, "name": "Brown"}, {"value": Material.Grey, "name": "Grey"},
{"value": Material.BlueGrey, "name": "BlueGrey"}]
currentIndex: settings.theme_color
onActivated: function(index) {
window.theme_color = index;
settings.theme_color = index;
}
delegate: ItemDelegate {
width: parent.width
text: modelData.name + (themeColorComboBox.currentIndex === index ? " (Current)" : "")
Material.foreground: "white"
background: Rectangle {
color: Material.color(modelData.value)
}
}
}
}
}
onAccepted: {}
onRejected: {}
}