전체 글 (755)
[vscode] html에서 여는 태그와 닫는 태그를 한 번에 수정하려면?, auto rename tag 확장 플러그인 2022.04.03 13:51:03
반응형

안녕하세요. 비스카이비전입니다. 

 

저는 코드 편집기 중에 visual studio code를 주로 사용하고 있습니다. 전세계 수많은 개발자들이 사용하고 있는 대표적인 코드 편집기죠. 보통 줄여서 vscode라고 부릅니다.

 

vscode에는 개발을 좀 더 편하게 해주는 다양한 확장 플러그인들이 있습니다. 오늘은 그 중에서 웹 개발에 유용한 확장 플러그인을 하나 소개해드리려고 합니다. 바로 Auto Rename Tag 입니다. html에서 여는 태그와 닫는 태그를 한 번에 수정할 수 있게 도와주는 아주 유용한 놈입니다. 

 

 

vscode의 유저인터페이스를 보면 좌측에 툴바가 있습니다. 거기서 네모 네 개로 이뤄진 아이콘을 클릭하시면 vscode의 확장 플러그인을 검색할 수 있습니다. 검색창에 Auto Rename Tag라고 입력하신 후에 나오는 첫번째 플러그인을 설치하시면 됩니다. 

 

자, 그럼 이제 한번 테스트해보겠습니다. 커서가 올라가 있는 div 태그의 여는 태그를 다른 태그로 수정해보겠습니다.

 

 

저는 지금 여는 태그만 수정했을 뿐인데, 닫는 태그도 동시에 자동으로 수정된 것을 확인할 수 있습니다.

 

Auto Rename Tag는 개발의 생산성을 한 층 높여줄 수 있는 아주 고마운 확장 플러그인입니다. vscode를 사용하고 계시지만 아직 사용해보지 않은 분들은 한 번 사용해보시길 추천드립니다. 

반응형
secret
[python+opencv] ip 카메라(cctv)로부터 영상 받기 2022.03.31 22:56:59
반응형

오늘은 python과 opencv를 이용해서 ip 카메라(cctv)로부터 영상 신호를 받는 방법에 대해 공유하고자 합니다.

 

우선 usb 카메라(흔히 우리가 부르는 웹캠)를 통해 영상 신호를 받을 때는 이런 코드가 사용됩니다. python+opencv를 사용해보신 분에게는 아주 익숙한 한 줄일 것입니다.

 

capture = cv2.VideoCapture(0)

 

 

그런데 만약 ip 카메라로부터 신호를 받을 때는 다음과 같은 코드를 사용해야 합니다. 필요한 url은 각자 상황에 따라 다를 것입니다. 사용하시는 ip 카메라의 메뉴얼 등의 문서를 참고하시면 됩니다. 192.168.0.33:554/h264major는 제 상황에 맞는 코드라는 것을 기억해주세요. 

 

capture = cv2.VideoCapture('rtsp://192.168.0.33:554/1/h264major')

 

보통 ip 카메라의 영상을 스트리밍할 때는 위와 같이 rtsp 프로토콜이 사용됩니다. rtsp는 real time streaming protocol의 약어로 그대로 번역하자면 실시간 스트리밍 프로토콜입니다. 프로토콜이라는 것은 통신규약이니까 rtsp는 간단히 실시간으로 동영상을 송수신할 때 지켜야할 규약이라고 생각하면 될 것 같습니다.  

 

만약 username과 password가 필요한 경우에는 아래와 같은 방식으로 코딩해주면 됩니다. 

 

capture = cv2.VideoCapture('rtsp://username:password@192.168.0.33:554/1/h264major')

 

usb 카메라만 연결해서 사용해보다가 ip 카메라도 연결해보니 재밌네요. 

 

 

(이 글은 2022-04-26에 마지막으로 수정되었습니다)

 

 

관련 글

[1] [python] 외장 웹캠을 사용할 때 cv2.VideoCapture(1)로 했는데 안되면?

 

참고자료

[1] https://stackoverflow.com/questions/49978705/access-ip-camera-in-python-opencv   

[2] https://ko.wikipedia.org/wiki/%EC%8B%A4%EC%8B%9C%EA%B0%84_%EC%8A%A4%ED%8A%B8%EB%A6%AC%EB%B0%8D_%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C

반응형
secret
[css] input 박스 또는 textarea 박스 클릭했을 때 테두리 강조되지 않게 하기(:focus 의사 클래스) 2022.03.20 18:19:43
반응형

html의 input 태그와 textarea 태그는 무언가를 입력할 때 자주 사용되는 태그들입니다. input 태그의 경우 아이디, 비밀번호와 같이 짧은 것을 입력할 때 많이 사용되고, textarea 태그는 문장을 입력할 때 주로 사용됩니다.

 

무언가를 입력하기 위해 input 박스와 textarea 박스를 클릭하면 기본적으로 검은색의 굵은 테두리가 생성됩니다. 

 

 

만약 이 테두리를 없애고 싶다면 :focus 의사 클래스를 사용하여야 합니다. :focus 의사 클래스란 입력 폼을 클릭된 상태를 의미합니다. 

 

textarea:focus {
    outline: none;
}

 

outline 속성을 none으로 해주면 다음 이미지와 같이 입력 폼을 클릭했을 때 더이상 굵은 테두리로 강조되지 않습니다.

 

 

 

반응형
secret
[flask+jinja2] 반올림하기, round 필터 2022.03.19 18:37:14
반응형

서버에서 130.456 실수를 담은 데이터 data를 전송했다고 가정하겠다. 넘어온 숫자를 반올림해서 보여주고 싶을 때는 round 필터를 사용하면 된다. 참고로 jinja2에서 필터란 데이터를 다른 형태로 바꿔주는 역할을 하는 것을 지칭한다. 

 

{{data|round}}

 

서버에서 넘어온 130.456은 이제 130.0으로 보여질 것이다. 정수로 반올림이 되었지만 뒤에 .0이 붙은 것이 불편한 사람들이 있다면, int 필터를 추가하여 정수로 보여지게 하면 된다.

 

{{data|round|int}}

 

130

 

만약 소수점 아래 두번째 자리까지 반올림하고 싶다면 round(2)를 사용하면 된다.

 

{{data|round(2)}}

 

130.46으로 표현될 것이다.

 

참고자료

[1] https://stackoverflow.com/questions/17957511/jinja2-round-filter-not-rounding

반응형
secret
[python+plotly] 그래프 x축, y축 값 범위 설정하기 2022.03.18 19:57:42
반응형

오늘은 plotly로 생성한 그래프의 x축, y축 값의 범위 설정하는 법에 대해 알아보자. 쉬운 이해를 위해 간단한 예제를 만들었다.

 

import plotly.express as px

(중략)

fig = px.scatter(df, x="width", y="height")
fig.update_xaxes(range=[0, 3])
fig.update_yaxes(range=[50, 200])

 

x축 값의 범위와 y축 값의 범위를 설정하기 위해서는 각각 update_xaxes 메소드와 update_yaxes 메소드를 활용하면 된다. 위 코드에서는 x축의 범위를 0에서 3, y축의 범위를 50에서 200으로 설정했다. 

 

plotly는 데이터 시각화에 있어서 굉장히 파워풀한 도구 중의 하나이니 아직 안 써보신 분들은 기회가 되실 때 한 번 써보시길 추천해드린다. 

 

관련 글

[1] [python+plotly] px.scatter 플롯에 추세선 넣기

반응형
secret
[python+plotly] px.scatter 플롯에 추세선 넣기 2022.03.17 19:29:02
반응형

plotly를 이용해서 산점도를 그릴 때 추세선(trendline)을 넣고 싶은 경우가 있다. 추세선을 넣어주면 데이터의 장기적인 증감 추세를 좀 더 한 눈에 볼 수 있기 때문이다. 추세선을 넣는 방법은 매우 간단하다. 

 

px.scatter(df, x="total_bill", y="tip", trendline="ols"

 

그런데 여기서 에러가 나는 분들이 있을 것이다. 아마도 statsmodels 모듈과 관련된 에러일 것이다. 이 경우에는 statsmodels를 설치해주면 된다.

 

pip install statsmodels

 

만약 추세선의 색상을 별도로 지정하고 싶을 때는 trendline_color_override를 설정해주면 된다.

 

px.scatter(df, x="total_bill", y="tip", trendline="ols", trendline_color_override="red"

 

 

 

참고자료

[1] https://towardsdatascience.com/scatter-plots-with-plotly-express-1b7f5579919b

반응형
secret
[pyqt5] 야구 중계에 스트라이크 존이 제공되지 않을 때 시청자를 위한 가이드 앱 2022.03.16 20:39:21
반응형

야구 중계를 보면 보통 시청자들에게 스트라이크 존에 대한 정보를 주기 위해 사각형 가이드가 함께 송출됩니다. 그런데 간혹 어떤 중계에서는 그러한 가이드가 없는 경우가 있습니다. 이런 경우에는 시청의 재미가 반갑됩니다.

 

저는 이런 경우를 위해서 pyqt를 이용하여 간단한 프로그램을 만들어봤습니다(예전에 심심풀이로 만들어봤는데, 이제야 공유해봅니다). 우선 설치 필요한 패키지는 pyqt5와 pyinstaller입니다.

 

pip install pyqt5

pip install pyinstaller

 

 

파이썬 소스코드는 다음과 같습니다.

 

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QSlider, QDialog, QPushButton, QHBoxLayout, QVBoxLayout
from PyQt5.QtGui import QPainter, QPen, QColor, QBrush, QIcon, QPixmap
from PyQt5.QtCore import QCoreApplication, Qt, QPoint
import icon

class MyApp(QWidget):

    def __init__(self):
        super().__init__()
        self.stzw = 200
        self.stzh = 200
        self.initUI()

    def changeStzW(self):
        self.stzw = self.slider1.value()
        print(self.stzw)
        self.resize(self.stzw, self.stzh)

    def changeStzH(self):
        self.stzh = self.slider2.value()
        print(self.stzh)
        self.resize(self.stzw, self.stzh)

    def initUI(self):
        self.slider1 = QSlider(Qt.Horizontal, self)
        self.slider1.setRange(100, 500)
        self.slider1.setValue(200)
        self.slider1.setSingleStep(2)

        self.slider2 = QSlider(Qt.Vertical, self)
        self.slider2.setRange(100, 500)
        self.slider2.setInvertedAppearance(True)
        self.slider2.setValue(200)
        self.slider2.setSingleStep(2)

        self.slider1.valueChanged.connect(self.changeStzW)
        self.slider2.valueChanged.connect(self.changeStzH)

        self.button = QPushButton('close', self)
        self.button.clicked.connect(QCoreApplication.instance().quit)
        self.button.move(30, 30)
        self.button.setStyleSheet("background-color: rgba(0, 0, 0, 0.1); color: white; font-weight: bold;")
        self.button.resize(60, 30)
        
        self.setWindowTitle('STZONE v1.0')
        self.setWindowIcon(QIcon(':/icon.png'))
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.setAttribute(Qt.WA_TranslucentBackground, True)
        self.move(300, 300)
        self.resize(self.stzw, self.stzh)
        self.show()

    def mousePressEvent(self, event):
        self.oldPos = event.globalPos()

    def mouseMoveEvent(self, event):
        delta = QPoint(event.globalPos() - self.oldPos)
        self.move(self.x() + delta.x(), self.y() + delta.y())
        self.oldPos = event.globalPos()

    def paintEvent(self, e):
        qp = QPainter()
        qp.begin(self)
        self.draw_rect(qp)
        qp.end()

    def draw_rect(self, qp):
        qp.setBrush(QColor(0, 0, 0, 0))
        qp.setPen(QPen(QColor(255, 0, 0), 5))
        qp.drawRect(int(self.width()/3), int(self.height()/3), int(self.width()/3), int(self.height()/3))

        qp.setPen(QPen(Qt.red, 2))
        qp.drawLine(int(self.width()/3), int(4*self.height()/9), int(2 * self.width()/3), int(4*self.height()/9))

        qp.setPen(QPen(Qt.red, 2))
        qp.drawLine(int(self.width()/3), int(5*self.height()/9), int(2 * self.width()/3), int(5*self.height()/9))

        qp.setPen(QPen(Qt.red, 2))
        qp.drawLine(int(4*self.width()/9), int(self.height()/3), int(4 * self.width()/9), int(2*self.height()/3))

        qp.setPen(QPen(Qt.red, 2))
        qp.drawLine(int(5*self.width()/9), int(self.height()/3), int(5 * self.width()/9), int(2*self.height()/3))


if __name__ == '__main__':
    app = QApplication(sys.argv)
    STZone = MyApp()
    sys.exit(app.exec_())

 

그리고 pyinstaller를 이용해서 빌드할 때 사용한 명령어는 다음과 같습니다. 

 

pyinstaller --onefile --noconsole --icon=icon.ico --add-data="icon.ico;." -n="STZone v1.0" stzone.py

 

릴리즈된 프로그램을 다운로드받고 싶으신 분들은 https://github.com/kyohoonsim/stzone에서 우측 Releases에서 STZone v1.0을 클릭하신 후 STZone.v1.0.exe를 클릭하시면 됩니다.

 

 

사용법은 아이콘을 더블클릭하여 프로그램을 실행한 후에 중계 화면 위에 올려놓고 적절하게 사이즈 조정하면 됩니다. 

 

 

간단한 프로그램이지만 심판이 제대로 스트라이크 판정을 하고 있는지 살펴볼 때 도움이 되더라고요. 이 프로그램이 마음에 드신 분들은 제 깃헙에 별 하나 남겨주세요^^

반응형
secret
[python] 파이썬스럽게 코딩하는 법, PEP8 정리 2022.03.15 21:05:28
반응형

필자가 처음으로 접했던 프로그래밍 언어는 C언어다. 윤성우의 열혈강의를 읽으면서 재밌게 코딩의 세계에 입문하게 되었던 기억이 난다. 그리고 석사 과정을 밟으면서는 matlab을 많이 사용했다. 해당 연구 분야의 시뮬레이션 코드들이 대부분 matlab으로 작성되어 있었기 때문이다. 그러나 머신러닝/딥러닝을 연구에 접목하면서부터는 matlab의 한계를 느끼기 시작했다. 대부분의 라이브러리가 유료였기 때문이다. 그래서 넘어오게 된 언어가 바로 파이썬이다. 파이썬에서는 머신러닝/딥러닝에 관한 많은 훌륭한 라이브러리가 무료다. 그리고 직장에 와서도 여전히 파이썬을 이용해서 웹 사이트와 응용 프로그램 등을 개발하고 있다. 파이썬이 필자에게 돈을 벌어다 주는 언어가 되었다.  

 

현재 필자는 프로그래밍 언어 중 파이썬 비교적 가장 많이 친하긴 했지만, 파이썬스럽게(pythonic 이란 표현을 쓰더군요) 코딩하고 있는 지는 아직 의문이 들 때가 많다. 내가 짠 코드는 파이썬 코딩을 하는 사람들이 쉽게 이해할 수 있는 코드인가? 누군가와 협업하기에 좋은 코드인가? 

 

필자와 같은 고민을 하고 있는 개발자 또는 개발자 지망생을 위한 문서가 있다. 

 

무려 파이썬 언어의 창시자 귀도 반 로섬이 작성한 PEP 8 - Style Guide for Python Code를 읽어보자. 파이썬 코딩을 위한 관습을 정리해놓은 문서다. 참고로 귀도 반 로섬은 1956년 생이고 네덜란드 출신의 프로그래머다. 환갑이 넘었지만 여전히 활발히 활동하고 있다.

 

자, 그러면 PEP 8의 내용을 함께 정리해보자. 필자 눈에 띄었던 부분을 위주로 정리했으니 좀 더 자세한 내용은 원본 문서를 참고하기 바란다.

 

1. 가장 중요한 것은 일관성이다. 일관성 있는 스타일로 코딩해야 한다는 것이다. 여기서는 이런 식으로, 저기서는 저런 식으로 하면 코드의 독자들의 가독성을 현저히 떨어뜨릴 수 있다. 

 

2. 들여쓰기 레벨마다 4개의 스페이스를 사용해라(4칸을 띄워라). 탭보다는 스페이스를 사용하는 것이 좋다.

 

 

리스트와 튜플 등에 대해서는 다음과 같은 방식으로 들여쓰기를 사용하라.

 

 

3. 한 줄은 최대 79개의 문자로 제한하라.

 

4. 이항 연산자 전에 줄을 바꿔라.

 

 

5. 클래스 정의 뒤에는 두 줄, 함수 정의 뒤에는 한 줄을 띄워라.

 

6. 임포트는 분리된 라인으로 이뤄져야 한다. 임포트는 항상 파일의 최상단에서 이뤄지게 하라. 와일드카드 임포트(from <모듈> import *)는 피해라.

 

 

7. 의미없는 공백(whitespace)을 만들지마라. 

 

 

하지만 가독성을 위해 공백을 사용해야 할 때도 있다. 

 

 

다른 것과 정렬시키기 위해 일부러 공백을 만들지마라.

 

 

8. 사칙연산 관련해서는 다음과 같이 공백을 활용하여 가독성을 높여라.

 

 

 

9. 여러 구문(statements)을 한 줄에 사용하는 것은 지양하라.

 

 

10. 주석은 코드가 바뀔 때마다 최신 상태에 맞게 업데이트해줘라. 모순된 주석은 없는 것보다 못하다. 가급적 영어로 주석을 달아라. 

 

11. 줄 주석(line comments)은 최소한으로, 아껴서 사용하라. 

 

12. 모든 모듈, 함수, 클래스, 메서드에 대해 독스트링(docstring)을 써라. 

 

참고로 독스트링이란 해당 모듈, 함수 등이 어떤 일을 수행하는지 설명하는 문자열이다. 아래 예제 코드에서 쌍따옴표 3개로 감싸진 부분이 독스트링이다. 

 

 

13. 함수명과 변수명은 소문자로 써라. 가독성을 높여야하는 경우 단어들을 언더바(_)로 연결해줘라(스네이크케이스를 사용하라).

 

참고로 스네이크케이스는 company_name, do_something과 같이 단어들을 언더바로 연결해주는 네이밍 방식을 말한다.

 

14. 클래스명은 캐멀케이스로 써라. 

 

참고로 캐멀케이스는 CompanyName, DoSomething과 같이 단어들의 첫글자를 대문자로 해서 이어주는 방식을 말한다. 

 

15. not ... is 연산자 보다는 is not 연산자를 써라.

 

 

16. 예외처리시에는 구체적인 예외를 언급하는 것이 좋다.

 

 

17. 객체 타입 비교시에는 직접적으로 비교하기보다는 isinstance()를 사용하라. 

 

 

18. 문자열, 리스트, 튜플 등의 시쿼스에 대해서는 빈 시퀀스는 false라는 사실을 활용하라.

 

 

19. 부울린(boolean) 값들을 True 또는 False와 비교하지 마라.

 

 

 

영어 문서다보니 필자가 잘못 이해하고 정리한 부분이 있을 수도 있으니, 그런 부분을 발견하신 분들께서 교정해주신다면 감사하겠다.

반응형
secret
[pyside6] QLabel에 이미지 넣을 때 종횡비(aspect ratio) 설정 2022.02.27 17:38:23
반응형

pyqt, pyside 앱에 이미지를 넣고 싶을 때는 QLabel 위젯을 활용할 수 있다. 이때 이미지를 넣을 때 이미지의 크기를 원래 크기와 다르게 해주려면 QPixmap 클래스의 scaled 메소드를 활용해야 한다. 이미지의 크기를 지정해줄 때는 이미지의 종횡비(aspect ratio)를 어떻게 설정할 것인가를 고려해야 한다. 종횡비는 이미지의 가로세로 비율이라고 생각하면 된다. 이미지의 경우 대체로 종횡비를 유지하는 것이 실제 비율을 해치지 않기 때문에 좋다. 

 

만약 원래 이미지의 종횡비를 무시하고 지정하는 크기에 맞출 것이면 aspectMode를 Qt.IgnoreAspectRatio로 해주면 된다. 종횡비를 유지할 것이면 Qt.KeepAspectRatio로 설정하면 된다. 하지만 아래 보는 것처럼 여백이 생긴다. 그리고 Qt.KeepAspectRatioByExpanding으로 설정하면 종횡비를 유지하기 위해서 우리가 설정한 사이즈를 넘어간다. 프로그램이 의도하는 바에 따라서 적당한 flag를 선택하면 될 것이다.

 

 

전체 코드는 다음과 같다. 이미지가 200 x 200 의 크기로 표출되기 원하는 상황에서 종횡비 모드를 다양하게 바꿔본 것이다. 

 

from PySide6.QtWidgets import QApplication, QMainWindow, QLabel, QPushButton, QHBoxLayout, QWidget
from PySide6.QtGui import QPixmap
from PySide6.QtCore import QSize, Qt

import sys


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

        pixmap = QPixmap("flower.jpg")
        pixmap1 = pixmap.scaled(QSize(200, 200), aspectMode=Qt.IgnoreAspectRatio)
        pixmap2 = pixmap.scaled(QSize(200, 200), aspectMode=Qt.KeepAspectRatio)
        pixmap3 = pixmap.scaled(QSize(200, 200), aspectMode=Qt.KeepAspectRatioByExpanding)

        widget1 = QLabel("IgnoreAspectRatio")
        widget2 = QLabel()
        widget2.setPixmap(pixmap1)
        widget2.setStyleSheet("border: 5px solid blue;")

        widget3 = QLabel("KeepAspectRatio")
        widget4 = QLabel()
        widget4.setPixmap(pixmap2)
        widget4.setStyleSheet("border: 5px solid blue;")

        widget5 = QLabel("KeepAspectRatioByExpanding")
        widget6 = QLabel()
        widget6.setPixmap(pixmap3)
        widget6.setStyleSheet("border: 5px solid blue;")

        layout = QHBoxLayout()
        layout.addWidget(widget1)
        layout.addWidget(widget2)
        layout.addWidget(widget3)
        layout.addWidget(widget4)
        layout.addWidget(widget5)
        layout.addWidget(widget6)

        container = QWidget()
        container.setLayout(layout)

        self.setCentralWidget(container)


app = QApplication(sys.argv)

window = MainWindow()
window.show()

app.exec()

 

참고자료

[1] https://doc.qt.io/qtforpython-5/PySide2/QtGui/QPixmap.html

반응형
secret
[pyside6] 위젯 내 글자 크기를 변경하고 싶을 때 2022.02.26 13:11:28
반응형

pyside 또는 pyqt에서 위젯이라는 것은 UI를 구성하는 하나하나의 요소를 뜻한다. 버튼, 라벨, 체크박스 등등이 모두 위젯이다. 

 

오늘은 이러한 위젯 내 글자 크기를 변경하는 방법에 대해 알아보자. 어렵지 않으니 바로 코드를 살펴보자.

 

from PySide6.QtWidgets import QApplication, QMainWindow, QLabel, QPushButton, QVBoxLayout, QWidget

import sys


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

        widget1 = QLabel("hello world")

        widget2 = QLabel("hello world")
        font = widget2.font()
        font.setPointSize(30)
        widget2.setFont(font)

        widget3 = QPushButton("버튼")
        font = widget3.font()
        font.setPointSize(50)
        widget3.setFont(font)

        layout = QVBoxLayout()
        layout.addWidget(widget1)
        layout.addWidget(widget2)
        layout.addWidget(widget3)

        container = QWidget()
        container.setLayout(layout)

        self.setCentralWidget(container)


app = QApplication(sys.argv)

window = MainWindow()
window.show()

app.exec()

 

 위 코드를 실행하면 다음과 같이 세 개의 위젯이 수직으로 정렬되어 있는 창이 하나 뜰 것이다.

 

 

첫번째 라벨 위젯보다 두번째 라벨 위젯의 폰트 크기가 큰 것을 확인할 수 있다. 또한 버튼 위젯에 있는 폰트 크기는 두번째 라벨 위젯보다 더 크다. 

 

위 전체 코드에서 폰트 사이즈를 변경해주는 코드는 바로 이 부분이다. 

 

widget2 = QLabel("hello world")
font = widget2.font()
font.setPointSize(30)
widget2.setFont(font)

 

현재 위젯에 설정되어 있는 폰트 객체를 가지고 와서 새롭게 폰트 사이즈를 지정해준다음에 그것을 해당 위젯에 적용해주는 형식이다. 

반응형
secret
-
+