sciapp / gr

GR framework: a graphics library for visualisation applications
Other
329 stars 54 forks source link

Fill Entire Workspace Fullscreen using GKSQt #184

Open EmDash00 opened 7 months ago

EmDash00 commented 7 months ago

Hi, currently using GR as a sort of UI for an experiment; however, I'm not aware of how to set the windowsize for the Qt window that it opens. This is an issue as I don't really know how else I would set some object to be a specific fraction of the screen's width.

I've attached images below. Screenshot from 2024-03-01 14-47-36

Initial draw size:

Padding after resizing window to fullscreen:

Screenshot from 2024-03-01 14-47-51

What I'd like is for gr.setviewport(0, 1, 0, 1)to fill the entire screen. Do you have any tips?

jheinen commented 7 months ago

I don't know, how you produce GR graphics (C/C++, Julia, Python, ...):

In Julia, you could try:

using GR
mw, mh, w, h = inqdspsize()

and then (in landscape mode):

setwswindow(0, 1, 0, mh/mw)
setwsviewport(0, mw, 0, mh)

if you are using low-level GR commands.

With the high-level plots methods, you should use the size argument, e.g.:

plot(randn(10), size=(w, h))

If you are embedding GR graphics in a Qt widget, you cause the above commands in the resize event callback.

EmDash00 commented 6 months ago

Hi thanks for the response. Sorry for the lack of clarification on my language! I'm using Python. I finally got around to trying out your suggestion, and it seems to almost do it; however, I still get some white at the edge of the workspace.

There also appears to be (possibly?) a bug in the behavior of gr.inqwindow() and gr.inqviewport()

#!/usr/bin/env python3

import gr
import gr.pygr

mw, mh, w, h = gr.inqdspsize()

gr.clearws()

print("Window:", gr.inqwindow())
print("Viewport:", gr.inqviewport())

gr.setwswindow(0, 1, 0, mh / mw)
gr.setwsviewport(0, mw, 0, mh)

print("Window:", gr.inqwindow())
print("Viewport:", gr.inqviewport())

x1, y1 = gr.pygr.ndctowc(0, 0)
x2, y2 = gr.pygr.ndctowc(1, mh / mw)

gr.setfillintstyle(gr.INTSTYLE_SOLID)

# Possibly a bug: gr.fillrect(*gr.inqwindow()) doesn't work. See printouts.
gr.fillrect(x1, x2, y1, y2)
gr.updatews()

input()

This prints:

Window: [0.0, 1.0, 0.0, 1.0]
Viewport: [0.2, 0.9, 0.2, 0.9]
Window: [0.0, 1.0, 0.0, 1.0]
Viewport: [0.2, 0.9, 0.2, 0.9]

And yields the following image:

Screenshot from 2024-03-13 16-21-54

It's possible the titlebar of the window is causing this. Do you know of any way of hiding it without using QTGR? There appears to be a setting in Qt that allows this to be possible.

Something like (in PyQt):

from PyQt5.QtCore import Qt

win.setWindowFlags(Qt.Window | Qt.FramelessWindowHint)
jheinen commented 6 months ago

You probably mean this:

#!/usr/bin/env python3

import gr
import gr.pygr

mw, mh, w, h = gr.inqdspsize()

gr.setwsviewport(0, mw, 0, mh)
gr.setwswindow(0, 1, 0, mh / mw)

gr.setviewport(0, 1, 0, mh / mw)
gr.setwindow(0, w, 0, h)

print("Viewport: ", gr.inqviewport())
print("Window: ", gr.inqwindow())

gr.setfillintstyle(gr.INTSTYLE_SOLID)
gr.fillrect(*gr.inqwindow())

gr.updatews()

input()

But you are right, the window decoration in Qt seems to cause some trouble. We'll have a look at that ...

jheinen commented 6 months ago

The problem should be fixed with this commit.

EmDash00 commented 6 months ago

The problem should be fixed with this commit.

Do you know when I can expect this patch to show up in pygr? I can install the commit to test it when you do.

EmDash00 commented 6 months ago

Okay so I've built it manually on my system. Still getting the same behavior oddly enough (using the code you suggested). Here's my precise printout (I suspect using Wayland won't have a big impact).

import gr
import gr.pygr

import os

print(gr.version())
print(os.environ.get('GRLIB'))

mw, mh, w, h = gr.inqdspsize()

gr.setwsviewport(0, mw, 0, mh)
gr.setwswindow(0, 1, 0, mh / mw)

gr.setviewport(0, 1, 0, mh / mw)
gr.setwindow(0, w, 0, h)

print("Viewport: ", gr.inqviewport())
print("Window: ", gr.inqwindow())

gr.setfillintstyle(gr.INTSTYLE_SOLID)
gr.fillrect(*gr.inqwindow())

gr.updatews()

input()

I've changed the username of the computer to be username for privacy below. I built my gr using the commit you referenced under ~/Apps/git/gr/build

Runtime: 0.73.3.post5 / Python: 1.24.0
/home/username/Apps/git/gr/build/lib/
Warning: Ignoring XDG_SESSION_TYPE=wayland on Gnome. Use QT_QPA_PLATFORM=wayland to run on Wayland anyway.
Viewport:  [0.0, 1.0, 0.0, 0.6153846153846154]
Window:  [0.0, 1920.0, 0.0, 1200.0]

As you can see still getting the same behavior with the screen edges. Any help would be appreciated.

Screenshot from 2024-03-14 16-44-40

IngoMeyer441 commented 6 months ago

I can reproduce that our bugfix doesn't work for your code sample. I think in your case, gr.inqdspsize reports the whole screen and not only the useable drawing area. Thus, you only get a black region that has the same aspect ratio as your display.

This example uses the pygr GRWidget to create a custom Qt widget:

#!/usr/bin/env python3

import signal
import sys

import gr
from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt
from qtgr import GRWidget

class FullScreenWidget(GRWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setWindowFlags(Qt.Window | Qt.FramelessWindowHint)

    def draw(self):
        mw, mh, w, h = gr.inqdspsize()

        gr.setwsviewport(0, mw, 0, mh)
        gr.setwswindow(0, 1, 0, mh / mw)
        gr.setviewport(0, 1, 0, h / w)
        gr.setwindow(0, w, 0, h)

        gr.setfillintstyle(gr.INTSTYLE_SOLID)
        gr.fillrect(*gr.inqwindow())

if __name__ == "__main__":
    signal.signal(signal.SIGINT, signal.SIG_DFL)  # Allow the application to be closed with <ctrl-c>
    app = QtWidgets.QApplication(sys.argv)
    widget = FullScreenWidget()
    widget.showFullScreen()
    print("Press <ctrl-c> to exit")
    sys.exit(app.exec())

In that case, inqdspsize returns the actual size of the drawing area. With this code, you can also deactivate the window decorations.

IngoMeyer441 commented 6 months ago

Another tip: You can always use the latest GR development version without compiling by specifying the GR_VERSION environment variable on the python-gr installation:

GR_VERSION=latest python3 -m pip install --force-reinstall --no-cache-dir gr

--no-cache-dir is important, because otherwise the GR C runtime wouldn't be downloaded again.

EmDash00 commented 6 months ago

I can reproduce that our bugfix doesn't work for your code sample. I think in your case, gr.inqdspsize reports the whole screen and not only the useable drawing area. Thus, you only get a black region that has the same aspect ratio as your display.

I tested this and it appears to work.

Unfortunately, I'm already using GR in an application with lots of other threads inside another framework and I think adding a Qt application framework inside would not be worth the extra work it introduces. A workaround I found is to just run the patch to get the size using gr.inqdspsize() and then save those numbers to use in my application.

It's easier for me that way but I feel like gr.inqdspsize() for a for a GKSQt workstation type shouldn't return the monitor size but the total area available after the window decoration is accounted for. Perhaps we can make another function which implements this more desired functionality?

EmDash00 commented 6 months ago

Okay so playing around with it a bit more I found possibly another issue. I'm not sure if it's actually correctly detecting the display size. Here's a quick test.

#!/usr/bin/env python3

import signal
import sys

import gr
import numpy as np
from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt
from qtgr import GRWidget

class FullScreenWidget(GRWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setWindowFlags(Qt.Window | Qt.FramelessWindowHint)

    def draw(self):
        mw, mh, w, h = gr.inqdspsize()

        gr.setwsviewport(0, mw, 0, mh)
        gr.setwswindow(0, 1, 0, mh / mw)
        gr.setviewport(0, 1, 0, h / w)

        gr.setwindow(-1, 1, -1, 1)

        gr.setfillintstyle(gr.INTSTYLE_SOLID)
        gr.fillrect(*gr.inqwindow())

        t = np.linspace(0, 2 * np.pi, num=100)
        gr.setlinecolorind(gr.inqcolorfromrgb(1, 1, 1))
        gr.polyline(np.cos(t), np.sin(t))

        gr.setborderwidth(0)
        gr.setmarkersize(1)
        gr.setmarkertype(gr.MARKERTYPE_SOLID_CIRCLE)
        gr.setmarkercolorind(gr.inqcolorfromrgb(1, 0, 0))

        gr.polymarker([0., -1., -1., 1., 1.], [0., -1., 1., -1., 1.])

if __name__ == "__main__":
    # Allow the application to be closed with <ctrl-c>
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    app = QtWidgets.QApplication(sys.argv)
    widget = FullScreenWidget()
    widget.showFullScreen()
    print("Press <ctrl-c> to exit")
    sys.exit(app.exec())

Screenshot from 2024-03-15 14-18-10

If you look closely the top gets cut off.