python - Change images in a window via real time MIDI messages - Stack Overflow

I am trying to create a program that willread in images from a dir and store in a listdisplay first im

I am trying to create a program that will

  • read in images from a dir and store in a list
  • display first image in list in a window
  • MIDI notes from USB MIDI Controller triggers a new image in the same window
    • the new image is retrieved using the note value and matching index from list of images

I've seen plenty of examples of being able to do the above separately, but am stuck on how to combine them all in one. I have not been able to find any examples of bindings to MIDI messages -- only for key and button presses.

Currently my code can open up a matplotlib window and display first image, but the code is stuck until the window is closed. After closing window, I can see MIDI notes in streaming output but no window for images is visible.

When I tried to use show(block=False) a window with no image would open, but I can at least see MIDI input stream. An image never shows up in window.

I'm open to trying other libraries (TKinter, PyQT5, etc) but I couldn't find any examples that they would be any different.

Here is my code so far

import matplotlib
import os, sys
import mido
import cv2

def get_images(path: str) -> list:
    """
    Recursively crawl through dir and load in all images
    """
    images = []
    image_files = glob.glob(os.path.join(path, '*'))
    print(image_files)

    for i in image_files:
        images.append(cv2.imread(i))

    return images


def get_midi_port():
    # Get a list of available input port names
    input_ports = mido.get_input_names()

    # Get first port that is not a Through type
    for p in input_ports:
        if "Midi Through" not in p:
            print(p)
            port = p
            return p

    if not input_ports or not p: 
        raise Exception("No MIDI input ports found.")

class MIDI_Images():
    def __init__(
        self, 
        path="."
    ):
        self.path = path
        self.loaded_images = get_images(self.path)
        self.curr = 0
        if auto_start:
            self.fig, self.ax = plt.subplots()
            self.display()
            plt.show(block=False) ## opens window but no image
            plt.pause(0.1)
            ### plt.show() ## shows image but code doesn't continue for MIDI notes
            
            ### Ingest MIDI messages
            try:
                p = get_midi_port()

                with mido.open_input(p) as inport:
                    inport.poll()
                    print(f"Listening for MIDI messages on port: {inport.name}")
                    while True:
                        for msg in inport.iter_pending():
                            print(msg)
                            self.curr = msg.note - 24 ## lowest note on my controller is 24
                            self.update_image()

            except Exception as e:
                print(f"Error: {e}")

    def update_image(self):
        """
        Load in next image
        """

        # advance to next image
        try:
            self.im.set_array(self.loaded_images[self.curr])
            self.display()
            ## self.fig.canvas.draw_idle()  ## waits for image window to close if uncommented
        except IndexError:
            print("Sorry no image in index: ", n)

    def display(self):
            """
            Orchestrating function to run
            """

            image = self.loaded_images[self.curr]

            self.im = self.ax.imshow(image)
            
            ### Examples of key bindings working just fine
            # self.fig.canvas.mpl_connect("key_press_event", self.next_image)


if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(description='')

    parser.add_argument('--imagedir', action="store", dest='imagedir', default='')

    args = parser.parse_args()

    MIDI_Images(args.imagedir)


I am trying to create a program that will

  • read in images from a dir and store in a list
  • display first image in list in a window
  • MIDI notes from USB MIDI Controller triggers a new image in the same window
    • the new image is retrieved using the note value and matching index from list of images

I've seen plenty of examples of being able to do the above separately, but am stuck on how to combine them all in one. I have not been able to find any examples of bindings to MIDI messages -- only for key and button presses.

Currently my code can open up a matplotlib window and display first image, but the code is stuck until the window is closed. After closing window, I can see MIDI notes in streaming output but no window for images is visible.

When I tried to use show(block=False) a window with no image would open, but I can at least see MIDI input stream. An image never shows up in window.

I'm open to trying other libraries (TKinter, PyQT5, etc) but I couldn't find any examples that they would be any different.

Here is my code so far

import matplotlib
import os, sys
import mido
import cv2

def get_images(path: str) -> list:
    """
    Recursively crawl through dir and load in all images
    """
    images = []
    image_files = glob.glob(os.path.join(path, '*'))
    print(image_files)

    for i in image_files:
        images.append(cv2.imread(i))

    return images


def get_midi_port():
    # Get a list of available input port names
    input_ports = mido.get_input_names()

    # Get first port that is not a Through type
    for p in input_ports:
        if "Midi Through" not in p:
            print(p)
            port = p
            return p

    if not input_ports or not p: 
        raise Exception("No MIDI input ports found.")

class MIDI_Images():
    def __init__(
        self, 
        path="."
    ):
        self.path = path
        self.loaded_images = get_images(self.path)
        self.curr = 0
        if auto_start:
            self.fig, self.ax = plt.subplots()
            self.display()
            plt.show(block=False) ## opens window but no image
            plt.pause(0.1)
            ### plt.show() ## shows image but code doesn't continue for MIDI notes
            
            ### Ingest MIDI messages
            try:
                p = get_midi_port()

                with mido.open_input(p) as inport:
                    inport.poll()
                    print(f"Listening for MIDI messages on port: {inport.name}")
                    while True:
                        for msg in inport.iter_pending():
                            print(msg)
                            self.curr = msg.note - 24 ## lowest note on my controller is 24
                            self.update_image()

            except Exception as e:
                print(f"Error: {e}")

    def update_image(self):
        """
        Load in next image
        """

        # advance to next image
        try:
            self.im.set_array(self.loaded_images[self.curr])
            self.display()
            ## self.fig.canvas.draw_idle()  ## waits for image window to close if uncommented
        except IndexError:
            print("Sorry no image in index: ", n)

    def display(self):
            """
            Orchestrating function to run
            """

            image = self.loaded_images[self.curr]

            self.im = self.ax.imshow(image)
            
            ### Examples of key bindings working just fine
            # self.fig.canvas.mpl_connect("key_press_event", self.next_image)


if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(description='')

    parser.add_argument('--imagedir', action="store", dest='imagedir', default='')

    args = parser.parse_args()

    MIDI_Images(args.imagedir)


Share edited Mar 4 at 12:03 ekhumoro 121k22 gold badges254 silver badges374 bronze badges asked Mar 4 at 7:15 Korean_Of_the_MountainKorean_Of_the_Mountain 1,5893 gold badges18 silver badges45 bronze badges 0
Add a comment  | 

1 Answer 1

Reset to default 1

Try executing the midi polling message loop in a child thread instead. Then you can use plt.show() instead of plt.show(block=False). Also you need to call plt.draw() after updating the image inside display().

Below is the required changes:

...
import threading

...

class MIDI_Images():
    def __init__(
        self,
        path="."
    ):
        self.path = path
        self.loaded_images = get_images(self.path)
        self.curr = 0
        if auto_start:  # note that auto_start is undefined in your code
            self.fig, self.ax = plt.subplots()
            self.display()
            # start the polling message loop in a child thread
            threading.Thread(target=self.poll_midi, daemon=1).start()
            plt.show()  # use blocking show()

    def poll_midi(self):
        try:
            p = get_midi_port()
            with mido.open_input(p) as inport:
                inport.poll()
                print(f"Listening for MIDI messages on port: {inport.name}")
                while True:
                    for msg in inport.iter_pending():
                        print(msg)
                        self.curr = msg.note - 24
                        self.update_image()
        except Exception as e:
            print(f"Error: {e}")

    def update_image(self):
        """
        Load in next image
        """

        # advance to next image
        try:
            self.im.set_array(self.loaded_images[self.curr])
            self.display()
        except IndexError:
            print("Sorry no image in index: ", self.curr)  # original is n which is undefined in your code

    def display(self):
            """
            Orchestrating function to run
            """
            image = self.loaded_images[self.curr]

            self.im = self.ax.imshow(image)
            plt.draw()  # update plot

...

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745057782a4608775.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信