46 | This is a toy test program which shows how to talk to your browser. 47 |
48 |49 | It doesn't do much yet. That will change. 50 |
51 |52 | First, here's a simple form. 53 |
54 | 60 | """) 61 | 62 | f=TestForm() 63 | f.id="plugh" 64 | fw=TableWidget() 65 | await self.set_content("plugh", fw(f)) 66 | 67 | await self.alert("info","Ready!", busy=False, timeout=2) 68 | if set_token: 69 | await self.set_token("A0") 70 | 71 | async def put_button(self,set_token=True): 72 | await self.set_content("df_main", "Success. Next, here's a button.
") 73 | await self.set_token("A1") 74 | 75 | async def put_modal(self,set_token=True): 76 | await self.set_content("df_main", "We're showing a modal so you don't see this.
") 77 | await self.set_content("df_modal_title", "Random Title") 78 | await self.set_content("df_modal_body", "Random content
") 79 | await self.set_content("df_modal_footer", '') 80 | await self.modal_show(keyboard=False) 81 | if set_token: 82 | await self.set_token("A2") 83 | 84 | async def put_done(self,set_token=True): 85 | await self.set_content("df_main", "Aww … you pressed the button!
") 86 | if set_token: 87 | await self.set_token("A3") 88 | 89 | 90 | async def form_form1(self, **kw): 91 | logger.debug("GOT %r",kw) 92 | await self.put_button() 93 | 94 | async def button_bt_m(self, **kw): 95 | await self.modal_hide() 96 | await self.put_done() 97 | 98 | async def button_butt1(self, **kw): 99 | logger.debug("GOT %r",kw) 100 | await self.put_modal() 101 | 102 | 103 | 104 | async def main(): 105 | del CFG.logging.handlers.logfile 106 | CFG.logging.handlers.stderr["level"]="DEBUG" 107 | CFG.logging.root["level"]="DEBUG" 108 | CFG.server.host="0.0.0.0" 109 | CFG.server.port=50080 110 | 111 | logging_config(CFG.logging) 112 | app=App(CFG,Work, debug=True) 113 | await app.run() 114 | trio.run(main) 115 | 116 | # See "deframed.default.CFG" for defaults and whatnot 117 | -------------------------------------------------------------------------------- /example/minefield_app.py: -------------------------------------------------------------------------------- 1 | """ 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | """ 14 | 15 | import trio 16 | import os.path 17 | import random 18 | 19 | from quart.static import send_from_directory 20 | import deframed.remi.gui as gui 21 | from deframed.remi import RemiWorker 22 | from deframed import App,Worker 23 | from deframed.default import CFG 24 | 25 | import logging 26 | from logging.config import dictConfig as logging_config 27 | logger = logging.getLogger("minefield") 28 | 29 | class Cell(gui.TableItem): 30 | """ 31 | Represent a cell in the minefield map 32 | """ 33 | 34 | def __init__(self, width, height, x, y, game): 35 | super(Cell, self).__init__('') 36 | self.set_size(width, height) 37 | self.x = x 38 | self.y = y 39 | self.has_mine = False 40 | self.state = 0 # unknown - doubt - flag 41 | self.opened = False 42 | self.nearest_mine = 0 # number of mines adjacent with this cell 43 | self.game = game 44 | 45 | self.style['font-weight'] = 'bold' 46 | self.style['text-align'] = 'center' 47 | self.style['background-size'] = 'contain' 48 | if ((x + y) % 2) > 0: 49 | self.style['background-color'] = 'rgb(255,255,255)' 50 | else: 51 | self.style['background-color'] = 'rgb(245,245,240)' 52 | self.oncontextmenu.do(self.on_right_click, js_stop_propagation=True, js_prevent_default=True) 53 | self.onclick.do(self.check_mine) 54 | 55 | def on_right_click(self, widget): 56 | """ Here with right click the change of cell is changed """ 57 | if self.opened: 58 | return 59 | self.state = (self.state + 1) % 3 60 | self.set_icon() 61 | self.game.check_if_win() 62 | 63 | def check_mine(self, widget, notify_game=True): 64 | if self.state == 1: 65 | return 66 | if self.opened: 67 | return 68 | self.opened = True 69 | if self.has_mine and notify_game: 70 | self.game.explosion(self) 71 | self.set_icon() 72 | return 73 | if notify_game: 74 | self.game.no_mine(self) 75 | self.set_icon() 76 | 77 | def set_icon(self): 78 | self.style['background-image'] = "''" 79 | if self.opened: 80 | if self.has_mine: 81 | self.style['background-image'] = "url('/my_resources:mine.png')" 82 | else: 83 | if self.nearest_mine > 0: 84 | self.set_text(str(self.nearest_mine)) 85 | else: 86 | self.style['background-color'] = 'rgb(200,255,100)' 87 | return 88 | if self.state == 2: 89 | self.style['background-image'] = "url('/my_resources:doubt.png')" 90 | if self.state == 1: 91 | self.style['background-image'] = "url('/my_resources:flag.png')" 92 | 93 | def add_nearest_mine(self): 94 | self.nearest_mine += 1 95 | 96 | 97 | class Minefield(RemiWorker): 98 | title = "Minefield" 99 | 100 | _timer = None 101 | 102 | async def display_time(self): 103 | while True: 104 | await trio.sleep(1) 105 | self.lblTime.set_text('Play time: ' + str(self.time_count)) 106 | self.time_count += 1 107 | 108 | def main(self): 109 | # the arguments are width - height - layoutOrientationOrizontal 110 | self.main_container = gui.Container(margin='0px auto') 111 | self.main_container.set_size(1020, 600) 112 | self.main_container.set_layout_orientation(gui.Container.LAYOUT_VERTICAL) 113 | 114 | self.title = gui.Label('Mine Field GAME') 115 | self.title.set_size(1000, 30) 116 | self.title.style['margin'] = '10px' 117 | self.title.style['font-size'] = '25px' 118 | self.title.style['font-weight'] = 'bold' 119 | 120 | self.info = gui.Label('Minefield game. Enjoy.') 121 | self.info.set_size(400, 30) 122 | self.info.style['margin'] = '10px' 123 | self.info.style['font-size'] = '20px' 124 | 125 | self.lblMineCount = gui.Label('Mines') 126 | self.lblMineCount.set_size(100, 30) 127 | self.lblFlagCount = gui.Label('Flags') 128 | self.lblFlagCount.set_size(100, 30) 129 | 130 | self.time_count = 0 131 | self.lblTime = gui.Label('Time') 132 | self.lblTime.set_size(100, 30) 133 | 134 | self.btReset = gui.Button('Restart') 135 | self.btReset.set_size(100, 30) 136 | self.btReset.onclick.do(self.new_game) 137 | 138 | self.horizontal_container = gui.Container() 139 | self.horizontal_container.style['display'] = 'block' 140 | self.horizontal_container.style['overflow'] = 'auto' 141 | self.horizontal_container.set_layout_orientation(gui.Container.LAYOUT_HORIZONTAL) 142 | self.horizontal_container.style['margin'] = '10px' 143 | self.horizontal_container.append(self.info) 144 | imgMine = gui.Image('/my_resources:mine.png') 145 | imgMine.set_size(30, 30) 146 | self.horizontal_container.append([imgMine, self.lblMineCount]) 147 | imgFlag = gui.Image('/my_resources:flag.png') 148 | imgFlag.set_size(30, 30) 149 | self.horizontal_container.append([imgFlag, self.lblFlagCount, self.lblTime, self.btReset]) 150 | 151 | self.minecount = 0 # mine number in the map 152 | self.flagcount = 0 # flag placed by the players 153 | 154 | self.link = gui.Link("https://github.com/dddomodossola/remi", 155 | "This is an example of REMI gui library.") 156 | self.link.set_size(1000, 20) 157 | self.link.style['margin'] = '10px' 158 | 159 | self.main_container.append([self.title, self.horizontal_container, self.link]) 160 | 161 | self.new_game(self) 162 | 163 | # returning the root widget 164 | return self.main_container 165 | 166 | async def talk(self): 167 | if self._timer is not None: 168 | self._timer = self.spawn(self.display_time) 169 | await super().talk() 170 | 171 | def cancel(self): 172 | if self._timer is not None: 173 | self._timer.cancel() 174 | self._timer = None 175 | super(MyApp, self).cancel() 176 | 177 | def coord_in_map(self, x, y, w=None, h=None): 178 | w = len(self.mine_matrix[0]) if w is None else w 179 | h = len(self.mine_matrix) if h is None else h 180 | return not (x > w - 1 or y > h - 1 or x < 0 or y < 0) 181 | 182 | def new_game(self, widget): 183 | self.time_count = 0 184 | self.mine_table = gui.Table(margin='0px auto') # 900, 450 185 | self.mine_matrix = self.build_mine_matrix(8, 8, 5) 186 | self.mine_table.empty() 187 | 188 | for x in range(0, len(self.mine_matrix[0])): 189 | row = gui.TableRow() 190 | for y in range(0, len(self.mine_matrix)): 191 | row.append(self.mine_matrix[y][x]) 192 | self.mine_matrix[y][x].onclick.do(self.mine_matrix[y][x].check_mine) 193 | self.mine_table.append(row) 194 | 195 | # self.mine_table.append_from_list(self.mine_matrix, False) 196 | self.main_container.append(self.mine_table, key="mine_table") 197 | self.check_if_win() 198 | self.set_root_widget(self.main_container) 199 | 200 | def build_mine_matrix(self, w, h, minenum): 201 | """random fill cells with mines and increments nearest mines num in adiacent cells""" 202 | self.minecount = 0 203 | matrix = [[Cell(30, 30, x, y, self) for x in range(w)] for y in range(h)] 204 | for i in range(0, minenum): 205 | x = random.randint(0, w - 1) 206 | y = random.randint(0, h - 1) 207 | if matrix[y][x].has_mine: 208 | continue 209 | 210 | self.minecount += 1 211 | matrix[y][x].has_mine = True 212 | for coord in [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]]: 213 | _x, _y = coord 214 | if not self.coord_in_map(x + _x, y + _y, w, h): 215 | continue 216 | matrix[y + _y][x + _x].add_nearest_mine() 217 | return matrix 218 | 219 | def no_mine(self, cell): 220 | """opens nearest cells that are not near a mine""" 221 | if cell.nearest_mine > 0: 222 | return 223 | self.fill_void_cells(cell) 224 | 225 | def check_if_win(self): 226 | """Here are counted the flags. Is checked if the user win.""" 227 | self.flagcount = 0 228 | win = True 229 | for x in range(0, len(self.mine_matrix[0])): 230 | for y in range(0, len(self.mine_matrix)): 231 | if self.mine_matrix[y][x].state == 1: 232 | self.flagcount += 1 233 | if not self.mine_matrix[y][x].has_mine: 234 | win = False 235 | elif self.mine_matrix[y][x].has_mine: 236 | win = False 237 | self.lblMineCount.set_text("%s" % self.minecount) 238 | self.lblFlagCount.set_text("%s" % self.flagcount) 239 | if win: 240 | self.dialog = gui.GenericDialog(title='You Win!', message='Game done in %s seconds' % self.time_count) 241 | self.dialog.confirm_dialog.do(self.new_game) 242 | self.dialog.cancel_dialog.do(self.new_game) 243 | self.dialog.show(self) 244 | 245 | def fill_void_cells(self, cell): 246 | checked_cells = [cell, ] 247 | while len(checked_cells) > 0: 248 | for cell in checked_cells[:]: 249 | checked_cells.remove(cell) 250 | if (not self.mine_matrix[cell.y][cell.x].has_mine) and \ 251 | (self.mine_matrix[cell.y][cell.x].nearest_mine == 0): 252 | for coord in [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]]: 253 | _x, _y = coord 254 | if not self.coord_in_map(cell.x + _x, cell.y + _y): 255 | continue 256 | 257 | if not self.mine_matrix[cell.y + _y][cell.x + _x].opened: 258 | self.mine_matrix[cell.y + _y][cell.x + _x].check_mine(None, False) 259 | checked_cells.append(self.mine_matrix[cell.y + _y][cell.x + _x]) 260 | 261 | def explosion(self, cell): 262 | print("explosion") 263 | self.mine_table = gui.Table(margin='0px auto') 264 | self.main_container.append(self.mine_table, key="mine_table") 265 | for x in range(0, len(self.mine_matrix[0])): 266 | for y in range(0, len(self.mine_matrix)): 267 | self.mine_matrix[y][x].style['background-color'] = 'red' 268 | self.mine_matrix[y][x].check_mine(None, False) 269 | self.mine_table.empty() 270 | 271 | # self.mine_table.append_from_list(self.mine_matrix, False) 272 | for x in range(0, len(self.mine_matrix[0])): 273 | row = gui.TableRow() 274 | for y in range(0, len(self.mine_matrix)): 275 | row.append(self.mine_matrix[y][x]) 276 | self.mine_matrix[y][x].onclick.do(self.mine_matrix[y][x].check_mine) 277 | self.mine_table.append(row) 278 | 279 | class MyApp(Worker): 280 | # DeFramed stuff 281 | async def show_main(self, token): 282 | self.w = Minefield(self) 283 | await self.w.show(width=800,height=500, name=self.w.title) 284 | await self.busy(False) 285 | await super().show_main() 286 | 287 | 288 | async def main(): 289 | del CFG.logging.handlers.logfile 290 | CFG.logging.handlers.stderr["level"]="DEBUG" 291 | CFG.logging.root["level"]="DEBUG" 292 | CFG.server.host="0.0.0.0" 293 | CFG.server.port=50080 294 | 295 | logging_config(CFG.logging) 296 | app=App(CFG, MyApp, debug=True) 297 | 298 | resources = __file__.replace(".py","_res").replace("_app_res","_res") 299 | @app.route("/my_resources: