質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.61%

PyQtのQScreenクラスのエラーが解決できません。

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 1,057
退会済みユーザー

退会済みユーザー

 前提・実現したいこと

PyQtでProcessingで書かれた迷路探索を自動で実行したい。
ここに質問の内容を詳しく書いてください。
Processingで作った迷路プログラムをchainerを使って学習するプログラムを作りたいと思っています。その際、GUI上の自動処理としてpyautoguiを用いています。

processing 3.4
python anaconda3-4.4.0
numpy 1.15.1
chainer 5.0.0(ニューラルネットワーク動かすためのもの)
pyautogui 0.9.38(キーボード操作、ディスプレイ内の画像検索するため)

 発生している問題・エラーメッセージ

エラーメッセージ

取得に0.41797sかかりました
1回目のゲーム開始
Traceback (most recent call last):
  File "dqn_shimura.py", line 525, in <module>
    main()
  File "dqn_shimura.py", line 130, in main
    state = get_state(window_id, left, top, learning_width, learning_height, interval_w, interval_h)
  File "dqn_shimura.py", line 244, in get_state
    pixmap = test(win_id, left, top, width, height)
  File "dqn_shimura.py", line 299, in __init__
    q=classmethod(QScreen.grabWindow())
TypeError: grabWindow(self, sip.voidptr, x: int = 0, y: int = 0, width: int = -1, height: int = -1): first argument of unbound method must have type 'QScreen'

 該当のソースコード

#-*- coding: utf-8 -*-
#ver0.41 2016/11/22
#python全体で使うような奴の追加
import argparse                    #起動時の引数設定
import os                        #ファイルの削除
import sys                        #シス
import copy                        #データコピーテム関連(ここではプログラムの強制終了とか)
import time                        #時間取得
import random                    #ランダム
import configparser                #iniファイルいじる
import threading                #マルチスレッド
import csv                        #csvファイルの扱い(出力用)
from collections import deque    #デック(deque)を扱えるようにする
from tqdm import tqdm            #プログレスバー
#ニューラルネットワークに関する追加
import numpy as np                #数値計算補助(CPU)
import cupy as cp                #数値計算補助(GPU)
import chainer                    #ディープネットワークのフレームワーク
import chainer.functions as F
import chainer.links as L
from chainer import cuda,Variable,optimizers#optimizerpipsを消した(自分)
from chainer import links as L
#画面操作に関する追加
import pyautogui as ag            #ディスプレイ内の画像検索、キーボード操作など
from PIL import Image            #RGB調べる
#from PyQt4.QtGui import QPixmap,QApplication
from PyQt5.QtWidgets import QApplication, QWidget, QLabel
from PyQt5.QtGui import *
from PyQt5.QtGui import QScreen
from PyQt5.QtWidgets import *


#引数設定
parser = argparse.ArgumentParser(description='Learning game')
#行動選択の間隔(ms)
parser.add_argument('--interval', default=1000, type=int,
                help='interval of capturing (ms)')
#ゲームを回す回数
parser.add_argument('--play_number','-p' , default=1000, type=int,
                help='play number of count')
#学習方法(初期設定DQN)(まだ使ってない)
#Q・・・Q-learning
#PS・・・Profit Sharing
parser.add_argument('--learning_type','-l' , default='Q', type=str,
                help='learning type')
args = parser.parse_args()

#起動時の時間を保存
start_time = time.time()
#サーチ間隔(ms)
interval = args.interval / 1000.0
#gpuがあればcupyなければnumpyを使う(cpu未確認)
xp = cuda.cupy if cuda.available else np
#ランダムシードの決定
np.random.seed(0)
#pyautoguiのコード実行後の待ち時間(s)
ag.PAUSE = 0
#画面の位置設定
#キャプチャするウィンドウの取得
app = QApplication(sys.argv)
window_id = app.desktop().winId()
#ゲーム画面(学習対象)の位置(左上からの)とサイズ
learning_left = 0
learning_top = 0
learning_width = 800
learning_height = 600
#学習画面の縮小度(xピクセルに一つの値を使用)
interval_w = 2
interval_h = 2


#メイン
def main():
    #ゲーム画面認識してあったら画面にフォーカス移動,なければ終了
    search = Thread_search('./image/start.png', 5)
    #スレッドスタート
    search.start()
    #ここでは他に動かすものがないので終わるまで待つ(この先も使うことはなかった)
    search.join()
    #値が返って来ていてディスプレイ内に認識したい画面が全部入っていたら
    if search.left != -1 and ag.onScreen(search.left + learning_left + learning_width, search.top + learning_top +learning_height):
        #画面内の適当な位置にカーソルを持って行きクリック
        ag.moveTo(search.left+48, search.top+16)
        ag.click()
        #画面外にカーソルを外しておく
        time.sleep(1)
        ag.press('t')
        time.sleep(1)
        #迷路の生成
        time.sleep(1)
        ag.press('r')
        time.sleep(1)
        #迷路の写真取る
        ag.press('m')
        time.sleep(1)
        ag.press('g')
        time.sleep(1)
        ag.press('m')
        ag.moveTo(search.left-48, search.top-16)
    #ダメならエラーはいて終了
    else :
        print('\033[31mError01:認識失敗 ディスプレイ内に画面がすべて入っていない場合があります\033[0m')#error01
        sys.exit()

    #学習したい画面左上の座標を保存しておく(状態取得に使う)
    left = search.left + learning_left
    top = search.top + learning_top
    #エージェント作成
    agent = Agent(learning_width, learning_height, interval_w, interval_h)
    #調整エージェント作成
    tmp_agent = Agent(learning_width, learning_height, interval_w, interval_h)
    #インターバル調整



    #ゲームを回す回数を決めておきその中かを確認
    for i in range(1,args.play_number+1) :
        #100回に1度インターバル調整(うまくいってない?)
        #if i % 100 == 1:
            #インターバル調整
        #    interval = interval_adjust(tmp_agent,window_id)
        #最後に保存したスコアとゲームの状態と行動価値の最大値を初期化
        game_state = 2
        print(('%d回目のゲーム開始'%i))
        #get_reward内でgame_state更新してるからこっちではそのまま調べる
        while game_state < 3 and agent.step < 3000:
            #今の時間をとっておく
            now_time = time.time()
            #現在の状態を取得
            state = get_state(window_id, left, top, learning_width, learning_height, interval_w, interval_h)
            action_state = set_state(agent, state, learning_width, learning_height, interval_w, interval_h)
            #取得した状態に応じて行動を選択
            action_choice, q = agent.get_action(action_state)
            fw = open('Q_1zikoku.csv','a')
            fw.write('%d回目,%f,%d\n'% (i, q, action_choice))
            fw.close()
            #選択された行動を実際に実行する
            set_action(action_choice)
            #報酬取得
            reward, game_state, ep_end, g = get_reward()
            time.sleep(0.2)
            #一時メモリに保存
            agent.stock_tmp(state, action_choice, reward, ep_end)
            #経験メモリに入れるだけの情報が集まったら
            if len(agent.tmp_memory) > agent.tmp_size:
                #経験をストックする
                agent.stock_experience(g)
            #ステップを1増やす
            agent.step += 1
            agent.reduce_epsilon()
            #最初の状態取得から次の取得の間の時間
            interval_time = time.time()-now_time
            #情報表示
            print(('%d回目\tstep%d\ttime:%01.4fs\taction:%d\tQ:%01.4f\tepsilon:%01.4f'% (i,agent.step, time.time()-now_time, action_choice, q, agent.epsilon)))
            agent.total_step += 1
        print(('%d回目のゲーム終了'%i))
        print(('ここまでのステップ数%d'%agent.total_step))
        f = open('keika_1zikoku.csv','a')
        f.write('%d,%01.4f\n'% (agent.step, agent.epsilon))
        f.close()

        #開始からの時間を表示
        time_tmp = time.time() - start_time
        print(('実行時間 : %02d:%02d:%02.2f' % (time_tmp//3600, time_tmp%3600//60, time_tmp%60)))
        #学習をゲーム終了時にする
        agent.train(i,state)

        agent.step = 0
        agent.goal += 1

        time.sleep(0.5)
        ag.press('i')
        time.sleep(0.5)


#-----------------↓関数とクラス↓--------------------

#特定画像がディスプレイ内に存在するか検索をするマルチスレッド用クラス
#マルチスレッドにして使うときは来なかったから意味はない
#pic・・・探したい画像パス
#timeout・・・タイムアウトまでの時間(秒)0のときタイムアウトを行わない
#スレッド実行後使う値・・・見つけた座標x,y。タイムアウトもしくは見つかるまで(-1,-1)
class Thread_search(threading.Thread):
    def __init__(self, pic, timeout):
        threading.Thread.__init__(self)
        self.pic = pic
        self.timeout = timeout
        #変数の初期化(特に必要なのはleft,top)
        self.left, self.top, self.width, self.height = [-1, -1, -1, -1]

    #startで実行される部分
    def run(self):
        #探し始めた時間を記録
        search_start_time = time.time()
        while True :
            #画像と同じ部分を探す(1箇所のみ)
            pos_search = ag.locateOnScreen(self.pic)
            #取得できた時
            if pos_search is not None:
                #取得した位置を変数に入れる
                self.left, self.top, self.width, self.height = pos_search
                print(('取得に%01.5fsかかりました'% (time.time()-search_start_time)))
                #座標をかえせる状態になったからbreak
                break
            #取得できなかった時
            else :
                #タイムアウトするかどうかの確認
                if (time.time()-search_start_time) > self.timeout and self.timeout != 0:
                    print(('%dsで取得できませんでした'% self.timeout))
                    print('タイムアウト')
                    break
                #タイムアウトの時間でないときwhileにもどる

#行動選択の数値を3種の動作に分けてからiniに保存
def set_action(action):
    #choice(0~2)をもとに3つの行動に分ける
    #0が前進、1が左に向く、2が右に向く

    if action == 0:
        ag.keyDown('w')
        time.sleep(0.1)
        ag.keyUp('w')

    elif action == 1:
        ag.keyDown('a')
        time.sleep(0.1)
        ag.keyUp('a')

    elif action == 2:
        ag.keyDown('d')
        time.sleep(0.1)
        ag.keyUp('d')


#状態state(画面のRGB情報)を入手
#win_id・・・みるウィンドウ
#left・・・学習する画面の左上のx座標
#top・・・学習する画面の左上のx座標
#width・・・学習する画面の横幅
#height・・・学習する画面の縦幅
#interval_w・・・横方向の情報入手間隔
#interval_h・・・縦方向の情報入手間隔
def get_state(win_id, left, top, width, height, interval_w, interval_h):
    pixmap = test(win_id, left, top, width, height)
    print(pixmap)
    #pixmapにウィンドウの情報を渡す
    pixmap = QScreen.grabWindow(win_id, left, top, width, height)
    #QImageへの変換
    image = pixmap.toImage()
    bits= image.bits()
    bits.setsize(image.byteCount())
    #数ピクセルに一つの割合でImage型RGBモードで保存しなおす
    screen = Image.fromarray(np.array(bits).reshape((height, width, 4))[::interval_w,::interval_h,2::-1])
    state = np.asarray(screen)

    ##いらない部分(画像保存できるか試してみる)
    #png2 = './piclog/' + str(agent.step) + '.png'
    #保存
    #screen.save(png2)

    #書き込み可能に
    state.flags.writeable = True
    #元画像の緑だけを取り出す
    state = state[:,:,1]
    #戻り値にstate(現在の状態)を返す    
    return state

#行動選択時に渡す入力を作成する
def set_state(agent, state, learning_width, learning_height, interval_w, interval_h):
    #返り値用のリスト作成
    tmp = []
    #まず持ってるだけ一時メモリの情報をもらう
    for i in range(min(agent.tmp_size-1, len(agent.tmp_memory))):
        tmp.append(agent.tmp_memory[i][0])
    #今の状態を追加
    tmp.append(state)
    #足りない分0行列を付け足す
    while len(tmp) < agent.tmp_size:
        tmp.insert(0, state)#np.zeros( [learning_width//interval_w, learning_height//interval_h] ))
    return tmp

#報酬の決定
#-1から1にクリッピングする
#報酬とゲームの状態と今のスコアを返す
def get_reward():
    goal = ag.locateOnScreen('./image/GOAL.png')
        #if goal == None and np.array_equal(stat,sd) == True :
        #    return -1,2
    if goal == None:
        return 0,2,0,goal
    else :
        return 1,3,1,goal

#インターバルの時間を調整する(インターバル以外の動作を行い時間を測る)


class test:
    def __init__(self, win_id, left, top, width, height):
        q=classmethod(QScreen.grabWindow(self,win_id, left, top, width, height))
        print(q)

注意:文字数制限のため、省略。

 試したこと

QpixmapがPyQt5に対応していない模様だったため、QScreenによる解決を図った。

 補足情報(FW/ツールのバージョンなど)

サイト利用が初めてなので、至らない点が多くあると思いますが足りないファイルなどありましたらあげさせていただきます。よろしくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

直接のエラーの原因は grabWindow の第一引数は Window id なのに、self を渡しているからではないでしょうか?

QScreen.grabWindow(self,win_id, left, top, width, height)

QPixmap QScreen::grabWindow(WId window, int x = 0, int y = 0, int width = -1, int height = -1)

QScreen::grabWindow

 追記

すいませんが、コードは長いので読んでいません。
QSCreen.grabWindow() の使い方ですが、指定した ID のスクリーンから指定した範囲の画像をキャプチャして返します。

QPixmap grabWindow(WId window, int x = 0, int y = 0, int width = -1, int height = -1)

渡している引数が正しければ、そのようなエラーはでないはずなので、引数を確認してみてください。

 サンプルコード

以下はボタンをクリックすると、スクリーンを grabWindow() で取得して表示するサンプルコードです。

import sys

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *


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

    def initUI(self):
        self.setGeometry(300, 300, 500, 500)
        self.setWindowTitle('Test')

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

        # ラベルを作成
        self.label = QLabel()
        self.label.setFixedSize(400, 400)
        # ボタンを作成
        self.button = QPushButton("capture")
        self.button.setFixedSize(400, 50)
        self.button.clicked.connect(self.display)
        # レイアウトを作成
        vbox = QVBoxLayout()
        vbox.addWidget(self.button)
        vbox.addWidget(self.label)
        centralWidget.setLayout(vbox)

        self.show()

    def display(self):
        print('capture screen')

        screen = QApplication.primaryScreen()
        screenRect = QApplication.desktop().screenGeometry()

        print("window id: ", QApplication.desktop().winId())
        pixmap = screen.grabWindow(
            QApplication.desktop().winId(),
            0, 0, screenRect.width(), screenRect.height())

        scaled = pixmap.scaled(
            self.label.width(), self.label.height(), Qt.KeepAspectRatio)
        self.label.setPixmap(scaled)


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

イメージ説明

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/10/30 18:21

    ご回答ありがとうございます。

    お指摘の通りなのですが、selfを渡す仕様でなくても同じエラーが出てしまいます。

    キャンセル

  • 2018/10/30 19:50 編集

    grabWindow() の引数は
    grabWindow(スクリーン番号, 左上のx座標, 左上のy座標, 幅, 高さ) です。
    型はすべて int です。回答に使い方のサンプルコードを追記しました。
    そのようになっているか確認してみてください。

    キャンセル

  • 2018/10/31 16:09

    ご回答ありがとうございます。
    返答遅れてしまいました。申し訳ありません。

    grabWindowの仕様などを調べて、引数の型なども確認しましたが解決には直結しませんでした。

    しかし、Qscreenの仕様に当てはめるために試行錯誤していく過程で、
    pixmap = Qscreen.grabWindowをpixmap = screen.grabWindowにしたところ、実行に成功し学習もできました。

    サンプルコードの追記までしていただいて、大変感謝しています。
    ご回答ありがとうございました。

    キャンセル

  • 2018/10/31 16:27 編集

    今気づきましたが、QSCreen.grabWindow() は static メソッドではないので、 QScreen オブジェクトの grabWindow() というメソッドを呼ばないとおかしいと思います。
    C++ のドキュメント http://doc.qt.io/qt-5/qscreen.html を見て、static メンバー関数になっていないものは、オブジェクトのインスタンスのメソッドとして呼ばないといけません。
    回答のコードも screen.grabWindow() となっています。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.61%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る