Kalmat / PyMonCtl

Cross-Platform module which provides a set of features to get info on and control monitors/screens/displays.
BSD 3-Clause "New" or "Revised" License
4 stars 3 forks source link

Test runs but does not restore correct monitor setup #1

Open Danie10 opened 1 year ago

Danie10 commented 1 year ago

Ran tests on Manjaro Linux, and after doing a force reinstall of PyMonCtl. Reason is I was testing the newest version of OBS Zoom and Follow script, which was giving an error ModuleNotFoundError: No module named 'pymonctl' in OBS. But I see that PyMonCtl is indeed running. So I decided to run the test script for PyMonCtl with python test_pymonctl.py.

I do see it going through the various changes on each monitor. At some point, though, it mirrored my monitor 2 to the display on monitor 3. When it exits the test run, it ended with this output below, but had lost the original config of my monitor order, and monitor 2 was off and no longer recognised by the OS (I imagine it will restore after a reboot):

IS ATTACHED?: True
DETACH
IS ATTACHED?: False
ATTACH
IS ATTACHED?: True

NAME DP-4
HANDLE/ID: 475
IS PRIMARY: False
SIZE: None
POSITION: None
Traceback (most recent call last):
  File "/home/danie/Downloads/OBS Studio/PyMonCtl-master/tests/test_pymonctl.py", line 38, in <module>
    print("FREQUENCY:", monitor.frequency)
  File "/home/danie/.pyenv/versions/3.10.11/lib/python3.10/site-packages/pymonctl/_pymonctl_linux.py", line 366, in frequency
    crtcInfo = randr.get_crtc_info(display, outputInfo.crtc, res.config_timestamp)
  File "/home/danie/.pyenv/versions/3.10.11/lib/python3.10/site-packages/Xlib/ext/randr.py", line 758, in get_crtc_info
    return GetCrtcInfo (
  File "/home/danie/.pyenv/versions/3.10.11/lib/python3.10/site-packages/Xlib/protocol/rq.py", line 1368, in __init__
    self.reply()
  File "/home/danie/.pyenv/versions/3.10.11/lib/python3.10/site-packages/Xlib/protocol/rq.py", line 1388, in reply
    raise self._error
Xlib.error.XError: <class 'Xlib.error.XError'>: code = 148, resource_id = 0, sequence_number = 307, major_opcode = 140, minor_opcode = 20
Kalmat commented 1 year ago

Hi! Thank you for your feedback!

Worry not. The missing monitor will be there again after reboot (or if you just unplug and re-plug it to your system). The config, unfortunately, will not be restored. Running the tests will end up with a totally new config every single time (even though it doesn't crash).... Good point to work on (I mean, restore initial config when tests finisth)!

Besides, the tests are built for a 2-monitor setup (unfortunately I have no access to three monitors), so the weird behavior you have detected (like mirroring) may be caused by this.

I have not tested in Manjaro Linux (I will try to install it on my system. Which desktop manager / window manager are you using?), but perhaps Manjaro is assigning a new monitor handle after attaching / detaching a monitor (Windows works this way), so this is why it doesn't find it anymore and crashes.

In short:

Danie10 commented 1 year ago

Thanks @Kalmat yes you are right. It seemed to assume two monitors. Did not work after reboot, but I did get it fixed. The script could maybe test for a monitor count before running, and maybe trying to read the existing config to save.

I'm using Manjaro KDE. Yes, I'm really using it on OBS (although I see it has really impressive monitor control).

Why I ran your tests was that OBS was reporting it could not find PyMonCtl, but it is definitely installed and 'works'. But it is probably an issue then I should report on that project (the not finding).

Kalmat commented 1 year ago

It didn't detect the monitor after reboot??? Well... I definitely have to test that by my own! I also have to test the crash after detaching/attaching a monitor (I guess the monitor handle changes, like in Windows).

I am working in a "more-than-two" tests script, which in addition restores the original config right before finishing (if it crashes... it will not be able, unfortunately).

Thank you for your feedback again!

Kalmat commented 1 year ago

Hi @Danie10! I have uploaded a new version (v0.6) to github (not to PyPi yet since I have to make more tests). This new version should take into account 2 or more monitors, and restore initial monitors config. I have tested it on Kubuntu (which shares the KDE environment with your setup) and it doesn't crash... If you have the time it will help me a lot if you could test this new version on your system to try to diagnose any issue that may arise.

Thank you!

Danie10 commented 1 year ago

@Kalmat OK still working as expected. I did the --force-reinstall again as the version had not changed, and downloaded the code to be sure I run the correct test script. It starts off fine on my 3rd monitor, then moves to the 2nd monitor, but ended with only my 1st monitor visible as other two had their resolution set to None. I recovered now by just running Nvidia-Settings and re-enabling the resolutions.

I then tried running the script again but directing output to a file. Problem this time is it next finished the script. It seemed to just stop at some point. I did a CTRL-C to exit, and included that terminal output at the end of the attached file. Maybe this helps shed some light.

I am using a single Nvidia RTX-2060 GPU with the monitors connected to available outputs, so it is a mix of one HDMI and two DP connectors.

Attached is the output file and a screenshot of my Nvidia layout.

Screenshot_20230916_152217

pymonctl-output.txt

Kalmat commented 1 year ago

Thank you so much!!! This is pure gold!

I think that attaching / detaching a monitor somehow affects the system monitor management (the error actually comes from Xlib itsefl). I will definitely have to install Manjaro/KDE to find a solution.

Thank you again!

Kalmat commented 1 year ago

Hi again! Just in case you're curious, PyMonCtl v0.7 is now working on my Manjaro/KDE system. Turns out that Manjaro/KDE was not managing monitors as automatically as Ubuntu/GNOME does... after some tweaking, it should be working OK now!

Thank you for your feedback and help!

Danie10 commented 1 year ago

The test script is showing the monitor count and primary monitor correctly, but fails with error below before it starts to do the tests:

python test_pymonctl.py
MONITORS COUNT: 3
PRIMARY MONITOR: DP-1

Traceback (most recent call last):
  File "/home/danie/Downloads/OBS Studio/PyMonCtl-0.7/tests/test_pymonctl.py", line 24, in <module>
    monDict = pmc.getAllMonitorsDict()
  File "/home/danie/.pyenv/versions/3.10.11/lib/python3.10/site-packages/pymonctl/_main.py", line 75, in getAllMonitorsDict
    return _getAllMonitorsDict()
  File "/home/danie/.pyenv/versions/3.10.11/lib/python3.10/site-packages/pymonctl/_pymonctl_linux.py", line 54, in _getAllMonitorsDict
    wx, wy, wr, wb = wa[0], wa[1], wa[2], wa[3]
TypeError: 'NoneType' object is not subscriptable
Kalmat commented 1 year ago

Thank you for helping out! Really bad news. In my system (an actual Manjaro/KDE with 2 monitors, not a VM), it works smoothly, but in yours it seems to be failing to retrieve WORKAREA property... the question is why! It didn't fail before, right? But I didn't change that part... really weird to me. Totally lost.

Danie10 commented 1 year ago

Yes it's odd. Three monitors should not make a difference. I'm using X11 (not Wayland) with Nvidia GPU, on AMD CPU. And yet previously it was actually going through all the tests. I did do a force reinstall and saw it confirmed the version (as it also shows in the errors).

I vaguely remember with something showing the available resolutions, that there was one that maybe said not available.

Kalmat commented 1 year ago

No, three monitors should not cause the problem. What it is really weird is that it worked before, but not now. That part is identical, and basically consists in a very low-level Xlib function. Another unexplainable point is that it works OK in my Manjaro/KDE, Ubuntu/GNOME, KUbuntu (KDE) and Mint/Cinnamon installs (two monitors, on actual installs for the first two OS, and VMs for the last two ones).

Just shooting in the air:

monDict = pmc.getAllMonitorsDict()   # The crash is inside this function, retrieving the WORKAREA property
for mon in monDict:
    print(monDict[mon])
print()

Those lines are at the beginning of the script, right after the "PRIMARY MONITOR" print.

Besides, I can prepare a debug wheel, but this can be a back and forth process, so I don't want to ask you too much abusing your time and patience!

Thanks a lot for your help so far!

Danie10 commented 12 months ago

OK interesting... I took a little longer as I was also cleaning up my Python - some packages had shown missing dependencies.

  1. After deleting and reinstalling the three packages, and removing the ewmhlib package folder, it still crashed at the beginning.
  2. I then commented out that code, and it ran for a few minutes working on each monitor, but did hang eventually, and I had to force logout to get back control. It had disabled the third monitor and could not recover at that point.
  3. I ran it again but directed output to the output.txt file. Actually it went all the way through and finished the test script. All monitors were actually on, but my mouse was confined to the right most monitor only. I could though open Nvidia-Settings and restored everything.
  4. I remembered I've had issues with the compositor, so I disabled that, and then ran it again directing output to the output-nocompositor.txt file. It did not finish though but hung at some point, and I noted the Xorg process was consuming a good 7% CPU. Mouse was again confined to 3rd monitor, but although I could use shortcuts to open a terminal screen, there was no response inside the terminal, so I had to force logout again. 3rd monitor though was still active.

Both files are attached, and the screenshot of the processes at end of 4 above.

I'm using the Nvidia proprietary drivers. No monitor changes done apart from restoring from previous tests. But yes there is one odd thing on my setup, and that is when some functions call the compositor like rectangular area screenshots, or opening the compositor settings, it will hang for 30 secs to a minutes, and then respond. Which is why I ran a test too with the compositor disabled.

Issue seems to somehow be with the 3rd monitor. Although earlier in the tests that switches on and off and adjusts contrast etc.

output-nocompositor.txt output.txt 20230923_114135

Danie10 commented 12 months ago

Also still wondering if this is related? When starting OBS Studio with the Zoom and Follow script, that script gives the error that "import pymonctl as pmc ModuleNotFoundError: No module named 'pymonctl'".

Yet the show command returns:

pip3 show pymonctl
Name: PyMonCtl
Version: 0.7
Summary: Cross-Platform toolkit to get info on and control monitors connected
Home-page: https://github.com/Kalmat/PyMonCtl
Author: Kalmat
Author-email: palookjones@gmail.com
License: BSD 3
Location: /home/danie/.pyenv/versions/3.10-dev/lib/python3.10/site-packages
Requires: python-xlib, typing-extensions
Required-by: PyWinCtl
Kalmat commented 11 months ago

Hi! Sorry for my late reply. I've been out these days with no access to my "stuff".

Thank you so much for your help, time and patience!!! It really helps me a lot. Never the less, I am quite lost at this point. Taking a look to output.txt and output-nocompositor.txt files, it seems that the script stops/hangs at the very same point! Right when trying to re-arrange all monitors. I will take a deep look into it just to try to figure out what the problem can be (a pitty I can not test with three monitors... it shouldn't be the problem but, who knows)

Another user has pointed out that some Desktop Managers do not set _NET_WORKAREA property (which is the part you commented), so some functions may crash. But this is not the case for KDE. No idea of why it returns None now (and not before) on your system.

Regarding OBS-Zoom-and-Follow, I can not help you since I don't know that script in detail. I read on the issues board of the github project that tryptech (the author) was going to include PyMonCtl amongst the dependencies... perhaps this is the cause of not finding it in your version?

Please, let me investigate in detail. I will come back to you with any news. And please, let me know if the problem with PyMonCtl and OBS-Zoom-and-Follow is eventually solved. If not, I should work together with tryptech to find the solution.

Thank you SO MUCH!!!

Danie10 commented 11 months ago

Thanks very much, yes that OBS-Zoom-and-Follow issue definitely seems related, and there are issues mentioned with multiple monitors too. So PyMonCtl and PyWinCtl are requirements, but there are odd things going on. The OBS-Zoom-and-Follow script has many users using your two projects. Quite a few have mentioned sticking only to PyWinCtl v0.0.38 as newer versions were breaking things for them. Some of us also have issues with OBS-Zoom-and-Follow script on startup not finding PyWinCtl even though it is installed and found by Python. Thing is tryptech is just closing those issues as he does not support Python dependencies, but it is his script failing, not that yours is not installed. Users are logging issues on the OBS project, and not against the dependency projects.

But OK let's see what you can come up with, as any fixes here may also help on the OBS project side too.

Kalmat commented 11 months ago

Some weird things going around, in deed.

In my opinion, it is a problem of versions and/or paths. Definitely, OBS is not configured to use the right Python installation in case there are several of them in the system, or maybe the paths are not properly set. These are usually the only reasons a module can not be found. Perhaps this can help?

Regarding the users using v0.0.38, maybe it is because of the Python version they have installed (3.6.x?), since higher versions of PyWinCtl are modified to be "typed", so they require typing_extensions module which is available only from Python 3.7 and above. The problem with this version (v0.0.38) is that it is not multi-monitor ready.

I will try to contact tryptech to try to find a solution together.

Thank you so much. I do really appreciate your help!!!

EDIT: I just remembered you are using Linux. Linux uses to come with a built-in version of Python, which you can not modify. It typically complains about "error: externally-managed-environment" when you try to install any module on it... Just guessing: Is it possible that OBS is using this default, built-in version instead of the one in which PyMonCtl is already installed?

Danie10 commented 11 months ago

Thanks, I saw your other message to tryptech, and it would really help that OBS project to have some tips and guidance on their page, as it relies heavily on your modules. Building up some tips on usage really helps others.

They had recommended Python 3.10.x actually. You are right, we can't set the Python location/version inside OBS on Linux - it should use the global Python on Linux. Possibly you've also pointed me towards that module not found issue - I'm using a specific build of OBS that was supposed to include a lot, but I suspect it may have issues with additional 3rd party scripts being added. I'll try a more standard OBS build again, and I suspect that module not found error may come right.

Many are using multiple monitors with OBS, so I think that project specifically picks up more issues around multi-monitor use. A lot of issues reported there were about the zoom being stuck up in a corner off the screen they were working on.

Back on your module though - I don't think the OBS setup of mine is affecting that, as I was not using OBS when testing. I used PyEnv to set my global Python to 3.10-Dev.

Kalmat commented 11 months ago

Fully agree.

Most OBS users are not developers nor even advanced users, so we should somehow facilitate all tasks. Unfortunately, I know nothing about OBS, but tryptech does, so we are the perfect team!

Besides, most OBS users have a multi-monitor setup, so this is a key feature. Sadly, it took some time to tryptech and myself to have an acceptable version covering that... Youth problems shall arise! HAHAHAHA! But we will solve them...

Back to the module, fully agree again. I don't think OBS has nothing to do with the testing. My next suspect is Nvidia drivers (that I don't use at the moment), so I will test as soon as I can with Nvidia drivers enabled (I already have an Nvidia card in my system).

Nice talk!!!

Kalmat commented 11 months ago

Hi! I know you are also watching this issue, but just in case, I found something that can be useful to solve your issue (and hopefully some others). See this if you haven't already!

Danie10 commented 11 months ago

Still not working for me but I've had the updates break for the Tytan version of OBS I was using. So I've had to true a few reinstalls myself. I was using a version that modded the FFMPEG as well as VLC files. Now I'm on an install version that uses the standard OS FFMPEG and VLC. But still shows the module not found for me when installing the OBS script. I'll have another work through it though by checking all the Python scripts and modules are up to date.

The Flatpak install would be ideal as it containerises all the dependencies, but you can't install scripts in it. We need to ask the maintainer to include that OBS Zoom and Follow script. It may be a better way to go though, as everyone gets a working install that is identical. So many of these issues are about out of date etc dependencies.

Kalmat commented 11 months ago

About Flatpak sounds good. Hopefully they eventually include Zoom and Follow script.

Did you try to install OBS directly from their repo? It worked for me. It automatically found Python (3.10) and all dependencies installed in it (PyWinCtl and PyMonCtl).

Danie10 commented 11 months ago

I'm on Arch based Linux so can't really use the Deb PPAs. I did try the Flatpak, but I can try it again and see if I can manually push the script in. The Git version that I installed is supposed to pull in the required files for OBS though. But for Linux it uses the system Python and mine is set for 3.10 still. Still not seeing why the system Python sees the Python module, but when running in OBS the script does not see it. I'll certainly need to do more testing to see what works.

Thinking that there should be an OBS script that runs a few Python tests like version, location of Python executable, what modules installed, etc for troubleshooting. Such a script can be run by any user inside OBS and then it says what it is seeing of Python.

Kalmat commented 11 months ago

Oh, it's true! I forgot you were on Manjaro, sorry!

Regarding the script, it should be more like a bash shell, right? If it is a Python script, it won't run either in case the config is wrong. Do you think we can build such shell?

Danie10 commented 11 months ago

Actually needs to execute from both within OBS script and a system Python script from terminal to compare. But the one in OBS needs to be a Python script that calls the Python version, maybe lists loaded modules and even locations if possible. Idea is to interrogate Python as far as possible, from where it is supposed to execute within OBS. That can be run by anyone who can load/install a script in OBS. It will highlight version problems, as well as possible odd locations of modules etc.

In a perfect world, running the same Python script from the terminal should show the identical information. It could show Windows users that the Python exe location is set wrong for example.

Danie10 commented 10 months ago

OK here is some really rough stuff that may give an idea of what I was thinking. The theory is to have a Python script that can be run from the command prompt, and also loaded into OBS Studio scripts. It will out put what version of Python, the location of the executable, and a list of installed modules. The theory is to help troubleshoot possible version differences etc between what the OS sees and what OBS sees... So

Any of these can be named say PyVer.py and then run. They do the same thing but I could not refine yet ptoperly. Would be nice to see the python modules in a vertical column with versions as well as possibly locations.

Script One:

import pkg_resources

print("Python version:", sys.version)
print("Python executable location:", sys.executable)
print("Python Modules installed:")

packages = pkg_resources.working_set
packages_list = ["%s==%s" % (i.key, i.version) for i in packages]
print(packages_list)

Script Two:

import pkgutil

print("Python version:", sys.version)
print("Python executable location:", sys.executable)
print("Python Modules installed:")

for i in pkgutil.iter_modules(None): # returns a tuple (path, package_name, ispkg_flag)
    print(i[1]) #or you can append it to a list

Script Three:

import pkg_resources

print("Python version:", sys.version)
print("Python executable location:", sys.executable)
print("Python Modules installed:")

installed_packages = pkg_resources.working_set
installed_packages_list = sorted(["%s==%s" % (i.key, i.version)
     for i in installed_packages])
print(installed_packages_list)
Kalmat commented 10 months ago

Hi again!

The scripts look great in order to help users! Nice job!

After running all of them, I found out that pkg_resources complains because it is deprecated. On the other hand, I found no way to retrieve the module version when using pkgutil. Finally, the location of the modules use to be common for them all, so perhaps the result is clearer printing it in a separate line, and not to include it in the modules list.

I tried to run pip directly, which is more "standard" and it automatically formats the output in a fancy way. In case a user has no pip available... he will never get any of this working!! HAHAHA! Please try this and let me know what you think:

import subprocess
import site
import sys

print("{:<18} {}".format("Python version:", sys.version))
print("{:<18} {}".format("Python location:", sys.executable))
print("Modules locations:")
for location in site.getsitepackages():
    print("\t", str(location))
print()

try:
    subprocess.call([sys.executable, '-m', 'pip', 'list'])
except:
    print("ERROR running pip. Please install pip according to your OS:")
    print("\tWINDOWS: https://www.geeksforgeeks.org/how-to-install-pip-on-windows/" )
    print("\tLINUX: https://itsfoss.com/install-pip-ubuntu/")
    print("\tMACOS: https://phoenixnap.com/kb/install-pip-mac")
Danie10 commented 10 months ago

Hi there, yes that update looks really good. Ran it with the |more to see everything on command line. It reads well and also shows location of packages.

Tried on OBS but it does not print the list of packages with versions out. It does show the paths and Python version. So OBS is executing /usr/bin/python3 whilst the command line shows /usr/bin/python. They should be the same as I think the commands are just redirects. But it can show up small differences that may impact.

My command line run does show PyWinCtl 0.0.43 but I'm not seeing MyMonCtl so it is telling me something. Now this will be useful to use to verify quickly of something has come right or not.

But even without the list of packages in OBS script, it is really useful to verify Python versions, locations, paths, etc.

Kalmat commented 10 months ago

Hi again! I'm trying to test myself in my Ubuntu install. Can I ask how do you run the script within OBS? I've been able to load it into the Scripts section, but I don't see any option to execute it!

Danie10 commented 10 months ago

Yes there is no run inside OBS for it. What I did was load it and then open the scripts log to view output. If you are running it a second time, then can press the reload scripts button in OBS scripts.

Sent with Proton Mail secure email.

On Saturday, 04 November 2023 at 14:23, Kalmat @.***> wrote:

Hi again! I'm trying to test myself in my Ubuntu install. Can I ask how do you run the script within OBS? I've been able to load it into the Scripts section, but I don't see any option to execute it!

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.[https://mail.proton.me/api/core/v4/images?Url=https%3A%2F%2Fgithub.com%2Fnotifications%2Fbeacon%2FAAIZVPRVXKGREQBDMVHGWLTYCYXUJA5CNFSM6AAAAAA4UZST5KWGG33NNVSW45C7OR4XAZNMJFZXG5LFINXW23LFNZ2KUY3PNVWWK3TUL5UWJTTK4WHGG.gif&DryRun=0&UID=uzb3qydlq7eteik3ioqzmunxagzkw3gw]

Kalmat commented 10 months ago

Thank you! But still don't know how to run any script or retrieve its output within OBS, sorry for my ignorance. I've been reading this, but it is still unclear. Please, help. Do you mean I have to load them in the Tools -> Scripts section (which I already did) and then... what? Should I record or broadcast anything in order to automatically execute the scripts or should I do click or activate anything else? Where is the reload scripts button, in the Tools -> Scripts section? Where are the scripts logs stored?

Again, sorry for my ignorance, and thank you for your help!

Danie10 commented 10 months ago

No problem, I fumbled around to get it to sort of work. Just go to that Tools/Scripts view. You should stand on the script you already added. Click on Script Log to see any output. Can click on Clear in that view to delete anything there.

To recheck, on the Scripts view, stand on that script and click on that reload circular button next to the trash button. Can then click on the Script Log button to again view any output.

So output basically goes into the script log.

Kalmat commented 10 months ago

Thank you for your help! Here it's the problem in my case (and maybe in many other cases):

Captura

Sorry it's in Spanish. I guess it can be understood anyway. The key part is: "Python no está cargado actualmente" (Python is not currently loaded)!!! I am totally sure python.exe and python3.exe are inside that folder.

Totally lost here. I am using last OBS version (29.1.2-64bits), so it should accept newerPython versions, right?

I will later on try in Linux, but this should also work in Windows and macOS...

EDIT: In Windows, I tried installing Python 3.10 in a different folder (a "user" folder, located in Documents\Python), and now it loads (this can be the problem of many users, in case they try to use the version installed from the Microsoft Store). Never the less, the script still doesn't work. Do you think I have to do any additional action or modify the script in any way?

Kalmat commented 10 months ago

Hi again!

I have been thinking about how to help other users... it's not easy in deed, specially in Linux OSs. I have added some checks / instructions to the initial script. Still very preliminary which needs a lot of refinement... Apart from this, I never got it working in OBS (I mean, no output from the script when loaded/reloaded in OBS scripts section neither in Windows nor Ubuntu).

WINDOWS: If you install OBS from the Store, it fails. If you install Python from the Store, OBS will not recognize the path, so you have to install both from their sites. LINUX: A mess... If you have Python3 installed it will be placed at /usr/bin/python3, so new releases (e.g. Python3.10) will be renamed differently (e.g. /usr/bin/python3.10). The Python path can be defined, or not (not sure if this affects OBS in any way). Besides, there is no "Python Config" tab in the Tools -> Scripts section... what version is OBS taking when in Linux? No idea! (You know much more about Manjaro than me, so maybe you can complete that part) MACOS: Pending

Check this and please let me know what you think (again, it's a rough base to start building on!):

import platform
import subprocess
import site
import sys

print("{:<18} {}".format("Python version:", sys.version))
print("{:<18} {}".format("Python location:", sys.executable))
print("Modules locations:")
for location in site.getsitepackages():
    print("\t", str(location))
print()

try:
    subprocess.call([sys.executable, '-m', 'pip', 'list'])
except:
    print("ERROR running pip. Please install pip by following these instructions:")
    if sys.platform == "win32":
        print("\tWINDOWS: https://www.geeksforgeeks.org/how-to-install-pip-on-windows/")
    elif sys.platform == "linux":
        if 'ubuntu' in platform.version().lower():
            print("\tUBUNTU: https://itsfoss.com/install-pip-ubuntu/")
        elif 'manjaro' in platform.version().lower():
            pass
    elif sys.platform == "darwin":
        print("\tMACOS: https://phoenixnap.com/kb/install-pip-mac")

print("\nCHECKING INSTALLATION")
if sys.platform == "win32":
    if "WindowsApps" in sys.executable:
        print("---> Do NOT install Python from Microsoft Store!!!\n")
    if "WindowsApps" in sys.executable or "3.10" not in sys.executable:
        print("---> Python 3.10 is highly recommended")
        print("To install it, download Python 3.10 installer from here: https://www.python.org/downloads/release/python-3100/")
        print("'Windows installer (64-bit)' is recommended in most cases")
        print("During installation, copy Python path, so you can properly configure OBS (Tools -> Scripts -> Python Config)")
        print("\n---> Once Python is installed, configure proper Python Path in OBS (Tools -> Scripts -> Python Config), or run this script again to get more help")

elif sys.platform == "linux":
    print("---> Do NOT install OBS Studio from Ubuntu Software or similar")
    print("If you already did, uninstall OBS and install it again from their site: https://obsproject.com/es/download\n")
    cmd = "python3 --version"
    ret = subprocess.run(cmd, text=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1)
    present = True
    if ret.returncode != 0:
        cmd = "python3.10 --version"
        ret = subprocess.run(cmd, text=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1)
        if ret.returncode != 0:
            print("---> Python3 is not installed or can't be found. Please install Python 3.10")
            present = False
    elif "python3.10" not in ret.stdout:
        print("---> Python3 is installed, but Python 3.10 is highly recommended")
        present = False
    if not present:
        print("To install it, open a Terminal and run this (enter your password when required):")
        if 'ubuntu' in platform.version().lower():
            print("\tsudo apt install software-properties-common")
            print("\tsudo add-apt-repository ppa:deadsnakes/ppa")
            print("\tsudo apt update")
            print("\tsudo apt install python3.10")
        elif 'manjaro' in platform.version().lower():
            print("\tsudo pacman -S pyenv")  # --> Check this
            print("\tsudo pamac install pyenv")  # --> OR this
            # print("\tsudo pacman -S git base-devel openssl zlib xz")
            # print("\tgit clone https://github.com/pyenv/pyenv.git ~/.pyenv")
            # print('\texport PYENV_ROOT="$HOME/.pyenv"')
            # print('\texport PATH="$PYENV_ROOT/bin:$PATH"')
            # print('\teval "$(pyenv init --path)"')
            # print("\tsource ~/.bashrc")
            print("\tpyenv install -v 3.10.13")
        print("\n---> Once Python is installed, configure proper Python Path in OBS (Tools -> Scripts -> Python Config), or run this script again to get more help")
    else:
        cmd = "echo $PYTHONPATH"
        ret = subprocess.run(cmd, text=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1)
        if not ret.stdout:
            cmd = "which python3"
            ret = subprocess.run(cmd, text=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1)
            if not ret.stdout:
                print("Python is installed, but you need to set proper PATH. Run this on a Terminal:")
                print("\texport PYTHONPATH=%s" % sys.executable)
                print("Configure Python Path in OBS (Tools -> Scripts -> Python Config), or run this script again to get more help")

elif sys.platform == "darwin":
    pass
Danie10 commented 10 months ago

OK you did need to click on that Script Log button though to see any output in OBS for the script. It does not pop any info up by default - it all directs to the log.

I'll need to check that Linux path but I think it does make sense as it is supposed to redirect. It always calls the main version so should stay python3 no matter if it is 3.10 or 3.11. That allows for updates but keeps the path consistent. So looking at mine I see that /usr/bin/python redirects to execute usr/bin/python3, and that usr/bin/python3 redirects to execute /usr/bin/python3.11. So trying to run /usr/bin/python will execute /usr/bin/python3.11. If the global version is set to 3.12 the usr/bin/python will execute 3.12.

But I'm thinking my problem is something to do with importing / installing WinMonCtl or whichever it was. The script was showing something odd, so maybe I must step through that installation carefully again.

Danie10 commented 10 months ago

OK two things with the script:

  1. On mine the output is rather long so the top is cutting off. May need some continue option? When the text wraps horizontally it can all fit, but that is more difficult to read than the nice vertical column layout.
  2. Because of issue 1 I can't see the initial version number. I'm pretty sure Python itself does show 3.11.5 but the script at the end is reporting Python3 which more like it is getting that from the command line path. I think it is best to pull it from Python itself to see what Python actually sees.
Kalmat commented 10 months ago

I am clicking the "Script Log" button to check the output, but it's always empty. Futhermore, if I keep it open while I reload or even load the script for the first time after deleting it, it is still empty. Weird thing. Perhaps there is something to do before running any script that I am missing.

Regarding the python version/command in Linux, as I mentioned in my comment, if you have more than one Python3 versions installed, the first one will be python3, whilst all the others will be python3.xx (this actually happened in my Ubuntu after installing Python 3.10 on top of Python 3.8). "sys.executable" will show the python path you are running the script with. You can also try "python3 --version" to check the actual version that python3 command is pointing to. Or you can also run "python3.10 YourScript.py" or whatever python executable you may find at /usr/bin folder. In my Ubuntu installation I get python3 (which points to Python 3.8), python3.6 and python3.10 executable files in there.

I think that OBS only supports Python 3.6 to 3.10, not 3.11 nor 3.12... I might be wrong, anyway.

Finally, regarding "continue" option, I will try to find a workaround (it's really strange that your console doesn't keep all those lines... it's not so long after all! Just curious, what happens if you run "python3 -m pip list" in a terminal? It is still cutting off?). Besides, the modules installed it's not the most important part yet, but the instructions to check / get everything in place.

In your case, I would run "ls /usr/bin/python*" to check which python versions you have. After that, I would run "python --version" and/or "python3 --version" to check which versions python and python3 commands are pointing to. And after that, I would run "python3.xx -m pip list" for them all (replacing .xx by the versions you found in the previous command) to check in which of them PyMonCtl is installed. Doing this, you will hopefully find in which version of python the PyMonCtl module is installed, but I have no clue about what to do with it. I mean, I don't know which version OBS is using and how it finds it, since there is no "Script Config" tab in the Scripts section. You can also check "echo $PYTHONPATH" or "which python3", play with "alias" command, or even uninstall all Python versions, and install Python3.10 only... I tried this myself, but I got no output, so I can not confirm how to better proceed.

EDIT: Did you mean the output cuts off inside "Scripts Log" not in the terminal console, right? If that is the case, just comment this line and replace it by "pass" sentence (this will allow you to check the Python versions):

try:
    subprocess.call([sys.executable, '-m', 'pip', 'list'])
except:

by:

try:
    # subprocess.call([sys.executable, '-m', 'pip', 'list'])
    pass
except:
Danie10 commented 10 months ago

OK quite interesting now.... My terminal was set to 300 lines so I increased to 500 and that sorted out the reading the whole list.

But interesting thing is I have Python versions switched with Pyenv to 3.10 as the global version, but OBS is still seeing Python 3.11. It should not if it is set globally to 3.10. Pymonctl is installed with Python 3.10 but not in 3.11. So this does explain something - why I don't know yet. I know OBS uses the system Linux but the global version should be 3.10..... See that script is already helping show stuff now. So I'll figure this out a bit further tomorrow.

On that test script in OBS. If I have installed that script, and restart OBS, it immediately throws up the script log error window with the output of that script showing Python version etc. Can also try opening scripts window (and with that script loaded) click that circle button to reload scripts, the clicking Script Log button should show some output. If not, then weird stuff is going on ;-)

Kalmat commented 10 months ago

Yes, weird things happening in my case (in both, WIndows and Linux), because I already followed all those steps, with no output...

Try this in a terminal:

"alias python3=usr/bin/python3.10"

and maybe also:

"alias python=usr/bin/python3.10"

This way, python3 an python commands become python3.10 (only in this session, it will reset after reboot or logout/login). Then try in OBS again, to check which python version is taking after that.

Kalmat commented 10 months ago

Buffffff! This is a complete mess... I finally got python scripts working in Windows. These are my findings:

  1. "Regular" versions of python do not work, despite where or how they are installed. OBS loads python, but the scripts do not run.
  2. "Embedded" versions of python do work. OBS loads python, and the scripts run (and produce proper output), but... -sys.executable returns OBS executable file and path, not python executable! The side effect is that this: subprocess.call([sys.executable, '-m', 'pip', 'list']) is launching infinite OBS instances in the background.
    • Embedded versions of python have no pip installed, do not set proper path at system level, etc... so, the management of packages and other stuff gets much more complicated for non-skilled users (I mean, it works, but it will be a nightmare for most OBS users)
    • Embedded python can be installed anywhere... so it is impossible to check if they are already installed and if they properly work. So, helping users gets really complicated.
    • Not even a clue about how to get Linux working (since it has no "Script Config" option, there is no way to point to a specific Python installation, despite it is "regular" or "embedded").

Unless I am missing something, I see no other option than making a step-by-step tutorial, starting from scratch (I mean: "uninstall everything and follow these exact steps"). This tutorial will likely not be found by users (amongst the tones of other tutorials available) and will also get obsolete quite fast.

Danie10 commented 10 months ago

Yep I realised that the alias was not going to work as there is more than one needed and it won't alias a path that OBS specifies. So I removed my PyEnv and now at least OBS no longer complains and scripts load fine, including again the Zoom and Follow one. Seems multiple Python versions can cause major issues when OBS is hardcoding where it expects to find Python. It really wants to work with the default Linux system Python.

Setting PyEnv to global system was also not fixing that as it won't install your scripts, complaining about the externally managed environment.

So yes the issue is the inflexible way that OBS Studio runs Python. You have to try stick to defaults.

Maybe there is some way of wrapping OBS Studio in it's own virtual python as that would be ideal but lots of things to break on the user end. Actually the Flatpak version of OBS Studio could achieve this as it sandboxes itself, and all correct dependencies, inside the Flatpak install. As I mentioned before then, the thing is just to see how scripts can still be installed. But I like Flatpaks as they have a sandboxed way of working with known correct dependencies.

Yep such a tutorial would also be difficult because no-one wants to start from scratch, or it effects other stuff. I'm still thinking of Flatpaks. I'll do some testing now again without my PyEnv, an dthen maybe try the OBS Studio Flatpak again.

I do see the output of your test script in OBS Studio, but only if I open the Script Log.

Danie10 commented 10 months ago

Just installed the Snap version of OBS Studio and interestingly (as expected) it has installed all its standard dependencies inc Python with OBS. So the version shown here is the Python bundled with OBS, not my system Python. I'm not sure yet how the import of PyWinCtl etc will work though as I don't think I can run pip to install etc:

[PyVerNew.py] Python version:    3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0]
[PyVerNew.py] Python location:   /usr/bin/python3
[PyVerNew.py] Modules locations:
[PyVerNew.py]    /usr/local/lib/python3.10/dist-packages
[PyVerNew.py]    /usr/lib/python3/dist-packages
[PyVerNew.py]    /usr/lib/python3.10/dist-packages
[PyVerNew.py] 
[PyVerNew.py] 
[PyVerNew.py] CHECKING INSTALLATION
[PyVerNew.py] ---> Do NOT install OBS Studio from Ubuntu Software or similar
[PyVerNew.py] If you already did, uninstall OBS and install it again from their site: https://obsproject.com/es/download
[PyVerNew.py] 
[PyVerNew.py] ---> Python3 is installed, but Python 3.10 is highly recommended
[PyVerNew.py] To install it, open a Terminal and run this (enter your password when required):
[PyVerNew.py] 
[PyVerNew.py] ---> Once Python is installed, configure proper Python Path in OBS (Tools -> Scripts -> Python Config), or run this script again to get more help

But we can see from https://stackoverflow.com/questions/71954531/obs-python-script-cannot-find-module-error-obs-snap-linux-edition that other users using the Snap version of OBS Studio also realise it is a read-only sandbox and you cannot use pip on Python at all. You can load plugins it seems from OBS config. And the Python modules most have issues with are PyWinCtl and PyMonCtl. Maybe there is some way you can get your modules installed upstream as part of a Snap or Flatpak. That would really help a lot of people as the Zoom and Follow scrip depends 100% on those modules. Loading the Zoom and Follow script is not the problem, but getting your module dependencies in is.

Kalmat commented 10 months ago

Hi!

To be honest, I almost gave up with OBS and Python in Linux (as well as in Windows). I am uncapable to find a good solution. I was working in a new version, but still it will not help users to get everything in place:

import platform
import subprocess
import site
import sys

# Description displayed in the Scripts dialog window
def script_description():
    return """Python Checker
        Check your python environment to run OBS Scripts
        You can run this script several times to verify your progress and get help
        Python 3.10 and pip are required, so you may be asked to install them"""

cmd1 = ""
cmd2 = ""
cmd3 = ""
if sys.platform == "win32":
    cmd1 = "where python"
    cmd2 = "where python3"
    cmd3 = ""
elif sys.platform == "linux":
    cmd1 = "which python"
    cmd2 = "which python3"
    cmd3 = "which python3.10"
elif sys.platform == "darwin":
    pass
executable = ""
ret = subprocess.run(cmd1, text=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1)
ret.stdout = ret.stdout.replace("\n", "")
if ret.returncode == 0 and ret.stdout:
    executable = ret.stdout
else:
    ret = subprocess.run(cmd2, text=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1)
    ret.stdout = ret.stdout.replace("\n", "")
    if ret.returncode == 0 and ret.stdout:
        executable = ret.stdout

version = ""
if executable:
    ret = subprocess.run(executable + " --version", text=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1)
    ret.stdout = ret.stdout.replace("\n", "")
    if ret.returncode == 0 and ret.stdout:
        version = ret.stdout
    if "3.10" not in version and cmd3:
        ret = subprocess.run(cmd3, text=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1)
        ret.stdout = ret.stdout.replace("\n", "")
        if ret.returncode == 0 and ret.stdout:
            executable = ret.stdout
            ret = subprocess.run(executable + " --version", text=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1)
            ret.stdout = ret.stdout.replace("\n", "")
            if ret.returncode == 0 and ret.stdout:
                version = ret.stdout

print("{:<18} {}".format("Python location:", executable))
print("{:<18} {}".format("Python version:", version))
print("Modules locations:")
for location in site.getsitepackages():
    print("\t", str(location))
print()

pythonPath = ""
if executable:
    pythonPath = executable.rsplit("\\", 1)[0]
    try:
        subprocess.call([executable, '-m', 'pip', 'list'])
    except:
        print("ERROR running pip. Please install pip by following these instructions:")
        if sys.platform == "win32":
            print("\tWINDOWS: https://www.geeksforgeeks.org/how-to-install-pip-on-windows/")
            print("\tIn case pip is still not found, open a Terminal, cd to the Python installation folder ('%s'), and run this:" % pythonPath)
            print("\t\t'mklink /D pip .\\Lib\\site-packages\\pip'")
        elif sys.platform == "linux":
            if 'ubuntu' in platform.version().lower():
                print("\tUBUNTU: https://itsfoss.com/install-pip-ubuntu/")
            elif 'manjaro' in platform.version().lower():
                pass
        elif sys.platform == "darwin":
            print("\tMACOS: https://phoenixnap.com/kb/install-pip-mac")

print("\nCHECKING INSTALLATION...")
if sys.platform == "win32":
    if not executable or "3.10" not in version or "WindowsApps" in executable:
        if "WindowsApps" in executable:
            print("---> Do NOT use Python installed from Microsoft Store (OBS won't recognize it)!!!\n")
            print("Unless you want to use it for other stuff, consider to uninstall it before continuing")
        print("---> Python 3.10 is not properly installed")
        print("To install it, get Python 3.10 installer here: https://www.python.org/downloads/release/python-3109/")
        print("Find 'Windows installer (64-bit)', download and install it in 'C:\\Python\\' folder")
    else:
        cmd = "echo %PATH%"
        ret = subprocess.run(cmd, text=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1)
        if pythonPath not in ret.stdout:
            print("Python is installed, but you need to set proper PATH. Run this on a Terminal:")
            pct = "%"
            print("set PATH=%s;%s\\Scripts;%sPATH%s" % (pythonPath, pythonPath, pct, pct))
        else:
            print("Congratulations! Python 3.10 is correctly installed in your system")
    print("\n---> Once Python is installed, configure Python Path ('%s') in OBS (Tools -> Scripts -> Python Config)" % pythonPath)

elif sys.platform == "linux":
    print("---> Do NOT install OBS Studio from Ubuntu Software or similar")
    print("If you already did, uninstall OBS. Install it from their site: https://obsproject.com/es/download\n")
    if not executable or "3.10" not in version:
        print("---> Python 3.10 is not installed")
        print("To install it, open a Terminal and run this (enter your password when asked to):")
        if 'ubuntu' in platform.version().lower():
            print("\tsudo apt install software-properties-common")
            print("\tsudo add-apt-repository ppa:deadsnakes/ppa")
            print("\tsudo apt update")
            print("\tsudo apt install python3.10")
        elif 'manjaro' in platform.version().lower():
            print("\tsudo pacman -S pyenv")  # --> Check this
            print("\tsudo pamac install pyenv")  # --> OR this
            # print("\tsudo pacman -S git base-devel openssl zlib xz")
            # print("\tgit clone https://github.com/pyenv/pyenv.git ~/.pyenv")
            # print('\texport PYENV_ROOT="$HOME/.pyenv"')
            # print('\texport PATH="$PYENV_ROOT/bin:$PATH"')
            # print('\teval "$(pyenv init --path)"')
            # print("\tsource ~/.bashrc")
            print("\tpyenv install -v 3.10.9")
    else:
        cmd = "echo $PYTHONPATH"
        ret = subprocess.run(cmd, text=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=1)
        if not ret.stdout:
            print("Python is installed, but you need to set proper PATH. Run this on a Terminal:")
            print("\t\texport PYTHONPATH=%s" % executable)
        else:
            print("Congratulations! Python 3.10 is correctly installed in your system")

elif sys.platform == "darwin":
    pass

The problem is that I don't know how OBS is potinting at Python in Linux, and how to manage to install any module on it! If it is the OS embedded Linux, no way, it will not allow you to install anything. If it is not, I don't know to make OBS to point at it.

In Windows, I managed to get a "standard" Python installation working (installing it on C:\Python\ folder), but pip won't work... I also tried to use an embedded Python version, but this is not suitable for users wanting to use Python for other stuffs.

I just saw your last comment:

Danie10 commented 10 months ago

Yes there are two ways for OBS to run Python in Linux: One is just a command as long as the Python executable is in the path, and the other is to specify a path. I'm suspecting OBS is executing /usr/bin/python3 by path. But "which Python" is important because of the installed modules.

On Linux Flatpak, Snap and AppImage are all types of a package for an app, where the app and ALL its dependencies are inside that environment. So the Snap app for OBS Studio includes its Python, the OBS plugins, etc. For example, the Snap for Ubuntu has included about 30 OBS plugins all ready to use. It's up to the dev who packages it, what to include.

For PyWinCtl and PyMonCtl they can be included as part of the base Snap, Flatpak or AppImage. The Zoom and Follow script is not so critical as it can be added afterwards by any user like normal, but it needs your modules installed with the Python already.

I'm going to try reach the Snap developers, as a start, to see if they will include them. If it works then it may be easier to get the Flatpack dev to do the same.

So in my case now, I no longer get the error about the PyMonCtl module not being found. I've just got the same issues as others with it zooming to the top left corner of the monitor. You may be able to help troubleshoot that, as it is either how your module works, or how the Zoom and Follow script is using your module, or possibly that users have not got the Zoom and Follow script settings correct.

I logged this request for the Snap at https://github.com/snapcrafters/obs-studio/issues/178.

Kalmat commented 10 months ago

Ok, understood now. Thank you.

Regarding the modules to be included, PyWinCtl already requires PyMonCtl and PyWinBox (amongst others, of course, but most likely those devs will know how to deal with it).

I am not aware about the other issue you mention, the one regarding the left corner of the monitor... Another user helped me pointing out a bug PyWinCtl had about the window client frame in GNOME (which has a totally different behavior that other Desktop Environments). Not sure if it is related (that method, I mean getClientFrame(), was included precisely because of a request from tryptech). If you can please leave a link about this issue, I will try to take a look.

I am waiting for that user to finish a PR in order to upload a new version of all three modules (PyWinCtl, PyMonCtl, and PyWinBox) which improves and solves many issues... Hopefuly this will be done by mid-end november.

Danie10 commented 10 months ago

The issue was similar to this one at https://github.com/tryptech/obs-zoom-and-follow/issues/40 - it zooms into a pixel in the top left corner. But I re-edited the Transform box (seems the Zoom and Follow script updates that insider OBS Studio in realtime as you press to zoom, and then move the mouse). It actually worked twice without any errors at all. So my issuesd now will only be script config one's I think.

Which means the setup of that Zoom and Follow script is a bit complex, and especially for those with multi-monitor setups. So it's not been a simple issue and probably why it has been difficult to always pin-point.

OK well looking forward to your updates later in the month, and hopefully the Snap people also add your modules in.

Kalmat commented 10 months ago

The issue you linked seems solved now, right?

Regarding multi-monitor, yes. Multi-monitor feature has been included very recently in PyWinCtl and OBS-Zoom-and-Follow. Not sure if those issues still remain or have already been solved too.

Let's hope that all those packaged solutions (I mean snap, flatpak and so on) will eventually include everything to ease the installation and use of the Zoom-and-Follow script!!! (I see no other alternative right now, to be honest).

Danie10 commented 10 months ago

Yes, thanks resolved. I'm going to keep an eye on the Zoom and Follow side around multi-monitor setups, ad watch out for the updates.