Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GUI] Better event filtering system #801

Merged
merged 25 commits into from
Apr 30, 2020
Merged

Conversation

archibate
Copy link
Collaborator

@archibate archibate commented Apr 16, 2020

Related issue = #... (if any)

[Click here for the format server]

We used to support filter by only type.
Now we also support filter by key, or by (type, key) combination.

by key:

import taichi as ti
gui = ti.GUI('Hello')
while not gui.get_event(ti.GUI.ESCAPE):  # has ESC event
    gui.show()

by key + type:

import taichi as ti
gui = ti.GUI('Hello')
while not gui.get_event(ti.GUI.RELEASE, ti.GUI.ESCAPE):  # has REL or ESC event
    gui.show()
import taichi as ti
gui = ti.GUI('Hello')
while not gui.get_event((ti.GUI.RELEASE, ti.GUI.ESCAPE)):  # has REL and ESC event
    gui.show()

by comb:

import taichi as ti
gui = ti.GUI('Hello')
filt = [(ti.GUI.PRESS, ti.GUI.ESCAPE), (ti.GUI.RELEASE, ti.GUI.SPACE)]
while not gui.get_event(*filt):  # PRESS and ESC  or  RELEASE and SPACE
    gui.show()

@archibate archibate requested review from yuanming-hu and k-ye April 16, 2020 14:34
@archibate archibate changed the title [GUI] better GUI filter system (gui.get_event) [GUI] better GUI event filtering system Apr 16, 2020
@archibate

This comment has been minimized.

@archibate

This comment has been minimized.

python/taichi/misc/gui.py Outdated Show resolved Hide resolved
python/taichi/misc/gui.py Outdated Show resolved Hide resolved
python/taichi/misc/gui.py Outdated Show resolved Hide resolved
@@ -123,30 +123,53 @@ def line(self, begin, end, radius, color):
self.canvas.path_single(begin[0], begin[1], end[0], end[1], color,
radius)

def show(self, file=None):
def show(self, file_or_image=None):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about having two args: file and image? Then users can both show it and save it as files

Copy link
Collaborator Author

@archibate archibate Apr 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest them:

  1. gui.set_image(image); gui.show(file).
  2. gui.show(); ti.imwrite(image, file).

I prefer 2, since I think show(file) is just a backward-compat-deprecated API. With ti.imwrite, users can even save images without GUI, provides more flexibility.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then i think this should just be image, because imwrite is already able to save to files? Again, if you think at the API layer, GUI is supposed to deal with one thing -- GUI. And we have separate APIs for file writings. WDYT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First of all, this part does not belong to the event filtering system. Please revert.

Please understand that having too many things in a PR makes reviewing really hard since the discussions cannot be focused on the real thing. This also increases the delay in merging your contribution since there are too many to discuss, and will result in nasty merge conflicts.

My thoughts on this specific issue: I agree that ti.GUI.show should not provide the option to write to disk. We might still need an API to get the image from the GUI or save the GUI image to disk, since sometimes people use drawing commands such as ti.GUI.circles to paint, and the painted result is only available from the GUI. Maybe we should do

  • gui.set_image to set a background image and allow people to paint on it
  • gui.show(image=None) to show the current canvas, or a plain 2D image (if image not none). Clear the temporary canvas after window content is updated.
  • gui.screenshot(filename=None) -> np.ndarray return the canvas. If filename is not None, use imwrite to save to disk. ti.imwrite(gui.screenshot(), 'X.png') sounds slightly verbose so I think a shortcut would be good.

The API design here should be put into a separate issue.

break
return True

def get_key_event(self):
def get_cursor_pos(self): # ABi
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's ABi?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_cursor_pos is an alias of self.core.get_cursor_pos, which is in C++ scope, so it's an ABI.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, feels a bit redundant, but sounds good

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry but I'm still confused about this: does ABi here stand for Application binary interface? Why is i lowercased here?

python/taichi/misc/gui.py Outdated Show resolved Hide resolved
@k-ye
Copy link
Member

k-ye commented Apr 18, 2020

Sorry for the late response..

@archibate
Copy link
Collaborator Author

archibate commented Apr 18, 2020

Btw, do we have some deprecated function need to be marked as deprecated? Like ti.x86_64 -> ti.x64, ti.global_var -> ti.var? I found this: https://python-deprecated.readthedocs.io/en/latest.

@archibate archibate requested a review from k-ye April 18, 2020 09:04
Copy link
Member

@k-ye k-ye left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for my late response recently... I'm following this tutorial http://www.realtimerendering.com/raytracing/Ray%20Tracing%20in%20a%20Weekend.pdf and writing a toy ray tracer on Metal these days... I believe it also offers me insights of how to use/improve Taichi (For example, I just found out that Metal doesn't support recursion function calls!)

@@ -123,30 +123,53 @@ def line(self, begin, end, radius, color):
self.canvas.path_single(begin[0], begin[1], end[0], end[1], color,
radius)

def show(self, file=None):
def show(self, file_or_image=None):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then i think this should just be image, because imwrite is already able to save to files? Again, if you think at the API layer, GUI is supposed to deal with one thing -- GUI. And we have separate APIs for file writings. WDYT?

python/taichi/misc/gui.py Outdated Show resolved Hide resolved
@archibate
Copy link
Collaborator Author

archibate commented Apr 19, 2020

Ok, now the API becomes:
peek: if has then return e; else return None.
get: if has then pop and return e; else return None.
wait: block until has.
wait_get: if has then pop and return e; else block until has, then pop and return e.

@archibate
Copy link
Collaborator Author

Then i think this should just be image, because imwrite is already able to save to files? Again, if you think at the API layer, GUI is supposed to deal with one thing -- GUI. And we have separate APIs for file writings. WDYT?

  1. our imread/imwrite seems to has some bug now ([misc] imread bug, snowing image #802).
  2. we need to ensure backward compatibility and make old examples work.
  3. It's very convenient to screenshot in one line.
  4. It's not a big deal to do an isinstance.

@archibate archibate requested a review from k-ye April 19, 2020 13:40
@archibate archibate changed the title [GUI] better GUI event filtering system [GUI] better event filtering system Apr 20, 2020
@k-ye
Copy link
Member

k-ye commented Apr 20, 2020

I'll just leave this to @yuanming-hu to decide the API changes. In general, I think there are a few things.

  • This PR is, once again, getting too big and losing focus from what it intends to be. I thought you were just going to enhance the filtering, but now we have to debate over peek/get/wait_get. And it's pulling in non GUI event things like gui.show and rgb_to_hex.
  • Again, while I think it's valuable to have both peek and get, this combination of event, events, {get_, is_} x {key_pressed, is_pressed, key_event} is just making things really confusing. Personally i think it would be better if we could first list out what APIs we want, then start implementing them.

@archibate
Copy link
Collaborator Author

Yes, it did get confusing... let's just merge the first commit in this PR?

@archibate

This comment has been minimized.

@yuanming-hu
Copy link
Member

Sorry I have meetings until early afternoon. I'll take care of this thread later today. Thanks for being patient and for the valuable discussions here.

@archibate
Copy link
Collaborator Author

archibate commented Apr 20, 2020

'm following this tutorial http://www.realtimerendering.com/raytracing/Ray%20Tracing%20in%20a%20Weekend.pdf and writing a toy ray tracer on Metal these days...

Oh this tutorial is cool! Have fun!

(For example, I just found out that Metal doesn't support recursion function calls!)

Interesting, so does GLSL... Seems most modern shader langs don't support recursion for simplicity, how about CUDA? And that may explain why simple ray tracers usually use CPU... hopefully taichi could change this role :)
How do you deal with this limitation then? Maybe portable in taichi's force-inlined function system too.

Copy link
Member

@yuanming-hu yuanming-hu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello? I thought I made a good PR description...

The description is indeed well-written. Good job! Sorry I've got too many things to do recently do my review might be delayed.

你没有阻止我那样做,甚至还对那些顺手牵羊的代码做了一些改进建议...

No need to be rude here. It should be the PR authors' (not reviewers') duty to prevent PRs from getting big. After all, reviewers cannot stop PR authors from adding unrelated commits. The only thing reviewers can do to keep development in order, is refusing to review/merge PRs with unrelated changes. I also highly recommend a re-reading of https://github.com/yuanming-hu/public_files/blob/master/graphics/taichi/google_review_comments.pdf

I'll continue my review after existing comments are addressed.

examples/image_io.py Outdated Show resolved Hide resolved
python/taichi/misc/gui.py Outdated Show resolved Hide resolved
break
return True

def get_key_event(self):
def get_cursor_pos(self): # ABi
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry but I'm still confused about this: does ABi here stand for Application binary interface? Why is i lowercased here?

e = self._peek_key_event()
return e

def get_key_event(self): # Unique APi
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, if APi stands for application programming interfaces, why is i lowercased?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For fun :)

@@ -123,30 +123,53 @@ def line(self, begin, end, radius, color):
self.canvas.path_single(begin[0], begin[1], end[0], end[1], color,
radius)

def show(self, file=None):
def show(self, file_or_image=None):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First of all, this part does not belong to the event filtering system. Please revert.

Please understand that having too many things in a PR makes reviewing really hard since the discussions cannot be focused on the real thing. This also increases the delay in merging your contribution since there are too many to discuss, and will result in nasty merge conflicts.

My thoughts on this specific issue: I agree that ti.GUI.show should not provide the option to write to disk. We might still need an API to get the image from the GUI or save the GUI image to disk, since sometimes people use drawing commands such as ti.GUI.circles to paint, and the painted result is only available from the GUI. Maybe we should do

  • gui.set_image to set a background image and allow people to paint on it
  • gui.show(image=None) to show the current canvas, or a plain 2D image (if image not none). Clear the temporary canvas after window content is updated.
  • gui.screenshot(filename=None) -> np.ndarray return the canvas. If filename is not None, use imwrite to save to disk. ti.imwrite(gui.screenshot(), 'X.png') sounds slightly verbose so I think a shortcut would be good.

The API design here should be put into a separate issue.

@archibate archibate requested a review from yuanming-hu April 21, 2020 04:12
@archibate
Copy link
Collaborator Author

Fully reverted, only event filtering preserved!

Copy link
Member

@k-ye k-ye left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking the suggestions!

python/taichi/misc/gui.py Outdated Show resolved Hide resolved
python/taichi/misc/gui.py Show resolved Hide resolved
python/taichi/misc/gui.py Outdated Show resolved Hide resolved
@archibate archibate requested a review from k-ye April 23, 2020 14:40
@archibate
Copy link
Collaborator Author

AppVeyor failed due to network reason again:

Build started
git clone -q https://github.com/taichi-dev/taichi.git C:\taichi
git fetch -q origin +refs/pull/801/merge:
git checkout -qf FETCH_HEAD
set TAICHI_REPO_DIR=C:\taichi
%PYTHON% %TAICHI_REPO_DIR%/misc/appveyor_filter.py || appveyor exit 0
cd C:\
curl --retry 10 --retry-delay 5 https://github.com/yuanming-hu/taichi_assets/releases/download/llvm8/taichi-llvm-8.0.1-msvc2017.zip -LO
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:--  0:00:34 --:--:--     0
curl: (52) Empty reply from server
Command exited with code 52

@yuanming-hu yuanming-hu changed the title [GUI] better event filtering system [GUI] Better event filtering system Apr 23, 2020
@archibate archibate self-assigned this Apr 25, 2020
@archibate archibate requested review from k-ye and removed request for k-ye April 26, 2020 04:53
Copy link
Member

@k-ye k-ye left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM (with one nit)

Personally I think the OR semantics is more intuitive than AND. There could be several open-ended questions regarding AND:

1.Since currently we don't check the tuple size, in theory a user could pass in an AND tuple with a size larger than 2. E.g. (TI.RELEASE, TI.ESCAPE, TI.SPACE). Should we disallow this? Or should we consider supporting it in the future?
2. If we do, then are (TI.RELEASE, TI.ESCAPE, TI.SPACE) and (TI.RELEASE, SPACE, TI.ESCAPE) equivalent, or even (TI.SPACE, TI.RELEASE, TI.ESCAPE)?

That said, I think this PR is good enough.

return False

def get_events(self, *filter):
filter = filter and GUI.EventFilter(*filter) or None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know I suggested filter or None, but in this case, i think the idiomatic way is GUI.EventFilter(*filter) if filter else None.

@archibate
Copy link
Collaborator Author

1.Since currently we don't check the tuple size, in theory a user could pass in an AND tuple with a size larger than 2. E.g. (TI.RELEASE, TI.ESCAPE, TI.SPACE). Should we disallow this? Or should we consider supporting it in the future?
2. If we do, then are (TI.RELEASE, TI.ESCAPE, TI.SPACE) and (TI.RELEASE, SPACE, TI.ESCAPE) equivalent, or even (TI.SPACE, TI.RELEASE, TI.ESCAPE)?

Thank for purposing this, but we won't support this. Why can a event be both ESC and Space? There is only one key pre-event, no multiple keys, if they want, they use gui.is_pressed(ESCAPE, SPACE). Let's just assert len(the_tuple) == 2 now.

@archibate
Copy link
Collaborator Author

Postponed by #839.

@archibate archibate requested review from yuanming-hu and removed request for yuanming-hu April 27, 2020 16:31
@archibate archibate requested a review from k-ye April 29, 2020 09:18
@archibate
Copy link
Collaborator Author

archibate commented Apr 29, 2020

Added doc!

docs/gui.rst Outdated Show resolved Hide resolved
@archibate archibate merged commit 6751bd5 into taichi-dev:master Apr 30, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants