0

I have a QPushButton named C3:

self.C3 = QtWidgets.QPushButton(self.centralwidget)

A keyboard shortcut for this button for the key 'X' for example would be:

self.C3.setShortcut(_translate("MainWindow", "X"))

But I want to trigger the button when I press the 'C3' key on my midi keyboard, not a key from pc keyboard. I can print the value of midi input using:

if midiInput.poll(): 
      midi_events = midiInput.read(10)
      print(midi_events[0][0][1])   

When I press the 'C3' key, this will print 48 which is the integer value for 'C3'. But I don't know how to make the program to recognise the input from midi on setShortcut.

Here is my code:

from PyQt5 import QtCore, QtGui, QtWidgets
from midiutil.MidiFile import MIDIFile
from music21 import *
import pygame.midi
import pygame
import time
from pygame.locals import *


import playsound
# This will be used for the playing .wav files for the keys

from threading import Thread
# This will support us for multithreading

import atexit

if hasattr(QtCore.Qt, 'AA_EnableHighDpiScaling'):
    QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True)

if hasattr(QtCore.Qt, 'AA_UseHighDpiPixmaps'):
    QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True)


device_output = 0
device_input = 1
instrument = 1 

def printMIDIDeviceList():
        for i in range(pygame.midi.get_count()):
            print(pygame.midi.get_device_info(i), i)

# ===================================================================================
# ===================================================================================
pygame.init()
# initize Pygame MIDI ----------------------------------------------------------
pygame.midi.init()

pygame.fastevent.init()
event_get = pygame.fastevent.get
event_post = pygame.fastevent.post

input_id = pygame.midi.get_default_input_id()

# printMIDIDeviceList()

# setez dispozitivul output --------------------------------------------------------
player = pygame.midi.Output(device_output)

# setez instrumentul
player.set_instrument(instrument)

# setez dispozitivul input
midiInput = pygame.midi.Input(device_input)
# ===================================================================================
# ===================================================================================

intervalAnt = 0
start = time.time()

# create your MIDI object
mf = MIDIFile(1)     # only 1 track
track = 0   # the only track

timp = 0    # start at the beginning
mf.addTrackName(track, timp, "Sample Track")
mf.addTempo(track, timp, 66)

def whatever():
        with open("output.mid", 'wb') as outf:
             mf.writeFile(outf)
             
atexit.register(whatever)



class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1226, 522)

        # We require a sender, that will tell us which key is pressed
        self.mw = MainWindow
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.C3 = QtWidgets.QPushButton(self.centralwidget)
        self.C3.setGeometry(QtCore.QRect(313, 260, 31, 161))
        self.C3.setStyleSheet("#C3{\n"
"background-color: rgb(242, 242, 242);\n"
"background-color: qlineargradient(spread:pad, x1:0.960545, y1:0.0962727, x2:0.795545, y2:0.119318, stop:0 rgba(0, 0, 0, 255), stop:1 rgba(250, 250, 250, 250));}\n"
"\n"
"\n"
"#C3:pressed{\n"
"background-color: rgb(250, 250, 250);\n"
"}\n"
"\n"
"")
       # ...
       # Here all the buttons are created, I skipped some of them in this example
       # ...
       
        self.D2.setText("")
        self.D2.setObjectName("D2")
        self.F2s = QtWidgets.QPushButton(self.centralwidget)
        self.F2s.setGeometry(QtCore.QRect(210, 260, 20, 101))
        self.F2s.setStyleSheet("#F2s{\n"
"background-color: rgb(0, 0, 0);\n"
"\n"
"background-color: qlineargradient(spread:pad, x1:0.784, y1:0.0175, x2:1, y2:0, stop:0 rgba(0, 0, 0, 255), stop:1 rgba(255, 255, 255, 255))\n"
"}\n"
"\n"
"#F2s:pressed{\n"
"background-color: rgb(0,0,0);\n"
"background-color: qlineargradient(spread:pad, x1:0.857955, y1:0.0170455, x2:1, y2:0, stop:0.125 rgba(0, 0, 0, 255), stop:0.977273 rgba(255, 255, 255, 255))\n"
"}")
        self.F2s.setText("")
        self.F2s.setObjectName("F2s")
        self.C7.raise_()
        self.B6.raise_()
        self.A6.raise_()
        self.G6.raise_()
        self.F6.raise_()
        self.E6.raise_()
        self.D6.raise_()
        self.C6.raise_()
        self.B5.raise_()
        self.A5.raise_()
        self.G5.raise_()
        self.F5.raise_()
        self.E5.raise_()
        self.D5.raise_()
        self.C5.raise_()
        self.B4.raise_()
        self.A4.raise_()
        self.G4.raise_()
        self.F4.raise_()
        self.E4.raise_()
        self.D4.raise_()
        self.C4.raise_()
        self.B3.raise_()
        self.A3.raise_()
        self.G3.raise_()
        self.F3.raise_()
        self.E3.raise_()
        self.D3.raise_()
        self.C3.raise_()
        self.B2.raise_()
        self.A2.raise_()
        self.G2.raise_()
        self.F2.raise_()
        self.E2.raise_()
        self.D2.raise_()
        self.C2.raise_()
        self.G3s.raise_()
        self.A3s.raise_()
        self.F4s.raise_()
        self.D4s.raise_()
        self.C4s.raise_()
        self.G4s.raise_()
        self.A4s.raise_()
        self.F3s.raise_()
        self.D3s.raise_()
        self.C3s.raise_()
        self.G6s.raise_()
        self.F5s.raise_()
        self.F6s.raise_()
        self.D6s.raise_()
        self.A6s.raise_()
        self.C6s.raise_()
        self.A5s.raise_()
        self.C5s.raise_()
        self.G5s.raise_()
        self.D5s.raise_()
        self.A2s.raise_()
        self.G2s.raise_()
        self.D2s.raise_()
        self.C2s.raise_()
        self.F2s.raise_()


        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

        # Here we make a dict for threads called th
        
        self.th = {}

        # Let's connect a key to the button
        # Let's say C3 is a button or key of piano

        
        # These are white keys
        self.C3.clicked.connect(self.run_threads)
        self.D3.clicked.connect(self.run_threads)
        self.E3.clicked.connect(self.run_threads)
        self.F3.clicked.connect(self.run_threads)
        self.G3.clicked.connect(self.run_threads)
        self.A3.clicked.connect(self.run_threads)
        self.B3.clicked.connect(self.run_threads)
        self.C4.clicked.connect(self.run_threads)
        self.D4.clicked.connect(self.run_threads)
        self.E4.clicked.connect(self.run_threads)
        self.F4.clicked.connect(self.run_threads)
        self.G4.clicked.connect(self.run_threads)
        self.A4.clicked.connect(self.run_threads)
        self.B4.clicked.connect(self.run_threads)
        self.C5.clicked.connect(self.run_threads)
       

        # These are the black keys
        self.C3s.clicked.connect(self.run_threads) 
        self.D3s.clicked.connect(self.run_threads) 
        self.F3s.clicked.connect(self.run_threads) 
        self.G3s.clicked.connect(self.run_threads) 
        self.A3s.clicked.connect(self.run_threads)
        self.C4s.clicked.connect(self.run_threads) 
        self.D4s.clicked.connect(self.run_threads) 
        self.F4s.clicked.connect(self.run_threads) 
        self.G4s.clicked.connect(self.run_threads) 
        self.A4s.clicked.connect(self.run_threads)

        # ok so lets run it.
        # To show how its working lets print the path of the sender
        # Similarly lets add all the keys.
        # Oh yes lets play and see if all keys are working.

    # Let's make function to play sound
    def play_notes(self, notePath):
        playsound.playsound(notePath, False)
        print(notePath)

    # Let's make another function to run the threads
    def run_threads(self):
        self.th[self.mw.sender().objectName()] = Thread(target = self.play_notes,
        args = ('Notes/'+'{}.wav'.format(self.mw.sender().objectName()),))
        
        # So here the th dict will have a key which is the name of piano key
        # The thread has a target function self.play_notes, and arguments is 
        # simply the path Notes/keyname.ogg
        # Then we start and join the thread

        self.th[self.mw.sender().objectName()].start()
        self.th[self.mw.sender().objectName()].join()

        # if midiInput.poll(): # Daca s-a declansat un input
        #         midi_events = midiInput.read(10)
        #         print(midi_events[0][0][1]) 


        stop = time.time()
        interval = stop - start
        global intervalAnt
        intervalAnt = interval

        #---------------------------------------------------

        # add some notes
        channel = 0
        volume = 100
        numeNota = self.mw.sender().objectName()
        if numeNota == 'C3s':
                pitchs = int(pitch.Pitch('C#3').ps)
        elif numeNota == 'D3s':
                pitchs = int(pitch.Pitch('D#3').ps)
        elif numeNota == 'F3s':
                pitchs = int(pitch.Pitch('F#3').ps)
        elif numeNota == 'G3s':
                pitchs = int(pitch.Pitch('G#3').ps)
        elif numeNota == 'A3s':
                pitchs = int(pitch.Pitch('A#3').ps)
        elif numeNota == 'C4s':
                pitchs = int(pitch.Pitch('C#4').ps)
        elif numeNota == 'D4s':
                pitchs = int(pitch.Pitch('D#4').ps)
        elif numeNota == 'F4s':
                pitchs = int(pitch.Pitch('F#4').ps)
        elif numeNota == 'G4s':
                pitchs = int(pitch.Pitch('G#4').ps)
        elif numeNota == 'A4s':
                pitchs = int(pitch.Pitch('A#4').ps)
        else:
                pitchs = int(pitch.Pitch(numeNota).ps)

        
        mf.addNote(track, channel, pitchs, interval, 2, volume)


    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.C3.setShortcut(_translate("MainWindow", "C"))
        self.D3.setShortcut(_translate("MainWindow", "V"))
        self.C3s.setShortcut(_translate("MainWindow", "F"))
        self.E3.setShortcut(_translate("MainWindow", "B"))
        self.A3.setShortcut(_translate("MainWindow", "Q"))
        self.G3.setShortcut(_translate("MainWindow", "M"))
        self.F3.setShortcut(_translate("MainWindow", "N"))
        self.B3.setShortcut(_translate("MainWindow", "W"))
        self.G3s.setShortcut(_translate("MainWindow", "K"))
        self.F3s.setShortcut(_translate("MainWindow", "J"))
        self.D3s.setShortcut(_translate("MainWindow", "G"))
        self.A3s.setShortcut(_translate("MainWindow", "2"))
        self.A4.setShortcut(_translate("MainWindow", "I"))
        self.F4s.setShortcut(_translate("MainWindow", "7"))
        self.A4s.setShortcut(_translate("MainWindow", "9"))
        self.E4.setShortcut(_translate("MainWindow", "T"))
        self.D4.setShortcut(_translate("MainWindow", "R"))
        self.D4s.setShortcut(_translate("MainWindow", "5"))
        self.B4.setShortcut(_translate("MainWindow", "O"))
        self.F4.setShortcut(_translate("MainWindow", "Y"))
        self.C4.setShortcut(_translate("MainWindow", "E"))
        self.G4.setShortcut(_translate("MainWindow", "U"))
        self.C4s.setShortcut(_translate("MainWindow", "4"))
        self.G4s.setShortcut(_translate("MainWindow", "8"))
        self.C5.setShortcut(_translate("MainWindow", "P"))
        self.A2.setShortcut(_translate("MainWindow", "Z"))
        self.A2s.setShortcut(_translate("MainWindow", "S"))
        self.B2.setShortcut(_translate("MainWindow", "X"))


if __name__ == "__main__":
        import sys
        app = QtWidgets.QApplication(sys.argv)
        MainWindow = QtWidgets.QMainWindow()
        ui = Ui_MainWindow()
        ui.setupUi(MainWindow)
        MainWindow.show()
        sys.exit(app.exec_())
Glooring
  • 21
  • 3
  • Despite the fact that doing it is almost impossible (and somehow pointless) in a reliable way, why do you want it to trigger the shortcut? If you connected the button to a function, then call that function in the midi input poll as long as the key is correct. – musicamante Apr 25 '21 at 19:01
  • @musicamante I want to make a virtual piano, and I already have the program finished but only works with the keyboard input. I just want to change keyboard keys shortcuts with the keys from piano. It would be easier for me if I could do that, I wouldn't have to change the code. – Glooring Apr 25 '21 at 19:14
  • You're trying to implement a GUI based interaction with a protocol designed to do something completely different, you *have* to alter the code in some way. Trying to interface midi events to keyboard shortcuts for this purpose is both pointless and unreliable, and trying to do so is also often complex (and impossible, under certain circumstances). Those buttons *are* already connected with a function (otherwise your program wouldn't work), so just call that function with the required arguments. – musicamante Apr 25 '21 at 19:30
  • @musicamante My button calls this function after is pressed: self.C3.clicked.connect(self.run_threads), but besides the mouse click and the use of the keyboard shortcut I would like to be able to press the button when an if is true. I could put in that if the condition: midi_events [0] [0] [1] = 48 – Glooring Apr 25 '21 at 19:55
  • Mh. Maybe you didn't explain it correctly, but the phrase "My button calls this function after is pressed: self.C3.clicked.connect(self.run_threads)" seems very wrong, for two reasons: the function called when C3 is clicked is `run_threads`, meaning that that function is called **every time** the button is clicked, and given the name of that function, this doesn't seem right at all. I suggest you to provide a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of your current code (please, read that link, it *has* to be both minimal *and* reproducible). – musicamante Apr 25 '21 at 23:45
  • @musicamante I added the reproductible example of my code. – Glooring Apr 26 '21 at 07:11
  • That's exactly what I was afraid of. Sorry, but your implementation is absolutely *terrible*. There are actually a lot of problems with the whole code, but the main issue is that you're *creating* a new thread everytime you play a note, which is clearly not good for such a case. Right now I cannot answer you, but in the meantime I suggest you to read [this partially related answer](https://stackoverflow.com/questions/66139144/would-it-be-possible-to-create-a-class-using-elements-from-pyqt5) and check the `block` argument explained in the playsound docs. – musicamante Apr 26 '21 at 10:40

0 Answers0