[PyQt5] 메인 윈도우와 다이얼로그 연동

메인 윈도우에서 대화상자를 열고, 대화상자에서 입력한 값을 메인 윈도우에 표시하고하는 경우에 대한 설명입니다. UI 라이브러리는 PyQt5를 사용했습니다. 먼저 메인 모듈에 대한 코드입니다. 참고로 이글은 PyQt5에 대한 최소한의 기초 내용을 파악하고 있는 개발자를 대상으로 합니다.

import sys
from MainWindow import MainWindow
from PyQt5.QtWidgets import *

if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = MainWindow()
    win.show()
    sys.exit(app.exec_())

위의 코드에서 메인 윈도우는 MainWindow.py 파일에 정의되어 있으며, 코드는 다음과 같습니다.

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from SubWindow import SubWindow

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle('Main Window')
        self.setGeometry(100, 100, 300, 200)

        layout = QVBoxLayout()
        layout.addStretch(1)

        label = QLabel("미지정")
        label.setAlignment(Qt.AlignCenter)
        font = label.font()
        font.setPointSize(30)
        label.setFont(font)
        self.label = label

        btn = QPushButton("값 얻어오기")
        btn.clicked.connect(self.onButtonClicked)

        layout.addWidget(label)
        layout.addWidget(btn)

        layout.addStretch(1)

        centralWidget = QWidget()
        centralWidget.setLayout(layout)
        self.setCentralWidget(centralWidget)

    def onButtonClicked(self):
        win = SubWindow()
        r = win.showModal()

        if r:
            text = win.edit.text()
            self.label.setText(text)

    def show(self):
        super().show()

위의 메인 윈도우는 아래와 같은 UI를 표시합니다.

“값 얻어오기” 버튼을 클릭하면 대화창을 표시되며, 표시된 대화창에서 텍스트를 입력하고 대화창의 “확인” 버튼을 클릭하면 대화창에서 입력한 텍스트값을 메인 윈도우의 라벨 위젯에 표시하게 됩니다. 대화창에 대한 코드 파일은 SubWindow.py이며 다음과 같습니다.

import sys
from PyQt5.QtWidgets import *

class SubWindow(QDialog):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle('Sub Window')
        self.setGeometry(100, 100, 200, 100)

        layout = QVBoxLayout()
        layout.addStretch(1)

        edit = QLineEdit()
        font = edit.font()
        font.setPointSize(20)
        edit.setFont(font)
        self.edit = edit

        subLayout = QHBoxLayout()
        
        btnOK = QPushButton("확인")
        btnOK.clicked.connect(self.onOKButtonClicked)

        btnCancel = QPushButton("취소")
        btnCancel.clicked.connect(self.onCancelButtonClicked)

        layout.addWidget(edit)
        
        subLayout.addWidget(btnOK)
        subLayout.addWidget(btnCancel)
        layout.addLayout(subLayout)

        layout.addStretch(1)

        self.setLayout(layout)

    def onOKButtonClicked(self):
        self.accept()

    def onCancelButtonClicked(self):
        self.reject()

    def showModal(self):
        return super().exec_()

아래는 메인 윈도우에서 위의 코드에 대한 대화창을 표시한 뒤 사용자가 “하이! PyQt5″텍스트를 입력한 화면입니다.

위의 화면에서 닫기 버튼을 클릭하면 창이 닫히고 메인 윈도우에 대화창에서 입력한 텍스트가 표시되는데, 아래와 같습니다.

OpenCV의 이미지에 한글 출력하기

사실 OpenCV의 이미지는 numpy의 배열입니다. 그런데 문제는 파이썬에서 OpenCV를 통해 텍스트를 출력할때 한글 출력이 쉽지 않습니다. 해서 한글 출력을 위해 PIL(Python Imaging Library)의 도움을 받을 수 있습니다. 아래의 예제가 바로 그것입니다.

import numpy as np
from PIL import ImageFont, ImageDraw, Image
import cv2

img = np.zeros((200,400,3),np.uint8)

b,g,r,a = 255,255,255,0
fontpath = "fonts/gulim.ttc"
font = ImageFont.truetype(fontpath, 20)
img_pil = Image.fromarray(img)
draw = ImageDraw.Draw(img_pil)
draw.text((60, 70),  "김형준ABC123#GISDeveloper", font=font, fill=(b,g,r,a))

img = np.array(img_pil)
cv2.putText(img,  "by Dip2K", (250,120), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (b,g,r), 1, cv2.LINE_AA)

cv2.imshow("res", img)
cv2.waitKey()
cv2.destroyAllWindows()

7-12번 코드가 PIL을 이용해 한글을 출력하는 코드이고, 14~15번 코드는 OpenCV의 텍스트 출력 코드입니다. 결과는 다음과 같습니다.

함수들에 대한 그래프 시각화

선형 함수에 대한 정의와 그래프 시각화는 다음 코드와 같다.

import numpy as np
import matplotlib.pylab as plt

def identity_func(x):
    return x

x = np.arange(-10, 10, 0.01)
plt.plot(x, identity_func(x), linestyle='-', label="identity")
plt.ylim(-10, 10)
plt.legend()
plt.show() 

결과는 다음과 같다.

기울기와 y절편을 고려한 선형 함수의 정의는 다음과 같다.

import numpy as np
import matplotlib.pylab as plt
  
def linear_func(x):
    return 2 * x + 1 
 
x = np.arange(-10, 10, 0.01)
plt.plot(x, linear_func(x), linestyle='-', label="linear_func")
plt.ylim(-10, 10)
plt.legend()
plt.show() 

결과는 다음과 같다.

계단함수에 대한 정의는 다음과 같다.

import numpy as np
import matplotlib.pylab as plt
 
def binarystep_func(x):
    return (x>=0)*1
 
x = np.arange(-10, 10, 0.01)
plt.plot(x, binarystep_func(x), linestyle='-', label="binarystep_func")
plt.ylim(-5, 5)
plt.legend()
plt.show() 

결과는 다음과 같다.

로지스틱(Logistic) 또는 시그모이드(Sigmoid)라고 불리는 함수 정의는 다음과 같다.

import numpy as np
import matplotlib.pylab as plt

def softstep_func(x):
    return 1 / (1 + np.exp(-x))

x = np.arange(-10, 10, 0.01)
plt.plot(x, softstep_func(x), linestyle='-', label="softstep_func")
plt.ylim(0, 1)
plt.legend()
plt.show()     

결과는 다음과 같다.

TanH 함수 정의 다음과 같다.

import numpy as np
import matplotlib.pylab as plt
 
def tanh_func(x):
    return np.tanh(x)
 
x = np.arange(-10, 10, 0.01)
plt.plot(x, tanh_func(x), linestyle='-', label="tanh_func")
plt.ylim(-1, 1)
plt.legend()
plt.show()     

그래프는 다음과 같다.

ArcTan 함수 정의는 다음과 같다.

import numpy as np
import matplotlib.pylab as plt

def arctan_func(x):
    return np.arctan(x)
 
x = np.arange(-10, 10, 0.01)
plt.plot(x, arctan_func(x), linestyle='-', label="arctan_func")
plt.ylim(-1.5, 1.5)
plt.legend()
plt.show()     

그래프는 다음과 같다.

Soft Sign 함수는 다음과 같다.

import numpy as np
import matplotlib.pylab as plt
 
def softsign_func(x):
    return x / ( 1+ np.abs(x) )
 
x = np.arange(-10, 10, 0.01)
plt.plot(x, softsign_func(x), linestyle='-', label="softsign_func")
plt.ylim(-1, 1)
plt.legend()
plt.show()     

그래프는 다음과 같다.

ReLU(Rectified Linear Unit) 함수는 다음과 같다.

import numpy as np
import matplotlib.pylab as plt
 
def relu_func(x):
    return (x>0)*x
 
x = np.arange(-10, 10, 0.01)
plt.plot(x, relu_func(x), linestyle='-', label="relu_func")
plt.ylim(-1, 11)
plt.legend()
plt.show()     

결과는 다음과 같다.

Leaky ReLU 함수는 다음과 같다.

import numpy as np
import matplotlib.pylab as plt
 
def leakyrelu_func(x, alpha=0.1):
    return (x>=0)*x + (x<0)*alpha*x
 
x = np.arange(-10, 10, 0.01)
plt.plot(x, leakyrelu_func(x), linestyle='-', label="leakyrelu_func")
plt.ylim(-2, 11)
plt.legend()
plt.show()   

결과는 다음과 같다.

ELU(Exponential Linear Unit) 함수는 다음과 같다.

def elu_func(x, alpha=0.9):
    return (x>=0)*x + (x<0)*alpha*(np.exp(x)-1)
 
x = np.arange(-10, 10, 0.01)
plt.plot(x, elu_func(x), linestyle='-', label="elu_func")
plt.ylim(-2, 11)
plt.legend()
plt.show()    

결과는 다음과 같다.

TreLU 함수는 다음과 같다.

import numpy as np
import matplotlib.pylab as plt
 
def trelu_func(x, thres=2):
    return (x>thres)*x
 
x = np.arange(-10, 10, 0.01)
plt.plot(x, trelu_func(x), linestyle='-', label="trelu_func")
plt.ylim(-2, 11)
plt.legend()
plt.show()     

결과는 다음과 같다.

SoftPlus 함수는 다음과 같다.

import numpy as np
import matplotlib.pylab as plt
 
def softplus_func(x):
    return np.log( 1 + np.exp(x) )
 
x = np.arange(-10, 10, 0.01)
plt.plot(x, softplus_func(x), linestyle='-', label="softplus_func")
plt.ylim(-1, 11)
plt.legend()
plt.show()     

결과는 다음과 같다.

Bent identity 함수는 다음과 같다.

import numpy as np
import matplotlib.pylab as plt
 
def bentidentity_func(x):
    return (np.sqrt(x*x+1)-1)/2+x

x = np.arange(-10, 10, 0.01)
plt.plot(x, bentidentity_func(x), linestyle='-', label="bentidentity_func")
plt.ylim(-6, 11)
plt.legend()
plt.show()

결과는 다음과 같다.

Gaussian 함수는 다음과 같다.

import numpy as np
import matplotlib.pylab as plt
 
def gaussian_func(x):
    return np.exp(-x*x)
 
x = np.arange(-10, 10, 0.01)
plt.plot(x, gaussian_func(x), linestyle='-', label="gaussian_func")
plt.ylim(-0.5, 1.5)
plt.legend()
plt.show()

결과는 다음과 같다.

pandas의 DataFrame에 대한 Inner Join, Outer Join, Left Join, Right Join

판다스에서 데이터프레임은 테이블 형식의 데이터셋입니다. DBMS의 Table들 간에도 Join을 맺을 수 있듯이, 마찬가지로 판다스의 데이터프레임들 간에도 Join을 맺을 수 있습니다. 물론 Join을 맺을 공통 필드가 존재한다면 말입니다.

Join에는 모두 4가지 방식이 존재합니다. 즉, 두 데이터셋 간의 중복된 요소만을 Join하는 Inner Join과 두 데이터셋에 대한 모든 데이터를 Join하는 Outter Join, 그리고 왼쪽 데이터셋을 기준으로 하는 Left Join과 오른쪽 데이터셋을 기준으로 하는 Right Join입니다. 보다 명확한 Join의 파악은 아래의 코드 예제를 통해 파악할 수 있습니다.

먼저 Join 하고자 하는 데이터셋으로, 판다스의 데이터프레임을 아래 코드처럼 정의합니다.

import pandas as pd

data_A = {'key': [1,2,3], 'name': ['Jane', 'John', 'Peter']}
dataframe_A = pd.DataFrame(data_A, columns = ['key', 'name'])

data_B = {'key': [2,3,4], 'age': [18, 15, 20]}
dataframe_B = pd.DataFrame(data_B, columns = ['key', 'age'])

print(dataframe_A)
print(dataframe_B)

결과는 아래와 같습니다.

   key   name
0    1   Jane
1    2   John
2    3  Peter
   key  age
0    2   18
1    3   15
2    4   20

두 데이터프레임 간에는 key라는 공통 필드가 존재하는 것을 볼 수 있습니다. 이를 토대로 먼저 Inner Join에 대한 코드입니다.

df_INNER_JOIN = pd.merge(dataframe_A, dataframe_B, left_on='key', right_on='key', how='inner')
print(df_INNER_JOIN)

위의 코드의 결과는 다음과 같습니다.

   key   name  age
0    2   John   18
1    3  Peter   15

다음은 Outer Join에 대한 코드입니다.

df_OUTER_JOIN = pd.merge(dataframe_A, dataframe_B, left_on='key', right_on='key', how='outer')
print(df_OUTER_JOIN)

결과는 다음과 같습니다.

   key   name   age
0    1   Jane   NaN
1    2   John  18.0
2    3  Peter  15.0
3    4    NaN  20.0

다음은 Left Join에 대한 코드입니다.

df_LEFT_JOIN = pd.merge(dataframe_A, dataframe_B, left_on='key', right_on='key', how='left')
print(df_LEFT_JOIN)

결과는 다음과 같습니다.

   key   name   age
0    1   Jane   NaN
1    2   John  18.0
2    3  Peter  15.0

다음은 Right Join에 대한 코드입니다.

df_RIGHT_JOIN = pd.merge(dataframe_A, dataframe_B, left_on='key', right_on='key', how='right')
print(df_RIGHT_JOIN)

다음은 실행 결과입니다.

   key   name  age
0    2   John   18
1    3  Peter   15
2    4    NaN   20

모든 Join은 pd.merge 함수를 통해 이루어지는데요. 위의 예제 코드를 보면 두 데이터프레임의 Join 필드가 모두 ‘key’라는 것을 알 수 있습니다. 이처럼 Join 필드의 이름이 동일할 경우 pd.merge의 left_on과 right_on 인자 대신 on 인자 하나로 대체가 가능합니다. 예를들어, Inner Join의 경우는 아래와 같습니다.

df_INNER_JOIN = pd.merge(dataframe_A, dataframe_B, on='key')

pd.merge 함수의 인자중 how도 생략되었는데, 이는 Inner Join이 pd.merge의 인자 how의 기본값이기 때문입니다.